/* * @(#)MovingFractalTree.java * * Moving 2D Fractal Tree (and Static 3D Fractal Tree and Plane Tree) * * First post: 2003/10/28 * * @José Ángel González Rodríguez (josechu2004@gmail.com) * @version 2.12 2006/04/07 */ import java.awt.*; import java.applet.Applet; import java.awt.event.*; public class MovingFractalTree extends Applet implements Runnable, MouseListener, MouseMotionListener { private static final long serialVersionUID = 1L; private boolean rotate_with_vectors = false; private boolean rotate_with_quats = !rotate_with_vectors; //si incremento mucho el tamaño de las matrices //aumenta la ocupación de procesador del applet. Por qué? private final int N_ITER_MAX = 11; private final int N_ITER_MIN = 2; private final int N_ITER_INICIAL = 6; //There are branches of n_iter different lengths private int n_iter; private final double N_TIMES = 20; //private final double PI_VECES = 60; //tpoints holds the entire number of //branch tips + 1 + the center point = 2^(N_ITER_MAX + 1) private int tpoints = (int) Math.pow(2, N_ITER_MAX + 1); private int[] nodo = new int[8]; //x, y and z will hold the 3D coordinates of every branch tip private double[] x = new double[tpoints]; private double[] y = new double[tpoints]; private double[] z = new double[tpoints]; //BRANCH_FACTOR holds the proportion: //"son's branch length"/"father's branch length" //which is constant and different from 2D to 3D // //BRANCH_FACTOR2D is a quantity that makes the grandson branch //to be half the length of its grandfather's length in 2D // //BRANCH_FACTOR3D is a quantity that makes the grandgrandson branch //to be half the length of its grandgrandfather's length in 3D private final double BRANCH_FACTOR_2D = 1 / Math.pow(2, 1. / 2.); private final double BRANCH_FACTOR_3D = 1 / Math.pow(2, 1. / 3.); private double width; private int screen_width; private double offsetx, offsety; private double kz; private final double colormax = 210; //max 255 = white private final double colormin = 0; //min 0 = black private final double colorrango = colormax - colormin; //Length of 2D fractal diagonal / width of complete fractal = // Math.sqrt(3./2.) //private final double DIAGO2D = 1.224744871; private final double DIAGO2D = Math.sqrt(3. / 2.); //Length of 3D fractal diagonal / width of complete fractal //private final double DIAGO3D = 1.423661051; private final double DIAGO3D = Math.sqrt(1. + Math.pow(1. / 2., 2. / 3.) + Math.pow(1. / 2., 4. / 3.)); private double multi, suma, vd; private int screen_height; private boolean perspective = true; private int tipoArbol = 0; //0 para árbol móvil 2D, 1 para árbol 3D, 2 para árbol de planos private int tipoArbolBak = 0; private double qw, qx, qy, qz; private double theta, nx, ny, nz; private double start_time; private double sintheta, costheta; private String info_n_iter, info_dimens; private int offset_fontX1, offset_fontX2, offset_fontY1; private double n_redraws = 0, time_elapsed; private boolean doubleclicked = false; private int border_width; private int interior_width; private int interior_height; private int fontsize; private int mx, my; // the most recently recorded mouse coordinates private boolean mouse_pressed = false; private int azimuth, elevation; private final int INTERIORGREY = 254; private final int TOPE = INTERIORGREY - 11, BORDERGREY = 230; private final int BUTTON3_MASK = 4; private final int BUTTON3_DOWN_MASK = 4096; Image dbImage; Graphics dbg; public void init() { this.addMouseListener(this); addMouseMotionListener(this); screen_width = this.getSize().width; offsetx = screen_width / 2; offsety = screen_width / 2; screen_height = this.getSize().height; border_width = screen_width / 50; interior_width = screen_width - 2 * border_width; interior_height = screen_height - 2 * border_width; fontsize = (int) ((double) screen_width / 3.8); setFont(new Font("Monospaced", 1, fontsize)); double wid_heigh_char_ratio = .610; double marginX = 15. / 256.; offset_fontX1 = (int) ((double) screen_width * marginX); offset_fontY1 = offset_fontX1; offset_fontY1 = screen_height - offset_fontY1 - (int) ((double) fontsize * .052); //offset_fontY1 = screen_width - offset_fontX1; offset_fontX2 = screen_width - offset_fontX1 - (int) (3. * wid_heigh_char_ratio * (double) fontsize); //do all the common processing at max iteration to ensure the arrays //are filled up with proper data n_iter = N_ITER_MAX; initCommon(); //2006-04-03 Para evitar en los .jar que aparezca //la figura incompleta en la primera iteración, //cuando todavía no ha calculado todas las ramas: try { Thread.sleep(200); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //revert back to original iteration n_iter = N_ITER_INICIAL; initCommon(); } public void initCommon() { //Dimension dim = getSize(); //dim.width is the width of the applet screen //screen_width = dim.width; if (rotate_with_vectors == true) { generateRandomUnitVectorAndSmallAngle(); } else { generateRandomUnitQuaternionWithSmallAngle(); } info_n_iter = "" + n_iter; if (tipoArbol == 0) {// árbol móvil 2-D width = .9 * screen_width; initTree2D(); info_dimens = "2-D"; multi = colorrango / (DIAGO2D * width); } if (tipoArbol == 1) {// árbol 3-D width = .6 * screen_width; if (tipoArbolBak == 0) initTree3D(); if (tipoArbolBak == 1) initTree3D(); info_dimens = "3-D"; multi = colorrango / (DIAGO3D * width); } if (tipoArbol == 2) {// árbol de planos 3-D width = .6 * screen_width; if (tipoArbolBak == 0) initTree3D(); if (tipoArbolBak == 2) initTree3D(); info_dimens = "Pln"; multi = colorrango / (DIAGO3D * width); } tipoArbolBak = tipoArbol; //vd is only used in perspective mode //vd is the viewer distance to the center vd = width * 0.7; suma = colorrango / 2; n_redraws = 0; start_time = System.currentTimeMillis(); //System.out.println(DIAGO3D+" "+DIAGO2D); } public void simpleInitCommon() { info_n_iter = "" + n_iter; n_redraws = 0; start_time = System.currentTimeMillis(); } //The methods "start()", "stop()", "destroy()", "run()", //"paint(Graphics g)" and "update()" make "double buffering" easier public void start() { Thread th = new Thread(this); th.start(); } public void stop() { } public void destroy() { } public void run() { Thread.currentThread().setPriority(Thread.MIN_PRIORITY); //ojo System.out.println(Thread.MIN_PRIORITY); while (true) { //inc += 0.1; try { Thread.sleep(44); } catch (InterruptedException ex) { } Thread.currentThread().setPriority(Thread.MAX_PRIORITY); //System.out.println( // Thread.MIN_PRIORITY+"!"+Thread.MAX_PRIORITY); repaint(); } } public void paint(Graphics g) { //The three followint lines are for antialiasing //They don't work in JVM 1.1.4 of Microsoft Explorer //((Graphics2D)g).setRenderingHint //(RenderingHints.KEY_ANTIALIASING, //RenderingHints.VALUE_ANTIALIAS_ON); //generate a unit Quaternio //(an axis and a rotation angle) //once in, say, N_TIMES times //generate it with a small arbitrary qtheta //the bigger is qtheta, the bigger is //the rotation speed if (mouse_pressed != true) { if (Math.random() < 1 / N_TIMES) { if (rotate_with_vectors == true) { generateRandomUnitVectorAndSmallAngle(); } else { generateRandomUnitQuaternionWithSmallAngle(); } } //rotate the entire figure with the Quaternions or Vectors if (rotate_with_quats != true) { rotateEntireFigureWithVectorAndAngle(); } else { rotateEntireFigureWithQuaternions(); } } drawTree(g); //ojo System.out.println(""+x[1]+" "+y[1]); //in 2D rotate the branches individually if (tipoArbol == 0) rotateBranchesRandomly(); n_redraws++; time_elapsed = System.currentTimeMillis() - start_time; } public void initTree2D() { //The center of the fractal (the center of the //longest branch) is in the middle of the array, //then the center of the first two son branches //are in the middle of these two halves //at (tpoints*1/4) and (tpoints*3/4) //...and so on... //finally, the central points of the shortest branches //are at positions 2, 6, 10, 14,... ,(tpoints - 2) //and their tips at (1, 3), (5, 7), (9, 11), (13, 15), ...(tpoints - // 3, tpoints - 1) x[tpoints / 2] = 0; y[tpoints / 2] = 0; z[tpoints / 2] = 0; long counter = 0; double half_branch_length; int paso; for (int i = 0; i < N_ITER_MAX; i++) { //half_branch_length starts at width/4 in 2-D //for i = 0, branch_factor2D^4 = 1/4 //and decreases by branch_factor in each iteration half_branch_length = width * Math.pow(BRANCH_FACTOR_2D, i + 4); //paso starts at tpoints/4 //paso is halved in each iteration paso = (int) Math.rint(tpoints / 4 * Math.pow((double) 1 / 2, i)); //In every j pass the tips of the branches //of a given length are assigned to the arrays. // //Longest branch tips first at (tpoints/4, tpoints*3/4) //Then the two son branches at (tpoints*1/8, tpoints*3/8), // (tpoints*5/8, tpoints*7/8) //... //... //And finally the many shortest branch tips last at (1, 3), (5, // 7), (9, 11),... (tpoints - 3, tpoints -1) for (int j = paso; j < tpoints; j = j + 4 * paso) { counter++; int jp = j + paso; int j2p = j + 2 * paso; if (i % 2 != 1) { x[j] = x[jp] - half_branch_length; x[j2p] = x[jp] + half_branch_length; y[j] = y[jp]; y[j2p] = y[jp]; } else { x[j] = x[jp]; x[j2p] = x[jp]; y[j] = y[jp] - half_branch_length; y[j2p] = y[jp] + half_branch_length; } z[j] = 0; z[j2p] = 0; } //System.out.println(i+" "+counter+"----"+paso+" "+width); } } public void initTree3D() { //The center of the fractal (the center of the //longest branch) is in the middle of the array, //then the center of the first two son branches //are in the middle of these two halves //at (tpoints*1/4) and (tpoints*3/4) //...and so on... //finally, the central points of the shortest branches //are at positions 2, 6, 10, 14,... ,(tpoints - 2) //and their tips at (1, 3), (5, 7), (9, 11), (13, 15), ...(tpoints - // 3, tpoints - 1) x[tpoints / 2] = 0; y[tpoints / 2] = 0; z[tpoints / 2] = 0; long counter = 0; double half_branch_length; int paso; for (int i = 0; i < N_ITER_MAX; i++) { //half_branch_length starts at width/4 in 3-D //for i = 0, branch_factor3D^6 = 1/4 //and decreases by branch_factor3D in each iteration half_branch_length = width * Math.pow(BRANCH_FACTOR_3D, i + 6); //paso starts at tpoints/4 //paso is halved in each iteration paso = (int) Math.rint(tpoints / 4 * Math.pow((double) 1 / 2, i)); //In every j pass the tips of the branches //of a given length are assigned to the arrays. // //Longest branch tips first at (tpoints/4, tpoints*3/4) //Then the two son branches at (tpoints*1/8, tpoints*3/8), // (tpoints*5/8, tpoints*7/8) //... //... //And finally the many shortest branch tips last at (1, 3), (5, // 7), (9, 11),... (tpoints - 3, tpoints -1) for (int j = paso; j < tpoints; j = j + 4 * paso) { counter++; int jp = j + paso; int j2p = j + 2 * paso; if (i % 3 == 0) { x[j] = x[jp] - half_branch_length; x[j2p] = x[jp] + half_branch_length; y[j] = y[jp]; y[j2p] = y[jp]; z[j] = z[jp]; z[j2p] = z[jp]; } if (i % 3 == 1) { x[j] = x[jp]; x[j2p] = x[jp]; y[j] = y[jp] - half_branch_length; y[j2p] = y[jp] + half_branch_length; z[j] = z[jp]; z[j2p] = z[jp]; } if (i % 3 == 2) { x[j] = x[jp]; x[j2p] = x[jp]; y[j] = y[jp]; y[j2p] = y[jp]; z[j] = z[jp] - half_branch_length; z[j2p] = z[jp] + half_branch_length; } } //System.out.println(i+" "+counter+"----"+paso+" "+width); } } //this method is the same for 2-D and 3-D private void drawTree(Graphics g) { //Primero dibuja las líneas base del árbol (las más largas) //como mucho hasta la tercera iteración int t = 3; if (n_iter < t) t = n_iter; for (int i = 0; i < t; i++) { //as before, paso starts at tpoints/4 //and is halved in each iteration int paso = (int) Math.rint(tpoints / 4 * Math.pow((double) 1 / 2, i)); //In every j pass the branches //of a given length are drawn onscreen. //In the first pass the longest branch is drawn onscreen if (perspective == true) {//con perspectiva for (int j = paso; j < tpoints; j = j + 4 * paso) { if (tipoArbol < 2) { dibujaRamasConPers(j, paso, g); } else { dibujaPlanosConPers(j, paso, i, g); } } } else {//sin perspectiva for (int j = paso; j < tpoints; j = j + 4 * paso) { if (tipoArbol < 2) { dibujaRamasSinPers(j, paso, g); } else { dibujaPlanosSinPers(j, paso, i, g); } } } } //si n_iter > 3, se divide el fractal en 8 partes //y se dibuja la más lejana primero //para pintar primero los más lejanos //de forma que las líneas lejanas, más claras, no tapen a las cercanas //sino al revés if (n_iter > 3) { for (int k = 0; k < 8; k++) { nodo[k] = (2 * k + 1) * tpoints / 16; } //sort los más lejanos primero for (int i = 0; i < 8; i++) { for (int j = i + 1; j < 8; j++) { if (z[nodo[i]] < z[nodo[j]]) { int guarda = nodo[i]; nodo[i] = nodo[j]; nodo[j] = guarda; } } } //en cada pasada de k se dibuja un octavo del fractal //empezando por el octavo más lejano for (int k = 0; k < 8; k++) { for (int i = t; i < n_iter; i++) { //paso starts at tpoints/32 //and is halved in each iteration int paso = (int) Math.rint(tpoints / 4 * Math.pow((double) 1 / 2, i)); if (perspective == true) {//con perspectiva int s = (int) (Math.pow((double) 2, i - 2) - 1) * paso; for (int j = nodo[k] - s; j < nodo[k] + s; j = j + 4 * paso) { if (tipoArbol < 2) { dibujaRamasConPers(j, paso, g); } else { dibujaPlanosConPers(j, paso, i, g); } } } else {//sin perspectiva int s = (int) (Math.pow((double) 2, i - 2) - 1) * paso; for (int j = nodo[k] - s; j < nodo[k] + s; j = j + 4 * paso) { if (tipoArbol < 2) { dibujaRamasSinPers(j, paso, g); } else { dibujaPlanosSinPers(j, paso, i, g); } } } } } } } //this method would be the same for 2-D and 3-D //aunque no se aplica al 3-D porque las ramas chocan entre sí private void rotateBranchesRandomly() { //Quaternions are not used to rotate the individual branches //The Pertti Lounesto formula is used instead double bx; double by; double bz; int paso; for (int i = 0; i < N_ITER_MAX; i++) { long counter = 0; paso = (int) Math.rint(tpoints / 4 * Math.pow((double) 1 / 2, i)); for (int j = paso; j < tpoints; j = j + 2 * paso) { double ax = x[j]; double ay = y[j]; double az = z[j]; int p2 = 2 * paso; counter++; if (counter % 2 != 0) { int jplsp2 = j + p2; bx = x[jplsp2]; by = y[jplsp2]; bz = z[jplsp2]; } else { int jmnsp2 = j - p2; bx = x[jmnsp2]; by = y[jmnsp2]; bz = z[jmnsp2]; } double xdif = bx - ax; double ydif = by - ay; double zdif = bz - az; double mn = Math.sqrt(xdif * xdif + ydif * ydif + zdif * zdif); double nx = xdif / mn; double ny = ydif / mn; double nz = zdif / mn; double theta = (4 * (Math.random() - .5) * Math.PI / 180); double costheta = Math.cos(theta); double sintheta = Math.sin(theta); for (int k = j - paso + 1; k < j + paso; k++) { double px = x[k] - ax, py = y[k] - ay, pz = z[k] - az; double npescalar = nx * px + ny * py + nz * pz; //System.out.println(npescalar); //npescalar = 0; double nnpx = npescalar * nx; double nnpy = npescalar * ny; double nnpz = npescalar * nz; double cx = costheta * (px - nnpx) + nnpx; double cy = costheta * (py - nnpy) + nnpy; double cz = costheta * (pz - nnpz) + nnpz; x[k] = sintheta * (ny * pz - nz * py) + cx + ax; y[k] = sintheta * (nz * px - nx * pz) + cy + ay; z[k] = sintheta * (nx * py - ny * px) + cz + az; } } } } //Use of quaternions to rotate the entire figure //not to rotate the individual branches private void generateRandomUnitQuaternionWithSmallAngle() { //Alternative Method to generateRandomUnitVectorAndAngle // //To generate a unit quaternion that represents a //rotation of theta degrees about an arbitrary //axis that crosses the origin v = (x, y, z) we make this: //q = [cos(theta/2), sin(theta/2)*v/|v|] //where the modulus |v| is: sqrt(x*x + y*y + z*z) // //You can easily prove that this generates a unit quaternion //q = [qw, qx, qy, qz] //where |q| = // = sqrt(cos(theta/2)^2 + // + [sin(theta/2)*qx/|v|]^2 + // + [sin(theta/2)*qy/|v|]^2 + // + [sin(theta/2)*qy/|v|]^2) // = 1 //we generate an arbitrary small angle and //an arbitrary axis //generates small theta: double theta = 3 * Math.random() * Math.PI / 180; double halph_theta = theta / 2; //the first of the four terms of the unit quaternion is qw qw = Math.cos(halph_theta); //generates arbitrary axis: double x = Math.random() - .5; double y = Math.random() - .5; double z = Math.random() - .5; //the modulus of the arbitrary axis is: double modu = Math.sqrt(x * x + y * y + z * z); double sinhalphtheta = Math.sin(halph_theta); //the three following terms of the unit quaternion will be: qx = sinhalphtheta * x / modu; qy = sinhalphtheta * y / modu; qz = sinhalphtheta * z / modu; } private void generateRandomUnitVectorAndSmallAngle() { //Alternative Method to generateRandomUnitQuaternion // // //Method to generate a random Unit Vector //and a small angle theta = 3 * Math.random() * Math.PI / 180; //generates arbitrary axis: double x = Math.random() - .5; double y = Math.random() - .5; double z = Math.random() - .5; //the modulus of the arbitrary axis is: double modu = Math.sqrt(x * x + y * y + z * z); sintheta = Math.sin(theta); costheta = Math.cos(theta); //the three following terms of the unit vector are: nx = x / modu; ny = y / modu; nz = z / modu; } private void rotateEntireFigureWithQuaternions() { //Alternative method to rotateEntireFigureWithVectorAndAngle // //Remember that if q = [qw, qx*i, qy*j, qz*k] is a //unit quaternion, then a rotation of //point "p" around this quaternion is given by: //q*p*q^-1 // //where the point "p" is a quaternion [pw, px*i, py*j, pz*k] //such that pw = 0, so p = [0, px*i, py*j, pz*k] // //The multiplication of the quaternion is such that //i*i = j*j = k*k = -1 //and //i*j = -j*i = k //j*k = -k*j = i //k*i = -i*k = j // //The inverse of a unit quaternion is also its conjugate //that is, if the unit quaternion q is: [qw, qx*i, qy*j, qz*k] //then q^-1 = [qw, -qx*i, -qy*j, -qz*k] //we will apply the rotation to every branch tip for (int i = 1; i < tpoints; i++) { //product q*p double px = x[i], py = y[i], pz = z[i]; double wqp = -qx * px - qy * py - qz * pz; double xqp = qw * px + qy * pz - qz * py; double yqp = qw * py - qx * pz + qz * px; double zqp = qw * pz + qx * py - qy * px; //product q*p*q^-1 //qx = -qx; qy = -qy; qz = -qz; //double wqpq = wqp*qw - xqp*qx - yqp*qy - zqp*qz; x[i] = -wqp * qx + xqp * qw - yqp * qz + zqp * qy; y[i] = -wqp * qy + xqp * qz + yqp * qw - zqp * qx; z[i] = -wqp * qz - xqp * qy + yqp * qx + zqp * qw; } } private void rotateEntireFigureWithVectorAndAngle() { //Alternative method to rotateEntireFigureWithQuaternions //we will apply the rotation to every branch tip // //Given: //- an arbitrary point in space "P", //- an arbitrary axis of space "AB", and //- an angle "theta", // if we want to rotate the point "P" "theta" radians // about a unit vector "n" which is parallel to "AB" // and crosses the origin, // the formula given by (Pertty Lounesto) is: // P' = A + [n·(P - A)]*n + cos(theta)*[(P - A) - // - [n·(P - A)]*n] +- sin(theta)*[n cross (P - A)] //This formula is also used to calculate the //individual branch rotations for (int i = 1; i < tpoints; i++) { double px = x[i], py = y[i], pz = z[i]; double npescalar = px * nx + py * ny + pz * nz; //System.out.println(npescalar); double nnpx = npescalar * nx; double nnpy = npescalar * ny; double nnpz = npescalar * nz; x[i] = sintheta * (ny * pz - nz * py) + costheta * (px - nnpx) + nnpx; y[i] = sintheta * (nz * px - nx * pz) + costheta * (py - nnpy) + nnpy; z[i] = sintheta * (nx * py - ny * px) + costheta * (pz - nnpz) + nnpz; } } public void update(Graphics g) { if (dbImage == null) { dbImage = createImage(this.getSize().width, this.getSize().height); dbg = dbImage.getGraphics(); } //dbg.setColor (getBackground ()); //dbg.fillRect (0, 0, this.getSize().width, this.getSize().height); //dbg.setColor (getForeground()); greyBackground(); paint(dbg); g.drawImage(dbImage, 0, 0, this); } public void mousePressed(MouseEvent e) { mx = e.getX(); my = e.getY(); e.consume(); mouse_pressed = true; } public void mouseReleased(MouseEvent e) { mouse_pressed = false; } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public void mouseMoved(MouseEvent e) { //System.out.println // ( "Moving at (" + e.getX() + ":" + e.getY() + ")" ); } public void mouseDragged(MouseEvent e) { //System.out.println // ( "Dragging at (" + e.getX() + ":" + e.getY() + ")" ); // get the latest mouse position int new_mx = e.getX(); int new_my = e.getY(); // adjust angles according to the distance travelled by the mouse // since the last event azimuth = -new_mx + mx; elevation = new_my - my; mx = new_mx; my = new_my; //the modulus of the axis is: double modu = Math.sqrt(elevation * elevation + azimuth * azimuth); //System.out.println(azimuth+" "+elevation+" "+modu); //System.out.println(""+x[1]+" "+y[1]); //2006/04/03 al arrastrar el ratón //modu suele tomar valores enteros, entre ellos 0, 1... //Para evitar divisiones por 0 cuando modu = 0, //lo que a fecha de abril de 2006 provocaba que //desapareciera el dibujo de repente al hacer //pequeños arrastres con el botón del ratón pulsado //(probablemente por el nuevo ratón óptico), //damos un pequeño valor en el caso de que sea cero: if (modu == 0) modu = .0001; double theta = modu * Math.PI / screen_width; //para rotateEntireFigureWithSmallVectorAndAngle: sintheta = Math.sin(theta); costheta = Math.cos(theta); nx = elevation / modu; ny = azimuth / modu; nz = 0; //Para rotateEntireFigureWithQuaternionsAnd double halph_theta = theta / 2; //the first of the four terms of the unit quaternion is qw qw = Math.cos(halph_theta); double sinhalphtheta = Math.sin(halph_theta); //the three following terms of the unit quaternion will be: qx = sinhalphtheta * elevation / modu; qy = sinhalphtheta * azimuth / modu; qz = 0; if (rotate_with_vectors == true) { rotateEntireFigureWithVectorAndAngle(); } else { rotateEntireFigureWithQuaternions(); } // update the backbuffer //drawWireframe( backg ); // update our data repaint(); e.consume(); //simpleInitCommon(); } public void mouseClicked(MouseEvent e) { //justinitted = true; //System.out.println // ( "Click at (" + e.getX() + ":" + e.getY() + ")" ); int ev = e.getModifiers(); //System.out.println(ev); //double click: if (e.getClickCount() == 2) { if (ev != BUTTON3_MASK & ev != BUTTON3_DOWN_MASK) { tipoArbol++; if (tipoArbol == 3) tipoArbol = 0; if (n_iter != N_ITER_MIN) { n_iter = n_iter - 1; } else { n_iter = N_ITER_MAX; } doubleclicked = true; } initCommon(); } //single click: else { if (ev != BUTTON3_MASK & ev != BUTTON3_DOWN_MASK) { n_iter++; if (n_iter > N_ITER_MAX) { n_iter = N_ITER_MIN; } } else { n_iter--; if (n_iter < N_ITER_MIN) { n_iter = N_ITER_MAX; } } doubleclicked = false; } simpleInitCommon(); } public void greyBackground() { //fill the entire pane with bordergrey Color cl = new Color(BORDERGREY, BORDERGREY, BORDERGREY); dbg.setColor(cl); dbg.fillRect(0, 0, screen_width, screen_height); //fill the interior box with a (very light) interiorgrey cl = new Color(INTERIORGREY, INTERIORGREY, INTERIORGREY); dbg.setColor(cl); dbg.fillRect(border_width, border_width, interior_width, interior_height); //paint the info in a gradually (with time) lighter grey int infogreyslow = (int) (time_elapsed) / 12 + 140; int infogreyfast = (int) (time_elapsed) / 4 + 140; if (infogreyslow > TOPE) infogreyslow = TOPE; if (infogreyfast > TOPE) infogreyfast = TOPE; cl = new Color(infogreyslow, infogreyslow, infogreyslow); dbg.setColor(cl); //dbg.drawString(n_redraws+" "+fps, fontx, 2*fonty); //dbg.drawString(""+rotate_with_vectors, fontx, 3*fonty); if (doubleclicked != true) { dbg.drawString(info_n_iter, offset_fontX1, offset_fontY1); } else { dbg.drawString(info_dimens, offset_fontX2, offset_fontY1); cl = new Color(infogreyfast, infogreyfast, infogreyfast); dbg.setColor(cl); dbg.drawString(info_n_iter, offset_fontX1, offset_fontY1); } } public void dibujaPlanosConPers(int j, int paso, int i, Graphics g) { int j2 = j + 2 * paso; int j3 = j - paso / 2; int j4 = j + paso / 2; int j5 = j2 - paso / 2; int j6 = j2 + paso / 2; kz = vd / (vd + z[j]); double kz2 = vd / (vd + z[j3]); double kz3 = vd / (vd + z[j4]); double kz5 = vd / (vd + z[j5]); double kz6 = vd / (vd + z[j6]); int x0 = (int) (x[j] * kz + offsetx); int y0 = (int) (y[j] * kz + offsety); int x2 = (int) (x[j3] * kz2 + offsetx); int y2 = (int) (y[j3] * kz2 + offsety); int x4 = (int) (x[j4] * kz3 + offsetx); int y4 = (int) (y[j4] * kz3 + offsety); kz = vd / (vd + z[j2]); int x1 = (int) (x[j2] * kz + offsetx); int y1 = (int) (y[j2] * kz + offsety); int x3 = (int) (x[j5] * kz5 + offsetx); int y3 = (int) (y[j5] * kz5 + offsety); int x5 = (int) (x[j6] * kz6 + offsetx); int y5 = (int) (y[j6] * kz6 + offsety); int gris = (int) (z[j + paso] * multi + suma); g.setColor(new Color(gris, gris, gris)); if (i != 0) g.drawLine(x0, y0, x1, y1); if (i < n_iter - 1) { g.drawLine(x2, y2, x3, y3); g.drawLine(x4, y4, x5, y5); } } public void dibujaPlanosSinPers(int j, int paso, int i, Graphics g) { int j2 = j + 2 * paso; int j3 = j - paso / 2; int j4 = j + paso / 2; int j5 = j2 - paso / 2; int j6 = j2 + paso / 2; int x0 = (int) (x[j] + offsetx); int y0 = (int) (y[j] + offsety); int x2 = (int) (x[j3] + offsetx); int y2 = (int) (y[j3] + offsety); int x4 = (int) (x[j4] + offsetx); int y4 = (int) (y[j4] + offsety); int x1 = (int) (x[j2] + offsetx); int y1 = (int) (y[j2] + offsety); int x3 = (int) (x[j5] + offsetx); int y3 = (int) (y[j5] + offsety); int x5 = (int) (x[j6] + offsetx); int y5 = (int) (y[j6] + offsety); int gris = (int) (z[j + paso] * multi + suma); g.setColor(new Color(gris, gris, gris)); if (i != 0) g.drawLine(x0, y0, x1, y1); if (i < n_iter - 1) { g.drawLine(x2, y2, x3, y3); g.drawLine(x4, y4, x5, y5); } } public void dibujaRamasConPers(int j, int paso, Graphics g) { kz = vd / (vd + z[j]); int x0 = (int) (x[j] * kz + offsetx); int y0 = (int) (y[j] * kz + offsety); int j2 = j + 2 * paso; kz = vd / (vd + z[j2]); int x1 = (int) ((x[j2]) * kz + offsetx); int y1 = (int) ((y[j2]) * kz + offsety); int gris = (int) (z[j + paso] * multi + suma); g.setColor(new Color(gris, gris, gris)); g.drawLine(x0, y0, x1, y1); } public void dibujaRamasSinPers(int j, int paso, Graphics g) { int x0 = (int) (x[j] + offsetx); int y0 = (int) (y[j] + offsety); int j2 = j + 2 * paso; int x1 = (int) (x[j2] + offsetx); int y1 = (int) (y[j2] + offsety); //g.setColor(Color.red); int gris = (int) (z[j + paso] * multi + suma); g.setColor(new Color(gris, gris, gris)); g.drawLine(x0, y0, x1, y1); } }