Grafica in Java v1
Grafica in Java v1
Nellesempio
dimY
500
pixel
e
dimX
800
pixel.
Il
comando
per
disegnare
una
linea
tra
due
punti
del
pannello
il
seguente:
System.graphics.drawLine
(
int
x1,
int
y1,
int
x2,
int
y2
):
disegna
il
segmento
che
unisce
i
punti
di
coordinate
(x1,
y1)
ed
(x2,
y2).
La
linea
viene
disegnata
con
il
colore
corrente.
Allinizio
del
programma
il
colore
corrente
quello
di
default
(tipicamente
il
nero).
E
possibile
rendere
corrente
un
colore
diverso
tramite
il
comando:
System.graphics.setColor
(Color
col):
viene
reso
corrente
il
colore
col.
Per
utilizzare
questo
metodo
occorre
importare
il
package
java.awt.Color.
Il
parametro
cc
pu
assumere
uno
dei
seguenti
valori:
Color.BLACK
(nero),
Color.BLUE
(blu),
Color.CYAN
(ciano),
Color.DARK_GRAY
(grigio
scuro),
Color.GRAY
(grigio),
Color.LIGHT_GRAY
(grigio
chiaro),
Color.GREEN
(verde),
Color.MAGENTA
(magenta),
Color.ORANGE
(arancio),
Color.PINK
(rosa),
Color.RED
(rosso),
Color.WHITE
(bianco),
Color.YELLOW
(giallo).
Questa
porzione
di
codice
disegna
in
nero
i
4
lati
del
pannello,
poi
in
blu
le
due
mediane
ed,
infine,
in
rosso
le
due
diagonali:
import java.awt.Color; import system.Scanner; import system.SystemApplet; import system.System; public class Prova_graphics extends SystemApplet { public static void main(String[] args) { int dimX = System.graphics.getDimX();
int dimY = System.graphics.getDimY(); System.graphics.setColor(Color.black); System.graphics.drawLine(0, 0, 0, dimY); // lato verticale a sinistra System.graphics.drawLine(dimX, 0, dimX, dimY); // lato verticale a destra System.graphics.drawLine(0, 0, dimX, 0); // lato orizzontale in alto System.graphics.drawLine(0, dimY, dimX, dimY); // lato orizzontale in basso System.graphics.setColor(Color.blue); System.graphics.drawLine(dimX/2, 0, dimX/2, dimY); // mediana orizzontale System.graphics.drawLine(0, dimY/2, dimX, dimY/2); // mediana verticale System.graphics.setColor(Color.red); System.graphics.drawLine(0, 0, dimX, dimY); // diagonale dallalto verso il basso System.graphics.drawLine(dimX, 0, 0, dimY); // diagonale dal basso verso lalto } }
Per ripulire il pannello, va utilizzato il comando: System.graphics.clear ( ). E possibile scrivere stringhe di testo con il comando: System.graphics.drawString (String str, int x, int y): scrive la stringa str in orizzontale a partire dal punto di coordinate (x, y). E possibile disegnare rettangoli, eventualmente colorati, con i comandi: System.graphics.drawRoundRect (int x, int y, int width, int height, int arcWidth, int arcHeight): disegna il rettangolo di base width e altezza height il cui vertice superiore sinistro posto nel punto di coordinate (x,y). I parametri arcWidth e arcHeight possono essere utilizzati per arrotondare gli angoli (porre a 0 questi due parametri per non arrotondare gli angoli). System.graphics.fillRoundRect (int x, int y, int width, int height, int arcWidth, int arcHeight): disegna un rettangolo con le caratteristiche del comando precedente e lo colora utilizzando il colore corrente. Sono disponibili ulteriori comandi per disegnare ellissi, cerchi, archi, spezzate e poligoni. Per dettagli consultare la guida alluso del package System.
Il
metodo
main
consiste
nella
lettura
dei
dati
della
parabola,
nella
scrittura
dei
suoi
dati
(vertice
e
intersezioni
con
gli
assi)
e
una
sessione
interattiva
in
cui
viene
chiesto
se
si
vuole
disegnare
la
parabola
e,
in
caso
positivo,
in
quale
range
dellasse
X.
Il
main
il
seguente:
public static void main(String[] args) { System.out.println("Disegno di una parabola y = ax^2+bx+c"); // lettura parametri a, b e c della parabola letturaParabola(); // scrittura vertice e intersezione della parabola con gli assi scritturaParabola(); // sessione interattiva per il disegno della parabola System.out.print("Vuoi disegnare la parabola [s/n]?"); char comando = Character.toUpperCase(reader.nextChar()); boolean inizio = true; while ( comando == 'S' ) { if (inizio ) { dimX = System.graphics.getDimX(); dimY = System.graphics.getDimY(); // vectorY memorizza un valore di Y per ogni punto di X vectorY = new double[dimX+1]; } inizio = false; // Lettura area di disegno: Xmin e Xmax letturaRangeX (); discretizzaPuntiParabola(); disegnaAssi(); disegnaParabola(); System.out.print("Disegno di altra porzione della parabola [s/n]?"); comando = Character.toUpperCase(reader.nextChar()); } System.out.println("bye"); }
Come
si
vede,
il
metodo
molto
astratto
in
quanto
fa
un
utilizzo
ampio
di
metodi
la
sua
lettura
immediata
in
quanto
i
dettagli
implementativi
sono
rinviati
alla
scrittura
dei
vari
metodi.
Inoltre
sono
utilizzate
le
seguenti
variabili
statiche:
dimX,
dimY
e
vectorY.
Va
precisato
luso
della
variabile
booleana
inizio:
essa
serve
per
dimensionare
solo
la
prima
volta
vectorY.
Mostriamo
ora
la
scrittura
dei
metodi
letturaParabola
e
scritturaParabola.
static void letturaParabola() { do { System.out.println("Inserisci a: ");
a = reader.nextDouble(); System.out.println("Inserisci b: "); b = reader.nextDouble(); System.out.println("Inserisci c: "); c = reader.nextDouble(); if ( a== 0 && b==0) System.out.println("Errore: a e b entrambi 0"); } while ( a == 0 && b == 0 ); } static void scritturaParabola() { double D = b*b-4*a*c; // discriminante if ( a!=0 ) { // parabola non degenere System.out.println("Coordinate del Vertice"); System.out.print("X = " + -b/(2*a)); System.out.println("; Y = " + -D/(4*a)); if ( D > 0 ) { System.out.println("2 intersezioni con asse X"); double rD = Math.sqrt(D); // radice quadrata del discriminante double x1 = (-b-rD)/(2*a), x2 = (-b+rD)/(2*a); System.out.print("X1 = " + x1 ); System.out.println("; X2 = " + x2 ); } else if ( D < 0 ) System.out.println("Nessuna intersezione con asse X"); else { System.out.println("1 intersezione con asse X"); System.out.print("X = " + (-b/(2*a)) ); } } else { System.out.println("Parabola degenere: retta"); System.out.println("Intersezione con asse X"); System.out.println("X = " + (-c/b) ); } System.out.println("Intersezione con asse Y"); System.out.println("Y = " + c ); }
I
tuoi
metodi
fanno
entrambi
utilizzo
delle
variabili
di
tipo
double
a,
b,
c:
esse
vengono
dichiarate
di
tipo
statico
in
modo
che
possano
essere
condivise.
Decidiamo
di
dichiarare
di
tipo
statico
anche
reader,
che
viene
utilizzato
anche
dal
metodo
letturaRangeX
che
legge
lintervallo
delle
X
in
cui
disegnare
la
parabola:
static void letturaRangeX ( ) { System.out.println("Lettura area di disegno: Xmin e Xmax"); do { System.out.println("Inserisci Xmin: "); rangeX[0] = reader.nextDouble(); System.out.println("Inserisci Xmax: "); rangeX[1] = reader.nextDouble(); if ( rangeX[1]<= rangeX[0]) System.out.println("Errore: Xmax <= Xmin"); } while ( rangeX[1] <= rangeX[0] ); }
Larray
rangeX,
che
memorizza
gli
estremi
dellintervallo
di
disegno,
definito
come
variabile
statica
in
quanto
condiviso
da
altri
metodi,
tra
cui
discretizzaPuntiParabola :
static void discretizzaPuntiParabola() { scaleFactorX = ( rangeX[1] - rangeX[0])/dimX; vectorY[0]=getOrdinata(rangeX[0]); vectorY[dimX]=getOrdinata(rangeX[1]); for (int i=1; i < dimX; i++ ) vectorY[i]=getOrdinata(rangeX[0]+scaleFactorX*i); rangeY[0]=trovaMinimo(vectorY); rangeY[1]=trovaMassimo(vectorY); scaleFactorY = ( rangeY[1] - rangeY[0])/dimY; // il vertice viene leggermente spostato dal bordo if ( a > 0 ) // concavit verso lalto rangeY[0] -= 10*scaleFactorY; else if ( a < 0 ) // concavit verso il basso rangeY[1] += 10*scaleFactorY; // se a != 0 viene ricalcolato il fattore di scala if ( a != 0 ) scaleFactorY = ( rangeY[1] - rangeY[0])/dimY; }
Il
metodo
innanzitutto
acquisisce
il
numero
di
punti
dimX
disponibili
per
il
disegno
nella
direzione
X
e
il
numero
dimY
nella
direzione
Y
come
avevamo
gi
scritto,
queste
variabili
sono
dichiarate
statiche.
Viene
quindi
calcolato
il
fattore
di
scala
in
direzione
X
in
modo
che
il
rangeX
da
disegnare
sia
discretizzato
nei
dimX
punti
a
disposizione.
In
corrispondenza
con
ciascuno
di
tali
punti
X
viene
memorizzato
il
valore
del
corrispondente
Y
(ordinata)
sulla
base
dellequazione
della
parabola
le
ordinate
sono
memorizzate
su
un
apposito
vettore
statico
vectorY
con
tanti
elementi
quanti
sono
i
punti
discretizzati
nella
dimensione
X.
Viene
quindi
calcolato
il
fattore
di
scala
per
la
direzione
Y
sulla
base
del
valore
minimo
e
massimo
delle
ordinate
tale
fattore
viene
un
po
spostato
per
evitare
di
dover
schiacciare
il
vertici
sul
bordo
del
disegno.
I
metodi
trovaMassimo
e
trovaMinimo
sono
semplici
metodi
per
cercare
rispettivamente
massimo
e
minimo
in
un
vettore
visto
il
loro
carattere
di
metodi
standard,
abbiamo
preferito
non
fare
uso
in
essi
di
variabili
statiche:
static double trovaMinimo ( double [] V ) { double minimo = V[0]; for ( int i = 1; i < V.length; i++ ) if ( V[i] < minimo ) minimo = V[i]; return minimo; } static double trovaMassimo ( double [] V ) { double massimo = V[0]; for ( int i = 1; i < V.length; i++ ) if ( V[i] > massimo ) massimo = V[i]; return massimo; }
Per
ottimizzare
il
codice
avremmo
anche
potuto
utilizzare
un
unico
metodo
che
calcolo
minimo
e
massimo
contemporaneamente:
static double[] double [] minMax[0] for ( int trovaMinimoMassimo ( double [] V ) { minMax = new double[2]; = minMax[1] = V[0]; i = 1; i < V.length; i++ )
if ( else
Il
metodo
getOrdinata
utilizza
un
parametro
formale
per
passare
lascissa
in
corrispondenza
della
quale
calcolare
lordinata
e
le
variabili
statiche
che
memorizzano
i
coefficienti
dellequazione
della
parabola:
public static double getOrdinata (double ascissa) {
return a*ascissa*ascissa+b*ascissa+c;
}
Il
metodo
disegna
assi
valuta
se
allinterno
del
range
X,
dato
in
input,
e
del
range
Y
calcolato,
ricadono
gli
assi
X
e
Y
e,
nel
caso,
li
disegna.
public static void disegnaAssi() { System.graphics.clear(); System.graphics.setColor(Color.black); // Asse Y if ( rangeX[0] <= 0 && rangeX[1]>=0 ) System.graphics.drawLine(getPuntoAscissa(0), 0, getPuntoAscissa(0), dimY); // Asse X if ( rangeY[0] <= 0 && rangeY[1]>=0 ) System.graphics.drawLine(0, getPuntoOrdinata(0), dimX, getPuntoOrdinata(0)); }
Per
il
disegno
dei
due
assi,
va
individuata
la
posizione
dellorigine
tramite
opportuna
scalatura
delle
ascisse
con
il
metodo
getPuntoAscissa
e
delle
ordinate
con
il
metodo
getPuntoOrdinata
(poich
le
ordinate
vanno
dallalto
verso
il
basso,
esse
vanno
ribaltate
sottraendole
a
dimY
in
modo
da
riportarle
dal
basso
verso
lalto)
:
public static int getPuntoOrdinata ( double v ) { return dimY - (int) Math.round((v-rangeY[0])/scaleFactorY); } public static int getPuntoAscissa ( double v ) { return (int) Math.round((v-rangeX[0])/scaleFactorX); }
Il
disegno
della
parabola
effettuato
dal
metodo
disegnaParabola
che
non
fa
altro
che
raccordare
con
delle
linee
tutti
i
dimX
punti
della
parabola,
equamente
intervallati
sulle
ascisse,
tramite
opportuna
scalatura
delle
ordinate
con
il
metodo
getPuntoOrdinata:
public static void disegnaParabola( ) { System.graphics.setColor(Color.red);
Per
concludere,
presentiamo
di
seguito
la
classe
Parabola
che
contiene
le
variabili
statiche
e
i
metodi
(che
per
brevit
non
vengono
riportati
nuovamente):
public class Parabola { // variabili globali statiche (visibili da tutti i metodi) static int dimX, dimY; // numero di punti nelle due direzioni static double a, b, c; // coefficienti dell'equazione della parabola // range (valori min e max) nella direzione X static double [] rangeX = new double[2]; // range (valori min e max) nella direzione Y static double [] rangeY = new double[2]; static double scaleFactorX, scaleFactorY; // fattori di scala nelle 2 direzioni static double [] vectorY; // valori di Y per ogni punto X del range static Scanner reader = new Scanner(System.in); // seguono le definizioni di tutti i metodi }
3.
Applicazione
del
disegno
di
una
parabola:
diagrammi
dei
momenti
e
dei
tagli
in
una
trave
poggiata
con
carico
distribuito
Consideriamo
una
trave
di
una
certa
lunghezza
L
(misurata
in
metri)
poggiata
ai
due
estremi
su
appositi
appoggi
A
e
B
non
solidali
con
essa
(ad
esempio
un
ponticello
poggiato
sopra
due
sponde
laterali)
su
cui
grava
un
carico
distribuito
q,
misurato
in
kg
/
m.
La trave scarica sui due appoggi A e B una forza totale verso il basso Q pari a q * L. Pertanto essa dovr essere equilibrata da due forze verso lalto agli appoggi Va e Vb, cio Va + Vb = q * L. Considerata la simmetria Va = Vb = q * L / 2. La trave ai due estremi quindi sollecitata da una forza verticale chiamata taglio mentre non esiste alcun momento (cio sollecitazione a rotazione) in quanto la trave non incastrata agli estremi ma libera di ruotare (e quindi non subisce alcun momento). E interessante conoscere le sollecitazioni (taglio e momento) su ogni sezione della trave, ad esempio ad una qualsiasi distanza x dallestremo A. Separiamo i due pezzi di trave "tagliati " dalla sezione a distanza x, e applichiamo ad esse le azioni che si scambiavano quando erano unite. Esse sono: una forza parallela alla sezione, che viene detta taglio e indicata con T, una forza perpendicolare alla sezione, che viene detto sforzo normale ed indicato con N e da un momento flettente che viene indicato con M.
Ovviamente, gli sforzi che il tratto di destra esercita sul tratto di trave di sinistra sono uguali e contrari agli sforzi che il tratto di sinistra esercita su quello di destra (azioni mutue). Per calcolare tali azioni, vanno scritte le 3 equazioni di equilibrio: equilibrio alla traslazione verticale, equilibrio alla traslazione orizzontale ed equilibrio alla rotazione, ovviamente, possiamo scrivere tali equazioni di equilibrio, dopo avere calcolato le reazioni vincolari Va e Vb . Assumendo che non ci sia sollecitazione orizzontale (cio la trave non sollecitata alla traslazione orizzontale), dobbiamo scrivere due equazioni di equilibrio: traslazione verticale e rotazione. Considerando il tratto di sinistra della trave di lunghezza x, abbiamo le seguenti equazioni: EQUAZIONE DEL TAGLIO: T(x) = Va q * x, cio alla distanza x dallappoggio A, vi una spinta verso il basso che deve controbilanciare, insieme alla porzione di carico distribuito q sulla tratta x, la forza verso lalto Va. Poich Va = q * L / 2, lequazione del taglio diventa: T(x) = q * x + q * L / 2 Tale equazione rappresenta una retta che nellintervallo [0, L] costituisce il diagramma dei tagli sulla trave. Tale diagramma ha il seguente formato:
Allestremo A, il taglio ha valore q * L / 2, convenzionalmente di segno positivo; a met trave il taglio 0; allestremo B ha valore q * L / 2. Dato un qualsiasi x nellintervallo [0, L], possibile calcolare il valore del taglio tramite il diagramma. EQUAZIONE DEL MOMENTO: M(x) = Va * x q * x * x / 2, cio alla distanza x dallappoggio A, vi una rotazione causata dalla forza in A moltiplicata per il braccio x, spinta verso il basso che deve controbilanciare, insieme al momento generato dalla porzione di carico distribuito q sulla tratta x, il momento generato dalla forza Va moltiplicata per il braccio x. Si noti che il momento generato dal carico distribuito va calcolato sostituendo il carico distribuito con un carico concentrato q * x posto alla distanza x / 2. Poich Va = q * L / 2, lequazione del momento diventa: M(x) = (q/2) x 2 + (q * L / 2) x Tale equazione rappresenta una parabola che nellintervallo [0, L] costituisce il diagramma dei momenti sulla trave. Tale diagramma ha il seguente formato:
Ai
due
estremi
A
e
B,
il
momento
nullo;
esso
ha
il
valore
massimo
di
q
*
L2
/
8
al
centro
della
trave
(cio
per
x
=
L
/
2),
convenzionalmente
di
segno
positivo;
a
met
trave
il
taglio
0;
allestremo
B
ha
valore
q
*
L
/
2.
Dato
un
qualsiasi
x
nellintervallo
[0,
L],
possibile
calcolare
il
valore
del
momento
tramite
il
diagramma.
Passiamo
ora
a
presentare
un
programma
che
chiede
in
input
la
dimensione
L
della
trave
(in
metri)
e
il
carico
distribuito
q
(in
kg
/
m),
calcola
le
reazioni
Va
e
Vb,
lequazione
della
parabola
dei
momenti,
quella
della
retta
dei
tagli,
disegna
i
diagrammi
dei
momenti
e
dei
tagli
e
poi
chiede
interattivamente
se
calcolare
le
sollecitazioni
in
qualche
punto
x
della
trave
per
ogni
valore
introdotto,
vengono
restituiti
i
valori
del
momento
e
del
taglio
e
viene
tracciata
sui
diagramma
la
posizione
di
x.
Il
metodo
main
il
seguente:
public static void main(String[] args) { System.out.println("Diagrammi dei momenti e tagli di una trave poggiata"); // lettura lunghezza e carico distribuito della trave letturaTrave(); // scrittura reazioni agli appoggi, momento massimo e // equazioni di taglio e momento scritturaMomentoTaglio(); dimX = System.graphics.getDimX(); dimY = System.graphics.getDimY(); vectorY_M = new double[dimX+1]; // un valore del momento per ogni X vectorY_T = new double[dimX+1]; // un valore del taglio per ogni X // disegno dei diagrammi dei momenti e dei tagli System.graphics.clear(); disegnaTrave(); discretizzaPuntiMomentiTagli(); disegnaMomento(); disegnaAsseTaglio(); disegnaTaglio(); // sessione interattiva per il calcolo del momento e taglio in un punto System.out.print("Momento e taglio in un punto [s/n]?"); char comando = Character.toUpperCase(reader.nextChar()); boolean inizio=true; while ( comando == 'S' ) { if ( !inizio ) { System.graphics.clear(); disegnaTrave(); discretizzaPuntiMomentiTagli(); disegnaMomento(); disegnaAsseTaglio(); disegnaTaglio(); }; inizio=false; double x = leggiAscissaPunto(); calcolaMomentoTaglioPunto(x); disegnaAssePunto(x); System.out.print("Momento e taglio in un altro punto [s/n]?");
La variabile booleana inizio serve per evitare di ridisegnare i diagrammi allinizio; successivamente vanno ridisegnati per rimuovere la traccia della sezione x disegnata al passo precedente. Lo schema del programma molto simile a quello del disegno di una parabola. In questo caso dobbiamo disegnare sia una parabola che una retta contemporaneamente pertanto dividiamo larea di disegno in due parti: in alto il diagramma dei momenti e in basso quello dei tagli. Si noti che, essendo la retta un caso degenere della parabola, possiamo riutilizzare lo stesso codice. Abbiamo per bisogno di due vettori in cui memorizzare le ordinate della parabola e quelle della retta: vectorY_M e vectorY_T. Lasciamo allo studente il compito di scrivere i metodi e la classe, che comunque riportata sul sito del corso.
Il
metodo
main
consiste
nella
lettura
dei
dati
delliperbole,
nella
scrittura
dei
suoi
dati
(centro
di
simmetria
dove
si
intersecano
i
due
asintoti
e
le
intersezioni
con
gli
assi)
e
una
sessione
interattiva
in
cui
viene
chiesto
se
si
vuole
disegnare
la
parabola
e,
in
caso
positivo,
in
quale
range
dellasse
X.
Il
main
il
seguente:
public static void main(String[] args) { System.out.println("Disegno di una iperbole y = (ax+b)/(cx+d)"); // lettura parametri a, b, c, d dell'iperbole letturaIperbole(); // scrittura centro simmetria e intersezione dell'iperbole con gli assi scritturaIperbole(); // sessione interattiva per il disegno della parabola System.out.print("Vuoi disegnare l'iperbole [s/n]?"); char comando = Character.toUpperCase(reader.nextChar()); while ( comando == 'S' ) { // Lettura area di disegno: Xmin e Xmax letturaRangeX (); discretizzaPuntiIperbole();
10
disegnaAssi();
Lasciamo
allo
studente
il
compito
di
scrivere
i
metodi
e
la
classe,
che
comunque
riportata
sul
sito
del
corso.
E
importante
sottolineare
che
in
prossimit
dellasintoto
verticale
le
ordinate
delliperbole
diventano
estremamente
elevati
in
valore
assoluto,
per
cui
la
operazioni
di
scalatura
potrebbero
schiacciare
sullasse
X
i
valori
delle
altre
ordinate.
Per
ridurre
questo
inconveniente,
nel
caso
che
un
valore
di
X
disti
dallasintoto
verticale
meno
di
met
del
passo
di
discretizzazione,
esso
viene
distanziato
a
met
del
passo.
Tale
tecnica
implementata
dal
seguente
metodo
public static double getOrdinata (double ascissa) { if ( ascissa <= X_CS && ascissa > X_CS-deltaX/2) ascissa = ascissa -deltaX/2; else if ( ascissa > X_CS && ascissa < X_CS+deltaX/2) ascissa = ascissa +deltaX/2; return (a*ascissa+b)/(c*ascissa+d); }
Lasintoto verticale passa per il punto con ascissa X_CS. Se lascissa x per cui va calcolata lordinata molto vicino allasintoto, essa va spostata a sinistra o a destra per mantenerla lontana dallassintoto di almeno mezzo passo.
11