MokaByte Numero 12 - Ottobre 1997

 
Grafica vettoriale: 
 
di Domenico De Riso
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 rivista web su Java

MokaByte ricerca nuovi collaboratori
Chi volesse mettersi in contatto con noi può farlo scrivendo a mokainfo@mokabyte.it