Sei sulla pagina 1di 11

GRAFICA

IN JAVA PER DISEGNARE FIGURE GEOMETRICHE Domenico Sacc


1. Utilizzo delloggetto graphics
Nel package System personalizzato per il corso disponibile loggetto graphics che include alcuni metodi elementari di grafica. A tale scopo viene automaticamente messo a disposizione un pannello (cio una specie di terminale grafico) in cui vengono visualizzate le operazioni di grafica - il pannello abilitato sia nel caso che il programma venga lanciato come una classica applicazione Java sia nel caso di esecuzione come applet. Il pannello un rettangolo suddiviso in un numero prefissato di punti (chiamati pixel). Esso pu essere considerato una matrice di pixel con (dimY +1) righe e (dimX +1) colonne la numerazione parte da 0. Le coordinate dei quattro vertici del pannello sono: (0, 0) vertice in alto a sinistra (0, dimY) vertice in basso a sinistra (dimX, 0) vertice in alto a destra (dimX, dimY) vertice in basso a destra. I comandi per conoscere le dimensioni del pannello sono: System.graphics.getDimX (): restituisce la larghezza (width) totale dellarea grafica (in pixel) come intero System.graphics.getDimY (): restituisce laltezza (height) totale dellarea grafica (in pixel) come intero. Si noti che nel caso il programma sia lanciato come applicazione Java, la dimensione del pannello prefissata (ma pu essere modificata in corso di utilizzo) mentre nel caso sia lanciata come applet essa dipende dallo spazio indicato nella pagina html per lapplet:
<applet code="nomeClasse.class" archive="nomeClasse.jar" height="500" width="800">

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.

2. Disegno di una parabola


Vogliamo disegnare una parabola descritta dallequazione Y = a X2 + b X + c cio con asse parallela allasse Y. Ricordiamo che il vertice della parabola (-b/2a; (b2-4ac)/4a ) e la concavit rivolta verso lalto se a > 0 e verso il basso se a < 0. Ad esempio la parabola Y = X2 X 6 ha la concavit rivolta verso lalto in quanto a = 1 > 0

mentre la parabola Y = 2X2 +3 ha concavit rivolta verso il basso:

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

V[i] < minMax[0] ) minMax[0] = V[i];

if ( V[i] > minMax[\] ) minMax[1] = V[i]; return minMax; }

Le seguenti due istruzioni in discretizzaPuntiParabola:


rangeY[0]=trovaMinimo(vectorY); rangeY[1]=trovaMassimo(vectorY);

vanno allora sostituite dalla seguente unica istruzione:


rangeY=trovaMinimoMassimo(vectorY);

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);

for(int i=0;i< dimX-1;i++){ System.graphics.drawLine(i, getPuntoOrdinata(vectorY[i]), i+1, getPuntoOrdinata(vectorY[i+1])); } }

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]?");

comando = Character.toUpperCase(reader.nextChar()); }; System.out.println("bye"); }

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.

4. Disegno di una iperbole


Vogliamo disegnare una iperbole con asintoti paralleli agli assi e con centro di simmetria traslato rispetto allorigine degli assi, descritta dallequazione Y = (aX+b)/(cX+d) con c 0 e ad bc. Il centro di simmetria ha coordinate (-b/a, b/d). In figura la rappresentazione grafica di una iperbole con centro di simmetria (2,2):

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

Potrebbero piacerti anche