Friday, October 26, 2007

Curbas cubicas paramétricas

Las curvas cubicas paramétricas son las de menor grado que no son planas en 3D. Los tres coeficientes de un polinomio de segundo grado pueden estar completamente especificados por tres puntos y tres puntos definen a un plano.

Los polinomios cúbicos que definen un segmento de cuva Q(t)=[x(t) y(t) z(t)] sonde la forma:

figura 01

Cada polinomio cubico tiene cuatro coeficientes, por lo que se requieren cuatro restricciones, que nos permitan formular cuatro ecuaciones en las cuatro incognitas y poder resolver el sistema de ecuaciones.

Las curbas que se discutiran son:
- Hermite
- Bezier
- Otros splines

* Hermite usa los punots terminales y los rectores tengente enlos puntos terminales

figura 02

* Bezier, esta definido por los dos puntos temrinales y otros dos puntos de control que controlan a los vecotes gtangente en lospuntos terminales

figura 03


Para ver como os coeficientes de la ec (1) pueden depender de cuatro restricciones, se escribe la matriz de coeficientes como

C = M.G
Donde M es la matriz base y G es un vector columna de cuatro elementos de las condiciones, tales como los puntos termianles y los vectores tangente , que definen a la curva.
Se usara G. para referirse al vector columna de solo los componentes x del vector de geometria.

Q(t) = T.C
4x3

C = M.G

Los elementos de M y G son constantes, de forma qeu el produto T.M.G es justo los tres polinomios cubicos en t
Q(t) = [x(t) y(t) z(t)]

figura 04

esta ecuacion enfatiza que la curva es una suma ponderada de los elementos de la matriz de geometria

B = T.M funciones mezcla (blending functions)


Splines Hermite
La curva esta determinada por los puntos terminales P1 y P4 y los vectores tangente R1 y R4 a los puntos teminales.
Tenemso que halar la matriz base Mh

Q(t) = T.Mh.Gh

Gh es el vector de geometria Hermite tomando su componente en x:

figura 05 (1)

figura 06 (2)

Las restricciones sobre x(0) y x(1) pueden encontrarse por sustitucion directa en la ec. (2).

figura 07

Labels: ,

Pila de matrices

NOTA: Port falta de medios no puedo subir algunas figuras que se explican en el pizarron por eso solo pongo lo copiado en la computadora, lo siento.

void glPushMatrix(void)

Mete en la pila todas las matrices actuales. La pila usada esta determinada por gl MatrixMode(). Se copia la matriz en el tope de la pila de forma que su contenido se duplica tanto en el tope y la segunda matriz de la pila. Si se meten muchas matrices, se genera un error.


void glPopMatrix(void)

Saca la matrix tope de la pila, destruyendo el contenido de la matriz. la pila viada esta determinada por glMatrixMode(). Si la pila solo contiene una matriz, llamar esta función genera un error.

En el modo MODELVIEW tenemos al menos una pila de 32 matrices
En el modo PROJECTION tenemos al menos una pila de 2 matrices


Figura 01



Realmente la cámara no existe en OpenGL. Dentro del modo PROJECTION solo debe llamarse a LoadIdentity() para limpiar la matrix y a Ortho() o Frustrum(), (o a glePerspective())
Las transformaciones de la cámara realmente devén de realizarse en el modo MODELVIEW. Si no se hace así habrá problemas el calcular la iluminación y la niebla.
La transformacion para crear la proyección oblicua si debe de crearse en el modo PROJECTION.

Labels: ,

Saturday, October 20, 2007

Mi carro en OpenGL


Mi carro en OpenGL
Dibujado de un carro en OpenGL y su proyección Ortogonal
Jose Angel Espinoza Portillo
Tarea 4 • Graficacion • 24 Octubre 2007


El FrameWork
Se utilizo un conjunto de códigos pre-configurados y listos para cargar una entidad de OpenGL en un widget de Qt, estos códigos fueron entregados por el Dr. Luis Gerardo de la Fraga y pude ser encontrado en el siguiente sitio: http://delta.cs.cinvestav.mx/~fraga/Cursos/Graficacion/2007/.
Algunas de las funciones ya implementadas son la rotación al dar click en el QGLWidget y arrastrar hacia alguna dirección.

Archivos
Utilizando el framework original de trabajo llamado “quad”. El cual contiene los archivos de código siguientes:
  • painter.cpp y painter.h: Implementación del QWidget es la ventana principal del sistema, aquí se implementan los botones, geometría, eventos y elementos que se observan en la ventana de la aplicación.
  • quad.cpp y quad.h: Implementa del QGLWidget que representa a el área en la cual se va a dibujar. Incluye funciones de trazado de inicialización de estados y de modos de renderización de el área de trazado.
  • main.cpp: Es el código que iniciativa la aplicación y le da la ejecución a el Qt para que interprete las instrucciones especificadas en canvas.cpp.


Diseño de el Carro
Especificación de las características de el carro y sus vértices.



Antes de empezar a programar hay que desarrollar una estructura de hilos para identificar la forma de el vehículo vista desde varios ángulos, y para especificar la dimensión de sus puntos (vértices).

De las estructuras anteriores se tomara la distancia de sus puntos desde un origen imaginario para definir un arreglo de vectores v1.



Definiendo el arreglo “v1”
Este arreglo se define en el archivo quad.h, para que pueda usarse en la clase, es inicializada en el constructor junto a su especificación con los puntos que en ella se representan de la siguiente manera.

Quad::Quad( QWidget *parent, const char *name )
...;
v1[0][0] = 0.0; v1[0][1] = 0.0; v1[0][2] = 4.0;
v1[1][0] = 5.0; v1[1][1] = 0.0; v1[1][2] = 4.0;
v1[2][0] = 5.0; v1[2][1] = 8.0; v1[2][2] = 4.0;
v1[3][0] = 0.0; v1[3][1] = 8.0; v1[3][2] = 4.0;

v1[4][0] = 1.0; v1[4][1] = 2.0; v1[4][2] = 4.0;
v1[5][0] = 4.0; v1[5][1] = 2.0; v1[5][2] = 4.0;
v1[6][0] = 4.0; v1[6][1] = 7.0; v1[6][2] = 4.0;
v1[7][0] = 1.0; v1[7][1] = 7.0; v1[7][2] = 4.0;

v1[8][0] = 1.25; v1[8][1] = 2.75; v1[8][2] = 5.25;
v1[9][0] = 3.75; v1[9][1] = 2.75; v1[9][2] = 5.25;
v1[10][0] = 3.75; v1[10][1] = 6.1; v1[10][2] = 5.25;
v1[11][0] = 1.25; v1[11][1] = 6.1; v1[11][2] = 5.25;

v1[12][0] = 0.0; v1[12][1] = 0.0; v1[12][2] = 1.0;
v1[13][0] = 5.0; v1[13][1] = 0.0; v1[13][2] = 1.0;
v1[14][0] = 5.0; v1[14][1] = 8.0; v1[14][2] = 1.0;
v1[15][0] = 0.0; v1[15][1] = 8.0; v1[15][2] = 1.0;
...;
}


Definiendo los valores en v1[n][o], v1[n][1] y v1[n][2] en los planos ‘x’, ‘y’ y ‘z’ respectivamente.

Dibujando el Carro
El arreglo anterior no es suficiente para que una figura sea dibujada en el espacio tridimensional, hay que indicarle a el OpenGL como debe unir los puntos anteriores y esto se hace como a continuación:

void Quad::paintGL(void){
...;
glBegin(GL_QUADS);
glColor3f( 1.0, 0.0, 0.0 );
glVertex3fv( &v1[0][0] );
glVertex3fv( &v1[1][0] );
glVertex3fv( &v1[2][0] );
glVertex3fv( &v1[3][0] );

glVertex3fv( &v1[8][0] );
glVertex3fv( &v1[9][0] );
glVertex3fv( &v1[10][0] );
glVertex3fv( &v1[11][0] );

glColor3f( 0.0, 1.0, 0.0 );
glVertex3fv( &v1[12][0] );
glVertex3fv( &v1[13][0] );
glVertex3fv( &v1[14][0] );
glVertex3fv( &v1[15][0] );

glColor3f( 1.0, 1.0, 1.0 );
glVertex3fv( &v1[0][0] );
glVertex3fv( &v1[3][0] );
glVertex3fv( &v1[15][0] );
glVertex3fv( &v1[12][0] );

glVertex3fv( &v1[8][0] );
glVertex3fv( &v1[4][0] );
glVertex3fv( &v1[7][0] );
glVertex3fv( &v1[11][0] );

glColor3f( 0.0, 0.0, 1.0 );
glVertex3fv( &v1[0][0] );
glVertex3fv( &v1[1][0] );
glVertex3fv( &v1[13][0] );
glVertex3fv( &v1[12][0] );

glVertex3fv( &v1[4][0] );
glVertex3fv( &v1[5][0] );
glVertex3fv( &v1[9][0] );
glVertex3fv( &v1[8][0] );

glColor3f( 1.0, 1.0, 0.0 );
glVertex3fv( &v1[1][0] );
glVertex3fv( &v1[2][0] );
glVertex3fv( &v1[14][0] );
glVertex3fv( &v1[13][0] );

glVertex3fv( &v1[5][0] );
glVertex3fv( &v1[6][0] );
glVertex3fv( &v1[10][0] );
glVertex3fv( &v1[9][0] );

glColor3f( 1.0, 0.0, 1.0 );
glVertex3fv( &v1[2][0] );
glVertex3fv( &v1[3][0] );
glVertex3fv( &v1[15][0] );
glVertex3fv( &v1[14][0] );

glVertex3fv( &v1[10][0] );
glVertex3fv( &v1[11][0] );
glVertex3fv( &v1[7][0] );
glVertex3fv( &v1[6][0] );
glEnd();
...;
}


Problemas


Al implementar el código anterior se detecto un problema con el eje de rotación y la orientación de los puntos, el dibujo se dibujaba como en esta figura:
Para solucionar estos problemas se le resto a todos los puntos la mitad del punto con mayor unidad de cada vértice y se intercambiaron los puntos de ‘z’ y ‘y’ con el siguiente código:

for (n=0;n<16;n++){ n="0;n<16;n++){" u =" v1[n][1];">

Calculando la proyección

Utilizando una matriz de transformación.

Principios matemáticos
Como fue visto en clase para hacer una proyección ortogonal es necesario un valor l para indicar la “Profundidad” visible del objeto y un ángulo a para designar la inclinación de la proyección y asignarlo a una matriz para especificar que transformación, la cual esta expresada en la Tabla (matriz) 01.
Tabla (matriz) 01

La cual multiplicada por la matriz de el objeto se dará el efecto deseado.

Implementación
Para la implementación de matrices en OpenGL se debe utilizar matrices tipo cuaternion que se implementa como un vector de 16 elementos de algún tipo en este caso GLfloat (que es una implementación de OpenGL que garantiza la potabilidad del tipo float en diferentes arquitecturas ) y se declara:

GLfloat matriz2[16];

Como básicamente es una matriz con ceros, se asignaron estos (los ceros) a todos los elementos y luego basandonos las posiciones de los indices señalados en la Tabla 02.
Tabla 02

Se escribió la matriz de la siguiente manera:

matriz2[0]=1;
matriz2[2]=-l*cos((angle*PI)/180);
matriz2[5]=1;
matriz2[6]=-l*sin((angle*PI)/180);
matriz2[15]=1;

Y luego antes de escribir el código de la representación de la figura escribir:

glMultMatrixf(matriz2);

Problema
Pero al implementar este código tal cual con las bases antes mencionadas encontrábamos otro problema, que era básicamente que no parecía dibujar nada en la pantalla, después de algunas comprobaciones matemáticas y con otros objetos nos percatamos de un patrón del error, el cual era básicamente la inversión de los indices de la matriz y fue en ese momento donde se descubrió que OpenGL no interpretaba los indices del cuaternion de la manera que inducimos sino como en la Tabla 03, y al final queda como a continuación:



matriz2[0]=1;
matriz2[8]=-l*cos((angle*PI)/180);
matriz2[5]=1;
matriz2[9]=-l*sin((angle*PI)/180);
matriz2[10] = 0.00001; /* Arregla un depth bug */
matriz2[15]=1;

Donde se usan l y angle que explicare a continuación.

Tabla 03



El painter
Dibujado de los componentes a utilizar
Se implementan por primera vez los objetos QSlider, para dar mas inter actividad a el sistema y conectandolo a unos SLOTS’s en el quad que modifican el valor de a (angulo) o l que se usan en el proceso de dibujado de el objeto en la ventana OpenGL.

Forma final de el Ejecutable

El cual tiene un switch entre la manipulación (Rotación) de el objeto que el Dr. de la Fraga tenia implementado originalmente con el nuevo objeto y la vista de la proyección del cuerpo. Los slides que modifican los valores de l y a (angulo).

Conclución
  • Es relativamente sencillo crear objetos 3D en OpenGL, pero lo que es complicado es la estructura que devén de tener los datos para su apropiado manejo.
  • Nunca seré un diseñador de autos.

Labels: ,

Friday, October 19, 2007

En Defensa de la Ciencia

Ultima mente el estilo de vida y mi capacidad económica me a obligado a asomarme a unas "excelentes" librerías que existen en esta gran ciudad donde agrupan una gran cantidad de libros usados. Y después de no encontrar mucho en el área de sistemas me moví a la estantería y encontré un espécimen que me llamo la atención e inmediatamente lo tome con migo y me retire con el por la tremenda suma de $30.00 MN con un ejemplar escrito por el Doctor Rui Pérez Tamayo.


Dr. Ruiz Perez Tamayo
El Doctor Pérez Tamayo ha sido miembro de diferentes Sociedades Científicas, dentro de las que destacan el ser miembro extranjero del American College of Physicians; Fundador de la Asociación Mexicana de Patólogos; Sociedad de Becarios e Internos del Instituto Nacional de Cardiología; Sociedad Médica del Hospital General entre muchas otras, pero lo que me interesa de este personaje es que gano el premio nacional de ciencias, hace varios años (unos 20 mas o menos). Este libro es solo una de sus aportaciones a la cultura de el pais tratando de hacer una critica del estado de las ciencias en el momento en que fue publicado (1976).

Esta es una lectura que e empesado a leer en estos ultimos dias y espero si la escuela me lo permite terminarlo en un par mas para poder comentarlo extensivamente.

Labels: ,

Monday, October 1, 2007

Dibujando un Reguilete

Vercion en PDF

Con QT y C++
Jose Angel Espinoza Portillo
Graficación
Septiembre - Octubre 2007


Preámbulo
Utilizando el framework original de trabajo llamado “linetest”. El cual contiene los archivos de código siguientes:


  • painter.cpp y painter.h: Implementación del QWidget es la ventana principal del sistema, aquí se implementan los botones, geometría, eventos y elementos que se observan en la ventana de la aplicación.

  • canvas.cpp y canvas.h: Implementa del QWidget que representa a el área en la cual se va a dibujar. Incluye funciones de trazado de lineas las cuales son la función en la que me base para dibujar el REGUILETE.

  • main.cpp: Es el código que iniciativa la aplicación y le da la ejecución a el Qt para que interprete las instrucciones especificadas en canvas.cpp.



Figura 1.

Insertar Botones a el FrameWork proporcionado
Se agrego el inicio del constructor de Painter::Painter() en painter.cpp


QPushButton *boton1 = new QPushButton("Direccion");
boton1->setFont( QFont( "Times", 18, QFont::Bold ) );
connect( boton1, SIGNAL(clicked()), canvas, SLOT(changeDirection()) );

QPushButton *boton2 = new QPushButton("Start/Stop");
boton2->setFont( QFont( "Times", 18, QFont::Bold ) );
connect( boton2, SIGNAL(clicked()), canvas, SLOT(switchMotioin()) );

Para crear la instancia en memoria de los botones con nombres boton1 y boton2.
Al final de la misma función se agrego:

leftBox->addWidget( boton1 );
leftBox->addWidget( boton2 );

Justo antes del “setLayout(grid);”, al compilar se vera una pantalla como en la Figura 1.

Comunicación entre el Canvas y el Painter
Este proceso a sido documentado anteriormente, así que me limito a agregar solo las modificaciones realizadas para este proyecto.
Se agregaron los Slots changeDirection y switchMotioin, para que puedan se accesados desde los botones del painter como se explico en el punto anterior y timeDone para se acezado desde un objeto QTimer, que se explicara a continuación.

Solucionando el Problema
Como un requisito de este problema es hacer que los reguiletes roten, se necesitó planear 3 partes:

  • Desarrollar una función drawReguilete a la cual le pasan por parámetros el numero de astas del reguilete y su centro y lo dibuja.

  • Agregar un QTimer para determinar el lapso para cambio de coordenadas rotación.

  • Programar las funciones que administren los estados de los últimos dos, puntos.


Desarrollando la función drawReguilete()
Basado en la implementación ya dada por el profesor para dibujar un destello la función de reguilete se implemento como sigue:

void Canvas::drawReguilete(int cx, int cy, int lados, QPainter *p){
int incrang = 360/lados; // Incremento del angulo para saber el angulo medio de cada hoja del reguilete
int l = 50; // Se define un largo l de las lineas de las hojas

int n = 360/incrang; //Se define el numero de repeticiones del algoritmo

for ( int i=0; isetPen(QColor("Blue"));
MidPointLine( cx, cy, px, py, p ); // Se dibuja la linea superior
MidPointLine( cx, cy, px2, py2, p );// Se dibuja la linea inferior
MidPointLine( px2, py2, px, py, p );// Se dibuja la linea que une las anteriores para cerrarla
}
}

Agregando el QTimer
Esta es un objeto que te llama una función cada determinado tiempo que el programador le especifique, para implementarla en el código, se agrego la siguiente librería en canvas.cpp:

#include

y dentro del constructor Canvas::Canvas() se agrego el siguiente codigo:

tiempo = new QTimer(this);
connect( tiempo, SIGNAL(timeout()), this, SLOT(timeDone()));

El cual instancia en memoria el el objeto QTimer, en la variable tiempo, y la conecta en su SIGNAL timeout() al objeto canvas en su SLOT timeDone(), el cual se describe a continuación:

void Canvas::timeDone(){
angulo += (10 * direccion);
repaint();
}

En este código se puede ver que se presenta la variable dirección, esta es manejada por las funciones de administración de estados y define la dirección de rotación de los reguiletes, también se observa la variable ángulo que se usa en el dibujado de el reguilete, como el ángulo acumulado de rotación del sistema.

Funciones de administración de estados.
Las siguientes funciones son las que responden a los SLOTS changeDirection y switchMotioin las cuales administran el cambio de estados de el reguilete por medio de la variables globales dirección y tiempo.

void Canvas::changeDirection(){
direccion *= -1;
}

void Canvas::switchMotioin(){
if (tiempo->isActive()){
tiempo->stop();
}else{
tiempo->start(400);
}
}

En changeDirection solo se multiplica su contenido por -1 haciendo que a todo lo que sea multiplicado esta variable tendrá una dirección contraria a la original.
Y en switchMotioin se pregunta a tiempo si esta activo (si se esta ejecutando algún tonteo de lapsos), si es así lo detiene de lo contrario lo inicia.

Labels: ,