/* Il seguente programma implementa l'algoritmo per la rappresentazione di funzioni in due variabili come proposto da Penna-Patterson ("Projective Geometry and its Application to Computer Graphics") a pag. 255; viene anche implementato l'algoritmo per le linee nascoste (specifico al caso) come suggerito dai due autori. Dario Cozzi dc486474@silab.dsi.unimi.it Giuseppe Cerrigoni gc529551@silab.dsi.unimi.it */ import java.awt.*; public class Funzioni3D extends java.applet.Applet{ //distanza del piano di vista dal centro dell'oggetto (modificabile) public static short d=100; //coordinate sferiche del centro di prospettiva E (occhio) modificabili public static short rho=(short)(d+25); //distanza del centro di prospettiva dall'oggetto relativa a d public static float theta=(float) (Math.PI/4), //angolo di rotazione di E attorno all'asse Z phi=(float) (Math.PI/4); //elevazione dell'occhio dal piano XY //coordinate cartesiane della finestra del piano di vista nello spazio private final short h=1,k=1; //fisse; angolo in alto a sinistra=(-h,k),simmetrica //coordinate dell'area grafica (canvas) public short u0,v0,u1,v1; // W=((u0,v0),(u1,v0),(u1,v1),(u0,v1)),O=(u0,v1) //range di variabilita' delle var. indipendenti X,Y (modificabili) public static float L=(float)(Math.PI), //range di variabilita' di X: -L2D) /* def. M12=M1 x M2; M1:="Matrice di Proiezione [4x4] rk=3" M2:="Matrice di parametrizzazione [4x3] rk=3" */ private float M12[][]=new float [4][3], //E' data dal Libro(cfr. Penna-Patterson pag. 238) M4[][]=new float [3][3]; //Matrice di trasformazione [3x3] rk=3 public static float M[][]=new float[4][3]; //M=M1*M2*M4; definisce l'operazione completa di vista public static short base, //larghezza dell'area grafica altezza; //altezza dell'area grafica //pannelli costituenti l'applet: Grafico grafico=new Grafico(this); //area grafica Parametri parametri=new Parametri(grafico); //barra dei parametri BarraMenu menu=new BarraMenu(this,parametri,grafico); //barra dei menu' Opzioni opzioni=new Opzioni(this,grafico,parametri,menu);//barra delle opzioni /*----------------------------------------------------------------------------------------------*/ public void init(){ setBackground(Color.gray); //strutturo l'applet a pannello a griglia setLayout(new BorderLayout(5,5)); add("North",menu); //in alto c'e la barra dei menu' add("South",parametri); //in basso le caselle dei parametri add("East",opzioni); //a destra ci sono le opzioni add("Center",grafico); //al centro visualizzo il grafico della funzione grafico.setBackground(Color.white); grafico.repaint(); //visualizzo la funzione iniziale (default) }//fine metodo init(); /*----------------------------------------------------------------------------------------------*/ public void impostaMatrici() {/* inizializza le matrici M12,M4 in base ai valori correnti dei parametri e poi calcola M=M12*M4. */ M12[0][0]=(float)((d-rho)*Math.sin(theta)); M12[0][1]=(float)((d-rho)*Math.cos(phi)*Math.cos(theta)); M12[0][2]=(float)(-Math.sin(phi)*Math.cos(theta)); M12[1][0]=(float)((rho-d)*Math.cos(theta)); M12[1][1]=(float)((d-rho)*Math.cos(phi)*Math.sin(theta)); M12[1][2]=(float)(-Math.sin(phi)*Math.sin(theta)); M12[2][1]=(float)((rho-d)*Math.sin(phi)); M12[2][2]=(float)(-Math.cos(phi)); M12[3][0]=M12[3][1]=M12[2][0]=0f; M12[3][2]=rho; M4[0][0]=(float)((u1-u0)/2); M4[0][1]=M4[0][2]=M4[1][0]=M4[1][2]=0f; M4[1][1]=(float)((v1-v0)/2); M4[2][0]=(float)((u1+u0)/2); M4[2][1]=(float)((v1+v0)/2); M4[2][2]=1f; //calcolo M (matrice completa di trasformazione) M=Product(M12,M4); }//fine metodo impostaMatrici(); /*----------------------------------------------------------------------------------------------*/ private float[][] Product(float[][] m1,float[][] m2) //implementa l'algoritmo del prodotto di matrici(specifico al mio caso) {float m[][]=new float[4][3]; for (byte riga=0; riga<4; riga++) for (byte colonna=0; colonna<3; colonna++) m[riga][colonna]=m1[riga][0]*m2[0][colonna]+ m1[riga][1]*m2[1][colonna]+ m1[riga][2]*m2[2][colonna]; return(m); }//fine metodo Product(); /*---------------------------------------------------------------------------------------------*/ public static void main(String args[]) { Finestra f = new Finestra("Funzioni a due variabili: z=f(x,y);"); f.resize(800,600); Funzioni3D funzioni3D= new Funzioni3D(); funzioni3D.init(); f.add("Center", funzioni3D); f.show(); } }//fine applet /************************************************************************************************/ class Finestra extends Frame { Finestra(String titolo) { super(titolo); } public boolean handleEvent(Event evt) { if (evt.id==Event.WINDOW_DESTROY) System.exit(0); return false; } }//fine classe finestra /*************************************************************************************************/ class BarraMenu extends Panel{ private Funzioni3D func; private Grafico graph; private Parametri param; public Choice funzione=new Choice(); //menu' di scelta per la funzione public Choice visualizza=new Choice(); //menu' di scelta per il tipo di visualizzazione public Choice assi=new Choice(); //menu' di scelta per gli assi cartesiani public Choice viste=new Choice(); //menu' di scelta dell'angolazione di vista public Checkbox hidden=new Checkbox(" Line",null,false); //opzione linee nascoste private byte old=1; //imposta i parametri della funzione ai valori di default public BarraMenu(Funzioni3D f,Parametri p,Grafico g) {this.func=f; this.param=p; this.graph=g; setLayout(new GridLayout(1,12,5,5)); //menu "funzione": funzione.addItem("Trigonometrica"); funzione.addItem("Sella"); funzione.addItem("Bump"); funzione.addItem("Paraboloide"); funzione.addItem("es. Patterson"); funzione.addItem("Discontinua"); //funzione predefinita funzione.select("Trigonometrica"); add(new Label("Funzione ")); add(funzione); // menu "Visualizza" visualizza.addItem("Colorato"); visualizza.addItem("Mono Color"); visualizza.addItem("Punti"); visualizza.addItem("Superficie"); visualizza.addItem("Scacchiera"); visualizza.addItem("Random()"); //scelta predefinita visualizza.select("Colorato"); add(new Label("Visualizza ")); add(visualizza); add(new Label("Hidden ",Label.RIGHT)); hidden.setState(graph.hidden); add(hidden); hidden.setBackground(Color.gray); //menu "Assi" assi.addItem("Standard"); assi.addItem("Framed"); assi.addItem("Nessuno"); //valore iniziale assi.select("Standard"); add(new Label("Assi")); add(assi); //menu "Viste" viste.addItem("Iniziale"); viste.addItem("Laterale"); viste.addItem("Alto"); viste.addItem("Angolata I"); viste.addItem("Angolata II"); viste.addItem("Manuale"); //vista di default viste.select("Iniziale"); add(new Label("Viste")); add(viste); } /*------------------------------------------------------------------------------------------*/ public boolean action(Event evt, Object arg) //gestisce gli eventi della barra menu' {byte index; index=(byte)(visualizza.getSelectedIndex()+1); switch (index) {case 1:graph.colorato=true; //Colorato graph.pieno=false; graph.scacchi=false; graph.random=false; graph.punti=false; break; case 2:graph.colorato=false;//Mono Color graph.scacchi=false; graph.pieno=false; graph.random=false; graph.punti=false; break; case 3:graph.colorato=false;//Punti graph.scacchi=false; graph.pieno=false; graph.random=false; graph.punti=true; break; case 4:graph.pieno=true; //Superficie graph.colorato=true; graph.scacchi=false; graph.random=false; graph.punti=false; break; case 5:graph.scacchi=true; //Scacchiera graph.colorato=false; graph.pieno=true; graph.random=false; graph.punti=false; break; case 6:graph.scacchi=false; //Random() graph.colorato=false; graph.pieno=true; graph.random=true; graph.punti=false; break; } index=(byte)(assi.getSelectedIndex()+1); switch (index) {case 1:graph.disegna=true; //Standard graph.normali=true; break; case 2:graph.disegna=true; //Framed graph.normali=false; break; case 3:graph.disegna=false;//Nessuno break; } index=(byte)(viste.getSelectedIndex()+1); switch (index) {case 1:func.theta=func.phi=(float)(Math.PI/4); //Iniziale break; case 2:func.theta=func.phi=(float)(Math.PI/2); //Laterale break; case 3:func.theta=func.phi=0f; //Alto break; case 4:func.theta=func.phi=(float)(Math.PI/3); //Angolata I break; case 5:func.theta=func.phi=(float)(Math.PI/6); //Angolata II break; } //menu' Funzione index=(byte)(funzione.getSelectedIndex()+1); graph.scelta=index; if (index!=old) //Ho scelto una nuova funzione {old=index; //salvo il valore vecchio di index switch (index) { case 1:graph.a=1.5f; //Trigonometrica graph.b=1; graph.c=graph.d=graph.e=0; param.aggiornaCaselle(); param.a.setEditable(true); param.b.setEditable(true); param.c.setEditable(false); param.d.setEditable(false); param.e.setEditable(false); break; case 2:graph.a=graph.b=graph.c=graph.d=1; //Sella graph.e=0; param.aggiornaCaselle(); param.a.setEditable(true); param.b.setEditable(true); param.c.setEditable(true); param.d.setEditable(true); param.e.setEditable(false); break; case 3:graph.a=5; //Bump graph.b=graph.c=-1; graph.d=graph.e=0; param.aggiornaCaselle(); param.a.setEditable(true); param.b.setEditable(true); param.c.setEditable(true); param.d.setEditable(true); param.e.setEditable(true); break; case 4:graph.a=graph.b=1; //Paraboloide graph.c=-10; graph.d=graph.e=0; param.aggiornaCaselle(); param.a.setEditable(true); param.b.setEditable(true); param.c.setEditable(true); param.d.setEditable(false); param.e.setEditable(false); break; case 5:graph.a=1; //Patterson graph.b=graph.c=0; graph.d=graph.e=0; param.aggiornaCaselle(); param.a.setEditable(true); param.b.setEditable(true); param.c.setEditable(true); param.d.setEditable(false); param.e.setEditable(false); break; case 6:graph.a=graph.b=graph.c=graph.d=graph.e=0; //Discontinua param.aggiornaCaselle(); param.a.setEditable(false); param.b.setEditable(false); param.c.setEditable(false); param.d.setEditable(false); param.e.setEditable(false); break; }//fine switch graph.repaint(); //con la nuova funzione l'applet riparte automaticamente }//fine if graph.hidden=this.hidden.getState(); //Casella di Controllo linee nascoste if (graph.hidden) {func.deltaY=.001f; func.opzioni.dy.setText("Not Used!");} else {if (func.deltaY==.001f) func.deltaY=.1f; func.opzioni.dy.setText("Delta Y : "+func.deltaY); func.opzioni.deltaY.setValue((int)(func.deltaY*10)); } return true; }//fine action() } /*************************************************************************************************/ class Opzioni extends Panel { private Funzioni3D func; private Grafico graph; private BarraMenu menu; private Parametri param; //etichette delle barre di scorrimento private Label d; //valore corrente di D private Label r; //valore corrente di Rho private Label l; //valore corrente di L private Label w; //valore corrente di W private Label dx; //valori dei passi di discretizzazione public Label dy; //deltaX e deltaY private Label rotaz; //valore del passo di rotazione degli assi //barre di scorrimento public Scrollbar D; //distanza dello schermo dall'origine degli assi spaziali public Scrollbar Rho; //distanza dell'occhio da d (distanza relativa) public Scrollbar L; //range di variabilita' di x public Scrollbar W; //range di variabilita' di y public Scrollbar deltaX; //passo di discretizazzione sull'asse x public Scrollbar deltaY; //passo di discretizazzione sull'asse y public Scrollbar ruota; //passo di rotazione degli assi //pulsanti di controllo public Button disegna; //comando esplicito per ridisegnare la funzione public Button reset; //ripristina le impostazioni iniziali public Opzioni(Funzioni3D f,Grafico g,Parametri p,BarraMenu m) {func=f; graph=g; param=p; menu=m; setLayout(new GridLayout(16,1,15,15)); add(d=new Label("d : "+func.d,Label.CENTER)); //visualizza il valore corrente D=new Scrollbar(Scrollbar.HORIZONTAL,(int)(func.d/10),0,1,100); D.setValue((int)(func.d/10)); //definisce il valore iniziale D.setLineIncrement(10); D.setPageIncrement(20); add(D); add(r=new Label("Rho: "+func.rho,Label.CENTER)); //visualizza il valore corrente Rho=new Scrollbar(Scrollbar.HORIZONTAL,(int)func.rho,0,0,300); Rho.setValue((int)(func.rho-func.d)); //definisce il valore iniziale add(Rho); add(l=new Label("X: "+func.L,Label.CENTER)); L=new Scrollbar(Scrollbar.HORIZONTAL,(int)(func.L*100),0,0,1000); L.setLineIncrement(10); L.setPageIncrement(100); add(L); add(w=new Label("Y: "+func.W,Label.CENTER)); W=new Scrollbar(Scrollbar.HORIZONTAL,(int)(func.W*100),0,0,1000); W.setLineIncrement(10); W.setPageIncrement(100); add(W); add(dx=new Label("Delta X: "+func.deltaX,Label.CENTER)); deltaX=new Scrollbar(Scrollbar.HORIZONTAL,(int)(func.deltaX*10),0,1,10); deltaX.setPageIncrement(2); add(deltaX); add(dy=new Label("Delta Y: "+func.deltaY,Label.CENTER)); deltaY=new Scrollbar(Scrollbar.HORIZONTAL,(int)(func.deltaY*10),0,1,10); deltaY.setPageIncrement(10); add(deltaY); add(rotaz=new Label("Ruota: "+graph.passo+"",Label.CENTER)); ruota=new Scrollbar(Scrollbar.HORIZONTAL,graph.passo,0,1,10); ruota.setPageIncrement(2); add(ruota); disegna=new Button("Disegna"); add(disegna); reset=new Button("Reset"); add(reset); } /*--------------------------------------------------------------------------------------------*/ public boolean handleEvent(Event evt) //gestisce gli eventi {short deltaRho; //distanza relativa tra rho e d deltaRho=(short)(func.rho-func.d); if (evt.target instanceof Scrollbar) {func.d=(short)D.getValue(); //barra di scorrimento D func.d=(short)(func.d*10); d.setText("d : "+func.d); func.rho=(short)(func.d+deltaRho); //mantengo sempre la medesima distanza relativa r.setText("Rho: "+func.rho); //aggiorno solo l'etichetta func.rho=(short)(Rho.getValue()+func.d); //barra di scorrimento Rho r.setText("Rho: "+func.rho); func.L=L.getValue(); //barra di scorrimento L func.L=func.L/100; l.setText("X: "+func.L); func.W=W.getValue(); //barra di scorrimento W func.W=func.W/100; w.setText("Y: "+func.W); func.deltaX=deltaX.getValue(); //barra di scorrimento deltaX func.deltaX=func.deltaX/10; dx.setText("Delta X: "+func.deltaX); if (!graph.hidden){ //disabilitata con l'algoritmo delle linee nascoste func.deltaY=deltaY.getValue(); //barra di scorrimento deltaY func.deltaY=func.deltaY/10; dy.setText("Delta Y: "+func.deltaY); } graph.passo=(byte)(ruota.getValue()); //barra di scorrimento Ruota rotaz.setText("Ruota :"+graph.passo+""); } else if (evt.target instanceof Button) //eventi dei Bottoni if (evt.arg.equals("Disegna")) graph.repaint(); else //il pulsante reset ripristina la situazione iniziale {//menu' Visualizza graph.colorato=true; //Colorato graph.pieno=false; graph.scacchi=false; graph.random=false; graph.punti=false; menu.visualizza.select(0); /*Casella di controllo Hidden Line graph.hidden=false; //no algoritmo linee nascoste(?) menu.hidden.setState(graph.hidden);*/ //menu' Assi //assi standard graph.disegna=true; graph.normali=true; menu.assi.select(0); //menu' Viste //vista iniziale func.theta=func.phi=(float)(Math.PI/4); menu.viste.select(0); //barra di scorrimento D func.d=100; //d=100 d.setText("d : "+func.d); D.setValue((int)(func.d/10)); //barra di scorrimento Rho //rho=125 func.rho=(short)(func.d+25); r.setText("Rho: "+func.rho); Rho.setValue((short)(func.rho-func.d)); //barra di scorrimento L //L=Pi func.L=(float)(Math.PI); l.setText("X: "+func.L); L.setValue((int)(func.L*100)); //barra di scorrimento W //W=Pi func.W=(float)(Math.PI); w.setText("X: "+func.W); W.setValue((int)(func.W*100)); //barra di scorrimento deltaX func.deltaX=.1f; //deltaX=0.1 dx.setText("Delta X: "+func.deltaX); deltaX.setValue((int)(func.deltaX*10)); //barra di scorrimento deltaY //deltaY=0.1 if (!graph.hidden){ func.deltaY=.1f; dy.setText("Delta Y: "+func.deltaY); deltaY.setValue((int)(func.deltaY*10)); } else func.deltaY=.01f; //algoritmo linee nascoste //barra di scorrimento Ruota //passo=1 graph.passo=1; rotaz.setText("Ruota :"+graph.passo+""); ruota.setValue(graph.passo); //Caselle dei parametri switch (graph.scelta) { case 1:graph.a=1.5f; //Trigonometrica graph.b=1; graph.c=graph.d=graph.e=0; break; case 2:graph.a=graph.b=graph.c=graph.d=1; //Sella graph.e=0; break; case 3:graph.a=5; //Bump graph.b=graph.c=-1; graph.d=graph.e=0; break; case 4:graph.a=graph.b=1; //Paraboloide graph.c=-10; graph.d=graph.e=0; break; case 5:graph.a=1; //esempio Patterson graph.b=graph.c=0; graph.d=graph.e=0; break; case 6:graph.a=graph.b=graph.c=graph.d=graph.e=0; //Discontinua break; }//fine switch param.aggiornaCaselle(); graph.repaint(); }//fine else if (evt.id==evt.KEY_PRESS) graph.repaint(); // sensibile anche agli eventi di tastiera return true; }//fine handleEvent() } /************************************************************************************************/ class Parametri extends Panel{ private Grafico graph; public TextField a=new TextField(5); public TextField b=new TextField(5); public TextField c=new TextField(5); public TextField d=new TextField(5); public TextField e=new TextField(5); public TextField f=new TextField(15); public Parametri(Grafico g)//metodo costruttore { graph=g; setLayout(new GridLayout(2,6,5,5)); add(new Label("a: ",Label.RIGHT)); add(a); a.setText(String.valueOf(graph.a)); a.setEditable(true); add(new Label("b: ",Label.RIGHT)); add(b); b.setText(String.valueOf(graph.b)); b.setEditable(true); add(new Label("c: ",Label.RIGHT)); add(c); c.setText(String.valueOf(graph.c)); c.setEditable(false); add(new Label("d: ",Label.RIGHT)); add(d); d.setText(String.valueOf(graph.d)); d.setEditable(false); add(new Label("e: ",Label.RIGHT)); add(e); e.setText(String.valueOf(graph.e)); e.setEditable(false); add(new Label("f(x,y): ",Label.RIGHT)); add(f); switch (graph.scelta) { case 1:f.setText("a*cos(bxy)"); break; case 2:f.setText("a*cos(by)+c*sin(dx)"); break; case 3:f.setText("a*exp( b*(x-d)^2+c*(y-e)^2 )"); break; case 4:f.setText("a*x^2+b*y^2+c"); break; case 5:f.setText("a*cos(sqrt((x-b)^2+(y-c)^2)"); break; case 6:f.setText("(2x^2+3y^2)/(x^2+y^2)"); break; }//fine switch } /*---------------------------------------------------------------------------------------------*/ public boolean action(Event evt,Object arg) {float letto; //valore rilevato dalla casella di testo if (evt.target instanceof TextField) {letto=0f; //valore di default //il parametro "a" deve essere vincolato solo per la funzione Paraboloide if (graph.scelta!=4) //leggo direttamente il parametro { try {graph.a=(Float.valueOf(a.getText())).floatValue(); } catch (NumberFormatException exc) {a.setText("Input non valido!");} } else //altrimenti ne controllo prima il valore {try {letto=(Float.valueOf(a.getText())).floatValue(); } catch (NumberFormatException exc) {a.setText("Input non valido!");} if (letto>5) {a.setText("Non minore di 5!"); graph.a=1f; } //lo riporto al valore iniziale else graph.a=letto; //rispetta i vincoli } //fine else //il parametro "b" deve essere controllato sia per il Paraboloide che per Bump if ( (graph.scelta!=3) && (graph.scelta!=4)) //leggo direttamente {try {graph.b=(Float.valueOf(b.getText())).floatValue(); } catch (NumberFormatException exc) {b.setText("Input non valido!");} } else //devo differenziare i due casi { if (graph.scelta==3) //Funzione Bump-> b deve essere negativo {try {letto=(Float.valueOf(b.getText())).floatValue(); } catch (NumberFormatException exc) {b.setText("Input non valido!");} if (letto>0) {b.setText("Non negativo!"); graph.b=-1f;} else graph.b=letto; //rispetta i vincoli }//fine Bump else // Paraboloide -> b deve essere minore di 5 {try {letto=(Float.valueOf(b.getText())).floatValue(); } catch (NumberFormatException exc) {b.setText("Input non valido!");} if (letto>5) {b.setText("Non minore di 5!"); graph.b=1f;} else graph.b=letto; //rispetta i vincoli }//fine Paraboloide }//fine else //per il parametro "c" idem come per "b" if ( (graph.scelta!=3) && (graph.scelta!=4)) {try {graph.c=(Float.valueOf(c.getText())).floatValue(); } catch (NumberFormatException exc) {c.setText("Input non valido!");} } else //devo differenziare i due casi { if (graph.scelta==3) //Funzione Bump-> c deve essere negativo {try {letto=(Float.valueOf(c.getText())).floatValue(); } catch (NumberFormatException exc) {c.setText("Input non valido!");} if (letto>0) {c.setText("Non negativo!"); graph.c=-1;} else graph.c=letto; //rispetta i vincoli }//fine Bump else // Paraboloide -> c deve essere minore di 5 {try {letto=(Float.valueOf(c.getText())).floatValue(); } catch (NumberFormatException exc) {c.setText("Input non valido!");} if (letto>5) {c.setText("Non minore di 5!"); graph.c=-10f;} else graph.c=letto; //rispetta i vincoli }//fine Paraboloide }//fine else //gli altri parametri non richiedono vincoli try {graph.d=(Float.valueOf(d.getText())).floatValue(); } catch (NumberFormatException exc) {d.setText("Input non valido!");} try {graph.e=(Float.valueOf(e.getText())).floatValue(); } catch (NumberFormatException exc) {e.setText("Input non valido!");} }//fine if graph.repaint(); //ridisegna la funzione coi nuovi parametri return true; }//fine action /*---------------------------------------------------------------------------------------------*/ public void aggiornaCaselle() {//aggiorna il contenuto delle caselle di testo a.setText(String.valueOf(graph.a)); b.setText(String.valueOf(graph.b)); c.setText(String.valueOf(graph.c)); d.setText(String.valueOf(graph.d)); e.setText(String.valueOf(graph.e)); switch (graph.scelta) { case 1:f.setText("a*cos(bxy)"); break; case 2:f.setText("a*cos(by)+c*sin(dx)"); break; case 3:f.setText("a*exp( b*(x-d)^2+c*(y-e)^2 )"); break; case 4:f.setText("a*x^2+b*y^2+c"); break; case 5:f.setText("a*cos(sqrt((x-b)^2+(y-c)^2)"); break; case 6:f.setText("(2x^2+3y^2)/(x^2+y^2)"); break; }//fine switch }//fine aggiornaCaselle() } /*************************************************************************************************/ class Grafico extends Canvas { private Funzioni3D func; //Valori di Massimo/Minimo assoluto assunti dalla Funzione public static float max=0,min=0; //variabile per la gestione del mouse private boolean premuto=false; //vera quando si preme il pulsante Sx. del mouse public static byte passo=1; //passo di rotazione degli assi //variabile per le diverse visualizzazioni public static boolean disegna=true, //vera se si desidera visualizzare gli assi normali=true, //vera se gli assi da visualizzare sono di tipo Normal colorato=true, //vera se si decide di visualizzare il reticolo a colori hidden=false, //vera se si visualizza con le linee nascoste pieno=false, //vera se visualizzo la funzione come superficie scacchi=false, //vera se visualizzo la funzione come scacchiera random=false, //vera se visualizzo con colori casuali punti=false; //vera se visualizzo la funzione per punti public static float a=1.5f,b=1,c=0,d=0,e=0; //parametri delle funzioni private Font f=new Font("Helvetica",Font.BOLD,12); //font di visualizzazione public static byte scelta=1; //specifica la funzione da rappresentare private byte scala=1; //scala di rappresentazione public static int maxV[],minV[],maxU[],minU[]; //usati dall'algoritmo delle linee nascoste /*----------------------------------------------------------------------------------------------*/ public Grafico(Funzioni3D f) //metodo costruttore { func=f; } /*----------------------------------------------------------------------------------------------*/ public void paint (Graphics g) { /* visualizza il grafico(supporto) della funzione Z=f(X,Y) e gli assi cartesiani in base alle impostazioni scelte */ this.set(); //rilevo le dim.dell'area grafico ed aggiorno le matrici g.setColor(Color.black); g.drawRect(1,1,func.base-1,func.altezza-1); if (!hidden) //Visualizzazione senza linee nascoste noHiddenLine(g); else hiddenLine(g); //si applica l'algoritmo delle linee nascoste //visualizzazione degli assi cartesiani if (disegna) // se si vuole visualizzare gli assi if (normali) //determino quale visualizzare assiNormali(g); else assiFramed(g); }//fine metodo paint() /*----------------------------------------------------------------------------------------------*/ public void set() { //rilevo le dimensioni dell'area grafica func.base=(short)(size().width); func.altezza=(short)(size().height); //inizializzo opportunamente le coordinate u0,v0,u1,v1; func.u0=func.v1=(short)0; //(u0,v1)=angolo in alto a sinistra func.u1=(short)(func.base-1); //(u1,v0)=angolo in basso a destra func.v0=(short)(func.altezza-1); //inizializzo le matrici precedenti func.impostaMatrici(); } /*-----------------------------------------------------------------------------------------------*/ private void noHiddenLine(Graphics g) { boolean firstPointY, //vera se si deve stampare il primo punto di una curva-Y linea; //vera se si sta visualizzando una linea coordinata Y float x,y, //Var. indipendenti (X,Y) z; //Var. dipendente (Z) Point py=new Point(0,0), //Punto da visualizzare sullo schermo curva-X px=new Point(0,0), //Punto da visualizzare sullo schermo curva-Y oldY=new Point(0,0), //Utile per stampare il primo punto oldX=new Point(0,0); //per il poligono short colore=0; //Nero, per l'effetto scacchiera /* linee coordinate X,Y: visualizzo il grafico della funzione sse il pulsante del mouse non e' stato premuto */ if (!premuto) //situazione iniziale {max=min=0; for (x=-(func.L); x<=func.L; x+=func.deltaX) { firstPointY=true; linea=false; for (y=-(func.W); y<=func.W; y+=func.deltaY) {z=F(x,y); //Visualizza Z=f(X,Y) (ci puo' essere una qualsiasi funzione) //imposto il colore in base all'opzione scelta if (!colorato) if (!scacchi && !random) g.setColor(Color.blue); else if (scacchi) //effetto a scacchiera.. {if ((colore%2)==0) g.setColor(Color.black); else g.setColor(Color.yellow); colore++; } else //effetto Arlecchino g.setColor(new Color((int)(Math.floor(Math.random()*256)), (int)(Math.floor(Math.random()*256)), (int)(Math.floor(Math.random()*256))) ); else //si e' deciso di visualizzare il grafico in base alle quote assunte,quindi.. //imposto il colore in base alla quota della funzione if (z<-1) g.setColor(Color.blue); else if (z>=-1 && z<=-.5) g.setColor(Color.green); else if (z>-.5 && z<.5) g.setColor(Color.yellow); else if (z>=.5 && z<=1) g.setColor(Color.orange); else g.setColor(Color.red); py=Converti(x,y,z);//Trasformo le coordinate spaziali in un punto bidimensionle if (firstPointY) oldY=py; //la prima volta disegna un punto.. firstPointY=false; if ((x+func.deltaX) <= func.L) //disegno il tratto di curva X compresa tra due curve Y succ. { z=F(x+func.deltaX,y); px=Converti(x+func.deltaX,y,z); //riempo la funzione col colore attuale (se l'opzione relativa e' attivata if (linea && pieno)//se sono sulla linea coordinata corrente allora riempo il disegno {int[] coordX={oldX.x,oldY.x,py.x,px.x}, coordY={oldX.y,oldY.y,py.y,px.y}; g.fillPolygon(coordX,coordY,4); } if (pieno) g.setColor(Color.black); if (punti) g.drawOval(px.x-1,py.y-1,1,1); //disegno solo un punto else g.drawLine(py.x,py.y,px.x,px.y); //disegno il tratto di curva X }//altrimenti non la disegno al fine di evitare sbavature della funzione if (pieno) g.setColor(Color.black); //linee coordinate in nero se visualizzo a sup. if (!punti) g.drawLine(oldY.x,oldY.y,py.x,py.y); oldX=px; //lo salvo per il ciclo successivo oldY=py; linea=true; }//fine for(y=-W..) }//fine for(x=-L..) //stampo in alto a Dx Max. e Min. della funzione g.setColor(Color.black); g.setFont(f); String s="MAX="+max+" Min="+min; g.drawString(s,func.base-(s.length()*10),50); //disegno ora gli assi cartesiani }//fine if(!premuto).. else //se viene premuto il pulsante del mouse o questo e' in movimento.. {//aggiorno il menu' visualizza func.menu.viste.select("Manuale"); //..stampo in alto a Sx. i valori Correnti di Theta e Phi (in gradi).. g.setColor(Color.blue); g.setFont(f); g.drawString("THETA="+Math.round(degree(func.theta))+" PHI="+Math.round(degree(func.phi))+"",20,50); //..e visualizzo soltanto gli assi cartesiani }//fine else }//fine noHiddenLine() /*----------------------------------------------------------------------------------------------*/ private Point Converti(float X, float Y, float Z) //trasforma le coordinate spaziali (X,Y,Z) nelle coordinate piane (x,y) {int x,y; //coordinate piane ricavate da X,Y,Z float w=func.M[0][2]*X+func.M[1][2]*Y+func.M[2][2]*Z+func.M[3][2]; x=(int)((func.M[0][0]*X+func.M[1][0]*Y+func.M[2][0]*Z+func.M[3][0])/w); y=(int)((func.M[0][1]*X+func.M[1][1]*Y+func.M[2][1]*Z+func.M[3][1])/w); return(new Point(x,y)); }//fine metodo Converti() /*-----------------------------------------------------------------------------------------------*/ private float F(float x, float y){ float z=0f; switch (scelta){ case 1:z=(float)(a*Math.cos(b*x*y)); break; case 2:z=(float)(a*Math.cos(b*y)+c*Math.sin(d*x)); break; case 3:z=(float)(a*Math.exp(b*Math.pow(x-d,2)+c*Math.pow(y-e,2))); break; case 4:z=(float)(a*Math.pow(x,2)+b*Math.pow(y,2)+c); break; case 5:z=(float)(a*Math.cos(Math.sqrt(Math.pow(x-b,2)+Math.pow(y-c,2)))); break; case 6:z=(float) ( (2*Math.pow(x,2)+3*Math.pow(y,2))/(Math.pow(x,2)+Math.pow(y,2)) ); break; } if (zmax) max=z; return(z); //N.B. Funzioni discontinue hanno punti all'infinito }//fine metodo F() /*-----------------------------------------------------------------------------------------------*/ private float degree(float rad) //trasforma un angolo da radianti a gradi {return ((float)(180*rad/Math.PI));} /*-----------------------------------------------------------------------------------------------*/ private float radiant(float deg) //trasforma un angolo da gradi a radianti {return ((float)(Math.PI*deg/180));} /*-----------------------------------------------------------------------------------------------*/ private void hiddenLine(Graphics g) { //visualizza il supporto della funzione applicando l'algoritmo delle linee nascoste boolean firstPointY, //vera se si deve stampare il primo punto di una curva-Y firstPointX; //vera se si deve stampare il primo punto di una curva-X float x,y, //Var. indipendenti (X,Y) z, //Var. dipendente (Z) traccia,//utile per disegnare i pezzetti di curve-X compresi tra due curve-Y ax, ay, slopeX, //pendenza segmento curva-X slopeY, //pendenza segmento curva-Y deltaU, //intrvallo di pixel in orizzontale tra due punti dello schermo deltaV; //intrvallo di pixel in verticale tra due punti dello schermo Point py=new Point(0,0), //Punto da visualizzare sullo schermo (curva-X) px=new Point(0,0), //Punto da visualizzare sullo schermo (curva-Y) old=new Point(0,0), //Utile per stampare il primo punto present=new Point(0,0); //punto del segmento old-p //inizializzo opportunamente gli array.. minV=new int[func.base]; //array sono oggetti maxV=new int[func.base]; minU=new int[func.altezza]; maxU=new int[func.altezza]; for (int i=0; i=-func.L; x-=func.deltaX) //qui cambia il for rispetto a noHiddenLine() { traccia=x; firstPointY=true; for (y=func.W; y>=-func.W; y-=func.deltaY) //anche qui cambia il for { //Visualizza Z=f(X,Y) (ci puo' essere una qualsiasi funzione) z=F(x,y); py=Converti(x,y,z);//Trasformo le coordinate spaziali in un punto bidimensionle if (colorato) if (z<-1) g.setColor(Color.blue); else if (z>=-1 && z<=-.5) g.setColor(Color.green); else if (z>-.5 && z<.5) g.setColor(Color.yellow); else if (z>=.5 && z<=1) g.setColor(Color.orange); else g.setColor(Color.red); else g.setColor(Color.blue); //monocolor if (firstPointY) { plotIfVisible(g,py.x,py.y); //lo stampo solo se visibile old=py; firstPointY=false; } else {//parametrizzo il segmento old-p deltaU=(old.x-py.x); //pixel in orizzontale che separano p.x da old.x if (deltaU==0) deltaU=1; slopeY=(old.y-py.y)/deltaU; //coeff. angolare della retta passante per p e old (pendenza) for (present.x=py.x;present.x<=old.x;present.x++) //incremento di un pixel alla volta { present.y=py.y; //manca sul libro!!! present.y=Math.round(present.y+slopeY); plotIfVisible(g,present.x,present.y); } old=py; } } //adesso traccio le x-curve comprese tra due y-curve for (ax=func.W;ax>=-func.W;ax-=func.deltaX) { firstPointX=true; for (ay=traccia;ay>=(traccia-func.deltaX);ay-=func.deltaY) { z=F(ay,ax); px=Converti(ay,ax,z); if (colorato) if (z<-1) g.setColor(Color.blue); else if (z>=-1 && z<=-.5) g.setColor(Color.green); else if (z>-.5 && z<.5) g.setColor(Color.yellow); else if (z>=.5 && z<=1) g.setColor(Color.orange); else g.setColor(Color.red); else g.setColor(Color.blue); //monocolor if (firstPointX) { plotIfVisible(g,px.x,px.y); old=px; firstPointX=false; } else { deltaV=(old.x-px.x); if (deltaV==0) deltaV=1; slopeX=(old.y-px.y)/deltaV; for (present.x=px.x;present.x<=old.x;present.x++) { present.y=px.y; present.y=Math.round(present.y+slopeX); plotIfVisible(g,present.x,present.y); } old=px; }//fine else }//fine for(ay=.. }//fine for(y=-W..) }//fine for(x=-L..) //stampo in alto a Dx Max. e Min. della funzione g.setColor(Color.black); g.setFont(f); String s="MAX="+max+" Min="+min; g.drawString(s,func.base-(s.length()*10),50); //disegno ora gli assi cartesiani } else //mouse premuto {func.menu.viste.select("Manuale"); //..stampo in alto a Sx. i valori Correnti di Theta e Phi (in gradi).. g.setColor(Color.blue); g.setFont(f); g.drawString("THETA="+Math.round(degree(func.theta))+" PHI="+Math.round(degree(func.phi))+"",20,50); //..e visualizzo soltanto gli assi cartesiani }//fine else }//fine metodo hiddenLine() /*-----------------------------------------------------------------------------------------------*/ private void plotIfVisible(Graphics g,int presentU,int presentV) {if ((func.u0maxV[presentU]) {maxV[presentU]=presentV; if (presentV0) g.drawLine(presentU,presentV,presentU,presentV); } } } /*-----------------------------------------------------------------------------------------------*/ private void assiNormali(Graphics g)//disegna gli assi cartesiani standard {Point O, //Centro del s.d.r. X,Y,Z; //Assi cartesiani O=Converti(0f,0f,0f); X=Converti(3f,0f,0f); Y=Converti(0f,3f,0f); Z=Converti(0f,0f,3f); g.setColor(Color.black); g.drawLine(O.x,O.y,X.x,X.y); //asse X g.drawLine(O.x,O.y,Y.x,Y.y); //asse Y g.drawLine(O.x,O.y,Z.x,Z.y); //asse Z g.setColor(Color.red); g.setFont(f); g.drawString("O",O.x,O.y); g.drawString("X",X.x,X.y); g.drawString("Y",Y.x,Y.y); g.drawString("Z",Z.x,Z.y); }//fine metodo assiNormali(); /*-----------------------------------------------------------------------------------------------*/ private void assiFramed(Graphics g) {Point Zmax,Zmin,Xmax,Ymax; //estremi degli assi cartesiani Zmax=Converti( func.L,-func.W,max); //estremo sup. asse Z Zmin=Converti( func.L,-func.W,min); //estremo inf. asse Z Ymax=Converti( func.L, func.W,min); //estremo sup. asse X Xmax=Converti(-func.L, func.W,min); //estremo sup. asse Y //disegno gli assi g.setColor(Color.black); g.drawLine(Zmax.x,Zmax.y,Zmin.x,Zmin.y); //asse Z g.drawLine(Zmin.x,Zmin.y,Ymax.x,Ymax.y); //asse X g.drawLine(Ymax.x,Ymax.y,Xmax.x,Xmax.y); //asse Y /*stampo i nomi degli assi*/ g.setFont(f); g.setColor(Color.red); g.drawString("Z",(Zmax.x+Zmin.x)/2+15,(Zmax.y+Zmin.y)/2); g.drawString("X",(Xmax.x+Ymax.x)/2+15,(Xmax.y+Ymax.y)/2); g.drawString("Y",(Ymax.x+Zmin.x)/2+15,(Ymax.y+Zmin.y)/2); g.setColor(Color.black); //disegno la scala sull' asse Z for (int i=(int)min; i<=((int)max); i++) {Point p=Converti( func.L,-func.W,i); g.drawString(i+"",p.x,p.y); } //disegno la scala sull' asse Y for (int i=Math.round(-func.W); i<=Math.round(func.W); i++) {Point p=Converti( func.L,i,min); g.drawString(i+"",p.x,p.y); } //disegno la scala sull' asse X for (int i=Math.round(-func.L); i<=Math.round(func.L); i++) {Point p=Converti(i,func.W,min); g.drawString(i+"",p.x,p.y); } }//fine metodo assiFramed() /*----------------------------------------------------------------------------------------------*/ //da qui seguono i metodo per la gestione del mouse// /*----------------------------------------------------------------------------------------------*/ public boolean mouseDown(Event evt, int x, int y) //gestisce la pressione del pulsante Sx. del mouse {premuto=true; //cambio stato alla var. booleana this.repaint(); //pulisco lo schermo e visualizzo solo gli assi(oltre a Theta e Phi) return(true); }//fine metodo mouseDown() /*----------------------------------------------------------------------------------------------*/ private int[] oldX=new int[2], oldY=new int[2]; private int i=0,j=0; public boolean mouseDrag(Event evt, int x, int y) //gestisce il trascinamento del mouse { oldX[i]=x; //determina se il movimento e' verso Sx/Dx oldY[i]=y; //determina se il movimento e' verso l'alto/basso if (x>oldX[j]) //movimento verso Dx.->diminuisco theta di un (passo) grado func.theta=radiant(( (degree(func.theta)-passo) % 360)); if (xaumento theta di un (passo) grado func.theta=radiant(( (degree(func.theta)+passo) % 360)); //in caso di uguaglianza non fa' nulla //N.B. x e y possono variare anche insieme quindi... if (yaumento phi di un (passo) grado func.phi=radiant(( (degree(func.phi)+passo) % 360)); if (y>oldY[j]) //movimento verso il basso->diminuisco phi di (passo) un grado func.phi=radiant(( (degree(func.phi)-passo) % 360)); if (x>oldX[j] || xoldY[j] || y