Da questo numero verranno analizzati alcuni methods della classe awt.Graphics e precisamente quelli di "grafica vettoriale" come il disegno di linee, circonferenze ed archi. Essi sono di importanza fondamentale perchè utili alla rappresentazione di qualsiasi grafico o semplice animazione sulle applet.
Degli altri methods della awt.Graphics ne sta già discutendo Massimo Carli con gli articoli dedicati all'Image Processing. I methods di cui voglio discutere sono pochi. Ma c'è da dire molto sulla loro NON versatilità, e da fare molto sulla loro espansione. Molte sono le applet scritte, ma per lo più con fini di carattere informatico: gestione in Java di computers in rete o di posta elettronica ecc., ma poche quelle utili alle persone che non si occupano di reti o di mailing ma semplicemente della funzione primaria: LA RAPPRESENTAZIONE GRAFICA DELLE INFORMAZIONI sulla Rete.
Dunque, quello che voglio presentare, a cominciare da questo numero, è la grafica vettoriale, dalla semplice grafica bidimensionale, tridimensionale fino ad accenni di quella multidimensionale e soprattutto allespansione di quella attualmente implementata.
La grafica vettoriale (per l'utente medio), è rimasta sempre nellombra in particolar modo da quando i computers fanno uso di interfaccie grafiche come Windows. La causa è da attribuire soprattutto al fatto che "grafica vettoriale" significa per lo più CAD (Computer Aided Design tradotto Disegno Aiutato dal Computer) e CAD è, purtroppo, molto sinonimo di AutoCAD che per anni è stato uno strumento escluso da Windows e inesistente su Apple e sono veramente poche le persone che lo sanno utilizzare, ma molto ricercate. Anzi, chi lo "smanetta" è praticamente già fuori dalle liste di disoccupazione! Ormai tutte le industrie e tecnici si servono del CAD e i plotter sono le uniche macchine che riescono a portare fuori dal computer uno schema tecnico. Su Internet si fa ancora fatica per rappresentare uno schema tecnico.
Immaginate di essere unazienda prodruttrice di box per doccia. Pubblicizzate, naturalmente il prodotto su Internet. Quindi pretendete che il vostro provider o chi vi cura le pagine inserisca lo schema tecnico di assemblaggio sullHTML relativo.
Secondo voi in che formato potrà essere visualizzato lo schema tecnico? In GIF? Sì, è possibile, ma deve essere unimmagine molto grande affinchè non venga perduta la qualità del disegno, e le immagini molto grandi occupano molti Kilobytes e molti Kilobytes occupano molto tempo per il caricamento e "il molto tempo" precede spesso un Netscape STOP! Inoltre il disegno che le immagini di questo tipo rappresentano non ha la possibilità di essere zoomato o traslato.
Da quando navigo e "incremento" le informazioni di Internet, uno dei primi problemi che ho incontrato è proprio la presentazione di schemi tecnici sul web. Netscape e Internet Explorer e altri browser non hanno problemi per la visualizzazione di immagini di tipo GIF, TIF o JPG, ma nessuno di questi ha la capacita di mostrare un disegno vettoriale di tipo DXF o HPGL o un formato "inventato", cioè un disegno le cui informazioni siano espresse in maniera geometrica. Certo! Esistono plug-in potentissimi come Acrobat Reader che visualizza immagini PDF, ma bisogna downloadarlo dal sito di Adobe ed è 3.5Mb e non tutti, e mi riferisco agli utenti semplici, hanno la pazienza e soprattutto la "coscienza" di scaricarlo.
Sono convinto che la grafica vettoriale in Java per Internet diventerà uno strumento di comunicazione utile soprattutto alla simulazione di processi scientifici e tecnici che, altrimenti, potrebbe essere possibile solo con lo scambio "postale classico" di videocassette! Infatti, piccole animazioni sono facilissime da realizzare con Java, mentre lalternativa di cui i browser oggi dispongono è solo di tipo OpenGL o VRML che pretendono sempre il dowload di plug-in extra, oppure accontentarsi del classico download di un qualche file AVI o MPEG.
Perciò ritengo opportuno che si faccia un pò di informazione su questo strumento un pò trascurato e che può essere utilissimo a chi prepara pagine e programmi per la Rete.
Comincerò con i semplici comandi che Java e le API dispongono, fino alla realizzazione di una qualche simulazione tecnico-scientifica. A dire il vero le API non contengono poi tante possibilità di disegno.
Dunque, ecco un elenco di lacune vettoriali di Java ed API che (spero) risolveremo insieme:
- Non si possono disegnare linee tratteggiate. Le linee di questo tipo sono importantissime per la rappresentazione di schemi tecnici (già sulla mailing-list italiana di Java ho trovato un'"invocazione" di aiuto per questo problema).
- Il disegno delle linee necessita sempre di quattro informazioni drawLine(x1, y1, x2, y2) mentre sarebbe stato comodo che il programma ricordasse lultimo punto considerato e il drawLine(...) avesse un ulteriore constructor di tipo drawLineTo(x, y), cioè una line dallultimo 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.
Allora sapreste dirmi cosa digitare per disegnare, secondo le API, una circonferenza con centro in (125,103) e raggio 28? Ve lo dico io dopo un calcolo apposito: drawOval(97, 75, 56, 56). Questi numeri sembra che non abbiano nulla a che vedere con i dati 125, 103 e 28.
Disegnare poi unaltra circonferenza concentrica alla precedente di raggio 15 è praticamente "cervellotico".
Ve lo dico sempre io dopo un ulteriore calcolo: drawOval(110, 88, 30, 30).
A questo punto sono confuso su chi è il calcolatore e chi il programmatore!
Immaginate poi se qualcuno chiede di spostare il tutto di 34 pixels più a destra!
Sarebbe stato molto più comodo se fosse stato implementato un drawOval(xc, yc, r), cioè centro e raggio. Così le due circonferenze diventerebbero drawOval(125, 103, 28) e drawOval(125, 103, 15) che sembra molto più versatile (cambia solo il parametro del raggio).
Eh! Eh! Qualcuno già sta pensando che il drawOval(...) attuale può disegnare anche ellissi. Ma replico subito che, invece di centro e raggio, si può stabilire un ulteriore drawOval(xc, yc, ra, rb) dove ra ed rb sono i semiassi maggiori e minori dellellisse.
Una cosa che non potrebbe mai realizzare un drawOval, come viene adesso implementato, è il disegno di unellisse obliqua. Infatti quale sarebbe il suo bounding box finale? E vero che si potrebbe aggiungere un ulteriore parametro di rotazione alfa cioè drawOval(x, y, width, heigth, alfa), ma questo farebbe corrispondere i primi quattro parametri ad unellisse inesistente, o almenno difficile da identificare, contenuta in un boundig box "disastrato", mentre aggiungendo alfa a drawOval(xc, yc, ra, rb, alfa) nessuno dei cinque parametri risulta falso perchà lellisse obliqua sarà sempre del tipo con centro in xc, yc, con semiassi ra, rb e rotazione alfa.
Negli articoli successivi mostrerò tale alternativa in una maniera polimorfa con constructors diversi per il disegno delle circonferenze sia quando si tratta di quelle regolari sia di quelle ellittiche con e senza rotazione.
- Punti o non punti?
Tutte le primitive geometriche (linee, circonferenze e archi) sono definite dalla geometria come insiemi di punti.
Appunto i punti!
Un punto è linsieme di UNO o più valori numerici che rappresentano le coordinate del punto stesso. A meno di non voler scendere... ehm ...salire a concetti filosofici, tali valori numerici rappresentano quasi gli assiomi di tali primitive chiamate punti. Un punto nella realtà, così come nell'OOP dovrebbe essere considerato come un insieme di valori numerici. Ma in Java, tutte le primitive geometriche che necessitano di punti per la loro rappresentazione chiedono vengano specificati tramite le coordinate una per volta. Ad esempio per rappresentare una linea, o meglio, un segmento tra due punti P1 in (x1, y1) e P2 in (x2, y2) bisogna esprimere drawLine(x1, y1, x2, y2).
Mi chiedo: Perché non esiste un constructor drawLine(p1, p2) ?
Dove p1 e p2 potrebbero essere oggetti di tipo già esistente Point(int x, int y), ANZI, Punto(int c\[\], int n) dove c\[\] è un array contenente le coordinate ed n la dimensione del punto bidimensionale, tridimensionale o multidimensionale che sia.
Non credo affatto che sia stata ideata la forma attuale per non complicare le cose. Infatti il drawPloygon(int xpoints\[\] int ypoints\[\], int npoints) usa gli array, ma in maniera errata. Col sistema suggerito prima avrebbe avuto la forma di drawPolygon(Punto P\[\], int n) molto più versatile.
Ma una mezza idea, di sintetizzare le cose, i fautori di Java lavevano avuta: esiste un oggetto che disegna una poligonale Polygon(int xpoints\[\] int ypoints\[\], int npoints) che è linsieme di due array costituenti linsieme delle coordinate xpoints ed ypoints ognuno contenenti npoints elementi. Così per disegnare un poligono basta anche solo drawPolygon(Polygon p) che è un secondo constructor di drawPolygon(...).
Ma con i suggerimenti precedenti loggetto Polygon p poteva addiruttura avere la seguente forma Polygon(Punto P\[\], int n) che come potete osservare non si differisce da quella suggerita di un punto Punto(int c\[\], int n).
La differenza sta nel fatto che è possibile, con la forma suggerita, disegnare primitive geometriche che hanno innanzitutto un ASPETTO INFORMATICO SIMILE, e tale forma resta la stessa in un numero qualsiasi di dimensioni. Anche 100 dimensioni. Immaginate invece un drawLine(x1,y1,z1,t1, ..., n1, x2,y2,z2,t2,...,n2) che fortunatamente non è implementato...!
Riassumendo: la situazione attuale è la seguente:
disegnare una linea ha la forma drawLine(x1, y1, x2, y2), per il poligono drawPolygon(int xp\[\], int yp\[\], int np) ecc. e come vedete le coordinate devono essere sempre espresse e la cosa funziona solo in 2 dimensioni.
Se invece viene implementato loggetto Punto(int c\[\], int n) la situazione cambia in modo radicale:
disegnare un punto in 2D diventerebbe
drawObject(Punto p\[\], int n) dove n=2 e p\[\] di 1 elemento;
disegnare una linea in 2D diventerebbe
drawObject(Punto p\[\], int n) dove n=2 e p\[\] di 2 elementi;
disegnare un poligono in 2D diventerebbe
drawObject(Punto p\[\], int n) dove n=2 e p\[\] di n elementi;
in 3D basta assegnare 3 ad n e, a dimensioni maggiori, il numero corrispondenti di dimensioni desiderate. Come potete osservare basta il solo drawObject(...)
Poi i methods di cui potrebbe essere dotato il drawObject(...) sono molteplici. Per esempio un method Perspective(Punto p, Observation o) che permetterebbe di filtrare l'insieme di punti contenuti nelloggetto Punto p\[\] nel modo in cui viene osservato dall"oggetto" o, cioè un osservatore Observation(Punto p, int mm) che guarda il disegno da un punto di vista p con un obiettivo fotografico con focale di mm millimetri).
Inutile citare le ulteriori possibilità che si possono avere in 3D. Mi chiedo perchè un programmatore dovrebbe essere esperto di formule di geometria analitica, quando molte utility grafiche standard possono essere implementare direttamente. Ok! Lo faremo!
- UNA NOVITA'!!
Inoltre il drawPolygon(...) trae in inganno perche chissà quanti avranno pensato "che bello! Posso disegnare poligoni!" Si sbagliano! Disegna solo poligonali che non sono altro che linee una dopo l'altra con gli estremi collegati. Se volete disegnare un poligono dovete calcolare le coordinate di ogni vertice!!
Adesso hanno introdotto nel JDK1.1 anche un drawPolyline(...) che non è altro che la copia del vecchio drawPolygon(...) che trae in inganno, ma il drawPolygon(...) esiste ancora e se vi dimenticate di fare in modo che la poligonale si chiuda viene fatto automaticamente (questa è la novità!). Cioè un programma realizzato con il JDK1.02 se ricompilato con il JDK 1.1 ed usa il drawPolygon(...) potrebbe non dare più gli stessi risultati grafici! Le poligonali disegnate con drawPolygon(...) che non dovevano chiudersi, ora si chiudono da sole se non vengono sostituite con drawPolyline(...)!
Concludendo: methods come drawLine(...), drawPolygon(..) drawRect(...) e ... anche il nuovo drawPolyline(...) potrebbero anche non esistere e drawOval(...) e drawArc(...) devono essere assolutamente migliorati.
A questo punto non mi resta che riaffermare la poca versatilità delle doti di grafica vettoriale di Java.
Nei futuri articoli cercheremo di trattare i "punti" nel modo descritto e costruiremo i methods utili ad effettuare spostamenti e rotazioni ... così disegnare con Java sarà più facile.
Vi anticipo che in www.javasoft.com stanno tramando le nostre stesse idee! (argomenti Java2D e Java3D) Ma non saranno pronti per almeno un anno, infatti si prevede la loro presentazione verso la fine del 1997 e per Java2D addirittura TBD (To Be Determined). Ci stanno copiando!
