[Libro.gif ( KB)]

Rappresentazione di funzioni in due variabili: z=f(x,y).

Introduzione

Prima di affrontare il problema della rappresentazione di funzioni in due variabili è necessario definire alcuni termini (e concetti) tipici della Grafica Tridimensionale (il cui obiettivo è quello di rappresentare un oggetto del mondo reale su uno schermo piano).

Per poter proiettare un oggetto tridimensionale su uno schermo piano bisogna definire alcuni elementi della Geometria Proiettiva che sono: il centro di prospettiva (o occhio), il piano di vista (o schermo nello spazio) e lo schermo (o monitor) sul quale rappresentare l'oggetto in prospettiva.


Figura 1: L'oggetto, il piano di vista ed il centro di prospettiva sono elementi del mondo reale.

Chiameremo Coordinate spaziali le coordinate del mondo reale ed indicheremo l'intero processo di proiezione (di un oggetto tridimensionale su uno schermo piano, come rappresentato in figura 1) col termine di "operazione completa di vista".

Il centro di prospettiva è il punto dal quale si osserva l'oggetto nel mondo reale e viene definito specificandone le sue coordinate spaziali (tipicamente sferiche).

Il piano di vista è il piano sul quale si proietta l'oggetto nello spazio e può essere definito in diversi modi: si può ad esempio specificarne un suo punto (detto punto di riferimento del piano) ed il vettore ad esso normale (la normale al piano) oppure definire un centro dell'oggetto (che non corrisponde necessariamente al baricentro di questo) e prendere come piano di vista quel piano ortogonale alla linea che congiunge il centro di prospettiva col centro dell'oggetto e distante d unità dallo stesso; la linea che congiunge questi due punti prende il nome di linea principale di vista.

L'ultimo modo di definire questo piano è tramite le sue coordinate omogenee.

Il metodo da adottare dipende molto dal tipo di problema che si deve affrontare; ad esempio, la definizione del piano tramite distanza dal centro dell'oggetto è utile qualora si desideri muovere l'intero meccanismo di vista (come nel nostro caso).

 


Figura 2:Piano di Vista

La Figura1 mette in evidenza la necessità di proiettare l'oggetto prima sul piano di vista e poi sullo schermo piano. L'operazione completa di vista è infatti definita come la composizione di tre operazioni: l'operazione che proietta l'oggetto sul piano di vista (detta appunto proiezione in prospettiva), l'operazione di parametrizzazione inversa e l'operazione di trasformazione di visualizzazione (OSS: la composizione delle prime due operazioni si chiama anche trasformazione di vista).

Assumiamo che la trasformazione di visualizzazione T trasformi i punti

A=(-h,-k), B=(h,-k); C=(h,k); D=(-h,k);

nei punti

A'=(u0,v0); B'=(u1,v0); C'=(u1,v1); D'=(u0,v0);

rispettivamente (come illustrato di seguito).

 


Figura 3

Allora la parametrizzazione del piano di vista è determinata specificando i seguenti oggetti geometrici: il punto di riferimento del piano di vista, il vettore direzione (verso l'alto) e una finestra definita su questo piano (vedi Figura 4).

Il punto di riferimento del piano ed il vettore direzione vengono scelti in modo da definire un sistema di riferimento (s.d.r) su tale piano; il punto di riferimento viene preso come origine di questo sistema (di coordinate) mentre il vettore direzione viene preso in modo che non risulti ortogonale al piano medesimo poiché la sua proiezione su questo servirà a definire l'orientamento positivo dell'asse verticale; la direzione positiva dell'asse orizzontale (sempre di questo sistema di riferimento) viene scelta in modo tale che il sisitema risulti destrorso ed ortonormale (visto dall'occhio).

 


Figura 4: Le coordinate dei vertici della finestra sono riferite al S.d.R. appena costruito

Si osservi che la distanza tra due punti misurata su tale sistema di riferimento è la stessa di quella misurata nello spazio (sempre tra questi due punti).

La finestra del piano di vista serve infine a visualizzare solo una parte del piano, l'operazione di parametrizzazione è allora definita imponendo che i punti A, B, C, D, siano trasformati nei punti A', B', C', D'.

 

Descrizione analitica della Trasformazione

Esistono diversi modi con cui si può implementare l'operazione completa di vista a seconda del tipo di problema che si deve affrontare.

Nella trattazione che segue mi limiterò a descrivere solo quella specifica al caso in questione (per una trattazione più esaustiva si rimanda al testo di Penna-Patterson pag.226); supporremo che le coordinate degli elementi siano tutte cartesiane (e non omogenee che sono meno famigliari) e che la trasformazione T operi come descritto nella sezione precedente.

Ipotesi di lavoro:

E = (r, q, f);

O = (0, 0, 0);

Sotto queste ipotesi gli elementi geometrici necessari ad individuare l'operazione completa di vista sono i seguenti:

  1. Il centro di prospettiva E (di coordinate sferiche r, q, f)
  2. Il centro dell'oggetto O (coincidente con l'origine del S.d.R. spaziale)
  3. Il piano di vista (ortogonale alla linea principale di vista e distante d unità dal centro dell'oggetto)
  4. Il vettore direzione del piano di vista
  5. v= <0, 0, 1>;

  6. E la finestra di vertici

A=(-1, 1), B=(1, -1); C=(1, 1); D=(-1,1);


Figura 5

Definendo in questo modo sia il centro di prospettiva che il piano di vista è facile implementare un meccanismo che simuli il movimento dell'intero processo di vista (poiché, come vedremo, la matrice associata alla trasformazione finale risulterà dipendente da questi parametri e quindi, cambiandone i valori, cambierà anche la matrice di proiezione).

Abbiamo già detto che l'operazione completa di vista si ottiene come composizione di tre trasformazioni più semplici: proiezione in prospettiva, parametrizzazione inversa e trasformazione di visualizzazione. Usando le coordinate omogenee ognuna di queste operazioni è rappresentabile da una opportuna matrice: la proiezione è specificata da una matrice 4 x 4 di rango 3 (che chiameremo M1), la parametrizzazione inversa da una matrice 4 x 3 anch'essa di rango 3 (la chiameremo M2) e la trasformazione di visualizzazione è rappresentata da una matrice 3 x 3 di rango 3 ( che chiameremo M4); l'operazione completa di vista è allora ottenuta dalla loro moltiplicazione:

M = M1 * M2 * M4;

ed è quindi rappresentata da una matrice 4 x 3 di rango 3.

Per calcolare la matrice M1 si procede, in generale, come segue. Supposto di conoscere il punto di riferimento del piano di vista

P = (x0, y0 ,z0);

e la normale al piano

n = < a ,b ,c >;

l'equazione del piano di vista è allora determinata da:

a(x-x0)+b(y-y0)+c(z-z0)=0;

da cui le coordinate omogenee del piano saranno:

[ m 1, m2 , m3 , m4] = [a , b , c , -(a x0 + b y0 +c z0) ];

e poiché le coordinate omogenee del centro di prospettiva sono [e1 ,e2 , e3, 1] allora la matrice M1 sarà rappresentata da:

m2e2 + m3e3

- m1e2

- m1e3

-m1

- m2e1

m1e1 + m3e3 + m4

- m2e3

-m2

- m3e1

- m3e2

m1e1 + m2e2 + m4

-m3

- m4e1

- m4e2

- m4e3

m1e1+m2e2+m3e3

Dal momento poi che la trasformazione di visualizzazione è definita sul rettangolo di vertici A(-h,-k) ,B(h,-k), C(h,k), D(-h,k), la matrice M4 sarà del tipo:

(u1 - u0) / 2h

0

0

0

(v1 - v0) / 2k

0

(u1 +u0) / 2

(v1 +v0) / 2

1

Per il calcolo della matrice M2 (che rappresenta l'operazione di parametrizzazione inversa) è necessario esaminare il sistema di coordinate definito sul piano di vista (vedi Figura 6).

Il vettore w1 definito come:

w1 = <w11, w12, w13> = (+/-) (v X n) / ( |v x n| );

(a patto di scegliere opportunamente il segno (+/-) ) è un versore (i.e. un vettore unitario) la cui direzione individua l'orientamento positivo dell'asse orizzontale; il vettore w2 così definito

w2 = <w21, w22, w23> = ( n X w1) / ( |n X w1| = ( n X w1 ) / |n|

è invece il versore la cui direzione individua l'orientamento positivo dell'asse verticale. Dalle definizioni date segue che le coordinate cartesiane del vertice C sono le componenti del vettore posizione

OP + h w1 + k w2

da cui quelle omogenee saranno:

C = [c1, c2, c3, 1]


Figura 6

La matrice M2 è allora calcolata come matrice inversa (destra) della matrice Mm dove quest'ultima rappresenta la parametrizzazione Tm : RP2 à m così definita:

Tm([1,0,0])=[<1,0,0>Mm]=[w11, w12, w13, 0]

Tm([0,1,0])=[<0,1,0>Mm]=[w21, w22, w23, 0]

Tm([0,0,1])=[<0,0,1>Mm]=[x0, y0, z0, 1]

Tm([1,1,1])=[<h,k,1>Mm]=[c1, c2, c3, 1]

Si può dimostrare che è sempre possibile trovare delle costanti reali non nulle (k1, k2, k3) tali per cui Mm sia della forma:

K1 w11

K1 w12

K1 w13

0

K2 w21

K2 w22

K2 w23

0

K3 x0

K3 y0

K3 z0

K3

E tale per cui valga...

k1<w11,w12,w13,0>+k2<w21,w22,w23,0>+k3<x0,y0,z0,1>=<c1,c2,c3,1>

Dal momento che le coordinate cartesiane del vertice C sono le componenti del vettore posizione precedentemente definito si deduce che

K1=1; K2=1; K3=1;

da cui la matrice Mm:

w11

w12

w13

0

w21

w22

w23

0

x0

y0

z0

1

 

Nel mio specifico caso, dalle coordinate polari del centro di prospettiva devo passare a quelle cartesiane (per quanto poc'anzi detto):

E = ( r sin(f) cos(q) , r sin(f) sin(q) , r cos(f) )

e da queste a quelle omogenee E=[e1,e2,e3,1]:

E = [r sin(f) cos(q), r sin(f) sin(q), r cos(f) , 1]

Per determinare l'equazione del piano di vista (che chiameremo m ) osserviamo che il vettore v (vettore unitario)

v = ( sin(f) cos(q) , sin(f) sin(q) , cos(f) )

è normale al piano m; poiché poi il suddetto piano dista d unità dall'origine, allora la sua equazione sarà

sin(f) cos(q) x + sin(f) sin(q) y + cos(f) z = d;

da cui le coordinate omogenee di m saranno:

[ sin(f) cos(q) , sin(f) sin(q) , cos(f) , -d ]

conseguentemente la matrice M1 è rappresentata da (vedi Penna-Patterson pag.238)

La matrice M4 è la stessa del caso generale mentre la matrice Mm è data da..

-sin(q)

cos(q)

0

0

-cos(f)cos(q)

-cos(f)sin(q)

sin(f)

0

dsin(f)cos(q)

d sin(f) sin(q)

d cos(f)

1

Da cui la matrice M2

-sin(q)

cos(f)cos(q)

(dsin(f)cos(q))/(d2+1

cos(q)

-cos(f)sin(q)

(dsin(q)sin(q))/(d2+1

0

sin(f)

d cos(f)/(d2+1)

0

0

1/( d2+1)

La matrice M (associata all'operazione completa di vista) è allora ottenuta come prodotto delle seguenti due matrici (M12 e M4) :

(d - r)sin(q)

(d-r)cos(f)cos( q)

Sin(f) cos(q)

(r - d)cos(q)

(d-r)cos(f) sin(q)

Sin(f) sin(q)

0

(r - d)sin(f)

-cos(f)

0

0

 

(u1-u0)/2

0

0

0

(v1-v0)/2

0

(u1+u0)/2

(v1+v0)/2

1

OSS: Poiché M dipende dalle coordinate sferiche del centro di prospettiva, modificando il valore di queste sarà possibile modificare l'intero meccanismo di vista.

Funzioni in due variabili

Le funzioni in due variabili sono fogli semplici del tipo:

x = u;

y = v;

z = f(u, v)

L'algoritmo per la rappresentazione delle funzioni in due variabili opera (solo) sulla porzione di piano..


Ossia..

-L < x < L, -W < y < W

Dove sia L che W sono numeri reali positivi (nel nostro caso fissati dall'utente visto che l'applet consente di modificarne i valori tramite opportune barre di scorrimento (per maggiori dettagli si rimanda al manuale in linea sviluppato).

Assumiamo dunque che gli elementi relativi all'operazione completa di vista siano i seguenti:


Figura 7

Il programma inizia allora col definire le coordinate del centro di prospettiva (rho, theta, phi, sferiche per quanto detto) assumendo come centro dell'oggetto l'origine degli assi cartesiani (spaziali) ed impostando la distanza del piano di vista ad un valore iniziale; come direzione del vettore v assume la terna <0, 0, 1> e le dimensioni della finestra nello spazio le considera per H=K=1 impostando di conseguenza gli estremi dello schermo (quello piano che nel nostro caso coincidono con gli estremi dell'area grafica dell'applicazione); di tutte queste è possibile modificare sia le coordinate dell'occhio che la distanza del piano di vista dal cento dell'oggetto tramite interfaccia utente. Si procede poi impostando le matrici M12 ed M4 in base ai valori appena rilevati e si calcola la matrice M come prodotto di queste (tramite questa proietteremo gli elementi spaziali sul piano del nostro schermo).

Le coordinate sferiche del centro di prospettiva (che permettono di modificare l'intero meccanismo di vista, come detto nella sezione precedente dove si era calcolata M in funzione di queste) operano nel modo seguente: variando rho varia la distanza dell'occhio dall'oggetto, variando Theta si ruota il centro di prospettiva attorno l'asse z e variando Phi si modifica l'elevazione dell'occhio dal piano di coordinate x, y.

Data una funzione continua a valori reali z= f(x, y) chiameremo linee coordinate x le curve così definite:

{ [ x, y0, f(x,y0) ] | -L < x < L }

fissato un valore per y0 e linee coordinate y le curve..

{ [ x0, y, f(x0,y) ] | -W < y < W }

per un fissato x0.


Figura 8

Il grafico della funzione sarà allora rappresentato disegnando le linee coordinate x ed y contemporaneamente; per il momento illustriamo il meccanismo relativo alla rappresentazione delle sole linee coordinate y:


1.	FOR X:= -L TO L STEP DeltaX DO
2. BEGIN
3. FirstPointOnCurve:=TRUE;
4. FOR Y:=-W TO W STEP DeltaY DO
5. BEGIN
6. Z:=f(X,Y);
7. Converti(X,Y,Z)TO(NewU,NewV)
8. IF (FirstPointOnCurve) THEN
9. (OldU,oldV):=(NewU,NewV);
10. DrawLine(OldU,OldV)TO(NewU,NewV);
11. (OldU,OldV):=(Newu,NewV);
12. FirstPointOnCurve:=FALSE;
13. END
14. END.

Entrambi gli assi cartesiani vengono discretizzati per mezzo delle variabili DeltaX e DeltaY (modificabili a livello di programma come spiegato da manuale ); fissato, un valore per x (linea 1), si varia y lungo il suo dominio di definizione (linea 4) calcolandosi ogni volta il valore di z (linea 6); la procedura Converti() proietta il punto dello spazio (appena calcolato) in un punto dello schermo a due dimensioni. Per ogni coppia di punti così determinata viene tracciato il segmento avente per estremi proprio questi punti (linea 10) e che, insieme agli altri, formeranno la linea coordinata y.

La variabili FirstPointOnCurve (linea 3) serve a stabilire se il punto proiettato è il primo della curva poiché in tal caso bisognerà disegnare solo un punto anziché una linea. Nella maggior parte dei linguaggi di programmazione il comando che traccia segmenti sullo schermo può essere egualmente usato per tracciare anche punti (a condizione che le coordinate degli estremi coincidano tra loro); le variabili OldU ed OldV vengono inizializzate ai valori correnti di NewU e NewV (linea 8) solo in questa circostanza altrimenti servono a salvare i valori di NewU e NewV per il ciclo successivo (per poterne tracciare il segmento, linea 10).

Figura 9: Risultato dell'operazione

OSS: Più piccoli saranno i valori dei passi di discretizzazione e migliore (i.e. più gradevole) risulterà il grafico della funzione visto che la lunghezza dei segmenti (che formano la curva-Y) sarà piccola e quindi inavvertibile. Osserviamo che la distanza relativa tra due curve-Y è pari al passo di discretizzazione lungo l'asse x (i.e. DeltaX).

La procedura Converti() non fa altro che usare la matrice M (come già detto) per proiettare il punto sul piano:


1.	PROCEDURE CONVERTI(X, Y, Z) TO (U, V)
2. BEGIN
3. W := M[1,3]*X + M[2,3]*Y + M[3,3]*Z + M[4,3];
4. U := (M[1,1]*X + M[2,1]*Y + M[3,1]*Z + M[4,1]) / W;
5. V := (M[1,2]*X + M[2,2]*Y + M[3,2]*Z + M[4,2]) /W;
6. Return(U,V);
7. END.

È chiaro che, variando le coordinate sferiche del centro di prospettiva e facendo ripartire l'algoritmo, la nuova matrice (M' ) proietterà la funzione a partire da un nuovo punto di vista.

Il grafico di una funzione si realizza però disegnando entrambe le linee coordinate; anche se in linea di principio le due operazioni possono essere effettuate in fasi distinte (ad esempio disegnando prima le curve-Y con l'algoritmo precedentemente proposto e poi quelle X con un algoritmo molto simile) per evitare una serie di piccole imprecisioni grafiche (si vedano ad esempio le figure riportate sul Penna-Patterson pag.266) conviene eseguirle contemporaneamente.

L'idea è allora quella di disegnare per ogni curva-Y, i tratti di curva-X compresi tra questa e la curva-Y successiva, mano a mano che si procede; l'algoritmo precedente viene allora modificato nel modo seguente:


1.	FOR X:= -L TO L STEP DeltaX DO
2. BEGIN
3. FirstPointOnCurveY:=TRUE;
4. FOR Y:=-W TO W STEP DeltaY DO
5. BEGIN
6. Z:=f(X,Y);
7. Converti(X,Y,Z)TO(NewY_U,NewY_V)
8. IF (FirstPointOnCurveY) THEN
9. (OldU,oldV):=(NewY_U,NewY_V);
10. DrawLine(OldU,OldV)TO(NewY_U,NewY_V);
11. (OldU,OldV):=(NewY_u,NewY_V);
12. FirstPointOnCurveY=false;
13. IF (X+DeltaX <= L) //tratto di curva-X
14. BEGIN
15. Z:=f(X+DeltaX,Y);
16. Converti(X+DeltaX,Y,Z)TO(NewX_U,NewY_V);
17. DrawLine(NewY_U,NewY_V,NewX_U,NewX_V);
18. END
19. END
20. END.

Oltre ad una serie di ridenominazioni di alcune variabili, il nuovo algoritmo differisce dal precedente solo per la parte in grassetto (da linea 13 a linea 18); dopo aver determinato un punto della curva-Y (qui non è rilevante distinguerlo dal primo) si calcola il valore della funzione per una x variata di un solo passo di discretizzazione (stando attenti di non uscire però dal suo dominio di definizione, linea 13 e 15) proiettandola poi sullo schermo (punti NewX_V, NewX_U, riga 16) e disegnandone il relativo segmento (linea 17).

E' possibile arricchire la rappresentazione della funzione tracciando le linee coordinate con colori che dipendano dalla quota assunta dalla funzione in quel momento. In tal caso non si dovrà far altro che impostare il colore corrente di visualizzazione (questo comando dipende molto dal linguaggio di programmazione adottato quindi il codice seguente è puramente indicativo) ad un colore che dipenda dal valore corrente di z Ad esempio:


5.	...
6. Z=f(X,Y); //Relativo alla curva-Y
7. IF (Z < -1) THEN SetColor(ROSSO)
8. ELSE IF (-1<= z < -0.5) SetColor(GIALLO)
9. ELSE ...
10. ...

E' chiaro che avrei potuto impostare il colore anche in base al valore di Z calcolato per la curva-X.

 


Figura 10: Rappresentazione di entrambe le linee coordinate (monocolor)

L'algoritmo proposto si presta a riempire il reticolato della figura in modo da rappresentare la funzione come superficie; individuati gli estremi di un quadrante (che dipenderanno dai punti Old e NewX/Y) si traccerà il poligono avente per vertici questi punti ed il cui colore potrà essere determinato con lo stesso criterio precedente (o in modo casuale se si vuole ottenere un effetto arlecchino). Poiché la rappresentazione delle linee coordinate avviene partendo dal fondo dello schermo il procedimento poc'anzi descritto coinciderà con l'algoritmo del pittore, coprendo le parti nascoste mano a mano che la funzione viene rappresentata.

Fin ora si sono rappresentati tutti i punti della funzione in modo indiscriminato, senza preoccuparsi di quali siano quelli effettivamente visibili e quali non lo siano; come si può evincere osservando la figura 10 l'effetto che ne consegue è di grande confusione, non si capisce più quale curva venga prima e quale dopo.

Per ovviare a questo spiacevole inconveniente si devono rappresentare le curve coordinate solo per le parti effettivamente visibili; a decidere quale porzione di curva è visibile dall'occhio e quale non lo è, è l'algoritmo delle linee nascoste. Di seguito ne verrà presentato uno specifico al caso in questione che, rispetto a quello della rappresentazione di oggetti tridimensionali su schermo piano presenta il vantaggio di essere più semplice anche se richiede una occupazione di memoria notevolmente maggiore.

Come fatto precedentemente, illustreremo questa tecnica solo per le linee coordinate y, visto che è molto simile anche per le altre linee coordinate.

L'algoritmo in questione presuppone di definire due vettori [1 x N] (dove N = u1-u0 cioè è pari alla lunghezza della base dello schermo) che chiameremo MaxV[] e MinV[] (chiaramente con l'aumentare della dimensione dello schermo aumenta anche lo spazio di memoria occupato dal programma a seguito delle crescenti dimensioni dei suddetti vettori). Tali elementi conterrannno un valore per ogni pixel della base dello schermo; inizialmente gli elementi di MaxV[] saranno impostati a v0 (poiché l'origine del nostro sistema di riferimento è in alto a sinistra allora tale valore sarà zero) mentre quelli di MinV[] ad v1 (pari al valore della base (meno una unità) per quanto poc'anzi detto).

L'idea dell'algoritmo è quella di disegnare le curve-y mano a mano che si diradano dall'osservatore; a differenza di prima, dove la rappresentazione iniziava dal fondo dello schermo e si dirigeva verso l'osservatore, qui la rappresentazione parte dall'osservatore e va verso il fondo dello schermo (da un punto di vista programmativo equivale ad invertire i due cicli principali).

Per ogni valore di pixel della base dello schermo (che indicheremo con la lettera U) il vettore MinV[U] contiene il minimo tra tutti i valori di V per tutti i punti dello schermo a coordinate (U, V) fino a quel momento calcolati (con V denotiamo le coordinate y dei pixel dell'altezza dello schermo) mentre il vettore MaxV[U] i massimi valori di V di tali punti.


Figura 11

L'algoritmo presuppone di suddividere lo schermo in tante linee verticali (tante quanti sono i pixel della base dello schermo) che determinano un piano con l'occhio (vedi figura 12) per ogni linea trovata; osservando il grafico della funzione dal centro di prospettiva dovrei allora vedere alcuni tratti di curva ed altri no in quanto coperti dai primi (vedi figura 11). Dopo aver determinato un punto dello schermo di coordinate (U, V) (risultato della procedura Converti()) ed essermi assicurato che entrambe le coordinate cadano entro i limiti dello stesso, verifico se V è un valore compreso tra MaxV[U] e MinV[U]; se questo è vero allora vuol dire che il punto suddetto non è visibile (in quanto coperto da qualche altra curva visibile) altrimenti, essendo visibile, lo visualizzo ed aggiorno opportunamente i vettori e precisamente se V<MinV[U] allora pongo MinV[U]=V e similmente se V>MaxV[U] allora pongo MaxV[U]=V.

Si continua ad eseguire questo test di visibilità per ogni punto dello schermo.


Figura 12

Implementando questo algoritmo è necessario modificare anche il modo con cui i punti della funzione sono stampati, in particolare, non è più possibile congiungere i punti trovati (Old e New) con semplici segmenti poiché può capitare che solo parte di questo sia effettivamente visibile, pertanto bisognerà rappresentare le linee coordinate con segmenti a lunghezza variabile.


Figura 13

Di seguito viene proposto un semplice metodo per la rappresentazione delle linee coordinate Y utilizzando il test di visibilità poc'anzi descritto; per il momento si presuppone di rappresentare la funzione per punti anziché per segmenti.


1. FOR X:= L TO -L STEP -DeltaX DO //Qui cambia il ciclo!
2. BEGIN
3. FirstPointOnCurve:=TRUE;
4. FOR Y:= -W TO W STEP DeltaY DO
5. BEGIN
6. Z:=f(X,Y);
7. Converti(X,Y,Z)To(NewU,NewV);
8. IF (FirstPointOnCurve) THEN
9. BEGIN
10. PlotIfVisible(NewU,NewV);
11. (OldU,OldV):=(NewU,NewV);
12. FirstPointOnCurve:=FALSE;
13. END
14. ELSE
15. BEGIN
16. DeltaU:=NewU-OldU;
17. IF (DeltaU=0) THEN DeltaU:=1;
18. Slope:=(NewV-OldV)/DeltaU;
19. PresentV:=OldV;//non c'e' sul libro
20. FOR (PresentU:=OldU TO NewU STEP 1 DO
21. BEGIN
22. PresentV:=PresentV+Slope;
23. PlotIfVisible(PresentU,PresentV);
24. END
25. (OldU,OldV):=(NewU,NewV);
26. END
27. END
28. END.

Affinché la visualizzazione del grafico cominci dall'osservatore e si diriga verso il fondo dello schermo è necessario invertire il senso di percorrenza del primo ciclo iterativo (i.e. dai valori positivi ai valori negativi, linea 1). Anche in questo caso è necessario differenziare il primo punto della curva Y dagli altri, poiché, come prima, si dovrà disegnare un punto anziché un segmento, ma a differenza di prima, dove questo era stampato indiscriminatamente, ora dobbiamo stabilire se il punto in questione sia o meno visibile e, solo in tal caso, visualizzarlo (linea 10). A tal fine la procedura plotIfvisible() (che implementa l'algoritmo delle linee nascoste precedentemente descritto) viene invocata passandogli come argomenti le coordinate cartesiane del punto da visualizzare (che sono quelle del nostro schermo). Qual ora questo non sia più il primo, si procede a calcolare la distanza in pixel (in orizzontale) che separa questo dal precedente (linea 16) per poi calcolare il coefficiente angolare della retta passante per questi due punti dello schermo (cioè la pendenza del segmento che ha per estremi questi due punti, linea 18) e si disegna il tratto di curva Y (congiungente tali punti) un punto alla volta applicando a ciascuno di questi il test di visibilità precedente (da linea 20 a linea 24).

Di seguito viene presentato il codice del metodo plotIfvisible().


1.	PROCEDURE PlotIfVisible(PresentU,PresentV)
2. BEGIN
3. IF (0 < PresentU) AND (PresentU > (U1-U0)) THEN
4. BEGIN
5. IF (PresentV>MaxV[PresentU] THEN
6. BEGIN
7. MaxV[PresentU]:=PresentV;
8. IF (PresentV < V1) THEN
9. Plot(PresentU,PresentV);
10. END
11. IF (PresentV < MaxV[PresentU]) THEN
12. BEGIN
13. MinV[PresentU]:=PresentV;
14. IF (PresentV > V0) THEN
15. Plot(Presentu,PresentV);
16. END
17. END
18. END.

Dopo aver controllato che le coordinate del punto da stampare cadano entro i limiti del nostro schermo, si procede ad applicare l'algoritmo delle linee nascoste aggiornando opportunamente i vettori precedentemente definiti.


Figura 14: Applicazione dell'Algoritmo delle linee nascoste per le curve coordinate Y


Il grafico della funzione si ottiene visualizzando entrambe le linee coordinate alle quali, però, applicheremo ora l'algoritmo delle linee nascoste.

A patto di invertire l'ordine di entrambi i cicli iterativi principali, il programma non differisce molto dal precedente (tanto che non ne viene riportato il codice) poiché, in tal caso, il metodo che implementa il test di visibilità rimarrà sempre lo stesso per entrambe le linee coordinate. Come nel caso iniziale l'idea è sempre quella di tracciare contemporaneamente le curve X ed Y disegnando per ogni curva Y i tratti di curva X compresi tra questa e la curva Y successiva.

Disegnando per punti l'intero supporto della funzione, il grafico rimane particolarmente sensibile ai passi di discretizzazione (tanto che nel programma si è deciso di non rendere modificabile la variabile deltaY per questa visualizzazione). Una miglioria dell'algoritmo potrebbe essere quella di disegnare le curve coordinate per segmenti a lunghezza variabile anziché per punti (che, tra l'altro, rallentano sensibilmente l'applicazione sviluppata) Una volta determinate le coordinate dei punti New e Old si devono cercare tutte le coppie di punti giacenti sul segmento (New , Old) compresi tra questi e che siano estremi di segmenti visibili tracciando poi il segmento che ha per estremi proprio questi punti.

Purtroppo non è stato possibile implementare questa miglioria; la lasciamo quindi come proposta di sviluppo precisando che sul libro trovate qualche informazione in più.

Figura 15:Supporto della funzione z=cos(xy).

L'algoritmo proposto non è ovviamente l'unico; facendo una ricerca in rete abbiamo trovato numerose altre soluzioni più o meno sofisticate; la più curiosa che intendiamo segnalare proponeva l'uso di spline cubiche per interpolare i punti della funzione calcolati ai passi di discretizzazione(per saperne di più contattate Giuseppe Cerrigone il quale ha fatto una ricerca presso la nostra biblioteca).

FINE.




[doorsin.gif (2,29KB)]
Home Page

Applet!
[email1.gif (5,40KB)]
alzati@vmimat.mat.unimi.it
[card.gif (4,87KB)]
Alberto Alzati
Web Page

Pagina Web realizzata da
Dario Cozzi
e
Giuseppe Cerrigone
A.A. 1998-1999
dc486474@silab.dsi.unimi.it
gc529551@silab.dsi.unimi.it