Correzione dello scritto di Interfacce Utente - Giugno 2003

Parte teorica

Esercizio 1

Vedere dispense capitolo "Introduzione alle interfacce utente".

Esercizio 2

Vedere dispense capitolo "Finestre, eventi e loro gestione".

Qui probabilmente era poco chiara sulle dispense una cosa. Abbiamo detto che ci sono due possibilita': o se ne incarica il server oppure il client.

E' questa la ragione per cui la strategia "client" e' la piu' usata. Invece, dalle vostre risposte sembra che sia piu' usata per qualche arcano motivo, nonostante sia piu' onerosa per il programmatore che deve scrivere la funzione di refresh.
Questo nella correzione non e' stato contato.

Esercizio 3

Vedere dispense capitolo "Introduzione alla Computer Graphics".

Esercizio 4

Per le trasformazioni di base in Java, vedere dispense capitolo "Grafica bi-dimensionale in Java".

Per il resto, vedere dispense capitolo "Introduzione alla Computer Graphics".
Non erano richieste le matrici.

Qui un errore comune e' stato citare la scalatura e la rotazione nel caso generale, mentre quelle di base sono la scalatura con punto fermo l'origine e la rotazione intorno all'origine.
Se cosi' non fosse, non si capirebbe il senso dell'ultima domanda (come si ottiene una rotazione attorno a un punto generico - se fosse disponibile come trasformazione di base la risposta sarebbe banale).

Altra cosa: parecchi hanno descritto le trasformazioni anche nel caso 3D. Java ha grafica solo 2D, quindi il 3D non si applica. Nella correzione questo non e' stato contato.

Parte pratica

Esercizio 1

Un modo e':

Il disegno della gerarchia lo ometto, ma andava fatto.

In questo modo il maggiore spazio in assoluto e' dedicato all'area di disegno (che si trova in CENTRO di entrambi i contenitori che lo contengono - risalendo la gerarchia di annidamento).
E nell'area a destra il maggiore spazio e' dedicato allo slider (perche' e' al CENTRO).

Nota: NON si poteva realizzare con BorderLayout e un unico livello di annidamento: si sarebbe ottenuto

+-----------+---+
|           |   |
|           |   |
|           |   |
|           |   |
+-----------+---+
|               |
+---------------+
invece che
+-----------+---+
|           |   |
|           |   |
|           |   |
|           |   |
+-----------+   |
|           |   |
+-----------+---+

Alcuni hanno usato dei GridLayout: hanno messo una griglia a 1 riga e 2 colonne nella finestra principale, contenente due pannelli; nel pannello a sinistra hanno messo una griglia a 1 colonna e 2 righe con l'area di disegno e il bottone; nel pannello a destra hanno messo una griglia a 1 colonna e 3 righe con etichetta, slider e altra etichetta.
Ma attenzione: tutti gli elementi di una griglia hanno stesse dimensioni!
In questo modo vincoliamo l'area grafica ad avere le stesse dimensioni del bottone (mentre ragionevolmente e' meglio averla piu' grande) e vincoliamo lo slider ad essere grande come ciascuna delle due etichette (mentre ragionevolmente e' meglio averlo piu' grande). Inoltre la parte dedicata ad area grafica + bottone risulta grande uguale alla parte dedicata a slider + etichette (mentre ragionevolmente e' meglio avere la prima piu' grande della seconda).

Esercizi 2 e 3

Un errore di fondo che hanno fatto in molti e' quello di confondere la parte di ridisegno con la parte di callback:

Mischiare le due cose e' un errore di impostazione piuttosto grave.

Esercizio 2

Esempio di descrizione in linguaggio naturale (si poteva usare uno stile piu' formale):

  1. pulisce l'area grafica
  2. se isClosed==true, allora
  3. imposta colore nero
  4. per i da 0 a num-2, disegna il segmento (x[i],y[i])-(x[i+1],y[i+1])
  5. se isClosed==true, allora disegna il segmento (x[num-1],y[num-1])-(x[0],y[0])

Per chi ha scritto in linguaggio naturale: vanno separate le istruzioni di assegnazione del colore e di disegno: le prime agiscono sullo stato corrente del sistema grafico e devono essere eseguite prima di disegnare la primitiva che si vuole abbia quel colore. Non va bene scrivere: "disegna il segmento... in nero".

Nota: alcuni hanno assunto che la funzione di ridisegno non inizi pulendo l'area, ma solo aggiunga nuove parti (nuovi segmenti o l'area piena del poligono) a quelle disegnate la volta precedente (e la pulizia sia fatto solo quando si svuota il poligono).
Questa NON e' un'assunzione legittima perche' la funzione di ridisegno tipicamnente viene chiamata in situazioni (finestra appena deiconifica, scoperta o redimensionata) in cui il disegno della volta precedente non e' disponibile o non e' piu' valido. Queste sono le situazioni "normali" in cui si effettua il ridisegno. Il fatto che si ridisegni anche dopo alcune azioni utente avvenute nell'interfaccia e' una caratteristica di questa particolare applicazione.

Esercizio 3

Sono necessarie tre callback:

La callback associata al pannello di disegno ha come input le coordinate del punto cliccato dal mouse (le arrivano tramite l'evento), e compie le seguenti operazioni:

  1. se isClosed==true, allora imposta num=0 e isClosed=false (cioe' praticamente svuota il poligono)
  2. mette in x[num],y[num] le coordinate del punto cliccato e pone num=num+1 (cioe' inserisce il nuovo punto)
  3. se num>=3 abilita il bottone, altrimenti lo disabilita
  4. chiama la funzione di disegno

La callback associata al bottone compie le seguenti operazioni:

  1. pone isClosed=true
  2. disabilita il bottone stesso
  3. chiama la funzione di disegno

La callback associata allo slider compie le seguenti operazioni:

  1. pone in saturation il valore attuale dello slider
  2. se isClosed==true, allora chiama la funzione di disegno (nota: potrei anche ridisegnare in ogni caso, ma la saturazione e' visibile solo se il poligono e' chiuso)

Nota: abbiamo gestito solo abilitazione/disabilitazione del bottone (che si assume disabilitato all'inizio). Invece abbiamo lasciato lo slider abilitato sempre: vorra' dire che le modifiche alla saturazione, comunque eseguite, si vedranno solo quando il poligono sara' chiuso. Si poteva anche scegliere di disabilitare lo slider quando il poligono non e' chiuso.