/**************************************************************** _ _ _ (_) ___ ___ ___ __ _ ___ __| |_ __ ___ (_) __ ___ ____ _ | |/ __/ _ \/ __|/ _` |/ _ \/ _` | '__/ _ \ | |/ _` \ \ / / _` | | | (_| (_) \__ \ (_| | __/ (_| | | | (_) | | | (_| |\ V / (_| | |_|\___\___/|___/\__,_|\___|\__,_|_| \___(_)/ |\__,_| \_/ \__,_| |__/ Applet Java che presenta un icosaedro roteante Copyright 2004 by Umberto Salsi Si compila con (ho usato il compilatore Sun JDK 1.4): javac icosaedro.java Si esegue con: appletviewer icosaedro.html dove icosaedro.html contiene: ****************************************************************/ import java.applet.*; import java.awt.*; class Point /* Banalissimo punto 3D. */ { double[] p = {0.0, 0.0, 0.0}; Point() { p[0] = 0.0; p[1] = 0.0; p[2] = 0.0; } Point(double x, double y, double z) { p[0] = x; p[1] = y; p[2] = z; } public String toString() { return "("+ p[0]+ ", "+ p[1]+ ", "+ p[2]+ ")"; } } class Vector /* Vettore 3D. Da non confondersi con l'omonima classe di Java. */ { double[] v = {0.0, 0.0, 0.0}; public String toString() { return "("+ v[0]+ ", "+ v[1]+ ", "+ v[2]+ ")"; } } class Matrix /* Generica matrice 3x3. Mi serve per esprimere le rotazioni. */ { double[][] m = { {1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0} }; public String toString() { int i; String s = "( "; for(i=0; i<3; i++){ s += "("+ m[i][0]+ ", "+ m[i][1]+ ", "+ m[i][2]+ ") "; } return s + " )"; } } class Trasf /* Classe che implementa le trasformazioni di roto-traslazione. Una matrice per le rotazioni e un vettore per le traslazioni. */ { /*********************************** Sistema delle coordinate: ^ asse y | | | | +---------------> asse x / / / asse z ***********************************/ private Matrix R = new Matrix(); private Vector T = new Vector(); public void Trasla(double dx, double dy, double dz) { T.v[0] += dx; T.v[1] += dy; T.v[2] += dz; } private void _ruota(Matrix r) /* Calcola: R=R*r; T=T*r; */ { int i, j; Matrix q = new Matrix(); Vector s = new Vector(); for( i=0; i<3; i++ ) for( j=0; j<3; j++ ) q.m[i][j] = R.m[i][j]; for( i=0; i<3; i++ ) for( j=0; j<3; j++ ) R.m[i][j] = r.m[i][0] * q.m[0][j] + r.m[i][1] * q.m[1][j] + r.m[i][2] * q.m[2][j]; for( i=0; i<3; i++ ) s.v[i] = T.v[i]; for( i=0; i<3; i++ ) T.v[i] = r.m[i][0] * s.v[0] + r.m[i][1] * s.v[1] + r.m[i][2] * s.v[2]; } public void ruota_x(double angolo) /* Applica rotazione intorno a x */ { double c, s; Matrix r = new Matrix(); c = Math.cos(angolo); s = Math.sin(angolo); r.m[1][1] = c; r.m[2][1] = -s; r.m[1][2] = s; r.m[2][2] = c; _ruota(r); } public void ruota_y(double angolo) /* Applica rotazione intorno a y */ { double c, s; Matrix r = new Matrix(); c = Math.cos(angolo); s = Math.sin(angolo); r.m[0][0] = c; r.m[2][0] = -s; r.m[0][2] = s; r.m[2][2] = c; _ruota(r); } public void ruota_z(double angolo) /* Applica rotazione intorno a z */ { double c, s; Matrix r = new Matrix(); c = Math.cos(angolo); s = Math.sin(angolo); r.m[0][0] = c; r.m[1][0] = -s; r.m[0][1] = s; r.m[1][1] = c; _ruota(r); } public void Trasforma(Point p, Point tp) /* Applica la trasformazione di roto-traslazione al punto p e ritorna il risultato in tp: tp = R*p + T ATTENZIONE! il punto tp deve gia' essere allocato! NOTA: sarebbe piu' elegante ritornare il risultato come valore di ritorno della funzione; tuttavia questa soluzione permette di utilizzare per tp un oggetto Point gia' esistente, cosi' risparmiando parecchio lavoro alla JVM. */ { double x, y, z; x = R.m[0][0] * p.p[0] + R.m[0][1] * p.p[1] + R.m[0][2] * p.p[2] + T.v[0]; y = R.m[1][0] * p.p[0] + R.m[1][1] * p.p[1] + R.m[1][2] * p.p[2] + T.v[1]; z = R.m[2][0] * p.p[0] + R.m[2][1] * p.p[1] + R.m[2][2] * p.p[2] + T.v[2]; tp.p[0] = x; tp.p[1] = y; tp.p[2] = z; } public String toString() { return "R=" + R + ", T=" + T; } } public class icosaedro extends Applet implements Runnable { private Image offscr = null; private Graphics g; private Thread thread; private int t = 0; private int a = 0; private int strset = 0; private int hsize, vsize; private Trasf w; private long delay = 100; /* Raggio sfera circoscritta all'icosaedro (cm): */ private static final double raggio = 2.5; /* L'icosaedro ha 12 vertici: */ private static final int nvertici = 12; /* Vettore dei vertici dell'icosaedro: */ private Point[] V = new Point[nvertici]; /* Vettore dei vertici proiettati: */ private Point[] V2 = new Point[nvertici]; /* Risoluzione video: */ private static final double SH = 30.0; /* pixel/cm */ private static final double SV = 30.0; /* pixel/cm */ /* Dimensione area applet (cm): */ private double TV_H; private double TV_V; /* Posizione occhio (cm): */ private double cx; private double cy; private double cz; /* Posizione z del centro icosaedro (per effetto zoom) (cm): */ private double zpos; /* Metto le scritte? (non usato) */ private boolean metto_scritte; /* Tavola dei livelli di grigio: */ private Color grayScale[]; private FontMetrics fm; void proietta(Point p, Point q) /* Lo schermo e' il piano XY, la telecamera si trova in (cx,cy,cz) ed e' rivolta verso (0,0,0): q = intersezione della retta data dai punti p e (cx,cy,cz), con il piano XY. ATTENZIONE! l'oggetto q deve esistere! NOTA: la coord. z del punto viene preservata. */ { /* PROIEZIONE PARALLELA: */ /**** q.p[0] = p.p[0]; q.p[1] = p.p[1]; q.p[2] = p.p[2]; ****/ /* PROIEZIONE PROSPETTICA: */ double t; t = cz / (cz - p.p[2]); q.p[0] = cx + t*(p.p[0] - cx); q.p[1] = cy + t*(p.p[1] - cy); //q.p[2] = 0.0; q.p[2] = p.p[2]; } /**** FUNZIONI NON USATE: void linea3d( double x1, double y1, double z1, double x2, double y2, double z2) { Point p1 = new Point(x1, y1, z1); Point p2 = new Point(x2, y2, z2); Point q1 = new Point(); Point q2 = new Point(); Point s1 = new Point(); Point s2 = new Point(); w.Trasforma(p1, q1); w.Trasforma(p2, q2); proietta(q1, s1); proietta(q2, s2); g.drawLine( (int) (SH * s1.p[0]), vsize - (int) (SV * s1.p[1]), (int) (SH * s2.p[0]), vsize - (int) (SV * s2.p[1])); } void linea3d(Point a, Point b) { linea3d(a.p[0], a.p[1], a.p[2], b.p[0], b.p[1], b.p[2]); } void stringa3d(double x, double y, double z, String s, int align) { double h, v; int len; Point p = new Point(x, y, z); Point q = new Point(); Point t = new Point(); w.Trasforma(p, q); proietta(q, t); len = fm.stringWidth(s); g.drawString(s, (int) (SH * t.p[0]+0.5) - len/2, vsize - (int) (SV * t.p[1]+0.5)); } void stringa3d(Point p, String s, int align) { stringa3d(p.p[0], p.p[1], p.p[2], s, align); } void linea(Point a, Point b) { g.drawLine( (int) (SH * a.p[0]), vsize - (int) (SV * a.p[1]), (int) (SH * b.p[0]), vsize - (int) (SV * b.p[1])); } void stringa(Point p, String s, int align) { int len; len = fm.stringWidth(s); g.drawString(s, (int) (SH * p.p[0]+0.5) - len/2, vsize - (int) (SV * p.p[1]+0.5)); } ************/ double sqr(double x) { return x*x; } void tri(Point a, Point b, Point c) /* Disegna il triangolo di vertici a,b,c con rimozione superfici nascoste. Di conseguenza, i vertici devono essere ordinati in modo che si possa camminare sulla superficie esterna passando per i vertici in senso antiorario. */ { /* WIREFRAME: */ /**** linea(a, b); linea(b, c); linea(c, a); ****/ /* SURFACE: */ int px[]; int py[]; Vector n = new Vector(); double k; int col; /* Calcola componente 'z' della normale (non normalizzata) al triangolo: */ n.v[2] = (b.p[0]-a.p[0])*(c.p[1]-a.p[1]) - (b.p[1]-a.p[1])*(c.p[0]-a.p[0]); /* Back-face removal: se la componente 'z' della normale e' negativa, la faccia anteriore del triangolo e' nascosta: */ if( n.v[2] < 0.0 ) return; /* Completa calcolo normale (non normalizzata): */ n.v[0] = (b.p[1]-a.p[1])*(c.p[2]-a.p[2]) - (b.p[2]-a.p[2])*(c.p[1]-a.p[1]); n.v[1] = (b.p[2]-a.p[2])*(c.p[0]-a.p[0]) - (b.p[0]-a.p[0])*(c.p[2]-a.p[2]); /* Livello di illuminazione diffusa: */ col = 50; /* Aggiungi illuminazione diretta da sorgente a distanza infinita orientata come (=0.577, 0.577, 0.577): */ k = ( n.v[0]*(-0.577) + n.v[1]*(0.577) + n.v[2]*(0.577) ) / Math.sqrt( sqr(n.v[0]) + sqr(n.v[1]) + sqr(n.v[2]) ); if( k > 0.0 ) col += (int)(204.0*k); px = new int[3]; py = new int[3]; px[0] = (int) (SH * a.p[0]); px[1] = (int) (SH * b.p[0]); px[2] = (int) (SH * c.p[0]); py[0] = vsize - (int) (SV * a.p[1]); py[1] = vsize - (int) (SV * b.p[1]); py[2] = vsize - (int) (SV * c.p[1]); g.setColor( grayScale[col] ); g.fillPolygon(px, py, 3); } void disegna_scena(int t) { Point p = new Point(); int i; int xsteps = 450; int ysteps = 265; int zsteps = 435; w = new Trasf(); w.ruota_x(2.0*3.1415/xsteps*(a%xsteps)); w.ruota_y(2.0*3.1415/ysteps*(a%ysteps)); w.ruota_z(2.0*3.1415/zsteps*(a%zsteps)); w.Trasla(TV_H/2, TV_V/2, zpos); g.setColor(Color.white); g.fillRect(0, 0, hsize, vsize); /* Calcola in V2[] i vertici trasformati e proiettati: */ for( i=0; i