Rotaci贸n de un cubo 3D usando Java
Resumen
Se muestra una manera sencilla de graficar y rotar un Cubo.
Se comienza con la teoría de transformaciones lineales y proyecciones en el plano, se describen las matrices de rotación y al final se muestra un código muy sencillo de una aplicación hecha en Java que ejemplifica y muestra las transfomaciones descritas.
Introducción
Se puede rotar una figura en tres dimensiones, sobre el eje X, sobre el eje Y o sobre el eje Z.
En el caso de una computadora, la pantalla es un espacio de dos dimensiones, por lo tanto hay que hacer una proyección sobre el plano XY cada vez que se manipulen figuras tridimensionales.
Existe un vector
en el plano R2 con coordenadas (x, y) y un ángulo
con respecto al eje X,
se desea rotar el vector en un ángulo
en sentido contrario de las manecillas del reloj con respecto al eje X. El nuevo vector
‘ forma un ángulo
+
respecto al vector
y existe la misma longitud r en ambos vectores como se muestra en la figura:
Utilizando las definiciones de seno y coseno obtenemos que para el vector ![]()

Para el vector
‘, de forma similar, se obtiene que:

La suma de dos ángulos esta dada por:

Si se considera r = 1 y se sustituyen los valores correspondientes de x e y de la figura 1 tenemos.


Escribiendo las mismas ecuaciones en forma de matrices se obtiene:

La matriz (1) es mejor conocida como matriz de rotación, la cual determina las nuevas coordenadas (x’, y’) obtenidas por rotar un ángulo
un punto (x, y) en sentido contrario de las manecillas del reloj.
Si las rotaciones se quieren hacer en el espacio R3, se necesita considerar el valor del eje de rotación (en el cual se desea girar) igual a uno y poner el resto de los valores igual a cero. En el caso de la matriz (1) se considera que rota con respecto al eje z.
Las matrices de rotación correspondientes a cada uno de los ejes están dadas por:

Para poder mostrar la rotación en tres dimensiones en un plano de dos dimensiones hay que hacer una proyección. Una proyección es una trasnformación lineal que toma un punto en el espacio de tres dimensiones y lo proyecta en un plano, que en este caso sera el plano xy. La matriz de transfomación que nos brinda la proyección de un punto en el plano xy es:

Ahora vamos a usar las matrices anteriores para tratar de rotar un cubo.
Para construir un cubo se pueden tomar una lista de vértices, aristas o caras, para ejemplos ilustrativos vamos a tomar una lista de ocho vértices y vamos a enumerarlos como sigue:

Si trazamos lineas que unan los vértices de forma tal que formen las aristas del cubo, se obtiene la siguiente figura:

La figura 3 muestra un cubo centrado en el origen pero a pesar de que la figura mostrada aparenta ser un cuadrado (dos dimensiones) se sabe que tiene una tercera dimensión dado que tiene las tres coordenadas en cada uno de sus ocho vértices, pero al proyectar sus puntos en el plano xy todos los valores z de los puntos son "planchados" al plano xy. Para darse cuenta de la forma tridimensional de la figura basta con rotar los puntos del cubo sobre el eje x o sobre el eje y.

El cubo aparenta estar representado en el espacio de tres dimensiones, pero carece de volumen, propiedad típica de los cuerpos tridimensionales. Para lograr una apariencia de volumen se necesitan crear diferentes polígonos que representen cada una de las caras del cubo, los vértices de cada cara del cubo serán los vértices de cada polígono y se puede asignar un color diferente a cada uno de ellos como se muestra.

Para ilustrar mejor lo antes mencionado, se contruy贸 un applet que muestra un cubo en 3D el cual puede rotar utlizando las matrices descritas.
Da click en el siguiente enlace para abrir el applet:
Rotaci贸n 3D de un cubo usando Java
Debes mover el rat贸n mientras mantienes presionado el bot贸n izquierdo sobre el cubo para observar su rotaci贸n.
A continuación se muestra el código que muestra la utilización de matrices de rotación y proyecciones en el plano.
/** this software is under GNU GPL @author: Jorge Alejandro Guti茅rrez Orozco compilation javac Cube.java Ejecution appletviewer Hola.html */ import java.applet.*; import java.awt.*; import java.awt.event.*; import java.lang.Math; import java.awt.Polygon; /* Represents a point in space */ class Point{ public double x; public double y; public double z; Point(){ this.x=0; this.y=0; this.z=0; } Point(double x,double y, double z){ this.x=x; this.y=y; this.z=z; } /* for move it at origin */ int getXCoordinate(){return Cube.origin + (int)this.x;} int getYCoordinate(){return Cube.origin - (int)this.y;} int getZCoordinate(){return 0;} /* projection on XY plane*/ } class Face{ Point p1,p2,p3,p4; Polygon side= new Polygon(); Face(){} Face(Point p1,Point p2,Point p3,Point p4){ this.p1=p1;this.p2=p2;this.p3=p3;this.p4=p4; this.side.addPoint(p1.getXCoordinate(),p1.getYCoordinate()); this.side.addPoint(p2.getXCoordinate(),p2.getYCoordinate()); this.side.addPoint(p3.getXCoordinate(),p3.getYCoordinate()); this.side.addPoint(p4.getXCoordinate(),p4.getYCoordinate()); } public void drawFace(Graphics g){ g.fillPolygon(side); } public boolean isVisible(){ Point aux1, aux2; aux1 = new Point(p2.x-p1.x, p2.y-p1.y, p2.z-p1.z); // p1->p2 aux2 = new Point(p4.x-p1.x, p4.y-p1.y, p4.z-p1.z); // p1->p4 if((aux1.x*aux2.y - aux1.y*aux2.x) > 0) return true; return false; } } public class Cube extends Applet implements Runnable, MouseMotionListener{ Thread luxury; static Point[] vertex = new Point[8]; // Cube vertexes static int maxSize =300; // screen size static int origin = maxSize/2; // (0,0) static int xMouseP=0, yMouseP =0; // X and Y mouse's positions int xAux , yAux ; // old X and Y mouse's positions int module = 50; // length of lines of cube. Image canvasAux; Graphics backBuffer; public void init(){ setSize(maxSize,maxSize); setBackground( new Color(0.2f,0.6f,0.1f,1.0f) ); addMouseMotionListener(this); /*Initial coordinates of eight vertex module is half the length of the cube*/ int[] coordX = new int[]{-module,module,module,-module,-module,module,module,-module}; int[] coordY = new int[]{-module,-module,module,module,-module,-module,module,module}; int[] coordZ = new int[]{module,module,module,module,-module,-module,-module,-module}; canvasAux = createImage(500,500); backBuffer = canvasAux.getGraphics(); for(int i =0; i<vertex.length; i++){ vertex[i] = new Point(coordX[i],coordY[i],coordZ[i]); } } public void start(){ try { luxury = new Thread(this); luxury.start(); } catch (Exception e){} } public void run() {} public void stop(){} public void paint(Graphics g){ /* Cube's points. Requires four points to create a face, requires six faces to create a cube.*/ int[] pts1 = new int[]{0,1,5,0,0,3}; int[] pts2 = new int[]{1,5,4,3,4,2}; int[] pts3 = new int[]{2,6,7,7,5,6}; int[] pts4 = new int[]{3,2,6,4,1,7}; Color[] colorRGB = new Color[]{ Color.black, Color.blue,Color.orange, Color.pink,Color.red, Color.yellow}; Face[] faces = new Face[6]; backBuffer.clearRect(0,0,500,500); for(int i=0; i<6; i++){ backBuffer.setColor(colorRGB[i]); faces[i] = new Face(vertex[pts1[i]],vertex[pts2[i]],vertex[pts3[i]],vertex[pts4[i]]); if(faces[i].isVisible()){ faces[i].drawFace(backBuffer); } } g.drawImage(canvasAux,0,0,this); g.drawString(" Touche ", 30,30); g.drawString(" X " + xMouseP, 30,50); g.drawString(" Y " + yMouseP, 30,65); return; } public void update(Graphics g){ paint(g); } public void destroy(){ try {Thread.sleep(1500);} catch (InterruptedException e) { System.out.println("Exception in sleep"); } } /* Rotate each vertex of cube over X, Y and Z axis */ void rota(double angleTeta, double anglePhi, double anglePsi) { double teta= Math.toRadians(angleTeta); double phi= Math.toRadians(anglePhi); double psi= Math.toRadians(anglePsi); Point pAux = new Point(); Point pAux1 = new Point(); Point pAux2 = new Point(); for(int i =0; i<8; i++){ /* Rotating over x */ pAux1.x = vertex[i].x; pAux1.y= vertex[i].y * Math.cos(teta) + vertex[i].z * (-Math.sin(teta)); pAux1.z = vertex[i].y * Math.sin(teta) + vertex[i].z * Math.cos(teta); /* Rotating over y */ pAux2.x = pAux1.x * Math.cos(phi) + pAux1.z * Math.sin(phi); pAux2.y = pAux1.y; pAux2.z = pAux1.x * (-Math.sin(phi)) + pAux1.z * Math.cos(phi); /* Rotating over z */ pAux.x= pAux2.x * Math.cos(psi) + pAux2.y * (-Math.sin(psi)); pAux.y = pAux2.x * Math.sin(psi) + pAux2.y * Math.cos(psi); pAux.z= pAux2.z; /* new position */ vertex[i].x = pAux.x; vertex[i].y = pAux.y; vertex[i].z = pAux.z; } } /* For move the cube over X and Y axis */ public void mouseDragged( MouseEvent e ){ /* old coordinates */ xAux = xMouseP; yAux = yMouseP; /* new coordinates */ xMouseP = e.getX(); yMouseP = e.getY(); if(yMouseP > yAux){ rota(2,0,0); } if(yMouseP < yAux){ rota(-2,0,0); } if(xMouseP > xAux){ rota(0,2,0); } if(xMouseP < xAux){ rota(0,-2,0); } repaint(); e.consume(); } public void mouseMoved( MouseEvent e ) { } }
El código anterior pertenece a un archivo llamado Cube.java, para poder visualizar el applet generado se necesita crear una archivo html.
El siguiente código solo es un html para desplegar el applet.
<!-- hola.html --> <HTML> <BODY> Cubo <APPLET width="500" height="500" CODE="Cube.class"><br /></APPLET> </BODY> </HTML>
En caso de contar con un IDE (Eclipse, jGrasp, etc.) no existe necesidad de crear dicho html para visualizar el cubo.
Quiz谩 te interese :
Programa para realizar suma de polinomios con listas enlazadas. package com.ubicuos.main; im ...
Leer datos con Java Read from Console Standard Input with Java Si alguna vez, necesitaste leer ...
脕rbol fractal en BorlandC para DOS /*arbol3*/ /*Fernando Galindo Soria*/ #include #incl ...






Podr铆as incrustar el Applet para verlo en funcionamiento?
?A帽adiendo el coloreado de sintaxis es m谩s f谩cil seguir el c贸digo :)
?La rotaci贸n en el eje y esta invertido.
Efectivamente hay mucha confusi贸n con respecto a la matr铆z de rotaci贸n en la web, la mayor铆a de las pags tienen una matr铆z donde el seno negativo est谩 en la segunda fila, pero ello representa una rotaci贸n en sentido de las manecillas del reloj:
http://en.wikipedia.org/wiki/Rotation_matrix
Puedes revisar el applet en el siguiente enlace para comprobar que el sentido de la rotaci贸n va acorde con el movimiento del rat贸n:
http://www.ubicuos.com/wp-content/uploads/2010/03/Hola.html
Much铆simas gracias por la observaci贸n.
?Yeah, es muy bueno, puedo visualizarlo creando el archivo html, pero no en netbeans, de todos modos revisare el codigo para ver la l贸gica, genial.
Jorge Alejandro dijo:
“Efectivamente hay mucha confusi贸n con respecto a la matr铆z de rotaci贸n en la web, la mayor铆a de las pags tienen una matr铆z donde el seno negativo est谩 en la segunda fila, pero ello representa una rotaci贸n en sentido de las manecillas del reloj:”
Esa es la desventaja del internet, cualquier persona puede escribir lo que quiera, sea cierto o no. Lo cierto es que realmenete no encontrar铆an tal “confusion” si utilizar谩n referencias confiables, o si entendieran como se deduce las matrices de transformaci贸n.
El an谩lisis no es d铆ficil, lo 煤nico que necesitas es conocer las identidades de suma de 谩ngulos.
Analic茅 el c贸digo pero no le veo la utilidad del Thread luxury. Se lo quit茅 al c贸digo y sigue funcionando el c贸digo, para que sirve entonces ? el m茅todo run() de todos modos no hace nada.
Buen programa.
Pues yo le he quitado el hilo y se alent贸 un poco, yo creo que este hilo es para manejar procesos adicionales ( tal vez por eso le llama “lujo”) sin necesidad de que la eficiencia de la rotaci贸n se viese afectada, no s茅, se me imagina que podr铆ais agregar iluminaci贸n al cubo o tal vez otros c谩lculos de manera independiente a la graficaci贸n. Excelente programa!
@Jorge_Alejandro: porqu茅 no agrega铆s la iluminaci贸n que te digo? Tomar茅 tu c贸digo para fines educativos jejeje. Buen trabajo.