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 all’espansione
di quella attualmente implementata.
La
grafica vettoriale (per l'utente medio), è rimasta sempre nell’ombra
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 un’azienda 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 sull’HTML
relativo.
Secondo voi
in che formato potrà essere visualizzato lo schema tecnico? In GIF?
Sì, è possibile, ma deve essere un’immagine 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 l’alternativa 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 l’ultimo punto
considerato e il drawLine(...) avesse un ulteriore constructor di tipo
drawLineTo(x, y), cioè una line 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.
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
un’altra 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 dell’ellisse.
Una cosa che
non potrebbe mai realizzare un drawOval, come viene adesso implementato,
è il disegno di un’ellisse 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 un’ellisse
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à l’ellisse 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 è
l’insieme 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 l’avevano avuta: esiste
un oggetto che disegna una poligonale Polygon(int xpoints[] int ypoints[],
int npoints) che è l’insieme di due array costituenti l’insieme
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 l’oggetto 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 l’oggetto 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 nell’oggetto 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!