MokaByte
Numero 07 - Aprile 1997
|
|||
|
qualche esperimento... IV parte |
||
Domenico De Riso |
|||
Con questo articolo si conclude la prima tappa di questa avventura.
E' stato mostrato
come si può disegnare con Java e OOP generico in modo facile e senza
quasi mai utilizzare formule e numeri.
Nonostante la
libreria di "classi-entità geometriche" presentate sia non molto
ricca (Punto, Linea e Circonferenza e qualcuna ausiliaria),
ognuno di esse è sufficientemente potente grazie al gran numero
di methods di sono dotate. Tali methods sono molto efficienti ed utili
alla creazione facilitata di qualsiasi altra "entità" complessa.
In un qualsiasi corso tradizionale l'esposizione di questi articoli dovrebbe avvenire per gradi. Cioè trattare in modo completo tutte le "clssi-entità" e poi passare alle applicazioni pratiche. Ma questo non è un corso tradizionale! Piuttosto una serie di articoli che ha il compito primario di non annoiare (se non lo ha fato già!) chi legge. Perciò è meglio procedere in modo parallelo: su ogni prossimo numero, a partire da questo, prima tratteremo i nuovi argomenti e poi cercheremo di arricchire le classi già presentate con nuovi methods.
Ritorniamo alle nostre entità!
La grafica vettoriale
si compone di quattro fasi fondamentali: creazione, gestione,
modifica e visualizzazione.
Fino ad ora
ci siamo occupati solo della fase di creazione tranne che per il
method disegna() che appartiene appunto alla fase di visualizzazione.
Praticamente
i methods e i costructors trattati fino ad ora hanno al massimo la funzione
di creazione di una entità con caratteristiche specificate
dal programmatore (come: sia L una linea tra i punti P1(10,20) e P2(30,40))
e/o dipendenti da eventi nello spazio geometrico causati da entità
già esistenti (come: sia L una linea tra i punti d'intersezione
delle circonferenze C1 e C2)
Ora tratteremo
la fase di gestione e precisamente ci preoccuperemo di scrivere
dei methods utili al cambio delle caratteristiche di base delle entità
già esistenti nel disegno (ovvero nel database). Tali methods riguardano
lo spostamento e la rotazione delle entità.
SPOSTAMENTO:
Per spostare
un oggetto reale basta afferrarlo per un punto e trascinarlo fino al luogo
di destinazione. Di solito un oggetto pesante è provvisto di una
maniglia per rendere più agevole lo spostamento. Bene! Tale maniglia
è di importanza fondamentale nella grafica vettoriale. Infatti
esiste sempre e si chiama "punto origine dello spostamento" ed ha un vantaggio
rispetto alla realtà: una valigetta ha la maniglia sempre legata
fisicamente ad essa, mentre un oggetto della grafica vettoriale può
avere la "maniglia" anche non legata all'oggetto stesso. Cioè il
punto origine dello spostamento può essere rappresentato da qualsiasi
punto. Quando poi si definisce il punto destinazione dello spostamento,
l'entità viene traslata parallelamente lungo la linea immaginaria
punto-origine punto-destinazione.
Se non è
chiaro provate l'applet a destra cliccando un punto origine e uno destinazione
e osservate lo spostamento della circonferenza rossa. Naturalmente lo
spostamento sarà più comprensibile scegliendo un punto origine
("la maniglia") interno alla circonferenza rossa. La circonferenza
nera mostra la posizione precedente di quella rossa. Se la circonfrenza
esce dalla finestra di visualizzazione cliccate su RESET.
Il method sposta(...)
relativo allo spostamento è definito in tutte e tre le classi di
entità di base Punto, Linea e Circonferenza,
ma quello che effettivamente causa lo spostamento è il method contenututo
nella class Punto.
void sposta(Punto Porig, Punto Pdest) {Invece, i methods sposta() delle classi Linea e Circonferenza richiamano quello contenuto in Punto.Linea lPoPd = new Linea(Porig, Pdest);
x = x + lPoPd.deltaX();
y = y + lPoPd.deltaY();
}
ROTAZIONE:
La rotazione
implica sempre la specifica di un angolo. Un angolo ha sicuramente un vertice,
ed un vertice è sicuramente un punto. Tale punto si chiama punto
di rotazione. Perciò per effettuare una rotazione di un'entità
bisogna specificare un punto di rotazione ed un angolo.
Il method successivo
ruota(...) contenuto nella class Punto ruota il punto
(this) intorno a quello specificato.
Tale method,
come quello dello spostamento è definito in tutte e tre le classi
di entità di base Punto, Linea e Circonferenza,
ma chi effettivamente causa la rotazione è quello contenututo nella
class Punto.
void ruota(Punto Prot, double alfa) {Tutto il codice necessario alla sperimentazione è contenuto nei files newGraphics.java, che contiene anche il codice dell'articolo dei numeri precedenti, ma aggiornato, e vectors4.java contenente il codice necessario alle applet visibili in questa pagina.// precalcolo del seno e coseno
double s=Math.sin(alfa), c=Math.cos(alfa);
// calcolo delle coordinate relative al punto di rotazione
Linea Cp =new Linea(new Punto(0,0), Prot);
double xo=x - Cp.deltaX();
double yo=y - Cp.deltaY();
// rotazione
double X = xo * c - yo * s;
double Y = xo * s + yo * c;
// riassegnazione delle coordinate assolute al punto ruotato
x=X+Cp.deltaX();
y=Y+Cp.deltaY();
}
I
methods esaminati fino ad ora sono utilissimi per ricavare informazioni
o a cambiare lo stato delle entità stesse, ma agiscono solo ed esclusivamente
su una entità alla volta.
Nei casi più
comuni, quando si deve rappresentare un grafico, soprattutto quando si
tratta di animazioni, gli spostamenti e le rotazioni sono gli eventi più
frequenti. Ma, il più delle volte, sono applicati a più oggetti
contemporaneamente: ad oggetti raggruppati.
Ad esempio:
se un astronomo deve rappresentare un sistema planetario ha bisogno
di rotazioni applicate innanzitutto ai pianeti intorno alla stella (Sole)
e poi applicate ai satelliti intorno ai pianeti e così via.
Passiamo allora
all'introduzione di una nuova class utile sia al raggruppamento di entità
semplici che a quella di entitè composte da gruppi stessi (sottogruppi).
La class si
chiama gruppo e contiene gli stessi methods (disegna(), sposta(),
ruota()) delle entità di base Punto, Linea e
Circonferenza. Solo che vengono applicati a tutti gli elementi del
gruppo. E se un elemento è a sua volta un sottogruppo
allora vengono applicati ricorsivamente.
Le entità
contenute in un gruppo vanno ad essere immesse in un Vector.
Inoltre se osservate
vectors4.java vi acorgerete che tutte le classi
di base sono diventate di tipo "extends entita" dove entità
è appunto una abstract class utile al casting (entita)
nei methods di gruppo disegna(), sposta(), ruota().
class gruppo extends entita {Vector v=new Vector();
Color colore;
// definisce un gruppo di entita' con specifica di colore
gruppo(Color colore) {
this.colore=colore;
}
// definisce un gruppo di entita' con colore standard Color.black
gruppo() {
}
// aggiunge un'entita' al gruppo
void aggiungi(entita e) {
v.addElement(e);
}
// sposta il gruppo parallelamente allo spostamento Porig - Pdest
void sposta(Punto Porig, Punto Pdest) {
for (int i=0; i<v.size(); i++) {
((entita) v.elementAt(i)).sposta(Porig, Pdest);
}
}
// ruota di un angolo specificato il gruppo intorno ad un punto
void ruota(Punto Prot, double alfa) {
for (int i=0; i<v.size(); i++) {
((entita) v.elementAt(i)).ruota(Prot, alfa);
}
}
// disegna l'intero gruppo di entita'
void disegna(Color colore, Graphics g) {
for (int i=0; i<v.size(); i++) {
((entita) v.elementAt(i)).disegna(colore, g);
}
v.removeAllElements();
}
void disegna(Graphics g) {
disegna(colore, g);
}
}
APPLET DI ESEMPIO:
Visto che abbiamo
accennato all'esempio dell'astronomo alle prese con un sistema planetario,
perchè non realizzarlo? Useremo, però, delle semplici rotazioni
non derivanti dalle formule di Keplero.
Allora sia S
una stella intorno alla quale girano i pianeti C1 e C2. Intorno
a C2 ruotano due satelliti C2a e C2b con versi opposti
di rotazione.
Analizzando
tale sistema si può ridurlo a due gruppi costituiti in questo
modo:
// Si definiscono le masse dei corpi celesti (solo le circonferenze)Niente formule o numeri!Circonferenza S =new Circonferenza(P1, 10);
Circonferenza C1 =new Circonferenza(P1.relativo(20, 0), 5);
Circonferenza C2 =new Circonferenza(P2, 10);
Circonferenza C2a =new Circonferenza(P2.relativo(20, 0), 5);
Circonferenza C2b =new Circonferenza(P2.relativo(20, 20), 3);
// si creano i gruppi assegnando già i colori
gruppo gr1 =new gruppo(Color.red);
gruppo gr2 =new gruppo(Color.blue);
public void paint(Graphics g) {
// si aggiungono le entità ai gruppi
gr1.aggiungi(S);
gr1.aggiungi(C1);
gr2.aggiungi(C2);
gr2.aggiungi(C2a);
gr2.aggiungi(C2b);
// si effettuano le rotazioni (se inverse basta il segno "-")
// si ruota il primo gruppo
gr1.ruota(S.centro, -Math.PI/16);
// rotazioni interne al secondo gruppo
C2a.ruota(C2.centro, Math.PI/6);
C2b.ruota(C2.centro, -Math.PI/8);
// poi si ruota l'intero secondo gruppo intorno ad S appart. al primo gruppo
gr2.ruota(S.centro, Math.PI/32);
// alla fine si disegna tutto
gr1.disegna(g);
gr2.disegna(g);
}
}
In seguito cercheremo di riprendere questo esempio ed applicare al posto del method ruota(), un method, magari, di nome orbita() che calcolerebbe una traiettoria secondo le leggi di Keplero e al posto della Circonferenza una classe Massa che potrà interagire con altre Massa secondo la Legge della Gravitazione Universale di Newton.
Nuovi METHODS:
I methods che seguono sono di arricchimento alla libreria di base che stiamo, di volta in volta, incrementando.
Per la class Punto sono stati introdotti seguenti nuovi methods:
// ritorna il punto di coordinate polari ro, teta relativo al punto in corsoLe 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.Punto polare(double ro, double teta) {
return new Punto(new Punto(x, y), ro, teta);
}
// ritorna il punto di coordinate x,y relative al punto in corso
Punto relativo(double xr, double yr) {
return new Punto(x+xr, y+yr);
}
// ritorna il perimetro del triangolo costituito dal punto in corso ed altri due punti
double perimetro(Punto Pb, Punto Pc) {
return new Linea(this, Pb).lunghezza() + new Linea(Pb, Pc).lunghezza() + new Linea(Pc, this).lunghezza();
}
// ritorna l'area del triangolo costituito dal punto in corso ed altri due punti
// applicando la formula di Erone applicata al perimetro
double area(Punto Pb, Punto Pc) {
// calcola il semiperimetro (perimetro/2)
double p=perimetro(Pb, Pc)/2;
return Math.sqrt(p*(p- new Linea(this, Pb).lunghezza())*(p- new Linea(Pb, Pc).lunghezza())*(p- new Linea(Pc, this).lunghezza()));
}
// verifica se il punto in corso e' interno ad un triangolo
boolean interno(Punto Pa, Punto Pb, Punto Pc) {
return (Math.abs(area(Pa, Pb) + area(Pb, Pc) + area(Pc, Pa) - Pa.area(Pb, Pc)) < .0000001);
}
// Verifica se il punto in corso e' interno ad una circonferenza data
boolean interno(Circonferenza C) {
return new Linea(C.centro, this).lunghezza() < C.raggio;
}
Per la class Linea sono stati introdotti seguenti nuovi methods:
// angoolo in radianti nel pianoo XY rispetto oall'asse delle ascisse X.
// L'angolo ritornato varia da -PI/2 a 2*PI ed è un fatto importante perchè, di
// solito gli angoli tra rette vengono espressi da -PI/2 a PI/2 senza poter
// rappresentare così se il verso della linea in questione è da P1 a P2 o viceversa
double angoloXY() {
double ang=0;
if ((deltaX()<0) && (deltaY()<0)) ang= Math.atan(m)+Math.PI;
if ((deltaX()<0) && (deltaY()>=0)) ang= Math.atan(m)+Math.PI;
if ((deltaX()>=0) && (deltaY()<0)) ang= Math.atan(m);
if ((deltaX()>=0) && (deltaY()>=0)) ang= Math.atan(m);
return ang;
}
// circonferenza tangente alla linea in corso con centro in un punto qualsiasi
Circonferenza tangente(Punto centro) {
return new Circonferenza(centro, perp(centro).lunghezza());
}
Buon divertimento!
|
||
|
||
MokaByte ricerca
nuovi collaboratori
|
||
|