Rotación de un cubo 3D usando Java

marzo 4 20109 comentarios

Guardado en : Ciencia, Programación

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 v en el plano R2 con coordenadas (x, y) y un ángulo alfa con respecto al eje X,
se desea rotar el vector en un ángulo teta en sentido contrario de las manecillas del reloj con respecto al eje X. El nuevo vector v ‘ forma un ángulo teta+alfarespecto al vector v y existe la misma longitud r en ambos vectores como se muestra en la figura:

fig1

Utilizando las definiciones de seno y coseno obtenemos que para el vector v

Formula1

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

formula2

La suma de dos ángulos esta dada por:

formula3

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

Formula4

Formula5

Escribiendo las mismas ecuaciones en forma de matrices se obtiene:

Formula7

La matriz (1) es mejor conocida como matriz de rotación, la cual determina las nuevas coordenadas (x’, y’) obtenidas por rotar un ángulo teta 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:

Matrcies rotacion

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:

Formula8

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:

Fig2

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

Fig3

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.

Fig4

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.

Fig5

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 :

Acerca del autor:

9 Respuestas a “Rotación de un cubo 3D usando Java”

  1. Daniel dice:

    Podrías incrustar el Applet para verlo en funcionamiento?

  2. Daniel dice:

    Añadiendo el coloreado de sintaxis es más fácil seguir el código :)

  3. wafto dice:

    La rotación en el eje y esta invertido.

  4. 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.

  5. Huguin dice:

    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.

  6. tyomero dice:

    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.

  7. Gustavo dice:

    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.

  8. Mary dice:

    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.

  9. Stella dice:

    eee genial programa
    en vdd

Deja un comentario


Licencia y uso

Las técnicas demostradas en los tutoriales pueden ser utilizadas sin ninguna limitación y tampoco es obligatorio dar una atribución.


Los textos, imágenes y tutoriales son propiedad de sus respectivos autores, nuestro contenido se encuentra bajo licencia Creative Commons Share-Alike.

Escribe algo para el sitio

El escribir un tutorial o un artículo, mandarnos un enlace para Ubicuos, no solamente es una forma de obtener publicidad, si no también de dar algo a la comunidad y nosotros te lo recompensamos con los premios del mes! Leer más de nuestras promociones

¿Sugerencias?

Este es TU sitio, si tienes sugerencias o ideas de cómo podemos mejorarlo para ti, ¡Por favor háznoslos saber!

Hacemos nuestro mayor esfuerzo en proporcionar un sitio útil y amigable y esperamos que disfrutes tu tiempo aquí.

Ayuda a Difundir

Te gusta Ubicuos?

Ve las formas en que nos puedes apoyar.

Apoyando a Ubicuos.com

Submit your linkClose

-->