MokaByte
Numero 12 - Ottobre 1997
|
|||
|
|
||
|
Uno sguardo a Java 2D API (la "concorrenza") | ||
Quasi
un anno fa furono annunciate da Sun le estensioni Java2D e Java3D, ma se
visitate il
sito Javasoft
noterete che di specifiche ne trovate parecchie, ma di classi pronte all’uso
nemmeno l’ombra. Se ricordate bene, su queste pagine, furono fatte alcune
critiche alle classi grafiche geometriche contenute nel JDK allora alla
versione 1.0.2. Si fecero notare alcune lacune vettoriali di Java ed API
che abbiamo colmato insieme.
Ad
esempio non esiste una classe Linea(...) e noi l’abbiamo creata.
Il disegno delle
linee necessita sempre di quattro informazioni drawLine(x1, y1,
x2, y2)
mentre sarebbe stato comodo che il programma ricordasse l’ultimo punto
considerato
e che il drawLine(...) avesse un ulteriore constructor di tipo
drawLineTo(x,
y), cioè una linea dall’ultimo punto considerato a quello specificato
(esiste in molti altri linguaggi)
Uno
strano modo di disegnare circonferenze ed archi:
Per disegnare
una circonferenza basterebbero due informazioni: il punto-centro e il raggio,
invece le API permettono solo un drawOval(x, y, width, height) cioè
il
"bounding
box" o "rettangolo di contenimento". Ma una circonferenza, quasi
sempre,
viene espressa
come posizione del centro e lunghezza del raggio.
Ora,
se date uno sguardo alle specifiche delle classi Java 2D API vi
accorgerete che hanno inserito delle cose fantastiche come classi tipo
java.awt.color, java.awt.image, java.awt.font
e solo il nome basta per capire di cosa si tratta e quali innovazioni siano
state aggiunte, ma guardando dentro java.awt.geom, ad essere
sinceri non si rimane molto sorpresi. Tranne che per qualche utility potente
come AffineTransform(). Si notano molte ripetizioni di classi che
hanno come differenza solo il fatto di accettare o ritornare tipi float
o double. In ogni caso i float e i double sono ben accetti visto
che le precedenti classi di base, le classi geometriche, accettano o ritornano
solo tipi int. Non è cambiato però per gli archi il
solito sistema di definizione tramite un bounding box, sistema ritenuto
alquanto orribile e molto contestato nell’articolo di Mokabyte di Gennaio
97.
Si
notano poi due tipi di classi simili :
QuadCurve2D()
e CubicCurve2D che non si capisce cosa siano, probabilmente si
tratta di classi
per il disegno di curve parametriche o forse parabole!? Saranno osservate
a fondo in qualche articolo successivo.
Una cosa molto
intelligente, ma un po’ azzardata è la classe Ellipse2D()
che non ha niente di diverso dal vecchio DrawOval() tranne che è
un oggetto a se stante come il Polygon() ma conserva sempre l’orribile
sistema di definizione simile a quello del Arc2D() o dello stesso
DrawOval() e cioè tramite il boundibox (vedi sempre articolo
di Gennaio 97). Ellipse2D() è ritenuta una class intelligente perchè
include sia circonferenze che ellissi. Al contrario, è ritenuta
azzardata perchè un non esperto di classi geometriche naufragherà
nel mare della API senza trovare un modo per creare una circonferenza!
Un’altra
cosa, anzi molte cose, non sono chiare: è stata introdotta la classe
Linea2D(), ma i soli constructors esistenti sono quelli per creare
la linea specificando le quattro coordinate usuali come nel vecchio drawLine().
Cioè Line2D(double, double, double, double) oppure specificando
due oggetti di tipo Point2D() cioè Line2D(Point2D, Point2D).
La nostra Linea() invece ha vari constructors come Linea(Punto,
Punto) oppure Linea(m. n) dove m e n sono due
tipi double che specificano coefficiente angolare e intercetta sull’asse
y e Linea(asse). I methods di Line2D() sono praticamente
inesistenti, mentre la nostra Linea ha i nethods angoloXY(),
deltaX(), deltaY(), lunghezza(), medio(), parallela(),
perp(), proiezione(), ruota(), sposta(), tangente().
Vi sembra poco? Figuriamoci poi se andiamo a vedere la nostra classe duePunti()
che anche se non è un oggetto geometrico vero e proprio è
molto simile alla Linea() e alcuni methods di altre classi che generano
tale tipo duePunti() sono ad esempio il methods
di Circonferenza(),
duePunti(Circonferenza Circonferenza) e duePunti(Linea, Circonferenza).
Scusate, ma due circonferenze generano una linea. Ma come si fa utilizzando
il solo Java2D a calcolarla? Noi l’abbiamo fatto!
Vogliamo continuare?
Prendiamo
la class Point2D(). Non contiene assolutamente niente. Come si farebbe
ad esempio, a creare un punto da due oggetti di tipo Line2D().
Cioè dall’intersezione di due rette?
Point2D()
ha semplicemente il constructor di tipo Point2D(double, double).
Il nostro punto
invece Punto(x, y), Punto(Punto, ro, teta), Punto(Linea,
Linea),
Punto(m1,
n1, m2, n2) dove i parametri m ed n sono sempre i soliti
come nel caso di Linea.
E i methods
di Point2D()? Praticamente inesistenti.
I nostri sono
area(), interno(), perimetro(), polare(), relativo(),
ruota(), sposta().
Altre
classi di Java2d sono utility che permettono di disegnare linee multiple
(polilinee) ed altro, fra le nostre classi, invece, è presente la
classe gruppo() che permette l'unione di più oggetti e considerarli
come un unico blocco da poter gestire ad esempio con i methods sposta()
e ruota().
Conclusione
Non si vuole
criticare il Java 2D, abbiamo solo dato uno sguardo all'impostazione
geometrica di questo package che dovrebbe rappresentare la base su cui
dovrebbe poggiare anche il Java 3D. Ma da uno sguardo anche
a questo pakage si nota che è stato molto curata la parte "artistica"
come tecniche di colorazione e di renderizzazione, ma trascurati i principi
di base che dovrebbero rendere più facile la vita di un programmatore.
Anzi, a dire il vero, è stato abbastanza complicato capire a cosa
servono alcune classi se non si consultava la documentazione fornita.
Errata Corrige
Nell'asrticolo
precedente fu introdotto il methods simmetrico() di Punto,
che è il principale responsabile della funzione di simmetria. Ebbene,
come qualcuno ha fatto notare c'era un bug!
Se la linea
di simmetria è perfettamente orizzontale (O° o 180°) e interseca
un'oggetto per cui bisogna calcolare il simmetrico allora capita che non
viene calcolata in modo esatto la simmetria.
Il fatto è
che per risolvere un problema si dovrebbe sempre scegliere la via più
breve, ma gli analisti, per quanto bravi essi siano, non sono impeccabili.
Perciò per questo method fu scelta una strada più complicata
del necessario introducendo e costringendo il programma a dare uso di angoli.
E' sufficiente, invece, per calcolare il simmetrico di un punto fare riferimento
ad una sola dimensione. Se il simmetrico di un punto P su una retta
(asse X ad esempio) è esattamente quello situato alla stessa
distanza ma dal lato opposto del punto Pi di simmetria si crea una
situazione del tipo P Pi Ps, dove Ps è il punto di
simmetria e dunque Pi rappresenta il punto medio del segmento P
Ps. Dalle formule del Punto medio di un segmento si ricava la formula
per calcolare il punto simmetrico e contengono solo operazioni di tipo
addizione e sottrazione!
Considerando
tali formule applicate anche all'asse Y s ottiene che il tutto funziona
anche in un piano XY e più in generale in qualsiasi numero
di dimensioni.
Perciò
il method simmetrico() di Punto() è ora diventato:
// ritorna
il punto simmetrico, rispetto alla linea di simmetria Ls, del punto in
corso
Punto simmetrico(Linea Ls) {
Linea perpLs= Ls.perp(this);
Punto Pi= new Punto(Ls, perpLs);
return new Punto(2*Pi.x-this.x, 2*Pi.y-this.y);
}
Le
frequenti modifiche alle classi già presentate sono dovute al continuo
miglioramento della loro struttura. E da tener presente, inoltre, che tali
articoli non sono già belli e confezionati, ma vengono scritti dall'autore
di volta in volta.
Basta, comunque, farsi
il download dei file newGraphics.java
e sostituirlo a quello già downloadato in precedenza.
Buon divertimento!
Domenico De Riso deriso@infomedia.it
|
||
|
||
MokaByte ricerca
nuovi collaboratori
|
||
|