Você está na página 1de 299

Departamento de Electrnica e Informtica, Universidad Centroamericana Jos Simen Caas

Una Humilde Introduccin a la Gracacin por Computadora y Otras Yerbas


por
Eduardo NAVAS

versin IFH PHIHFHQFHV

Este libro fue desarrollado nicamente con software libre. Entre las herramientas usadas,

AT X, L X, GNU/Linux, GNOME, KDE, XaoS, Maxima, KmPlot, se encuentran: L E Y OpenOce.org, Geany, Inkscape, GIMP, Python, GCC, SDL, PyGAME, GTK+, Qt4,
etc.

CC-BY-NC-SA
Este es un libro libre con la licencia

Creative Commons Attribution-Noncommercial-Share Alike 3.0.


Los detalles pueden ser consultados en: http://creativecommons.org/licenses/by-nc-sa/3.0/deed.es La versin digital y el material adicional puede ser descargado de: www.aliamondano-eo.wikidot.com/libro-gracos http://dei.uca.edu.sv/publicaciones/ ISBN: 978-99923-73-36-1 Editado y preparado desde el

Departamento de Electrnica e Infomtica de la


Universidad Centroamericana Jos Simen Caas, El Salvador, Centroamrica.

hedio est orFFF FE e todos los que on(ron en m @on muh freueni muho ms de lo que yo mismoA en este vije en trenD que se llm mi vida Y FE e mi redor y seor que siempre se qued onmigoD n undo yo no me qued siempre on lY FE e todos los que quieren yudr onstruir
el Otro Mundo Posible

FE el tiuj kiuj e kontr u)ue kunhelps fri pli onn mondon

Prlogo
Descripcin general
Este libro est siendo desarrollado con el propsito de ser libro de texto para diversas materias impartidas y a impartir para la carrera de Licenciatura en Ciencias de la Computacin y otras carreras de grado y postgrado en la Universidad Centroamericana Jos Simen Caas en El Salvador. Est pensado para estudiantes que ya han aprobado los cursos en los que se estudia clculo innitesimal, geometra analtica vectorial, estructura de datos y programacin orientada a objetos. La gracacin por computadora es una de las principales reas de estudio de las ciencias de la computacin, con aplicacin en todos los mbitos de la computacin; desde la necesidad de representar medianas o grandes cantidades de datos numricos que seran ilegibles en texto, hasta el desarrollo de sosticadas aplicaciones cientcas de simulacin de modelos matemticos del clima; desde el uso de software de edicin de fotografas, hasta el desarrollo de videojuegos. En todos los mbitos, surge la necesidad de tener conocimientos elementales de gracacin por computadora. El contendio del libro comienza con una breve introduccin a SDL (y PyGame) que es la biblioteca grca base a ser usada en los primeros captulos del libro. Se sigue con una introduccin terica a la gracacin por computador, en la que se presentan algunos conceptos elementales que son imprescindibles para la programacin de aplicaciones grcas interactivas. En el captulo siguiente se aborda mpliamente el tema de la discretizacin de lneas rectas y circunferencias de un pixel de grosor. Se aborda tambin el tema de relleno de circunferencias. Luego se procede a hacer un riguroso anlisis vectorial del tema de cambio de coordenadas o cambio de marco de referencia. All se incluye una aplicacin de ejemplo en la que se pone en prctica la teora presentada. Despus, se describe la teora matemtica matricial relacionada con las transformaciones geomtricas bidimensionales y tridimensionales. Se incluye entonces una aplicacin de corte pedaggico que permite practicar transformaciones geomtricas tridimensionales y permite ver en tiempo real sus efectos sobre un conjunto de objetos geomtricamente sencillos. El siguiente captulo, muestra la teora necesaria para comprender cmo transformar

un objeto de tres dimensiones a una imagen bidimensional y cmo implementar tal transformacin. A continuacin, se incluye una reexin sobre el diseo de la interaccin entre el humano y las aplicaciones grcas. Una vez hecha dicha reexin, se procede al estudio de la aproximacin de curvas arbitrarias por medio de segmentos cbicos paramtricos. Y luego de la presentacin de la teora matemtica implicada, se presentan cinco implementaciones sencillas. Despus se hace una breve mencin de supercies paramtricas. Hacia el nal del contenido principal del libro, se presenta una descripcin de las estructuras de datos utilizables para representar mallas poligonales, que representan la tcnica usada para modelar supercies arbitrarias en todo el software de modelacin tridimensional. Finalmente se hace una introduccin a la gracacin por medio de tcnicas fractales que incluye implementacin de aplicaciones de dibujo fractal. Por ltimo, pero no menos importante, se incluyen algunos temas no relacionados directamente con la gracacin por computadora, pero s relevantes para facilitar la lectura y comprensin de algunos temas y cdigos fuente includos. Entre ellos, instrucciones sencillas y concisas para compilar proyectos en lenguaje C estndar distribuidos en varios archivos fuente; y un brevsimo resumen de notacin UML para diagramas de clases y diagramas de objeto.

Lo que queda fuera


Como se dijo antes, la gracacin por computadora es una de las grandes reas de las ciencias de la computacin, razn por la cual, cualquier obra que pretenda cubrirla toda, se quedar corta. En el caso de este libro, estos son los temas que no se cubren en la versin actual de la obra: Temas avanzados de SDL Interfaces grcas de usuario (GUI) Modelado de slidos Renderizacin de texto Operaciones de bajo nivel (en ensamblador, por ejemplo) Manipulacin de mapas de bits Hardware grco Iluminacin, sombreado y texturas

Detalles de instalacin de las herramientas en ambientes Windows Esto no signica que no sean temas interesantes para un computlogo, o para un cientco. Pero esta edicin simplemente no los contempla.

Lo que conviene saber


Antes de escrutar este libro, es recomendable tener una base aceptable en los siguientes temas: Operaciones bsicas con matrices lgebra vectorial y nociones de clculo vectorial Operaciones bsicas con nmeros complejos Parametrizacin de curvas Sistemas de coordenadas rectangulares, cilndricas y esfricas Estructuras de datos bsicas (listas, pilas, colas, rboles, directorios de nodos, grafos, etc.) Nociones claras de Programacin Estructurada y Programacin Orientada a Objetos Conocimientos slidos del lenguaje C estndar en ambiente GNU/Linux Conocimientos intermedios en lenguaje Python en ambiente GNU/Linux Conocimientos intermedios del lenguaje Java Destrezas elementales del uso de sistemas operativos GNU/Linux

Motivacin al lector
En general, el autor desea expresar su deseo de seguir mejorando y ampliando, en la medida de lo posible, esta obra para benecio de la sociedad salvadorea. Y tambin desea invitar al apreciable lector a que le saque todo el provecho posible, que aplique sus aportes en todas las reas posibles de su trayectoria profesional; y tambin que no lo considere una serie de armaciones incuestionables, sino una contribucin al desarrollo tecnolgico salvadoreo y a la independencia tecnolgica de la regin centroamericana. Como toda obra acadmica de programacin, o como la mayora, los cdigos (y programas) includos pretenden mantener simplicidad y claridad para no dicultar innecesariamente la comprensin, pero se recomienda hacer el ejercicio de optimizarlos.

Cambios en esta versin


En esta nueva versin (que se corresponde con la segunda edicin), se han hecho muchos cambios a lo largo de todo el texto. Se han corregido diversos errores tipogrcos en todos los captulos, se han revisado todos los cdigos fuente que ya estaban y las imgenes se han revisado en su resolucin y distribucin. El tamao de las pginas tambin se ha cambiado al tamao carta, que es el estndar de facto en El Salvador. Todos los bloques de cdigo fuente se han resaltado para su ms cmoda y ms fcil lectura. El captulo 1 se ha ampliado en el tema de la instalacin de SDL. Los ejemplos bsicos fueron revisados y reordendados. El captulo 2 incluye ahora una seccin especial sobre el ciclo de interaccin de aplicaciones grcas interactivas. El captulo 9 incluye mucho nuevo material, incluyendo nuevas imgenes, ms algoritmos, ms profundidad y nuevos programas de ejemplo. El captulo 10 tambin cuenta con nuevas imgenes, hechas con Maxima. El contenido del 12 fue revisado y cuenta con nuevas imgenes. Y nalmente, se ha integrado la biblioteca texto, en paralelo con

hv

pygme

para Python en la primera parte del

para C.

10

ndice general
I. Gracacin por computadora 21

1. Introduccin a SDL y PyGAME


1.1. Instalacin de las bibliotecas para C 1.1.1. 1.1.2. 1.1.3. 1.1.4. 1.1.5. 1.1.6. 1.2. 1.3. . . . . . . . . . . . . . . . . . . . . Vericar si ya estn instalados los paquetes de desarrollo . . . . . Diseo modular de SDL . . . . . . . . . . . . . . . . . . . . . . .

23
23 23 24 24 25 25 26 26 26 26 28 29 31 33 35 36 37 42 45 46 46 47 48 50 51 52 53 54

Instalacin en distribuciones basadas en Debian . . . . . . . . . . Instalacin en openSuSE . . . . . . . . . . . . . . . . . . . . . . . Instalacin en Fedora y derivados de RedHat Otras distribuciones . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Compilacin de los programas que usan SDL en C

Ejemplos bsicos en SDL . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1. 1.3.2. 1.3.3. 1.3.4. 1.3.5. 1.3.6. 1.3.7. 1.3.8. Inicializacin bsica del sistema de video (el Hola Mundo de SDL): Inicializacin de los subsistemas . . . . . . . . . . . . . . . . . . . Modos de video . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eventos de ratn . . . . . . . . . . . . . . . . . . . . . . . . . . . Eventos de teclado . . . . . . . . . . . . . . . . . . . . . . . . . . Redimensionamiento de la ventana . . . . . . . . . . . . . . . . .

pacilitar

la compilacin

. . . . . . . . . . . . . . . . . . . . . . .

Compilacin desde varios archivos fuente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1.4. 1.5. 1.6.

Dibujo de primitivas grcas con SDL

Instalacin de PyGAME . . . . . . . . . . . . . . . . . . . . . . . . . . . Ejemplos bsicos en Python . . . . . . . . . . . . . . . . . . . . . . . . . 1.6.1. 1.6.2. 1.6.3. 1.6.4. 1.6.5. 1.6.6. Inicializacin bsica del sistema de video (el Hola Mundo de

pygme):

Inicializacin de los subsistemas . . . . . . . . . . . . . . . . . . . Modos de video . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eventos de ratn . . . . . . . . . . . . . . . . . . . . . . . . . . . Eventos de teclado . . . . . . . . . . . . . . . . . . . . . . . . . . Redimensionamiento de la ventana . . . . . . . . . . . . . . . . .

1.7. 1.8.

Dibujo de primitivas grcas con Ejercicios

pygme

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

11

ndice general

2. Introduccin a la Gracacin por Computadora


2.1. Marco conceptual para la gracacin interactiva . . . . . . . . . . . . . . 2.1.1. 2.1.2. 2.1.3. 2.2. 2.2.1. 2.2.2. 2.2.3. 2.2.4. 2.3. 2.3.1. 2.3.2. 2.3.3. 2.4. 2.5. El Modelado La Interaccin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . La Presentacin . . . . . . . . . . . . . . . . . . . . . . . . . . . .

57
57 57 58 58 59 60 61 67 69 74 74 75 75 75 76 76 77 77 78 79

Ciclo de interaccin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ciclo de eventos con SDL Ciclo de juego con SDL

Ciclo de eventos con pygame

Ciclo de juego con pygame . . . . . . . . . . . . . . . . . . . . . . Grcos de barrido o raster . . . . . . . . . . . . . . . . . . . . .

Tipos de representacin de grcos . . . . . . . . . . . . . . . . . . . . . Grcos vectoriales . . . . . . . . . . . . . . . . . . . . . . . . . . Representacin hbrida . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Paletas de colores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Paletas estndares actuales 2.5.1. 2.5.2. 2.5.3. RGB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . RGBA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . CMY(K) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

2.6. 2.7.

Espacios de Colores (gamuts) Ejercicios

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3. Discretizacin de Primitivas Grcas


3.1. 3.2. 3.3. 3.4. 3.5. 3.6. 3.7. Recordatorio bsico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Simbologa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Algoritmo incremental bsico 3.4.1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Algoritmo de lnea de punto medio

81
81 82 83 84 90 94 95 95 99 101 102 103 105

Simetra del algoritmo de lnea de punto medio

La simetra de la circunferencia . . . . . . . . . . . . . . . . . . . . . . . Algunas ideas sobre circunferencias . . . . . . . . . . . . . . . . . . . . . Algoritmo de circunferencia de punto medio . . . . . . . . . . . . . . . . 3.7.1. 3.7.2. Versin sin multiplicaciones . . . . . . . . . . . . . . . . . . . . . Circunferencias con centro arbitrario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3.8. 3.9.

Relleno de rectngulos . . . . . . . . . . . . . . . . . . . . . . . . . . . . Relleno de circunferencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3.10. Ejercicios

4. Marcos de Referencia y Cambio de Coordenadas


4.1. 4.2. Notacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anlisis vectorial del cambio de coordenadas . . . . . . . . . . . . . . . . 4.2.1. Ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

109
109 110 112

12

ndice general
4.3. 4.4. 4.5. Simplicacin escalar para ventana completa Transformacin de distancias . . . . . . . . . . . . . . . 114 115 115 116 118 119 121 129 130

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Aplicacin: Simulador de campo elctrico bidimensional 4.5.1. 4.5.2. 4.5.3. 4.5.4. 4.5.5. Campo Elctrico

. . . . . . . . . . . . . . . . . . . . . . . . . . .

Uso de colores para

hvgfxrimitives

. . . . . . . . . . . . . .

Las escalas y sus conversiones . . . . . . . . . . . . . . . . . . . . Programa principal . . . . . . . . . . . . . . . . . . . . . . . . . . El

wkefile .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . .

4.6.

Ejercicios

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

5. Transformaciones Geomtricas Bidimencionales


5.1. Operaciones geomtricas bsicas 5.1.1. 5.1.2. 5.1.3. 5.2. 5.3. 5.4. 5.5. 5.6. 5.7. . . . . . . . . . . . . . . . . . . . . . . Traslacin o Desplazamiento . . . . . . . . . . . . . . . . . . . . . Escalamiento . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

133
133 133 134 136 137 138 139 139 140 143

Rotacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Representacin matricial . . . . . . . . . . . . . . . . . . . . . . . . . . . Composicin de tranformaciones geomtricas . . . . . . . . . . . . . . .

Atencin a la eciencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . Versin matricial del cambio de coordenadas . . . . . . . . . . . . . . . . Reversin de transformaciones geomtricas . . . . . . . . . . . . . . . . . Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

6. Transformaciones Geomtricas Tridimencionales


6.1. 6.2. 6.3. 6.4. Sistemas de referencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Representacin Matricial de Transformaciones Geomtricas

145
145 145

Composicin y reversin de Transformaciones Geomtricas Tridimensionales148 Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150

7. Vista Tridimensional (de 3D a 2D)


7.1. 7.2. 7.3. 7.4. 7.5. 7.6. Proyeccin Ortogonal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Proyeccin en Perspectiva . . . . . . . . . . . . . . . . . . . . . . . . . . Portal de Visin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Implementacin de proyecciones ortogonales . . . . . . . . . . . . . . . . Implementacin de proyecciones en perspectiva Ejercicios . . . . . . . . . . . . . .

151
151 153 153 156 158 162

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

8. Interaccin
8.1. 8.2. Posicionamiento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Seleccin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.2.1. 8.2.2. Seleccin por puntero . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

163
163 164 164 165

Seleccin por nominacin

13

ndice general
8.3. 8.4. Transformacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Conclusin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165 166

9. Curvas Paramtricas
9.1. 9.2. Representacin algebrica de Curvas Paramtricas 9.2.1. 9.2.2. . . . . . . . . . . . . Continuidad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Curvas suaves . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Tipos de continuidad . . . . . . . . . . . . . . . . . . . . . . . . . Continuidad Geomtrica . . . . . . . . . . . . . . . . . . . . . . . Continuidad Paramtrica 9.2.3. 9.3. 9.3.1. 9.3.2. 9.3.3. 9.3.4. 9.3.5. Interpolacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

167
167 169 169 170 170 170 171 172 172 172 173 173 175 175 175 176 176 176 177 178 178 194 210 210 210 211 211 214 215 215 221 239 240 240

Trazadores interpolantes cbicos - Curvas Spline

Curvas Spline y B-Spline . . . . . . . . . . . . . . . . . . . . . . . Descripcin geomtrica . . . . . . . . . . . . . . . . . . . . . . . . Descripcin matemtica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Algoritmo de clculo de coecientes

Algoritmos de clculo de puntos interpolados

Clculo de un slo punto . . . . . . . . . . . . . . . . . . . . . . . Clculo de todos los puntos . . . . . . . . . . . . . . . . . . . . 9.3.6. Extensin para curvas paramtricas multidimensionales El caso bidimensional El caso tridimensional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Eciencia en el clculo de un slo punto Eciencia en el clculo todos los puntos 9.3.7. 9.3.8. 9.4. 9.4.1. 9.4.2. 9.4.3. 9.4.4. 9.4.5. 9.4.6.

Ejemplo de implementacin . . . . . . . . . . . . . . . . . . . . . Aplicacin para animaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Descripcin geomtrica . . . . . . . . . . . . . . . . . . . . . . . . Descripcin matemtica Polinomios de Bernstein . . . . . . . . . . . . . . . . . . . . . . . Generalizacin de curvas de Bzier de grado Ejemplos de implementacin

Curvas de Bzier

. . . . . . . . . .

Unin de segmentos de Bzier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bezier1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bezier2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . editorBezier.py . . . . . . . . . . . . . . . . . . . . . . . . . . . .

9.5. 9.6.

Splines vs. Bzier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

10.Supercies paramtricas
10.1. Representacin algebrica de Supercies paramtricas . . . . . . . . . .

243
243

14

ndice general
10.2. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245

11.Mallas Poligonales
11.1. Representacin explcita . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.2. Apuntadores a una lista de vrtices . . . . . . . . . . . . . . . . . . . . . 11.3. Apuntadores a una lista de aristas 11.5. Ejercicios . . . . . . . . . . . . . . . . . . . . . 11.4. Apuntadores slo a una lista de aristas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

247
247 249 250 253 256

12.Introduccin a los Fractales


12.1. Caractersticas de los fractales . . . . . . . . . . . . . . . . . . . . . . . . 12.2. El copo de nieve de von Koch 12.3. El tringulo de Sierpiski . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

257
257 262 263 263 263 271 272 272 272 275

12.4. Los Conjuntos Julia-Fatou . . . . . . . . . . . . . . . . . . . . . . . . . . 12.4.1. Denicin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.4.2. Implementacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.5. Conjunto de Mandelbrot . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.5.1. Denicin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.5.2. Implementacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.6. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

II. Otras Yerbas

277

13.Compilacin desde Mltiples archivos fuente (en lenguaje C) 14.Diagramas de Clases y Diagramas de Objetos (una untadita de UML)
14.1. Notacin de clases 14.3. Notacin de objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14.2. Notacin de relaciones o asociaciones . . . . . . . . . . . . . . . . . . . .

279 283
283 284 287

III. Apndices

289

A. Plantilla de Aplicacin Grca en J2SE B. Referencias y manuales


B.1. SDL  Simple DirectMedia Layer . . . . . . . . . . . . . . . . . . . . . . B.1.1. Sitios de recursos . . . . . . . . . . . . . . . . . . . . . . . . . . . B.1.2. Artculos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.2. Python y pygame . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

291 297
297 297 297 298

15

ndice general
B.3. Java Me . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.3.1. Sitios de recursos . . . . . . . . . . . . . . . . . . . . . . . . . . . 298 298

16

ndice de guras
1.1. 2.1. 2.2. 2.3. 2.4. 3.1. 3.2. 3.3. 3.4. 3.5. 3.6. 3.7. 3.8. 3.9. 4.1. 4.2. 4.3. 4.4. 4.5. 4.6. 5.1. 5.2. 5.3. 5.4. 5.5. 5.6. 5.7. 5.8. 6.1. Display de 7 segmentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . Modelo de color RGB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 77 78 79 80 82 85 85 92 94 96 101 104 106 110 111 112 113 114 131 134 135 135 136 137 141 141 144 146

Modelo de color CMY(K)

Plano Matiz-Saturacin de la luz visible

Comparacin del Gamut de RGB y CMYK Representacin abstracta de nueve pixeles

Justicacin del algoritmo de lnea de punto medio bsico - 1 Justicacin del algoritmo de lnea de punto medio bsico - 2

Anlisis inverso de punto medio . . . . . . . . . . . . . . . . . . . . . . . Simetra de las circunferencias . . . . . . . . . . . . . . . . . . . . . . . . Algoritmo de circunferencia de punto medio . . . . . . . . . . . . . . . . Esquema para algorito de circunferencias con centro arbitrario . . . . . . Relleno de circunferencias . . . . . . . . . . . . . . . . . . . . . . . . . . Cuadrcula de prctica de primitivas grcas . . . . . . . . . . . . . . . . Un mismo punto en dos marcos de referencia diferentes . . . . . . . . . . Transformacin vectorial de coordenadas . . . . . . . . . . . . . . . . . . Cambio de escala de vectores . . . . . . . . . . . . . . . . . . . . . . . . Situacin de ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Caso particular de pantalla completa . . . . . . . . . . . . . . . . . . . . Diagrama para el ejercicio 1 . . . . . . . . . . . . . . . . . . . . . . . . . Ejemplo de traslacin simple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Ejemplo de escalamiento simple . . . . . . . . . . . . . . . . . . . . . . . Ejemplo de escalamiento compuesto Ejemplo de rotacin simple . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Ejemplo de rotacin compuesta . . . . . . . . . . . . . . . . . . . . . . . Cambio de coordenadas a travs de mltiples marcos de referencia Ejercicio de transformacin bidimensional Cambio de coordenadas con rotacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Sistema de referencia de mano derecha . . . . . . . . . . . . . . . . . . .

17

ndice de guras
6.2. 6.3. 7.1. 7.2. 7.3. 7.4. 9.1. 9.2. 9.3. 9.4. 9.5. Sistema de referencia de mano izquierda . . . . . . . . . . . . . . . . . . . . . . . 146 147 152 154 158 159 168 169 171 212 213 244 244 245 248 248 248

Otra manera de ver el sistema de referencia de mano izquierda

Ejemplos de proyeccin ortogonal . . . . . . . . . . . . . . . . . . . . . . Ejemplos de proyeccin en perspectiva Proyeccin ortogonal y en perspectiva Deduccin de proyeccin en perspectiva . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Ejemplo de curvas paramtricas planas . . . . . . . . . . . . . . . . . . . Resorte en alrededor del eje Interpolacin

y,

generado con Octave . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Grca de los Polinomios de Bernstein de grado 3 . . . . . . . . . . . . . Aporte de cada uno de los polinomios de Bernstein de grado 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

10.1. Cilindro generado con Maxima 10.2. Toroide

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

10.3. Cinta de Mbius

11.1. Diagrama de clases de una malla poligonal en representacin explcita

11.2. Objeto tridimensional simple de ejemplo . . . . . . . . . . . . . . . . . . 11.3. Diagrama de objetos del objeto de la gura 11.2 . . . . . . . . . . . . . . 11.4. Diagrama de clases de una malla poligonal en representacin de apuntadores a una lista de vrtices . . . . . . . . . . . . . . . . . . . . . . . . . 11.5. Otro diagrama de objetos del objeto de la gura 11.2 . . . . . . . . . . . 11.6. Diagrama de clases de una malla poligonal en representacin de apuntadores a una lista de aristas . . . . . . . . . . . . . . . . . . . . . . . . . . 11.7. Objeto tridimensional de ejemplo para representacin de apuntadores a una lista de aristas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.8. Diagrama de objetos del objeto de la gura 11.7 . . . . . . . . . . . . . . 11.9. Detalles de implementacin de una malla poligonal en representacin de apuntadores a una lista de aristas . . . . . . . . . . . . . . . . . . . . . . 11.10. Diagrama de clases de las aplicaciones

249 250

251

251 252

254

trnsformionesQhFjr

perspetivQhFjr

. . . . . . . . . . . . .

255

12.1. Fractal natural: Brassica oleracea, un Romanescu fresco, cortesa del programa Botany Photo of the Day de http://www.ubcbotanicalgarden.org/. 258 12.2. Las ramas de los rboles siguen leyes fractales de distribuin volumtrica. 258 12.3. Las hojas de casi todos los helechos tienen la caracterstica de la autosimilitud nita. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.4. Helecho fractal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259 259 260

12.5. Espiral de satlites con islas de Julia, cortesa del Dr. Wolfgang Beyer

18

ndice de guras
12.6. Acercamiento del conjunto de Mandelbrot realzado por el autor de este libro con el programa XaoS. . . . . . . . . . . . . . . . . . . . . . . . . . 12.7. Otro acercamiento del conjunto de Mandelbrot realizado con la aplicacin 260 261 262 264 265 266 267 268 269 270 273 274 276 276 283 284 285 286 287 291

mndelrotFout

presentada ms adelante en este captulo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

12.8. Copo de nieve de von Koch

12.9. Tringulo de Sierpiski . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.10. Carpeta de Sierpiski . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.11. Pirmide de Sierpiski . . . . . . . . . . . . . . . . . . . . . . . . . . . .

juliFout . . . . . 12.13. Segunda imgen del programa juliFout 12.14. Tercera imgen del programa juliFout . 12.15. Cuarta imgen del programa juliFout .
12.12. Imgen del programa

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

12.16. Forma clsica del conjunto Mandelbrot, generado con erado con la aplicacin XaoS 12.18. Fractal de ejercicio 12.19. Fractal de otro ejercicio

mndelrotFout

12.17. Conjunto Mandelbrot con suavizacin de color interna y externa, gen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

14.1. Representacin de una clase . . . . . . . . . . . . . . . . . . . . . . . . . 14.2. Diagrama de clase ocultando sus atributos y operaciones . . . . . . . . . 14.3. Relaciones bidireccionales entre clases 14.4. Otros tipos de relacin entre clases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

14.5. Representacin de una instancia de una clase

A.1. Modelo de diseo Modelo-Vista-Controlador . . . . . . . . . . . . . . . .

19

ndice de guras

20

Parte I

Gracacin por computadora

21

1 Introduccin a SDL y PyGAME


SDL, Simple DirectMedia Layer, es una biblioteca de funciones multimedia programada
en lenguaje C estndar, disponible para usarse en UNIX / GNU/Linux, en Windows, en MacOS y en muchas otras plataformas. Tambin existe una considerable cantidad de bibliotecas derivadas de ella para otros lenguajes de programacin. Para tener una descripcin ms completa, rerase al sitio ocial: www.libsdl.org.

1.1.

Instalacin de las bibliotecas para C

Al igual que con muchas otras bibliotecas de funciones de lenguaje C, la expresin Instalar SDL tiene dos signicados: El primero es instalar las bibliotecas compiladas que contienen el cdigo objeto de las funciones de SDL que los dems programas ya compilados pueden utilizar; y el segundo es, instalar los archivos de cabecera necesarios para la compilacin de cdigo nuevo que use las funciones de SDL. Los usuarios de programas hechos con SDL (juegos, emuladores, etc.) slo necesitan una instalacin del primer tipo, y se dice instalacin de la biblioteca de ejecucin o instalacin de los paquetes de ejecucin. Los programadores necesitamos de la segunda, que se dice instalacin de la biblioteca de desarrollo o instalacin de los paquetes de desarrollo.

1.1.1. Vericar si ya estn instalados los paquetes de desarrollo


SDL es una biblioteca muy utilizada en el mundo de los juegos de computadora y los emuladores, especialmente si son Software Libre, por lo que es muy probable que en una instalacin tpica de una distribucin de GNU/Linux para escritorio, ya estn instalados los paquetes de ejecucin. As que es probable, que nicamente sea necesario instalar los paquetes de desarrollo. Para averiguar si ya se tienen instalados los paquetes de desarrollo, basta con ejecutar la siguiente instruccin:

6 sdlEonfig EEversion
Si devuelve algo como:

IFPFIP

23

1 Introduccin a SDL y PyGAME


signica que la biblioteca de desarrollo est instalada y el texto devuelto indica la versin de la biblioteca en esa mquina. Pero si devuelve algo como:

shX sdlEonfigX orden no enontrd


signica que no est instalada la biblioteca de desarrollo, aunque tal vez s la de ejecucin.

1.1.2. Diseo modular de SDL


SDL consiste de una nica biblioteca de funciones base, pero existen otras bibliotecas de funciones adicionales, desarrolladas por la amplia comunidad de usuarios/programadores de SDL. Aunque la biblioteca base incluye lo necesario para desarrollar aplicaciones de todo tipo, conviene instalar adems esas otras bibliotecas porque extienden su funcionalidad, permitiendo desarrollar aplicaciones ms rpidamente. Por ejemplo, se recomienda instalar las siguientes bibliotecas:

sdl-gfx

Incluye funciones de dibujo de primitivas grcas

sdl-image

antialiasing . Se le conoce tambin como SDL Graphics Efects Primitives. Incluye la funcionalidad de abrir un amplio grupo de formatos de imgenes

1 huecas y rellenas, con o sin

desde archivo (la biblioteca base es muy limitada en este aspecto, porque slo

sdl-sound Permite reproducir sonido en diversos formatos. sdl-mixer Permite manipular (mezclar ) sonido en diversos formatos. sdl-net Permite la programacin de aplicaciones con acceso a redes de computadoras. sdl-pango Manipulacin y renderizacin de fuentes con la biblioteca Pango (que es la sdl-ttp Manipulacin y renderizacin de fuentes TrueType. sdl-stretch Permite manipulacin bidimensional de mapas de bits.
Hay muchos otros mdulos/bibliotecas desarrollados por la comunidad, pero estos son los ms portables y populares. Los dems habr que evaluarlos a la luz de su portabilidad (si es que ese factor es relevante) y la funcionalidad que aportan. biblioteca de renderizacin de texto de GTK+).

puede abrir archivos

BFmp).

1.1.3. Instalacin en distribuciones basadas en Debian


Para instalar todos los paquetes de desarrollo, ejecutar la instruccin:

5 ptEget instll lisdlBEdev


1 2
Ver captulo 3 http://es.wikipedia.org/wiki/Antialiasing http://en.wikipedia.org/wiki/Anti-aliasing

24

1.1 Instalacin de las bibliotecas para C


Con esto se instalarn todos los paquetes de desarrollo (development ) que a su vez, tienen como dependencias a los paquetes de ejecucin. Si no se desean instalar todos los paquetes de desarrollo, se pueden consultar todos los paquetes que contengan el texto  lisdl con el comando:

6 ptitude serh lisdl

e instalar slo aquellos que se requieran. El comando anterior muestra banderas para cada paquete, y en particular, la bandera  i indica que el paquete listado ya est instalado. Si en lugar de dicha letra, aparece una  p o una  v signica que el paquete no est instalado.

1.1.4. Instalacin en openSuSE


La distribucin OpenSuSE tiene una herramienta de conguracin global llamada YaST que incluye una interfaz para instalar paquetes. Esta aplicacin tiene interface de texto con ncurses, pero tiene una hermana con interfaz de alto nivel, YaST2. Para instalar SDL, invocar el mdulo de instalacin:

5 ystP EEinstll 8
o

5 yst EEinstll
Hay que esperar a que se descargue y actualize la informacin de los repositorios en lnea y entonces hay que buscar en la interfaz del YaST / YaST2 los paquetes del tipo

lihvBEdevel

e instalarlos.

Tambin se pueden instalar los paquetes desde la lnea de comandos. Para hacerlo, se

zypper: 5 zypper instll lihvBEdevel


utiliza el comando Si no se desean instalar todos los paquetes de desarrollo, se pueden consultar todos los paquetes que contengan el texto  lihv con el comando:

6 zypper serh lihv

e instalar slo aquellos que se requieran. El comando anterior muestra, para cada paquete, la bandera  i si el paquete listado ya est instalado y no muestra nada si no lo est.

1.1.5. Instalacin en Fedora y derivados de RedHat


Para instalar todos los paquetes de desarrollo, ejecutar la instruccin:

5 yum instll hvBEdevel

25

1 Introduccin a SDL y PyGAME


Si no se desean instalar todos los paquetes de desarrollo, se pueden consultar todos los paquetes que contengan el texto  lisdl con el comando:

6 yum serh lisdl

e instalar slo aquellos que se requieran.

1.1.6. Otras distribuciones


Para lograr instalar con xito los paquetes de desarrollo de SDL (y sus dependencias), se recomienda usar las interfaces (grcas o de linea de comandos) para la instalacin desde los repositorios de cada distribucin. Alternativamente se pueden descargar los instaladores del sitio de SDL www.libsdl.org en diversos formatos, y adems, tambin se pueden descargar los fuentes para compilar los paquetes, in situ.

1.2.

Compilacin de los programas que usan SDL en C

Si el programa est en un slo archivo fuente (y no utiliza mdulos adicionales al base), se procede de la siguietne manera:

6 g rhivofuenteF Eo rhivoojeto 6@sdlEonfig EEflgs EElisA


o

6 g rhivofuenteF Eo rhivoojeto sdlEonfig EEflgs EElis


Valga la aclaracin que la instruccin

6 sdlEonfig EEflgs EElis


imprime los parmetros necesarios para que el programa mente el cdigo mquina del programa compilado.

pueda enlazar correcta-

1.3.

Ejemplos bsicos en SDL

A continuacin se presenta una serie de ejemplos elementales a manera de introduccin a la programacin con SDL en lenguaje C estndar. No pretenden ser exhaustivos, sino solamente introductorios. Se asume que el lector tiene buen dominio del lenguaje C estndar y que tiene buenas prcticas autodidactas.

1.3.1. Inicializacin bsica del sistema de video (el SDL):

1
Listing 1.1: Hola Mundo en SDL

Hola Mundo de

/* c01 / ejemplo -01. c

26

1.3 Ejemplos bsicos en SDL


2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49

Hola Mundo en SDL */ # include < SDL / SDL .h > # define ANCHO 323 # define ALTO 400 # define PROFUNDIDAD_COLOR 16 int main ( void ) { SDL_Surface * pantalla = NULL ; SDL_Surface * imagen = NULL ; // Inicializar el subsistema principal de SDL , el de video if ( SDL_Init ( SDL_INIT_VIDEO ) < 0) { printf ( " Error al iniciar SDL : %s \ n " , SDL_GetError () ) ; exit (1) ; } /* * Fuerza la ejecucin de la funcin ' SDL_Quit () ' * an en caso de salida con error , al llamar * a ' exit ( int ) ' o al retornar de la funcin ' main '. * */ atexit ( SDL_Quit ) ; // Abrir la ventana para grficos pantalla = SDL_SetVideoMode ( ANCHO , ALTO , PROFUNDIDAD_COLOR , SDL_SWSURFACE ) ; if ( pantalla == NULL ) { printf ( " Error al inicializar el modo de video : ' %s '\ n " , SDL_GetError () ) ; exit (1) ; } // Cambiar el ttulo de la ventana SDL_WM_SetCaption ( " Hola mundo ! " , NULL ); // Cargar una imagen : imagen = SDL_LoadBMP ( " logo_uca . bmp " ) ; if ( imagen == NULL ) { printf ( " Error al cargar la imagen : ' %s '\ n " , SDL_GetError () ) ; exit (1) ; } // Copiar la imagen al buffer temporal en ' pantalla ': SDL_BlitSurface ( imagen , NULL , pantalla , NULL ) ; // Volcar el buffer a la memoria de video : SDL_Flip ( pantalla ) ; // Esperar un tiempo de 5000 milisegundos SDL_Delay (5000) ;

27

1 Introduccin a SDL y PyGAME


50 51 52 53 54

// y luego apagar el sistema de SDL con ' SDL_Quit () ', // en este caso , invocada automticamente con el return . return 0;

1.3.2. Inicializacin de los subsistemas


SDL cuenta de forma nativa con 5 subsistemas. Las constantes para identicarlos son:

SDL_INIT_TIMER Subsistema de temporizador. SDL_INIT_AUDIO Subsistemas de carga y reproduccin de pistas de audio. SDL_INIT_VIDEO Subsistema de video. SDL_INIT_CDROM Subsistema de control y reproduccin de cdrom de audio. SDL_INIT_JOYSTICK Subsistema de interaccin con palancas de mando o controles
de juego.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

Listing 1.2: Inicializacin de subsistemas en SDL

/* c01 / ejemplo -02. c Inicializacin de subsistemas en SDL */ # include < SDL / SDL .h > int main ( void ) { SDL_Surface * pantalla = NULL ; /* Los subsistemas disponibles son : * SDL_INIT_TIMER * SDL_INIT_AUDIO * SDL_INIT_VIDEO * SDL_INIT_CDROM * SDL_INIT_JOYSTICK * SDL_INIT_EVERYTHING * */ if ( SDL_Init ( SDL_INIT_VIDEO | SDL_INIT_CDROM | SDL_INIT_AUDIO ) < 0) { printf ( " Error al iniciar SDL con los subsitemas de video , de unidad ptica y de sonido : %s \ n " , SDL_GetError () ) ; exit (1) ; } atexit ( SDL_Quit ) ; // inicializar el subsistema de palanca de mandos : SDL_InitSubSystem ( SDL_INIT_JOYSTICK ) ; // apaga los subsistemas de video y de audio

28

1.3 Ejemplos bsicos en SDL


27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51

SDL_QuitSubSystem ( SDL_INIT_VIDEO | SDL_INIT_CDROM ) ; if ( SDL_WasInit ( SDL_INIT_VIDEO ) ) printf ( " El video est encendido \ n " ) ; else printf ( " El video est apagado \ n " ) ; if ( SDL_WasInit ( SDL_INIT_CDROM ) ) printf ( " El cdrom est encendido \ n " ) ; else printf ( " El cdrom est apagado \ n " ) ; if ( SDL_WasInit ( SDL_INIT_AUDIO ) ) printf ( " El audio est encendido \ n " ) ; else printf ( " El audio est apagado \ n " ) ; if ( SDL_WasInit ( SDL_INIT_JOYSTICK ) ) printf ( " El joystick est encendido \ n " ) ; else printf ( " El joystick est apagado \ n " ) ; // Apagar todos los subsistemas de SDL automticamente return 0;

1.3.3. Modos de video


Los modos de video disponibles son:

hvpegi hvrpegi hvhyfvifp hvyixqv

Usa la memoria de sistema Usa la memoria de video Activa doble buer en la memoria de video Crea una supercie de dibujo que ocupa toda la pantalla.

hvpvvgiix hvisefvi hvxypewi

1 2 3 4 5

Crea una supercie renderizable con opengl. Crea una ventana de dibujo redimensionable.

Crea una ventana de dibujo sin borde. Listing 1.3: Modos de video

/* c01 / ejemplo -03. c Modos de video en SDL */ # include < SDL / SDL .h > # define ANCHO 400

29

1 Introduccin a SDL y PyGAME


6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48

# define ALTO

400

int main ( void ) { SDL_Surface * pantalla = NULL ; SDL_Surface * imagen = NULL ; if ( SDL_Init ( SDL_INIT_VIDEO ) < 0) { printf ( " Error al iniciar SDL con el subsitemas de video : %s \ n " , SDL_GetError () ) ; exit (1) ; } atexit ( SDL_Quit ) ; /* Modos disponibles : * SDL_SWSURFACE usa la memoria de sistema * SDL_HWSURFACE usa la memoria de video * SDL_DOUBLEBUF activa doble buffer en la memoria de video * SDL_FULLSCREEN * SDL_OPENGL * SDL_RESIZABLE * SDL_NOFRAME * */ // Ventana normal de tamao fijo : pantalla = SDL_SetVideoMode ( ANCHO , ALTO , 0 , // Ventana normal de tamao variable : // pantalla = SDL_SetVideoMode ( ANCHO , ALTO , 0 , SDL_RESIZABLE ) ; SDL_SWSURFACE ) ; SDL_SWSURFACE |

// Ventana normal de tamao fijo maximizada ( tamao igual a pantalla completa ) // pantalla = SDL_SetVideoMode ( 0, 0 , 0 , SDL_SWSURFACE ) ; // Ventana sin borde de tamao fijo // pantalla = SDL_SetVideoMode ( ANCHO , ALTO , 0 , SDL_NOFRAME ) ; // Pantalla completa con resolucin fija // pantalla = SDL_SetVideoMode ( ANCHO , ALTO , 0 , SDL_FULLSCREEN ) ; // Pantalla completa con resolucin mxima // pantalla = SDL_SetVideoMode ( 0, 0, 0, SDL_FULLSCREEN ) ; SDL_SWSURFACE |

SDL_SWSURFACE |

SDL_SWSURFACE |

if ( pantalla == NULL ) { printf ( " Error al inicializar el modo de video : ' %s '\ n " , SDL_GetError () ) ; exit (1) ;

30

1.3 Ejemplos bsicos en SDL


49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73

} printf ( " Tamao de la pantalla : %dx %d \ n " , pantalla - >w , pantalla - > h ) ; // Cargar una imagen : imagen = SDL_LoadBMP ( " logo_uca . bmp " ) ; if ( imagen == NULL ) { printf ( " Error al cargar la imagen : ' %s '\ n " , SDL_GetError () ) ; exit (1) ; } // Copiar la imagen al buffer temporal en ' pantalla ': SDL_BlitSurface ( imagen , NULL , pantalla , NULL ) ; // Volcar el buffer a la memoria de video : SDL_Flip ( pantalla ) ; // Espera un tiempo de 10 segundos SDL_Delay (10000) ; // Apaga manualmente los subsistemas de SDL SDL_Quit () ; } return 0;

1.3.4. Eventos de ratn

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Listing 1.4: Eventos de ratn

/* c01 / ejemplo -04. c Eventos del ratn en SDL */ # include < SDL / SDL .h > # define ANCHO 400 # define ALTO 400 char * nombreBotones [] = { " izquierdo " , " medio " , " derecho " , " rueda arriba " , " rueda abajo " }; char * nombreBoton ( Uint8 boton ) { switch ( boton ) { case SDL_BUTTON_LEFT : return nombreBotones [0]; case SDL_BUTTON_MIDDLE : return nombreBotones [1]; case SDL_BUTTON_RIGHT : return nombreBotones [2];

31

1 Introduccin a SDL y PyGAME


18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63

case SDL_BUTTON_WHEELUP : return nombreBotones [3]; case SDL_BUTTON_WHEELDOWN : return nombreBotones [4];

int main ( void ) { SDL_Surface * pantalla = NULL ; SDL_Event evento ; int corriendo = 1; if ( SDL_Init ( SDL_INIT_VIDEO ) < 0) { printf ( " Error al iniciar SDL : %s \ n " , SDL_GetError () ) ; exit (1) ; } atexit ( SDL_Quit ) ; pantalla = SDL_SetVideoMode ( ANCHO , ALTO , 0 , SDL_SWSURFACE ) ; if ( pantalla == NULL ) { printf ( " Error al inicializar el modo de video : ' %s '\ n " , SDL_GetError () ) ; exit (1) ; } SDL_WM_SetCaption ( " Prueba de ratn , mueva el ratn dentro de la ventana " , NULL ) ; while ( corriendo ) { while ( SDL_PollEvent (& evento ) ) { switch ( evento . type ) { /* Consultar el archivo : / usr / include / SDL / SDL_mouse . h All se encuentra la declaracin de los botones en forma de constantes */ case SDL_MOUSEBUTTONDOWN : printf ( " Botn de ratn ' %s ' pulsado en ( %d, %d) \ n " , nombreBoton ( evento . button . button ) , evento . button . x , evento . button . y ) ; break ; case SDL_MOUSEBUTTONUP : printf ( " Botn de ratn ' %s ' liberado en ( %d, %d ) \ n " , nombreBoton ( evento . button . button ) , evento . button . x , evento . button . y ) ; break ; case SDL_MOUSEMOTION :

32

1.3 Ejemplos bsicos en SDL


64 65 66 67 68 69 70 71 72 73 74 75 76

printf ( " El ratn se movi %d, %d pixeles hasta ( %d, %d ) \n", evento . motion . xrel , evento . motion . yrel , evento . motion .x , evento . motion . y ) ; break ; case SDL_QUIT : corriendo = 0; break ;

} }

return 0;

1.3.5. Eventos de teclado

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
Listing 1.5: Eventos de teclado

/* c01 / ejemplo -05. c Eventos del teclado en SDL */ # include < SDL / SDL .h > # include < stdio .h > # define ANCHO 640 # define ALTO 480 void mostrarEstado ( SDL_KeyboardEvent * tecla ) { if ( tecla - > type == SDL_KEYUP ) printf ( " SOLTADA : " ); else // SDL_KEYDOWN printf ( " PRESIONADA : " ); } void mostrarModificadores ( SDL_KeyboardEvent * tecla ) { SDLMod modificador = tecla - > keysym . mod ; if ( modificador & KMOD_NUM ) printf ( " NUMLOCK " ) ; if ( modificador & KMOD_CAPS ) printf ( " CAPSLOCK " ) ; if ( modificador & KMOD_MODE ) printf ( " MODE " ) ; if ( modificador & KMOD_LCTRL ) printf ( " LCTRL " ) ; if ( modificador & KMOD_RCTRL ) printf ( " RCTRL " ) ; if ( modificador & KMOD_LSHIFT ) printf ( " LSHIFT " ) ; if ( modificador & KMOD_RSHIFT ) printf ( " RSHIFT " ) ; if ( modificador & KMOD_LALT ) printf ( " LALT " ) ; if ( modificador & KMOD_RALT ) printf ( " RALT " ) ; if ( modificador & KMOD_LMETA ) printf ( " LMETA " ) ;

33

1 Introduccin a SDL y PyGAME


29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77

} /*

if ( modificador & KMOD_RMETA ) printf ( " RMETA " ) ;

*/ void mostrarTecla ( SDL_KeyboardEvent * tecla ) { printf ( " Cdigo : %d , Nombre : %s \ n " , tecla - > keysym . sym , SDL_GetKeyName ( tecla - > keysym . sym ) ) ; } int main ( void ) { SDL_Surface * pantalla = NULL ; SDL_Event evento ; int corriendo = 1; if ( SDL_Init ( SDL_INIT_VIDEO ) < 0 ) { fprintf ( stderr , " No se puede iniciar SDL : %s \ n" , SDL_GetError () ) ; exit (1) ; } atexit ( SDL_Quit ) ; pantalla = SDL_SetVideoMode ( ANCHO , ALTO , 0 , SDL_SWSURFACE ) ; if ( pantalla == NULL ) { fprintf ( stderr , " No se puede establecer el modo de video %dx %d : % s\n", ANCHO , ALTO , SDL_GetError () ) ; exit (1) ; } SDL_WM_SetCaption ( " Prueba de teclado , presione las teclas " , NULL ) ; while ( corriendo ) { while ( SDL_PollEvent (& evento ) ) { switch ( evento . type ) { case SDL_KEYDOWN : case SDL_KEYUP : mostrarEstado (& evento . key ) ; mostrarModificadores (& evento . key ) ; mostrarTecla (& evento . key ); break ; case SDL_QUIT : corriendo = 0; break ;

Consultar el archivo : / usr / include / SDL / SDL_keysym . h All se encuentra la declaracin de las teclas en forma de constantes

34

1.3 Ejemplos bsicos en SDL


78 79 80 81 82

} }

return 0;

1.3.6. Redimensionamiento de la ventana

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
Listing 1.6: Redimensionamiento

/* c01 / ejemplo -06. c * Redimensionamiento de la ventana en SDL */ # include < SDL / SDL .h > int main ( void ) { SDL_Surface * pantalla = NULL ; SDL_Event evento ; int ANCHO = 400 , ALTO = 400; int corriendo = 1; if ( SDL_Init ( SDL_INIT_VIDEO ) < 0) { printf ( " Error al iniciar SDL : %s \ n " , SDL_GetError () ) ; exit (1) ; } atexit ( SDL_Quit ) ; pantalla = SDL_SetVideoMode ( ANCHO , ALTO , 0 , SDL_SWSURFACE | SDL_RESIZABLE ) ; if ( pantalla == NULL ) { printf ( " Error al inicializar el modo de video : ' %s '\ n " , SDL_GetError () ) ; exit (1) ; } SDL_WM_SetCaption ( " Prueba de redimensionamiento " , NULL ) ; while ( corriendo ) { while ( SDL_PollEvent (& evento ) ) { switch ( evento . type ) { /* Consultar el archivo : / usr / include / SDL / SDL_events . h A l l se encuentra la definicin de la unin ' SDL_Event '. */ case SDL_VIDEORESIZE :

35

1 Introduccin a SDL y PyGAME


36 37

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57

// Redimensionar la pantalla , no hace falta liberar la anterior . pantalla = SDL_SetVideoMode ( evento . resize .w , evento . resize .h , PROFUNDIDAD_COLOR , SDL_SWSURFACE | SDL_RESIZABLE ) ; if ( pantalla == NULL ) { printf ( " Error al redimensionar la pantalla : ' %s '\ n " , SDL_GetError () ) ; exit (1) ; } printf ( " La ventana se ha redimensionado de %dx %d pixeles a %dx %d \ n " , ANCHO , ALTO , evento . resize .w , evento . resize . h ) ; ANCHO = evento . resize . w ; ALTO = evento . resize . h ; // Aqu habra que redibujar lo que se estaba mostrando // ya que ' pantalla ' aparece borrada ( en negro ) . break ; case SDL_QUIT : corriendo = 0; break ;

} }

return 0;

1.3.7. Facilitar la compilacin


Para facilitar el proceso de compilacin para programas que usen SDL que consistan de un solo archivo fuente (sin contar el archivo de cabecera), puede crearse en el mismo directorio el siguiente archivo

wkefile:

1 2 3 4 5 6 7 8 9 10 11

Listing 1.7: Makele para programas SDL de un archivo fuente

# c01 / Makefile LDFLAGS = $ ( shell sdl - config -- cflags ) LDLIBS = $ ( shell sdl - config -- libs ) CC = gcc # Existiendo este archivo en el directorio , # se pueden compilar archivos *. c que usen SDL # con slo ejecutar : # '$ make < ejecutable > ' # ( claro que tiene que existir un archivo '< ejecutable >. c ')

36

1.3 Ejemplos bsicos en SDL


12 13

# Si este archivo Makefile no existiera , para compilar h a b r a que ejecutar : # '$ gcc -o < ejecutable > < ejecutable >. c $ ( sdl - config -- libs -- cflags ) '

Con este archivo en el mismo directorio, podemos usar la utilera compilar un programa fuente:

mke

de los sistemas

basados en Unix. De tal manera que slo haya que ejecutar la siguiente instruccin para

6 mke ejemploEHI
Con esta instruccin y el archivo mencionado, se buscar el archivo compilar en el archivo

ejemploEHI.

ejemploEHIF

y se

1.3.8. Compilacin desde varios archivos fuente


Lo anterior no aplica para un programa que consista de varios archivos fuente (sin contar sus archivos de cabecera). Sin embargo, siempre nos podemos apoyar en el comando echarle un vistazo al captulo 13 en la pgina 279 antes de continuar aqu):

mke

para automatizar el proceso de compilacin. Veamos un ejemplo sencillo (se recomienda

1 2 3 4 5

Listing 1.8: Archivo de cabecera de otro archivo fuente

/* c01 / ejemplo -07/ otro . h * */ # include < stdio .h > # define CONSTANTE 5 void funcionOtro ( int entero , FILE *f ) ;

Listing 1.9: Otro archivo fuente

1 2 3 4 5 6 7

/* c01 / ejemplo -07/ otro . c * */ # include " otro . h " void funcionOtro ( int entero , FILE *f ) { fprintf (f , " Cdigo en ' otro . c ' ,\ nparmetro : %d\ nconstante : %d \ n " , entero , CONSTANTE ) ; }

A continuacin se presentan los archivos fuente que continen la implementancin de un par de funciones de primitivas grcas:

1 2 3

Listing 1.10: Archivo de cabecera principal

/* c01 / ejemplo -07/ primitivas . h * */ # include < SDL / SDL .h >

37

1 Introduccin a SDL y PyGAME


4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

/* * Enciende el pixel (x , y ) con el color dado . * * SDL no contiene de forma nativa una funcin para * encender pixeles , a diferencia de otras bibliotecas grficas . * Esto en s mismo es un tema de discucin , pero tpicamente * el cdigo de esta funcin aplica en SDL . */ void ponerPixel ( SDL_Surface *s , int x , int y , Uint32 color ) ; /* Dibuja un cuadrado vaco dado por ' rectangulo ' */ void dibujar_rectangulo ( SDL_Surface *s , SDL_Rect rectangulo , Uint32 color );


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

Listing 1.11: Cdigo fuente de primitivas grcas

/* c01 / ejemplo -07/ primitivas . c * */ # include " primitivas . h " void dibujar_rectangulo ( SDL_Surface *s , SDL_Rect rectangulo , Uint32 color ){ int i; for ( i = rectangulo . x ; i < rectangulo . x + rectangulo . w ; i ++) { ponerPixel (s , i , rectangulo .y , color ) ; ponerPixel (s , i , rectangulo . y + rectangulo .h -1 , color ) ; } for ( i = rectangulo . y ; i < rectangulo . y + rectangulo . h ; i ++) { ponerPixel (s , rectangulo .x , i , color ) ; ponerPixel (s , rectangulo . x + rectangulo .w -1 , i , color ) ; } } void ponerPixel ( SDL_Surface * surface , int x , int y , Uint32 color ) { int bpp = surface - > format - > BytesPerPixel ; // A q u p es la direccin del pixel al que queremos ponerle color Uint8 * p = ( Uint8 *) surface - > pixels + y * surface - > pitch + x * bpp ; if (( x > surface - > w ) ||( y > surface - > h ) ||( x <0) ||( y <0) ) return ; switch ( bpp ) { case 1: * p = color ; break ; case 2: *( Uint16 *) p = color ;

38

1.3 Ejemplos bsicos en SDL


31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

break ; case 3: if ( SDL_BYTEORDER == SDL_BIG_ENDIAN ) { p [0] = ( color >> 16) & 0 xff ; p [1] = ( color >> 8) & 0 xff ; p [2] = color & 0 xff ; } else { p [0] = color & 0 xff ; p [1] = ( color >> 8) & 0 xff ; p [2] = ( color >> 16) & 0 xff ; } break ; case 4: *( Uint32 *) p = color ; break ; }


Listing 1.12: Cdigo principal del ejemplo

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

/* c01 / ejemplo -07/ main . c * Programa principal * */ # include " otro . h " # include " primitivas . h " # include < stdio .h > int main ( int argc , char * argv []) { SDL_Surface * pantalla = NULL ; SDL_Event evento ; Uint32 color_fondo , color1 , color2 ; SDL_Rect rect1 , rect2 ; int ANCHO = 400 , ALTO = 400; int corriendo = 1; if ( SDL_Init ( SDL_INIT_VIDEO ) < 0) { printf ( " Error al iniciar SDL : %s \ n " , SDL_GetError () ) ; exit (1) ; } atexit ( SDL_Quit ) ; pantalla = SDL_SetVideoMode ( ANCHO , ALTO , 0 , SDL_SWSURFACE | SDL_RESIZABLE ) ; if ( pantalla == NULL ) { printf ( " Error al inicializar el modo de video : ' %s '\ n " , SDL_GetError () ) ; exit (1) ;

39

1 Introduccin a SDL y PyGAME


26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73

} // Esta es la forma bsica de construir un color en SDL : color_fondo = SDL_MapRGB ( pantalla - > format ,0 ,0 ,0) ; color1 = SDL_MapRGB ( pantalla - > format ,255 ,0 ,0) ; color2 = SDL_MapRGB ( pantalla - > format ,0 ,255 ,0) ; SDL_WM_SetCaption ( " Cdigo distribuido " , NULL ) ; // Hacer primer dibujo // Dibujar un rectngulo rojo y otro verde rect1 . x = 0; rect1 . y = 0; rect1 . w = ANCHO /2; rect1 . h = ALTO /2; dibujar_rectangulo ( pantalla , rect1 , color1 ) ; rect2 . x = ANCHO /2; rect2 . y = ALTO /2; rect2 . w = ANCHO /2 -1; rect2 . h = ALTO /2 -1; dibujar_rectangulo ( pantalla , rect2 , color2 ) ; // Volcar el buffer en la pantalla SDL_Flip ( pantalla ) ; // llamar una funcin que se encuentra en otro archivo de cdigo funcionOtro (10 , stdout ) ; while ( corriendo ) { while ( SDL_PollEvent (& evento ) ) { switch ( evento . type ) { case SDL_VIDEORESIZE : { ANCHO = evento . resize . w ; ALTO = evento . resize . h ; // Redimensionar la pantalla , no hace falta liberar la anterior . pantalla = SDL_SetVideoMode ( ANCHO , ALTO , 0 , SDL_SWSURFACE | SDL_RESIZABLE ) ; if ( pantalla == NULL ) { printf ( " Error al redimensionar la pantalla : ' %s '\ n " , SDL_GetError () ) ; exit (1) ; } // La pantalla nueva aparece en blanco // o mejor dicho , en negro , // por lo que hay que dibujar de nuevo : rect1 . x = 0;

40

1.3 Ejemplos bsicos en SDL


74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97

rect1 . y = 0; rect1 . w = ANCHO /2; rect1 . h = ALTO /2; dibujar_rectangulo ( pantalla , rect1 , color1 ) ; rect2 . x = ANCHO /2; rect2 . y = ALTO /2; rect2 . w = ANCHO /2 -1; rect2 . h = ALTO /2 -1; dibujar_rectangulo ( pantalla , rect2 , color2 ) ; // Vuelca el buffer en la pantalla : SDL_Flip ( pantalla ) ;

} break ;

} }

case SDL_QUIT : corriendo = 0; break ;

return 0;


Finalmente, para poder compilar fcilmente todo este cdigo, requerimos de un archivo

wkefile

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

como el siguiente: Listing 1.13: Archivo Makele para varios fuentes usando SDL

# c01 / ejemplo -07/ Makefile LDFLAGS = $ ( shell sdl - config -- cflags ) LDLIBS = $ ( shell sdl - config -- libs ) RM = / bin / rm -f # Esto indica que los siguientes identificadores , # no son archivos , sino comandos de make : . PHONY : limpiar . PHONY : limpiartodo . PHONY : all # Nombre del programa ejecutable : PROG = ejemplo -07 # Un '*.o ' por cada '*.c ' OBJ = otro . o main . o primitivas . o # Cuando se ejecuta '$ make ' , se ejecuta esto : all : $ ( PROG ) limpiar

41

1 Introduccin a SDL y PyGAME


21 22 23 24 25 26 27 28 29 30 31 32 33

# Esto compila todo el cdigo y lo enlaza : $ ( PROG ) : $ ( OBJ ) gcc -o $ ( PROG ) $ ( OBJ ) $ ( LDLIBS ) $ ( LDFLAGS ) # Borra todos los archivos intermedios y de copia de seguridad limpiar : $ ( RM ) *~ $ ( OBJ ) # Borra todos los archivos intermedios , de copia de seguridad # y el programa ejecutable , si es que existe limpiartodo : make limpiar $ ( RM ) $ ( PROG )

La funcin

ponerixel implementada en el archivo 1.11 es la forma tpica de encender

o colorear pixeles en SDL.

1.4.
3

Dibujo de primitivas grcas con SDL

Desafortunadamente, la biblioteca base de SDL no incluye funciones para encender pixeles ni para dibujar lneas, rectngulos, crculos, polgonos, etc (es decir, primitivas grcas ). Esto es debido a que sus desarrolladores pretenden que se mantenga como una biblioteca multimedia de bajo nivel para que sea ampliada al gusto (y habilidad) del programador que la use, dando la libertad/responsabilidad de construirlas uno mismo. Pero por cuestiones prcticas, en este libro vamos a preferir dibujar primitivas grcas con la ayuda de la biblioteca gfxPrimitives del paquete subseccin 1.1.2 en la pgina 24. Su uso se ilustra en el siguiente programa:

sdlEgfx

mencionada en la

1 2 3 4 5 6 7 8 9 10 11

Listing 1.14: Ejemplo de uso de gfxPrimitives

/* c01 / ejemplo -08/ primitivas . c * */ # include < SDL / SDL .h > # include < SDL / SDL_gfxPrimitives .h > # include < stdio .h > # define ANCHO 800 # define ALTO 640 Uint32 rojo , verde , blanco ;
3
La denicin de Primitiva Grca aparece en el captulo 3 en la pgina 81, pero no es otra cosa que lneas rectas, crculos, rectngulos, polgonos, etc.

42

1.4 Dibujo de primitivas grcas con SDL


12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59

/*

Las funciones ____Color de SDL_gfx , indicadas en el archivo / usr / include / SDL / SDL_gfxPrimitives .h requieren el color en un entero de 32 bis as : 0 xRRGGBBAA , no acepta el color en ningn otro formato . Adems , utiliza las transparencias por defecto , siempre hay que especificar la opacidad del color .

*/ Uint32 color ( Uint8 r , Uint8 g , Uint8 b ) { return r << 24 | g << 16 | b << 8 | 255; // este valor es la opacidad del color // y debe ser mxima para que sea slido } /* Ver todas las funciones disponibles en el archivo de cabecera : / usr / include / SDL / SDL_gfxPrimitives .h

Hay unas ____Color y otras _____RGBA . */ void dibujar ( SDL_Surface * pantalla ) { // Borrar la pantalla SDL_FillRect ( pantalla , NULL , SDL_MapRGB ( pantalla - > format ,0 ,0 ,0) ) ; /* Las operaciones fuera de SDL_gfx deben ser tratadas con ' SDL_MapRGB ' y ' SDL_MapRGBA ', no con ' Uint32 color ( Uint8 , Uint8 , Uint8 ) ' */ // Dibujar un rectngulo rojo y otro verde boxColor ( pantalla , 0 ,0 , ANCHO /2 , ALTO /2 , rojo ) ; boxColor ( pantalla , ANCHO /2 , ALTO /2 , ANCHO , ALTO , verde ) ; // Lineas ( notar la diferencia visual entre ambas ) : lineColor ( pantalla , 0 ,0 , ANCHO , ALTO , blanco ) ; aalineColor ( pantalla , 0 , ALTO , ANCHO , 0 , blanco ) ; // Lnea horizontal amarilla hlineColor ( pantalla , 0 , ANCHO , ALTO /2 , color (255 , 255 , 0) ) ; stringColor ( pantalla , ANCHO /2 , ALTO /2 , " HOLA " , color (255 ,0 ,255) ) ; stringRGBA ( pantalla , 3* ANCHO /4 , ALTO /4 , " BIENVENIDOS A SDL_gfx " , 255 ,255 ,255 ,255) ;

43

1 Introduccin a SDL y PyGAME


60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104

int main ( int argc , char * argv []) { SDL_Surface * pantalla = NULL ; SDL_Event evento ; int corriendo = 1; if ( SDL_Init ( SDL_INIT_VIDEO ) < 0) { printf ( " Error al iniciar SDL : %s \ n " , SDL_GetError () ) ; exit (1) ; } atexit ( SDL_Quit ) ; pantalla = SDL_SetVideoMode ( ANCHO , ALTO , 0 , SDL_SWSURFACE ) ; if ( pantalla == NULL ) { printf ( " Error al inicializar el modo de video : ' %s '\ n " , SDL_GetError () ) ; exit (2) ; } rojo = color (255 ,0 ,0) ; verde = color (0 ,255 ,0) ; // verde = SDL_MapRGBA ( pantalla - > format ,0 ,255 ,0 , 255) ; FUNCIONA blanco = color (255 , 255 , 255) ; SDL_WM_SetCaption ( " Ejemplo de gfx " , NULL ) ; // Hacer primer dibujo dibujar ( pantalla ) ; // Volcar el buffer en la pantalla SDL_Flip ( pantalla ) ; // Esperar que se cierre la ventana while ( corriendo ) { while ( SDL_PollEvent (& evento ) ) { switch ( evento . type ) { case SDL_QUIT : corriendo = 0; break ;

// AS NO

} }

return 0;


Listing 1.15: Makele necesario para usar gfxPrimitives

44

1.5 Instalacin de PyGAME


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

# c01 / ejemplo -08/ Makefile # Ntese que se incluye un parmetro adicional por gfx : LINEA = $ ( shell sdl - config -- cflags -- libs ) - lSDL_gfx RM = / bin / rm -f

. PHONY : limpiar . PHONY : limpiartodo . PHONY : all PROG = gfx OBJ = primitivas . o all : $ ( PROG ) limpiar $ ( PROG ) : $( OBJ ) gcc -o $ ( PROG ) $ ( OBJ ) $ ( LINEA ) limpiar : $ ( RM ) *~ $ ( OBJ )

limpiartodo : make limpiar $ ( RM ) $ ( PROG )

1.5.

Instalacin de PyGAME

PyGAME es un conjunto de mdulos del lenguaje Python diseados para programar juegos y programas multimedia. PyGAME agrega funcionalidad sobre la biblioteca SDL y se puede interpretar como SDL para Python. En distribuciones basadas en Debian y openSuSE, el paquete en cuestin se llama python-

pygame, por lo que basta con instalarlo, en Debian, con:

5 ptEget instll pythonEpygme


o en OpenSuSE con:

5 yst Ei pythonEpygme
o

(para la interfaz de texto) (para la interfaz grca de alto nivel) (en lnea de comandos)

5 ystP Ei pythonEpygme
o

5 zypper instll pythonEpygme


y ejemplos de PyGAME.

En OpenSuse, existe tambin el paquete

pythonEpygmeEdo que instala documentacin

45

1 Introduccin a SDL y PyGAME


En distribuciones Fedora y derivados de RedHat, el paquete se llama simplemente

pygame y se puede instalar con:

5 yum instll pygme


Al igual que con el caso de las bibliotecas de SDL, se recomienda usar las interfaces (grcas o de linea de comandos) para la instalacin desde los repositorios de cada distribucin. Si esto no es posible o simplemente no se desea, se pueden descargar los instaladores del sitio de PyGAME www.pygame.org en diversos formatos y diferentes versiones, y adems, tambin se pueden descargar los archivos fuentes

4 para compilar los paquetes,

in situ.

1.6.

Ejemplos bsicos en Python

Pygame tiene un diseo y un estilo muy parecido al tpico de los mdulos de Python. Por lo cual, hay marcadas diferencias en la forma de programar con SDL en C y con PyGAME en Python. Sin embargo, la nomenclatura de las funciones, constantes y objetos es muy parecida o igual a la de SDL en C.

1.6.1. Inicializacin bsica del sistema de video (el pygame):

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
Listing 1.16: Hola Mundo en Pygame

Hola Mundo de

# coding : utf -8 ' ' ' c01 / ejemplo -01. py Hola Mundo en pygame ''' import pygame # Inicializar el Sistema de pygame pygame . init () tam = ancho , alto = 323 , 400 # Abrir la ventana para grficos pantalla = pygame . display . set_mode ( tam ) # Cambiar el ttulo de la ventana pygame . display . set_caption ( " Hola Mundo ! " ) # Cargar una imagen :
4
El principal intrprete de Python y los paquetes de

pygame estn programados en lenguaje C estndar.

46

1.6 Ejemplos bsicos en Python


19 20 21 22 23 24 25 26 27 28 29 30 31

imagen = pygame . image . load ( " logo_uca . bmp " ) # Mostrar la imagen : pantalla . blit ( imagen , (0 ,0) ) # Volcar el buffer a la memoria de video : pygame . display . flip () # Esperar 5000 milisegundos pygame . time . delay (5000) # y terminar el programa .

# El cerrado de los subsistemas de pygame es automtico

1.6.2. Inicializacin de los subsistemas


Pygame cuenta con 5 subsistemas nativos. Estos son:

pygame.cdrom Mdulo para control de cdrom de audio. pygame.display Mdulo de video. pygame.font Mdulo de renderizacin de fuentes TrueType. pygame.joystick Mdulo de interaccin con palancas de mando o controles de juego. pygame.mixer Mdulo de carga y reproduccin de pistas de audio.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
Listing 1.17: Inicializacin de subsistemas en Pygame

# coding : utf -8 ' ' ' c01 / ejemplo -02. py Inicializacin de subsistemas en pygame . Los subsistemas / mdulos disponibles son : . - pygame . cdrom . - pygame . display . - pygame . font . - pygame . joystick . - pygame . mixer . - pygame . scap ''' import pygame def mensaje ( nombre , valor_de_verdad ) : ' ' ' Esta funcin simplemente sirve para no escribir la misma cadena varias veces . La combinacin 'and - or ' en Python funciona como el operador ternario de C

47

1 Introduccin a SDL y PyGAME


19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51

siempre y cuando el valor entre el ' and ' y el ' or ' se evale a verdadero . ' ' ' print ( " El subsistema de " + nombre + " " + ( valor_de_verdad and " s " or " no " ) + " est encendido " ) ' ' ' La funcin ' init ' inicializa todos los subsistemas disponibles y devuelve el nmero de subsistemas que pudieron ser inicializados y los que no ' ' ' funcionando , no_funcionando = pygame . init () print ( " El nmero de submdulos de pygame funcionando es : " + str ( funcionando ) ) print ( " El nmero de submdulos de pygame que no estn funcionando es : " + str ( no_funcionando ) ) mensaje ( " palanca de mandos " , pygame . joystick . get_init () ) mensaje ( " cdrom " , pygame . cdrom . get_init () ) # Apagar el mdulo de fuentes y de video : pygame . font . quit () mensaje ( " fuentes " , pygame . font . get_init () ) pygame . display . quit () mensaje ( " video " , pygame . display . get_init () ) # Encender el mdulo de fuentes : pygame . font . init () mensaje ( " fuentes " , pygame . font . get_init () ) print ( " Apagando pygame ... " ) ' ' ' Esta funcin apaga todos los pero es llamada automticamente el programa termina , por lo que en el caso que un programa deba sin pygame . ''' pygame . quit () mdulos , cuando slo es necesario seguir corriendo

1.6.3. Modos de video


Los modos de video disponibles son:

pygmeFpvvgiix

Crea una supercie de dibujo que ocupa toda la pantalla.

pygmeFhyfvifp Activa doble buer en la memoria de video, recomendado con rpegi y con yixqv. pygmeFrpegi
Activa aceleracin de hardware, disponible slo con

pvvgiix (en

pantalla completa).

48

1.6 Ejemplos bsicos en Python


pygmeFyixqv
Crea una supercie renderizable con opengl. Crea una ventana de dibujo redimensionable.

pygmeFisefvi pygmeFxypewi

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41

Crea una ventana de dibujo sin borde. Listing 1.18: Modos de video en pygame

# coding : utf -8 ' ' ' c01 / ejemplo -03. py Modos de video en Pygame ''' import pygame def mostrarImagen () : # Copiar la imagen al buffer temporal en ' pantalla ': pantalla . blit ( imagen , (0 ,0) ) # Volcar el buffer a la memoria de video : pygame . display . flip () # Espera un tiempo de 10 segundos pygame . time . delay (10000) # Apaga manualmente el sistema de video pygame . display . quit () print ( " Tamao de la pantalla : {0} x {1} " . format ( pantalla . get_width , pantalla . get_height ) ) pygame . init () tam = ancho , alto = 400 , 400 titulo = " Modos de video ! - " imagen = pygame . image . load ( " logo_uca . bmp " ) raw_input ( " Ventana normal de tamao fijo : " + str ( tam ) ) pantalla = pygame . display . set_mode ( tam ) pygame . display . set_caption ( titulo + " Ventana normal de tamao fijo : " + str ( tam ) ) mostrarImagen () raw_input ( " Ventana normal de tamao variable " ) pantalla = pygame . display . set_mode ( tam , pygame . RESIZABLE ) pygame . display . set_caption ( titulo + " Ventana normal de tamao variable " ) mostrarImagen () raw_input ( " Ventana normal de tamao fijo maximizada ( tamao igual a pantalla completa ) " ) pantalla = pygame . display . set_mode () mostrarImagen ()

49

1 Introduccin a SDL y PyGAME


42 43 44 45 46 47 48 49 50 51 52

raw_input ( " Ventana sin borde de tamao fijo : " + str ( tam ) ) pantalla = pygame . display . set_mode ( tam , pygame . NOFRAME ) mostrarImagen () raw_input ( " Pantalla completa con resolucin fija : " + str ( tam ) ) pantalla = pygame . display . set_mode ( tam , pygame . FULLSCREEN ) mostrarImagen () raw_input ( " Pantalla completa con resolucin mxima " ) pantalla = pygame . display . set_mode ((0 ,0) , pygame . FULLSCREEN ) mostrarImagen ()

1.6.4. Eventos de ratn

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
Listing 1.19: Eventos de ratn

# coding : utf -8 ' ' ' c01 / ejemplo -04. py Eventos del ratn en pygame ''' import pygame pygame . init () tam = ancho , alto = 400 , 400 titulo = " Eventos del ratn " botones = [ " izquierdo " , " medio " , " derecho " , " rueda arriba " , " rueda abajo " ] pantalla = pygame . display . set_mode ( tam ) pygame . display . set_caption ( titulo ) corriendo = True while corriendo : for evento in pygame . event . get () : if evento . type == pygame . MOUSEBUTTONDOWN : print ( " Botn de ratn '{0} ' presionado en {1} " . format (\ botones [ evento . button -1] , evento . pos ) ) elif evento . type == pygame . MOUSEBUTTONUP : print ( " Botn de ratn '{0} ' liberado en {1} " . format (\ botones [ evento . button -1] , evento . pos ) ) elif evento . type == pygame . MOUSEMOTION : print ( " El ratn se movi {0} pixeles hasta {1} " . format (\ evento . rel , evento . pos ) ) print ( " Los botones presionados son : {0}. " . format ( evento . buttons ) ) print ( " \ tBotn izq : {0}\ n \ tBotn cen : {1}\ n \ tBotn der : {2} " . format (\

50

1.6 Ejemplos bsicos en Python


31 32 33 34 35 36

( evento . buttons [0] and " s " or " no " ) , ( evento . buttons [1] and " s " or " no " ) , ( evento . buttons [2] and " s " or " no " ) ) ) elif evento . type == pygame . QUIT : corriendo = False

1.6.5. Eventos de teclado

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
Listing 1.20: Eventos de teclado

# coding : utf -8 ' ' ' c01 / ejemplo -05. py Eventos del teclado en pygame ''' import pygame def devolverModificadores ( m ) : c = ' << ' c += m & pygame . KMOD_NUM and " NUMLOCK " or " " c += m & pygame . KMOD_CAPS and " CAPSLOCK " or " " c += m & pygame . KMOD_MODE and " MODE " or " " c += m & pygame . KMOD_LCTRL and " LCTRL " or " " c += m & pygame . KMOD_RCTRL and " RCTRL " or " " c += m & pygame . KMOD_LSHIFT and " LSHIFT " or " " c += m & pygame . KMOD_RSHIFT and " RSHIFT " or " " c += m & pygame . KMOD_LALT and " LALT " or " " c += m & pygame . KMOD_RALT and " RALT " or " " c += m & pygame . KMOD_LMETA and " LMETA " or " " c += m & pygame . KMOD_RMETA and " RMETA " or " " c += ' >> ' return c def devolverTecla ( tecla ) : return " Cdigo {0} , nombre : '{1} ' " . format ( tecla , pygame . key . name ( tecla ) ) pygame . init () tam = ancho , alto = 400 , 400 titulo = " Eventos del teclado " pantalla = pygame . display . set_mode ( tam ) pygame . display . set_caption ( titulo ) corriendo = True while corriendo :

51

1 Introduccin a SDL y PyGAME


37 38 39 40 41 42 43 44 45 46 47 48 49 50

for evento in pygame . event . get () : if evento . type == pygame . KEYDOWN : mensaje = " Presionada " mensaje += devolverModificadores ( evento . mod ) mensaje += devolverTecla ( evento . key ) print ( mensaje ) elif evento . type == pygame . KEYUP : mensaje = " Liberada " mensaje += devolverModificadores ( evento . mod ) mensaje += devolverTecla ( evento . key ) print ( mensaje ) elif evento . type == pygame . QUIT : corriendo = False

1.6.6. Redimensionamiento de la ventana

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
Listing 1.21: Redimensionamiento

# coding : utf -8 ' ' ' c01 / ejemplo -06. py Redimensionamiento de la ventana en pygame ''' import pygame pygame . init () tam = ancho , alto = 400 , 400 titulo = " Redimensionamiento de la ventana " pantalla = pygame . display . set_mode ( tam , pygame . RESIZABLE ) pygame . display . set_caption ( titulo ) corriendo = True while corriendo : for evento in pygame . event . get () : if evento . type == pygame . VIDEORESIZE : pantalla = pygame . display . set_mode ( evento . size , pygame . RESIZABLE ) print ( " La ventana se ha redimensionado de {0} pixeles a {1} " .\ format ( tam , evento . size ) ) tam = evento . size # Aqu habra que redibujar lo que se estaba mostrando # ya que ' pantalla ' aparece borrada . elif evento . type == pygame . QUIT :

52

1.7 Dibujo de primitivas grcas con pygame


28

corriendo = False

1.7.
En

Dibujo de primitivas grcas con pygame


existe un mdulo asociado al mdulo

pygme,

funciones para dibujar primitivas grcas. Este Su uso se ilustra en el siguiente programa:

pygmeFdisply, que mdulo es pygmeFdrw.

contiene las

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

Listing 1.22: Ejemplo de uso de pygame.draw

# coding : utf -8 ' ' ' c01 / ejemplo -08/ primitivas . py Primitivas grficas ''' import pygame , random ancho , alto = 400 , 400 titulo = " Primitivas grficas " rojo , verde , blanco , negro = (255 ,0 ,0) , (0 ,255 ,0) , (255 ,255 ,255) , (0 ,0 ,0) def dibujar () : # Borrar la pantalla pantalla . fill ( negro ) # Dibujar un rectngulo rojo y otro verde pygame . draw . rect ( pantalla , rojo , pygame . Rect (0 ,0 , ancho /2 , alto /2) ) pygame . draw . rect ( pantalla , verde , pygame . Rect ( ancho /2 , alto /2 , ancho /2 , alto /2) ) # Lineas ( notar la diferencia visual entre ambas ) : pygame . draw . line ( pantalla , blanco , (0 ,0) , ( ancho , alto ) ) pygame . draw . aaline ( pantalla , blanco , (0 , alto ) , ( ancho , 0) ) ; # Lnea horizontal amarilla pygame . draw . line ( pantalla , (255 , 255 , 0) , (0 , alto /2) , ( ancho , alto /2) ) # Inicializar la fuente TrueType por defecto fuente = pygame . font . Font ( None , alto /20) ' ' ' Las fuentes de sistema pueden encontrarse tpicamente en / usr / share / fonts /... y dependen de cada sistema / distribucin . ' '' # Crear el texto saludo = fuente . render ( " HOLA " , True , (255 ,0 ,255) )

53

1 Introduccin a SDL y PyGAME


35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63

bienvenida = fuente . render ( " Bienvenidos a pygame " , False , (255 ,255 ,255) , (128 ,128 ,128) ) # Copiar el texto pantalla . blit ( saludo , ( ancho /2 , alto /2) ) pantalla . blit ( bienvenida , (3* ancho /4 , alto /4) ) # Redibujar el buffer pygame . display . flip () pygame . init () pantalla = pygame . display . set_mode (( ancho , alto ) , pygame . RESIZABLE ) pygame . display . set_caption ( titulo ) corriendo = True # Hacer primer dibujo dibujar () while corriendo : for evento in pygame . event . get () : if evento . type == pygame . VIDEORESIZE : pantalla = pygame . display . set_mode ( evento . size , pygame . RESIZABLE ) print ( " La ventana se ha redimensionado de {0} pixeles a {1} " .\ format (( ancho , alto ) , evento . size ) ) ancho , alto = evento . size dibujar () elif evento . type == pygame . QUIT : corriendo = False

1.8.

Ejercicios
tos segn las teclas numricas que presione el usuario. Por ejemplo, si el usuario presiona el nmero 2 (ya sea del teclado numrico o del teclado normal) debera ver algo parecido a lo que aparece en la gura 1.1.

1. Construya una aplicacin grca que muestre en pantalla un display de 7 segmen-

54

1.8 Ejercicios

Figura 1.1: Display de 7 segmentos

55

1 Introduccin a SDL y PyGAME

56

2 Introduccin a la Gracacin por Computadora


2.1. Marco conceptual para la gracacin interactiva
En general, para desarrollar aplicaciones que incluyan gracacin interactiva, se deben atender los siguientes puntos:

2.1.1. El Modelado
Cmo se modelan las cosas en la computadora?
Tiene que describirse, de algn modo, cmo es aquello que queremos representar en la computadora y hasta qu nivel de detalle lo necesitamos representar. Esta descripcin, es el modelo. Obviamente, la forma de modelar las cosas, est supeditada al hecho que debe facilitar el proceso de dibujado de tal modelo y de su respectiva manipulacin por parte del usuario (si es que esto ltimo es parte del propsito de la aplicacin). Por ejemplo, pinsese en cmo modelar una esfera. . . esfrica

(a) Una forma es por su ecuacin

r = R (asumiendo que est centrada en el origen), (b) por su ecuacin rect2 2 2 angular: (x x0 ) + (y y0 ) + (z z0 ) = R (en este caso puede estar descentrada),
(c) por las coordenadas de la mayor cantidad posible de puntos que la conforman (para
formar una nube densa de puntos),

(d) etc.

En el primer caso, el modelo es sumamente simple (nada ms es el radio de la esfera), pero el proceso de dibujo se vuelve bastante complejo debido a que hay que recalcular los puntos o lneas a dibujar en cada ocasin que necesitemos generar una imagen de la esfera. Lo cual no es precisamente lo ms eciente si se necesita dibujar la esfera con mucha frecuencia. En el segundo caso, la situacin es igual, excepto que el modelo es ms exible, porque permite describir esferas descentradas, pero nada ms. En el tercer caso, el proceso de dibujado ser ms rpido debido a que los puntos (que se calculan slo la primera vez) ya estarn calculados, y bastar con volverlos a dibujar

57

2 Introduccin a la Gracacin por Computadora


cuando se necesite. Pero la alteracin de la posicin de la esfera, por ejemplo, ser ms lento, debido a que habr que cambiar la posicin de cada punto. Considrese en contraste el cambio de posicin con el segundo caso en el que basta con cambiar tres variables. Para que los objetos del mundo real puedan ser representados en la memoria (primaria y/o secundaria) de la computadora, necesitamos primero pensar en algn modelo matemtico que describa esos objetos. Y adems, en el caso de necesitar representarlos en la memoria secundaria, transformarlos a un modelo estrictamente lineal.

2.1.2. La Presentacin
Cmo se dibuja el modelo?
Dado el modelo, hay que describir las tcnicas necesarias (o al menos tenerlas muy claras) para poder proyectar o representar el modelo en la pantalla de la computadora (o en algn otro tipo de dispositivo de salida grca). Usualmente esa proyeccin es hacia dos dimensiones (y eso ser as, al menos, mientras no tengamos monitores tridimensionales). Se espera que una buena presentacin describa el modelo lo sucientemente bien como para que el usuario lo comprenda y pueda manipularlo (o al menos imaginrselo) sin necesidad de entender los mecanismos internos de representacin de dicho modelo. Hay que tener en cuenta que la rapidez de los algoritmos empleados para este proceso debe ser, en general, lo ms alta posible para que la aplicacin pueda presentar una interaccin con rapidez humana. Es decir, que el proceso de dibujo del modelo no debe ser muy lento. Tampoco debe ser demasiado rpido, pero eso es un problema menor, la mayora de las veces.

2.1.3. La Interaccin
Cmo interacta el usuario con el modelo?
El modelo no necesariamente tiene una estructura idntica a lo que el usuario ve por medio de la presentacin descrita en la subseccin anterior. Por ello tambin hay que denir la forma en que el usuario interactuar con el modelo (rotar un objeto tridimensional, desplazarlo o cambiarle el tamao). Dependiendo de la naturaleza de la aplicacin, el usuario nal, no necesariamente debe comprender cmo es almacenado y manipulado el modelo internamente, sino que debera poder manipularlo nicamente a travs de lo que ve en la pantalla. Sobre este tema se hablar ms en el captulo 8 en la pgina 163.

58

2.2 Ciclo de interaccin

2.2.

Ciclo de interaccin

En general existen dos formas de implementar el ciclo de interaccin principal de una aplicacin. Estas dos formas son por ciclo de eventos y por ciclo de juego (conocido en ingls como gameloop ). Ambos tienen un propsito diferente, ya que el ciclo de eventos permite escrutar y procesar todos los eventos relevantes para la aplicacin, mientras que el ciclo de juego hace un muestreo del estado de los dispositivos relevantes para la aplicacin en diferentes momentos. Con un ciclo por eventos, se tienen que procesar todos los eventos, teniendo que decidir si analizarlos o ignorarlos, pudiendo provocar una sobrecarga en el algoritmo de procesamiento, llegando a tener que procesar muchos eventos que sucedieron hace mucho

tiempo . Con un ciclo de juego, no hay forma de garantizar que todas las acciones del
usuario sern percibidas y procesadas por la aplicacin, pero el procesamiento tiende menos a retrasarse con respecto a las acciones del usuario. El ciclo de eventos itera a travs de la secuencia de eventos generados, pudiendo estos ocurrir en diferentes lapsos de tiempo, mientras que el ciclo de juego itera tan rpido como la computadora es capaz, o itera aproximadamente cada cierto tiempo, denido por el programador. De manera esquemtica, un ciclo de eventos tiene la siguiente estructura:

1 2 3 4 5 6 7 8 9 10

Listing 2.1: Ciclo de ventos

hayQueEjecutar = Verdadero . . . MIENTRAS ( hayQueEjecutar ) { MIENTRAS ( hayEventos () ) { evento = siguienteEvento () procesarEvento ( evento ) ; } }

La estructura de un ciclo de juego suele parecerse al siguiente esquema:

1 2 3 4

Listing 2.2: Ciclo de juego

hayQueEjecutar = Verdadero . . .
1
Hace mucho tiempo para una aplicacin grca podra signicar un par de segundos.

59

2 Introduccin a la Gracacin por Computadora


5 6 7 8 9 10 11 12 13 14 15 16

MIENTRAS ( hayQueEjecutar ) { t = ahora () ; estado = leerEstadoDeDispositivosRelevantes () ; actualizarObjetosYPosiciones ( estado ); redibujarObjetosVisibles () ; dt = ahora () - t ; SI ( dt < TiempoPorCiclo ) { hacerPausa ( TiempoPorCiclo - dt ); }

2.2.1. Ciclo de eventos con SDL


Como ya se ilustr en los ejemplos de la seccin 1.3, para procesar los eventos de los dispositivos soportados por SDL (ratn, teclado, joystick y el manejador de ventanas), se extraen los eventos de una cola de eventos de la aplicacin. Pueden utilizarse las funciones:

int hvollivent@hvivent BeventoAY que verica si hay eventos pendientes


de procesar en la cola de eventos y retorna 1 si hay alguno. Retorna 0 si no hay ninguno en el momento de ser llamada. Siempre retorna inmediatamente.

int hvitivent@hvivent BeventoAY que espera indenidamente hasta que


haya otro evento en la cola de eventos y retornando 1 cuando llega. Retorna 0 si hubo algn error mientras se esperaba el evento. Si ya haba eventos en la cola al momento de la llamada, retorna inmediatamente. Ambas colocan en la direccin para esta aplicacin. En el caso de utilizar la primera, el ciclo principal del programa, el ciclo de eventos debera tener una forma parecida a esta:

evento

el siguiente evento extrado de la cola de eventos

1 2 3 4 5 6 7 8 9 10

Listing 2.3: Ciclo de ventos con

hvollivent

SDL_Event evento ; int corriendo = 1; . . . while ( corriendo ) { while ( SDL_PollEvent (& evento ) ) { switch ( evento . type ) { case SDL_MOUSEBUTTONDOWN :

60

2.2 Ciclo de interaccin


11 12 13 14 15 16 17 18 19 20 21 22 23 24 25


En el caso de utilizar la segunda, el ciclo de eventos debera parecerse a:

break ; case ...: . . . case SDL_QUIT : corriendo = 0; break ;

. . .

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

Listing 2.4: Ciclo de ventos con

hvitivent

SDL_Event evento ; . . . while ( SDL_WaitEvent (& evento ) ) { switch ( evento . type ) { case SDL_MOUSEBUTTONDOWN : . . . break ; case ...: . . . case SDL_QUIT : break ; } }

2.2.2. Ciclo de juego con SDL


Para implementar un ciclo de juego con SDL, se dispone de las siguientes funciones:

intQP hvqetiks@voidAY

Devuelve el nmero de milisegundos desde la ini-

cializacin de SDL. Esta funcin es til para el control del tiempo que dura cada

61

2 Introduccin a la Gracacin por Computadora


iteracin del ciclo de juego.

void hvhely@intQP milisegundosAY

Espera durante una cantidad especi-

cada de milisegundos antes de continuar. Esta funcin esperar al menos el tiempo indicado, pero es posible que lleve ms tiempo debido al proceso de asignacin y cambio de procesos (multitarea) del Sistema Operativo en ese momento particular.

intV Bhvqetueytte@int BnumtelsAY


direccin

Devuelve el estado instantaneo del

teclado. El estado es representado por un arreglo cuya dimensin se coloca en la de la forma

numtels si no es xvv. hvuB. Cada casilla

El arreglo se puede indexar por las constantes puede contener un 1 que indica que la tecla

est presionada o un 0 que indica que no est presionada. Para poder usar esta funcin, es necesario llamar primero a la funcin

void hvumpivents@voidAY hvumpivents,


por lo

para actualizar el estado del arreglo. Hay que tener en cuenta que las funciones

hvollivent

hvitivent

llaman internamente a

que si se utilizan las primeras, no es necesario llamarla manualmente.

hvwod hvqetwodtte@voidAY

Devuelve el estado actual de las teclas modi-

cadoras (CTRL, ALT, CAPS-LOCK, etc.). El estado (de tipo entera que contiene las banderas de la forma a nivel de bits.

uwyhB

hvwod) es una variable

combinadas con disyuncin

intV hvqetwousette@int BxD int ByAY Devuelve el estado de los botones


del ratn en una variable entera. El estado contiene las banderas

hvfyx@vGwGAweu combinadas con disyuncin a nivel de bits. Si las direcciones x y y no son xvv, se almacena all la coordenada del ratn en el momento de la llamada. Al igual que hvqetueytte, esta funcin requiere una llamada previa a hvumpivents, que puede ser ejecutada a travs de hvollivent o hvitivent. intV hvqeteltivewousette@int BdxD int BdyAY Devuelve el estado de los botones del ratn igual que la funcin anterior, pero almacena en dx y dy (si no son xvv) los cambios de posicin del cursor desde la ltima llamada a esta
misma funcin o desde la inicializacin de SDL. La plantilla para un ciclo de eventos con SDL podra ser:

1 2 3 4 5 6 7 8 9

Listing 2.5: Ciclo de juego con SDL

# define MARCOS_POR_SEGUNDO 20 { SDL_Event evento ; Uint8 * teclas ; int corriendo = 1; Uint32 t , dt ; Uint32 TiempoCiclo = ( int ) (1000.0 / MARCOS_POR_SEGUNDO ) ; . .

62

2.2 Ciclo de interaccin


10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39

. while ( corriendo ) { t = SDL_GetTicks () ; SDL_PollEvent (& evento ) ; if ( evento . type == SDL_QUIT ) { corriendo = 0; break ; } actualizarPosiciones () ; // revisar colisiones , mover cosas , etc . teclas = SDL_GetKeyState ( NULL ) ; if ( teclas [ SDLK_ESCAPE ]) { corriendo =0; break ; } if ( teclas [ SDLK_LEFT ]) {...} . . . redibujarObjetosVisibles () ; dt = SDL_GetTicks () - t ; if ( dt < TiempoCiclo ) SDL_Delay ( TiempoCiclo - dt ) ;


A continuacin se incluye un pequeo programa de ejemplo, sencillo, sobre ciclo de juego con SDL:

1 2 3 4 5 6 7 8 9 10 11 12 13 14

Listing 2.6: Juego de tenis para dos jugadores con SDL

// c02 / tenis / tenis . c

/* * * Programa de ejemplo para ciclos de juego * */ # include < SDL / SDL .h > # include < stdio .h > # include < stdlib .h > # define ANCHO 640 # define ALTO 480 # define INCREMENTO_DESPLAZAMIENTO 10 # define INCREMENTO_DESPLAZAMIENTO_PELOTA 4

63

2 Introduccin a la Gracacin por Computadora


15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63

# define # define # define # define

MARCOS_POR_SEGUNDO 25 FONDO " fondo - trabajar . bmp " PELOTA " pelotita . bmp " TITULO " Tenis ! - Partida %d "

Uint32 color_fondo , color1 , color2 ; SDL_Surface * imgPelota , * imgFondo ; SDL_Rect jugador1 , jugador2 , pelota ; int velx , vely ; void reiniciarPosiciones ( void ) { jugador1 . x = jugador2 . x = ANCHO /2 - jugador1 . w /2; jugador1 . y = 25; jugador2 . y = ALTO - 25 - jugador2 . h ; pelota . x = ( ANCHO /2) - ( pelota . w /2) ; pelota . y = ( ALTO /2) - ( pelota . h /2) ; velx = (( rand () % 2) ? -1: 1) * INCREMENTO_DESPLAZAMIENTO_PELOTA ; vely = (( rand () % 2) ? -1: 1) * INCREMENTO_DESPLAZAMIENTO_PELOTA ;

int puntoEnRectangulo ( int x , int y , SDL_Rect * r ) { return ( x > r - > x && x < r - > x +r - > w ) && ( y > r - >y && y < r - > y+r - > h ) ; } int main ( int argc , char * argv []) { SDL_Surface * pantalla = NULL ; SDL_Event evento ; Uint8 * teclas ; Uint32 t , dt ; Uint32 TiempoCiclo = ( int ) (1000.0 / MARCOS_POR_SEGUNDO ) ; int corriendo = 1; int marcador1 = 0 , marcador2 = 0; int partida = 1; char titulo [50]; /* Inicializacin */ { srand ( time ( NULL ) ) ; if ( SDL_Init ( SDL_INIT_VIDEO ) < 0 ) { fprintf ( stderr , " No se puede iniciar SDL : %s \n " , SDL_GetError () ) ; exit (1) ; } atexit ( SDL_Quit ) ;

64

2.2 Ciclo de interaccin


64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111

// Cargar el fondo : imgFondo = SDL_LoadBMP ( FONDO ) ; if ( imgFondo == NULL ) { printf ( " Error al cargar el fondo : ' %s '\ n " , SDL_GetError () ) ; exit (1) ; } // Cargar la Pelota : imgPelota = SDL_LoadBMP ( PELOTA ) ; if ( imgPelota == NULL ) { printf ( " Error al cargar la pelota : ' %s '\ n " , SDL_GetError () ) ; exit (1) ; } SDL_SetColorKey ( imgPelota , SDL_SRCCOLORKEY , SDL_MapRGB ( imgPelota - > format ,255 ,255 ,255) ) ; jugador1 . w = jugador2 . w = 40; jugador1 . h = jugador2 . h = 10; pelota . w = imgPelota - > w ; pelota . h = imgPelota - > h ; reiniciarPosiciones () ; SDL_WM_SetIcon ( imgPelota , NULL ) ; pantalla = SDL_SetVideoMode ( ANCHO , ALTO , 0 , SDL_SWSURFACE ) ; if ( pantalla == NULL ) { fprintf ( stderr , " No se puede establecer el modo de video %dx % d : %s \ n " , ANCHO , ALTO , SDL_GetError () ) ; exit (1) ; } sprintf ( titulo , TITULO , partida ) ; SDL_WM_SetCaption ( titulo , NULL ) ; color_fondo = SDL_MapRGB ( pantalla - > format ,255 ,255 ,255) ; color1 = SDL_MapRGB ( pantalla - > format ,255 ,0 ,0) ; color2 = SDL_MapRGB ( pantalla - > format ,0 ,0 ,255) ;

while ( corriendo ) { t = SDL_GetTicks () ; /* Actualizacin */ { SDL_PollEvent (& evento ) ; if ( evento . type == SDL_QUIT ) { corriendo = 0; break ; } // Movimiento de la pelota

65

2 Introduccin a la Gracacin por Computadora


112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159

pelota . x += velx ; pelota . y += vely ; // Rebotes laterales if ( pelota . x < 0 || pelota . x + pelota . w > ANCHO ) velx *= -1; // Deteccin de choque vertical if ( pelota . y < 0 || pelota . y + pelota . h > ALTO ) { if ( pelota . y < 0) { printf ( " Jugador 2 gana !\ n " ) ; marcador2 ++; } else { printf ( " Jugador 1 gana !\ n " ) ; marcador1 ++; } sprintf ( titulo , TITULO , ++ partida ) ; SDL_WM_SetCaption ( titulo , NULL ) ; reiniciarPosiciones () ; continue ; } // Rebotes verticales if ( puntoEnRectangulo ( pelota . x + pelota . w /2 , pelota .y , & jugador1 ) || puntoEnRectangulo ( pelota . x + pelota . w /2 , pelota . y + pelota .h , & jugador2 ) ) { vely *= -1; continue ; } // Movimiento de los jugadores teclas = SDL_GetKeyState ( NULL ) ; if ( teclas [ SDLK_ESCAPE ]) { corriendo =0; break ; } if ( teclas [ SDLK_LEFT ]) { jugador2 . x -= INCREMENTO_DESPLAZAMIENTO ; if ( jugador2 . x < 0) jugador2 . x = 0; } if ( teclas [ SDLK_RIGHT ]) { jugador2 . x += INCREMENTO_DESPLAZAMIENTO ; if ( jugador2 . x + jugador2 . w > pantalla - > w ) jugador2 . x = pantalla - >w - jugador2 . w ; }

66

2.2 Ciclo de interaccin


160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198

if ( teclas [ SDLK_a ]) { jugador1 . x -= INCREMENTO_DESPLAZAMIENTO ; if ( jugador1 . x < 0) jugador1 . x = 0; } if ( teclas [ SDLK_d ]) { jugador1 . x += INCREMENTO_DESPLAZAMIENTO ; if ( jugador1 . x + jugador1 . w > pantalla - > w ) jugador1 . x = pantalla - >w - jugador1 .w ; }

/* Redibujar */ { // Borra la pantalla SDL_BlitSurface ( imgFondo , NULL , pantalla , NULL ) ; // SDL_FillRect ( pantalla , NULL , color_fondo ) ; // Dibujo de los jugadores SDL_FillRect ( pantalla , & jugador1 , color1 ); SDL_FillRect ( pantalla , & jugador2 , color2 ); // Dibujo de la bola SDL_BlitSurface ( imgPelota , NULL , pantalla , & pelota ); // Vuelca el buffer en la memoria de video SDL_Flip ( pantalla ) ;

/* Sincronizacin de tiempo */ dt = SDL_GetTicks () - t ; if ( dt < TiempoCiclo ) SDL_Delay ( TiempoCiclo - dt ) ;

printf ( " \n < < < < < < < < < < < Marcador final : > > > > > > > > > > >\ nJugador 1: %d \ nJugador 2: %d \n \ n " , marcador1 , marcador2 ) ; } return 0;


Se juega con las teclas a y d para el jugador 1 y con los cursores izquierda y derecha para el jugador 2. Cuando un jugador gana, se inicia una nueva partida.

2.2.3. Ciclo de eventos con pygame


Al igual que SDL, pygame dispone de las funciones para extraer eventos de la cola de eventos:

67

2 Introduccin a la Gracacin por Computadora


pygmeFeventFpoll@A
que verica si hay eventos pendientes de procesar en la

cola de eventos y retorna el primero de ella. Retorna

pygmeFxyiix

si no hay

ninguno en el momento de ser llamada. Siempre retorna inmediatamente.

pygmeFeventFwit@A

que espera indenidamente hasta que haya otro evento en

la cola de eventos y devuelve el primero, cuando llega. Si ya haba eventos en la cola al momento de la llamada, retorna inmediatamente.

pygmeFeventFget@A que extrae todos los eventos existentes en la cola de eventos


en ese momento especco. En el caso de utilizar la primera, el ciclo principal del programa, el ciclo de eventos debera tener una forma parecida a esta :

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

Listing 2.7: Ciclo de ventos con

pygmeFeventFpoll@A

corriendo = True . . . while corriendo : evento = pygame . event . poll () if evento == pygame . NOEVENT : continue if evento . type == pygame . MOUSEBUTTONDOWN : . . . elif evento . type == ...: . . . elif evento . type == pygame . QUIT : corriendo = False
3

En el caso de utilizar la segunda, el ciclo de eventos debera parecerse a :

1 2 3 4 5 6 7 8

Listing 2.8: Ciclo de ventos con

pygmeFeventFwit@A

corriendo = True . . . while corriendo : evento = pygame . event . wait () if evento . type == pygame . MOUSEBUTTONDOWN : .
2 3
puede consultarse el archivo puede consultarse el archivo

c02/ejemplo-raton-poll.py c02/ejemplo-raton-wait.py

68

2.2 Ciclo de interaccin


9 10 11 12 13 14 15 16

elif evento . type == ...: . . . elif evento . type == pygame . QUIT : corriendo = False

. .

En todo caso, la forma usada (porque tiende ms al estilo de programacin de Python) es con la funcin

pygmeFeventFget@A.

Con esta funcin, el ciclo de eventos debera

tener una forma similar a:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

Listing 2.9: Ciclo de ventos con

pygmeFeventFget@A

corriendo = True . . . while corriendo : for evento in pygame . event . get () : if evento . type == pygame . MOUSEBUTTONDOWN : . . . elif evento . type == ...: . . . elif evento . type == pygame . QUIT : corriendo = False break

2.2.4. Ciclo de juego con pygame


Para implementar un ciclo de juego con pygame, se dispone de las siguientes funciones:

pygmeFtimeFgettiks@A
izacin de pygame.

Devuelve el nmero de milisegundos desde la inicial-

pygmeFtimeFdely@milisegundosA Realiza una espera activa durante un nmero


de milisegundos especicados. Es bastante preciso ya que usa el procesador durante ese tiempo en lugar de pasar a inactividad.

pygmeFtimeFwit@milisegundosA Realiza una espera pasiva durante un nmero de milisegundos especicados. Es ligeramente menos preciso que pygmeFtimeFdely
ya que depende del sistema operativo para ser despertado.

69

2 Introduccin a la Gracacin por Computadora


pygmeFkeyFgetpressed@A
tecla de la forma Devuelve una secuencia de booleanos representando

el estado (presionado o no) de cada tecla del teclado. Se usan las constantes de

pygmeFuB para indexar la secuencia. Esta funcin requiere pygmeFeventFpump, que se realiza automticamente cuando se usan otras funciones del paquete pygmeFevent, como pygmeFeventFget, pygmeFeventFwit o pygmeFeventFpoll.
una llamada previa a

pygmeFkeyFgetmods@A

Devuelve un valor entero que representa el estado actu-

al de las teclas modicadoras (CTRL, ALT, CAPS-LOCK, etc.). El estado es una variable entera que contiene las banderas de la forma con disyuncin a nivel de bits.

pygmeFuwyhB

combinadas

pygmeFmouseFgetpressed@A

Devuelve una tupla de tres booleanos indicando

el estado (presionado o no) de los tres botones del ratn. El primero siempre es el botn izquierdo, el segundo el botn central y el tercero es el botn derecho. Al igual que

pygmeFkeyFgetpressed, esta funcin requiere una llamada previa a pygmeFeventFpump, que se realiza automticamente cuando se usan las otras funciones del paquete pygmeFevent.
Devuelve una tupla de la forma

pygmeFmouseFgetpos@A

@xDyA

con las coorde-

nadas del cursor (dentro de la ventana).

pygmeFmouseFgetrel@A Devuelve una tupla de la forma @dxDdyA con la cantidad


de movimiento del cursor desde la ltima llamada a esta misma funcin. La plantilla para un ciclo de eventos con pygame podra ser:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

Listing 2.10: Ciclo de juego con pygame

MARCOS_POR_SEGUNDO = 20 t =0; dt =0 TiempoCiclo = int (1000.0 / MARCOS_POR_SEGUNDO ) ; corriendo = True . . . while corriendo : t = pygame . time . get_ticks () for evento in pygame . event . get () : if evento . type == pygame . QUIT : pygame . display . quit () # Cierra la ventana de inmediato corriendo = False break if not corriendo : break actualizarPosiciones () # revisar colisiones , mover cosas , etc .

70

2.2 Ciclo de interaccin


21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

teclas = pygame . key . get_pressed () if teclas [ pygame . K_ESCAPE ]: corriendo = False break if teclas [ pygame . K_LEFT ]: ... . . . redibujarObjetosVisibles () dt = pygame . time . get_ticks () - t if dt < TiempoCiclo : pygame . time . delay ( TiempoCiclo - dt )

A continuacin se incluye un pequeo programa de ejemplo, sencillo, sobre ciclo de juego con pygame:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

Listing 2.11: Juego de tenis para dos jugadores con pygame

# !/ usr / bin / env python # coding : utf -8 " " " c02 / tenis / tenis . py - Programa de ejemplo para ciclos de juego """ import pygame , random , sys ANCHO , ALTO = 640 , 480 INCREMENTO_DESPLAZAMIENTO = 10 INCREMENTO_DESPLAZAMIENTO_PELOTA = 4

X, Y = 0, 1 MARCOS_POR_SEGUNDO = 25 FONDO = " fondo - trabajar . bmp " PELOTA = " pelotita . bmp " TITULO = " Tenis ! - Partida {0} " def reiniciarPosiciones () : jugador1 . x = jugador2 . x = ANCHO /2 - jugador1 . w /2; jugador1 . y = 25; jugador2 . y = ALTO - 25 - jugador2 . h ; pelota . x = ( ANCHO /2) - ( pelota . w /2) ; pelota . y = ( ALTO /2) - ( pelota . h /2) ; vel [ X ] = ( random . randint (0 ,1) and -1 or 1) * INCREMENTO_DESPLAZAMIENTO_PELOTA vel [ Y ] = ( random . randint (0 ,1) and -1 or 1) * INCREMENTO_DESPLAZAMIENTO_PELOTA

71

2 Introduccin a la Gracacin por Computadora


28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77

# Inicializacin : t =0; dt =0 TiempoCiclo = int (1000.0 / MARCOS_POR_SEGUNDO ) ; marcador1 = 0; marcador2 = 0 partida = 1 corriendo = True pygame . init () # Cargar el fondo : imgFondo = pygame . image . load ( FONDO ) # Cargar la Pelota : imgPelota = pygame . image . load ( PELOTA ) imgPelota . set_colorkey ((255 ,255 ,255) ) jugador1 = pygame . Rect ((0 ,0) , (40 ,10) ) jugador2 = pygame . Rect ((0 ,0) , (40 ,10) ) pelota = imgPelota . get_rect () vel = [0 ,0] reiniciarPosiciones () pygame . display . set_icon ( imgPelota ) pantalla = pygame . display . set_mode (( ANCHO , ALTO ) ) pygame . display . set_caption ( TITULO . format ( partida ) ) color_fondo = (255 ,255 ,255) color1 = (255 ,0 ,0) color2 = (0 ,0 ,255) while corriendo : t = pygame . time . get_ticks () # Actualizacin : ###################### for evento in pygame . event . get () : if evento . type == pygame . QUIT : pygame . display . quit () corriendo = False break if not corriendo : break # Movimiento de la pelota pelota . x += vel [ X ]; pelota . y += vel [ Y ];

72

2.2 Ciclo de interaccin


78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127

# Rebotes laterales if pelota . left < 0 or pelota . right > ANCHO : vel [ X ] *= -1; # Deteccin de choque vertical if pelota . top < 0 or pelota . bottom > ALTO : if pelota . top < 0: print ( " Jugador 2 gana !\ n " ) marcador2 += 1 else : print ( " Jugador 1 gana !\ n " ) marcador1 += 1 partida += 1 pygame . display . set_caption ( TITULO . format ( partida ) ) reiniciarPosiciones () continue # Rebotes verticales if jugador1 . collidepoint ( pelota . midtop ) or \ jugador2 . collidepoint ( pelota . midbottom ) : vel [ Y ] *= -1 continue # Movimiento de los jugadores teclas = pygame . key . get_pressed () if teclas [ pygame . K_ESCAPE ]: corriendo = False break if teclas [ pygame . K_LEFT ]: jugador2 . x -= INCREMENTO_DESPLAZAMIENTO if jugador2 . left < 0: jugador2 . left = 0; if teclas [ pygame . K_RIGHT ]: jugador2 . x += INCREMENTO_DESPLAZAMIENTO if jugador2 . right > pantalla . get_width () : jugador2 . x = pantalla . get_width () - jugador2 . w if teclas [ pygame . K_a ]: jugador1 . x -= INCREMENTO_DESPLAZAMIENTO if jugador1 . x < 0: jugador1 . x = 0 if teclas [ pygame . K_d ]: jugador1 . x += INCREMENTO_DESPLAZAMIENTO if jugador1 . x + jugador1 . w > pantalla . get_width () : jugador1 . x = pantalla . get_width () - jugador1 . w # Redibujar : #############################

73

2 Introduccin a la Gracacin por Computadora


128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147

# Borra la pantalla pantalla . blit ( imgFondo , (0 ,0) ) # pantalla . fill ( color_fondo ) # Dibujo de los jugadores pantalla . fill ( color1 , jugador1 ) pantalla . fill ( color2 , jugador2 ) # Dibujo de la bola pantalla . blit ( imgPelota , pelota ) # Vuelca el buffer en la memoria de video pygame . display . flip () # Sincronizacin de tiempo ################### dt = pygame . time . get_ticks () - t if dt < TiempoCiclo : pygame . time . delay ( TiempoCiclo - dt ) print ( " \n < << < < < < < < < < Marcador final : > > > > > > > > > > >\ nJugador 1: {0}\ nJugador 2: {1}\ n \ n " . format ( marcador1 , marcador2 ) )

Se juega con las teclas a y d para el jugador 1 y con los cursores izquierda y derecha para el jugador 2. Cuando un jugador gana, se inicia una nueva partida.

2.3.

Tipos de representacin de grcos

Fundamentalmente existen dos formas de representar y almacenar objetos grcos en la memoria de una computadora:

2.3.1. Grcos de barrido o raster


Esta representacin establece la manipulacin directa de los pixeles que conforman las imgenes. Estos objetos grcos son conocidos tpicamente como mapas de bits. Muchas aplicaciones utilizan este esquema, principalmente usadas para la manipulacin de imgenes a nivel de pixel. Algunas aplicaciones tpicas que funcionan as son aquellas tipo Paint, aplicaciones de retoque de fotos como Picasa de Google, y otras ms conocidas como PhotoShop, GIMP, etc. Los formatos tpicos son los .bmp, .gif, .jpg, .png, etc.

74

2.4 Paletas de colores

2.3.2. Grcos vectoriales


Esta representacin establece la manipulacin de las caractersticas descriptivas de los objetos. Por ejemplo: Se almacenan los puntos de inicio y n de un segmento de recta y su ancho, en lugar de los pixeles que la conformaran; Se almacena el punto de la esquina superior izquierda de un cuadrado y su ancho y alto, en lugar de almancenar los pixeles de sus cuatro lados; Se almacena el centro y radio de un crculo y el grueso de su circunferencia, en lugar de almacenar los pixeles de toda la circunferencia; etc. Las aplicaciones que utilizan este esquema generalmente estn orientados a la ingeniera y en general al diseo asistido por computadora (Computer-Aided Design, CAD). Algunas aplicaciones tpicas que funcionan as son: AutoCAD de AutoDesk, Google SketchUp, OO.org Draw, Inkscape, QCad, Dia, etc. Los formatos especcos varan mucho dependiendo de la aplicacin, pero algunos ms conocidos son los .dia, .svg, .dwg, .dxf, .odg, etc.

2.3.3. Representacin hbrida


Existen, sin embargo, aplicaciones con funcionalidades hbridas. Por ejemplo, en aplicaciones tpicamente de barrido como GIMP (o Photoshop), el texto puede almacenarse como imagen, pero tambin como una cadena de caracteres inmersos en un rectngulo descrito vectorialmente, dentro del rea de trabajo. As mismo, en aplicaciones primariamente vectoriales como OO.org Draw, se pueden insertar archivos de imgen de barrido (png, gif, jpg) y cambiarles, por ejemplo, el contraste a dicha imgen.

2.4.

Paletas de colores

Para representar el color de un objeto grco, ya sea un pixel o un objeto vectorizado, es necesario utilizar una cantidad nita de memoria para ello. Y en los tiempos iniciales de la computacin grca, la memoria era un recurso sumamente costoso y demasiado valioso como para desperdiciarlo. Por ello surgi la necesidad de utilizar poca memoria para representar los colores a utilizar por una aplicacin grca. Por ejemplo, se puede utilizar una paleta de 8 bits (un byte), para permitirle a una aplicacin, usar hasta que el primero, el etc.

28 = 256

colores. De estos, hay que especicar, arbitrariamente,

00H

sea negro, el

01H

sea blanco,. . ., el

09H

sea azul, el

0 AH

sea verde,

75

2 Introduccin a la Gracacin por Computadora


Esa asignacin arbitraria puede realizarse con ayuda de la biblioteca grca a usar (generalmente traen paletas predenidas) o directamente con cdigo ensamblador, en funcin de las capacidades del dispositivo de gracacin (monitor, impresor, etc.).

2.5.

Paletas estndares actuales

Con el advenimiento de dispositivos de gracacin cada vez ms avanzados (que pueden desplegar ms colores y tienen mayor resolucin), con el abaratamiento exponencial de la memoria primaria para computadoras personales y con tarjetas grcas cada vez ms sosticadas, el uso de paletas de colores ha ido poco a poco cayendo en desuso entre los programadores grcos. En su lugar, suele especicarse (y almancenarse) el color directamente como lo entienden los dispositivos de gracacin modernos a travs de estndares internacionales. Estos nuevos estndares tienen, de hecho, la forma de paletas de colores realmente grandes, que no se pueden cambiar y que permiten variaciones de color aparentemente continuas para el ojo humano . Las ms usadas, soportadas en casi todos los lenguajes de programacin, son los de mezcla aditiva de luz (RGB y RGBA) y los de mezcla sustractiva de luz (CMY(K) ).

2.5.1. RGB
[Red, Green, Blue] Un color en este formato se suele especicar en la forma de un conjunto de tres bytes continuos (24 bits), donde los primeros ocho bits, de derecha a izquierda, representan la intensidad de luz azul. Los segundos ocho bits, de derecha a izquierda, la intensidad de luz verde, y los ltimos ocho, de derecha a izquierda, la intensidad de luz roja. En la gura 2.1 podemos ver el tpo diagrama de colores en el modelo RGB. Este modelo representa un color mediante la mezcla por adicin de los tres colores primarios de la luz, rojo, verde y azul. De tal manera que en una supercie negra, su suma a nula intensidad, resulta en negro; y su suma a mxima intensidad, resulta en blanco. Eventualmente, y dependiendo de la arquitectura del procesador, el orden en que se indican estas intensidades puede ser al revs de explicado arriba . Este es un detalle de poca o nula importancia cuando que utilizan APIs de alto nivel.

4 5

es decir, la diferencia entre un color y otro, es menor que el umbral diferencial de la luz, en los humanos. Para mayor informacin, vanse los artculos:

//en.wikipedia.org/wiki/Endianness

http://es.wikipedia.org/wiki/Big-endian

http:

76

2.5 Paletas estndares actuales

Figura 2.1: Modelo de color RGB

2.5.2. RGBA
[Red, Green, Blue, Alpha] Este otro modo de especicacin de color, es similar al anterior, excepto que usa 32 bits. Los ocho que se han agregado (normalmente al nal), representan la medida de opacidad/transparencia (en el caso de SDL,

00H

es transparencia total y

F FH

es opacidad

total, pero en otras bibliotecas grcas, es al revs). E igual que en el caso del RGB, su representacin binaria vara dependiendo del hardware.

2.5.3. CMY(K)
[Cyan, Magenta, Yellow, Black (o Key)] Este modelo representa un color mediante la mezcla sustractiva de los colores cyan, magente y amarillo. De tal manera que impresos sobre un papel blanco, su mezcla a mnima intensidad no absorbe nada y la luz se reeja completamente blanca, pero si se mezclan a mxima intensidad, absorben la luz completamente, por lo que no vemos luz reejada (eso es el color negro). Esta es la forma en que se especican los colores para los dispositivos de impresin con algn tipo de tinta. Y la inclusin de tinta de color negro directamente, se debe a razones tcnicas especcas .

se les llama sistemas de impresin de cuatro tintas

77

2 Introduccin a la Gracacin por Computadora

Figura 2.2: Modelo de color CMY(K)

2.6.

Espacios de Colores (gamuts)

En la teora del color, el gamut de un dispositivo o proceso usado para la creacin de un

color, es la porcin del espacio de color de la luz visible que se puede representar con ese dispositivo o proceso.
Es obvio que existen limitaciones fsicas en todos los dispositivos y procesos, que les impiden mostrar la gama completa del espacio de color de la luz visible (que es bastante grande). Por eso se habla de una porcin del espacio de color de la luz visible. Tambin se podra denir como el lugar geomtrico de los puntos del plano matiz-

saturacin que se pueden representar mediante el dispositivo o proceso en cuestin. (ver


gura 2.3). El color visto en el monitor de una computadora es diferente al color del mismo objeto en una impresin en papel, pues los modelos CMYK y RGB tienen diferentes gamuts. Por ejemplo, el azul puro (rgb: 0,0,100 %) es imposible de reproducir en CMYK. El equivalente ms cercano en CMYK es un tono azulviolceo. En general, en los materiales impresos, las combinaciones de luz RGB no pueden ser reproducidas directamente, por lo que las imgenes generadas en los ordenadores, cuando se usa un programa de edicin, dibujo vectorial, o retoque fotogrco se debe convertir a su equivalente en el modelo CMYK que es el adecuado cuando se usa un dispositivo que usa tintas, como las impresoras o los ploters.

78

2.7 Ejercicios

Figura 2.3: Plano Matiz-Saturacin de la luz visible

En la gura 2.4 se presentan (sin pretender ofrecer una gran precisin) las diferencias de los gamuts de RGB y de CMYK.

El gamut del modelo RGB es el tringulo y la otra gura es el gamut del modelo CMYK.

2.7.

Ejercicios

1. Describa las caractersticas de los grcos de barrido y los grcos vectoriales. 2. Suponga que una aplicacin modela dos crculos, A y B. El crculo A es modelado como grco de barrido y el B como grco vectorial. Describa cmo podra representar dicha aplicacin ambos crculos. 3. Explique con sus propias palabras, qu es el Gamut de un impresor laser. 4. Investigue cmo hacer para detectar combinaciones del tipo

li derehoCtrl,

li izquierdoCshift,

etc.

79

2 Introduccin a la Gracacin por Computadora

Figura 2.4: Comparacin del Gamut de RGB y CMYK

80

3 Discretizacin de Primitivas Grcas


Antes de iniciar este captulo, vale la pena aclarar el concepto de Primitiva Grca: Se reere a las guras bsicas de dibujo vectorial, a partir de las cuales se realizan todas las dems. Estas guras bsicas son los puntos, los segmentos de rectas, crculos, elipses y polgonos. Por otro lado, antes de comenzar con este captulo, se sugiere hacer un repaso de los temas de geometra analtica bsica. Aqu slo se mencionarn las formas de las ecuaciones usadas en el resto del libro, sin describirlas formalmente ya que se asume que el lector ha aprobado algn curso bsico de geometra analtica vectorial.

3.1.

Recordatorio bsico

Recurdese la ecuacin pendiente-intersecto de las lneas rectas:

y = mx + B
Recurdese la ecuacin punto-pendiente de las lneas rectas:

(3.1)

y y0 = m(x x0 )
La equacin dos puntos de las lneas rectas no verticales:

(3.2)

y y0 =

y1 y0 x1 x0

(x x0 )

(3.3)

Tambin se dispone de la ecuacin cannica o implcita de las lneas rectas:

ax + by + c = 0

(3.4)

Recurdese la ecuacin paramtrica de las circunferencias centradas en un punto dado:

(x x0 )2 + (y y0 )2 = R2

(3.5)

81

3 Discretizacin de Primitivas Grcas

Figura 3.1: Representacin abstracta de nueve pixeles

3.2.

Simbologa

Antes que nada, consideremos que los dispositivos principales sobre los que vamos a trabajar, son monitores cuyas pantallas estn compuestas por pixeles. Los pixeles estn dispuestos en forma de una matriz rectangular de puntos casi perfectamente cuadrados y cada uno puede brillar de un color independiente de los dems. La gura 3.1 es la imgen que se usar para esquematizar en este captulo cmo funcionan los algoritmos presentados La cuadrcula de la gura 3.1 representa un grupo de nueve pixeles de una computadora. Las lneas gruesas de guiones representan las fronteras entre dichos pixeles y las intersecciones de las lneas continuas delgadas representan los centros geomtricos de ellos. Por simplicidad, vamos a suponer, en las deducciones, que la numeracin de los pixeles se extiende desde la esquina inferior izquierda, el pixel

(0, 0)

de las pantallas, hasta la

esquina superior derecha. En realidad no es as, pero as es ms parecido a los textos bsicos de matemtica, con los cuales el lector est familiarizado. Se dice tambin que, respecto del pixel central, el pixel que est a su derecha, es su pixel ESTE o simplemente E. El pixel de la esquina superior derecha, es el pixel NOR-ESTE o NE respecto del mismo pixel central. El pixel de la esquina inferior izquierda, es el pixel SUR-OESTE o SO respecto del pixel central, etc.

82

3.3 Algoritmo incremental bsico

3.3.

Algoritmo incremental bsico

En esta seccin, se proceder a describir un algoritmo para dibujar lneas rectas que es sencillo y eciente desde el punto de vista matemtico. Analicemos el problema de dibujar una lnea del punto

(x0, y0 ),

al

(x1, y1 ).
la ecuacin

Lo primero a realizar es la denicin del modelo: Sea

yi = mxi + B
y1 y0 x1 x0

que dene la linea, y los pixeles a encender (o a colorear) sern los pares ordenados:

(xi , redondear (yi )). xi = x

Evidentemente,

m=

y1 y0 x1 x0 y

B = y0

x0 . xi+1

Replanteando el problema en trminos de un proceso iterativo, consideramos que plantear lo siguiente:

para iterar sobre el eje horizontal, de izquierda a derecha. Entonces podemos

yi+1 = mxi+1 + B = m(xi + x) + B = mxi + mx + B = mx + (mxi + B ) yi+1 = yi + mx


y si asumimos que

x = 1 ya que avanzamos de columna en columna de pixeles, entonces: yi+1 = yi + m x i+ i = x i + 1


(3.6)

Esta idea es la base para el algoritmo que se conoce como Analizador Diferencial Digital (DDA, Digital Dierential Analyzer ). Que est restringido al caso en que Para otros valores de

1 m 1.

m,

se puede usar simetra para modicar el algoritmo.

Esta es su especicacin en lenguaje c:

1 2 3 4 5 6 7 8 9 10 11

Listing 3.1: Algoritmo Analizador Diferencial Digital

void dda ( int x0 , int y0 , int x1 , int y1 ) { /* Supone que -1 <= m <= 1 , x0 < x1 . x vara de x0 a x1 en incrementos unitarios */ int x ; float y , m ; m = ( float ) ( y1 - y0 ) / ( x1 - x0 ) ; y = y0 ; for ( x = x0 ; x <= x1 , x ++) { marcarPixel (x , ( int ) y ) ;

83

3 Discretizacin de Primitivas Grcas


12 13 14


A pesar de que esta solucin funciona y se puede adaptar a cualquier pendiente, tiene un detalle indeseable: Utiliza artimtica de coma otante . Por ello, en las siguientes secciones se presenta una elegante solucin que utiliza nicamente aritmtica entera.

y += m ;

3.4.

Algoritmo de lnea de punto medio

A continuacin, se proceder a describir uno de los mejores algoritmos de dibujo de lneas rectas: El algoritmo de lnea de punto medio (tambin conocido como algoritmo

de lnea de Bresenham ).
Antes de abordar la resolucin general del dibujo de segmentos de recta, se abordar un caso particular ms sencillo que permite describir la lgica del algoritmo.

El problema consiste en dibujar un segmento de recta, desde el pixel inferior izquierdo

(x0 , y0 ), hasta el pixel superior derecho (x1 , y1 ) usando nicamente aritmtica entera.

Considere la lnea que se representa en la gura 3.2, donde el pixel previamente seleccionado aparece sombreado y los dos pixeles candidatos a ser seleccionados en el siguiente paso, son el pixel a la derecha del marcado, el pixel ESTE del anterior, el pixel NOR-ESTE geomtricos de

E , y el pixel que est arriba

N E.

El punto

es el punto medio entre los centros

y de

N E. E
y el

Acabamos de marcar el pixel en cuestin, el pixel tenemos que elegir entre el pixel pasa ms cerca del centro geomtrico Si el punto medio

P , en las coordenadas (x0 , y0 ) y ahora N E . Eso lo hacemos averiguando si la recta ideal de E o de N E .

M , est por encima de la lnea, el pixel E es el ms cercano a la lnea. Si el punto medio est debajo, el pixel N E es el ms cercano. En cualquier caso, el error
es siempre menor o igual a

1 2 de pixel.

En el caso de la gura 3.2, el pixel escogido como siguiente es el gura 3.3 en la pgina siguiente es el

N E,

y en el caso de la

E.

Lo que se necesita entonces, es una manera de averiguar de qu lado de la lnea est

M . Para ello se ax + by + c = 0.

usar la siguiente ecuacin de la lnea recta, la ecuacin implcita:

Con ella, podemos construir la siguiente funcin:

84

3.4 Algoritmo de lnea de punto medio

Figura 3.2: Justicacin del algoritmo de lnea de punto medio bsico - 1

Figura 3.3: Justicacin del algoritmo de lnea de punto medio bsico - 2

85

3 Discretizacin de Primitivas Grcas

F (x, y ) = ax + by + c
Esta tiene la siguiente caracterstica: El valor de

(3.7)

es cero para los puntos que pertenecen

a la lnea, positivo si el punto evaluado est debajo de la lnea, y es negativo si el punto evaluado est encima de la lnea, siempre y cuando el coeciente sea negativo. . . . . . . . . . . . . . . F (x, y )

b,

el coeciente de

< 0 = (x, y ) . . . . . . . . . . . . . . F (x, y ) > 0 = (x, y ) . . . . . . . . . . . . . . F (M ) = F xp + 1, yp +


1 2
para saber si

Entonces, tenemos que evaluar el signo de est arriba o abajo de la recta ideal.

Para ordenar un poco el panorama, denamos una nueva variable, nuestra variable de decisin:

d=F

xp + 1, yp +

1 2

= a (xp + 1) + b yp +

1 2

+c

(3.8)

Con esta variable, podemos establecer el siguiente convenio: Si

d > 0,

se elige el pixel

N E;

si

d 0,

se elige el pixel

(si

d = 0,

podramos elegir cualquiera, pero por

convencin, eligimos

E ).
si se eligi el pixel si se eligi el pixel

Una vez tomada la decisin y marcado el pixel, se pasa al siguiente punto y se hace lo mismo.

dnuevo

dnuevo = F (ME ) = F (xp + 1) + 1, yp + 1 2 = F (MN E ) = F (xp + 1) + 1, (yp + 1) + 1 2 a, b


y

E, y N E.

Ahora bien, desconocemos los valores de y analicemos el siguiente fenmeno:

c.

Qu hacemos?. Hagamos una pausa

Sea

P (x) = mx + B

y sea

xi+1 = xi + 1

(ya que consideramos que los sucesivos

xi

son las coordenadas de pixeles). Entonces:

P (xi+1 ) = mxi+1 + B = m(xi + 1) + B = (mxi + B ) + m P (xi+1 ) = P (xi ) + m P (xi+1 ) P (xi ) = m


Ahora veamoslo aplicado a nuestro problema de En el caso de elegir el pixel

(ver la ecuacin 3.7):

E:

86

3.4 Algoritmo de lnea de punto medio

F (xi+1 , yi ) = a(xi + 1) + byi + c = axi + a + byi + c = (axi + byi + c) + a F (xi+1 , yi ) = F (xi , yi ) + a F (xi+1 , yi ) F (xi , yi ) = a dE nuevo dviejo = a = E
Veamos el caso en que se elige el pixel

N E:

F (xi+1 , yi+1 ) = a(xi + 1) + b(yi + 1) + c = axi + a + byi + b + c = (axi + byi + c) + a + b F (xi+1 , yi+1 ) = F (xi , yi ) + a + b F (xi+1 , yi+1 ) F (xi , yi ) = a + b
E dN nuevo dviejo = a + b = N E

A estas diferencias recin introducidas, las llamaremos Resta entonces el problema del valor inicial de paso):

N E

respectivamente.

(equivale al valor

dviejo

del primer

dinicial

= F (M ) 1 1 = F (x0 + 1, y0 + ) = a(x0 + 1) + b(y0 + ) + c 2 2 1 = ax0 + a + by0 + b + c 2 1 = ax0 + by0 + c + a + b 2 1 = (ax0 + by0 + c) + a + b 2 1 = F (x0, y0 ) + a + b; F (x0, y0 ) = 0 2 1 = a+ b 2 a
y

dinicial

Se deben conseguir los parmetros

b (c

no es necesario). Para ello, partimos de

y=

y x x

+ B,

as:

87

3 Discretizacin de Primitivas Grcas

y x+B x x y = y x + x B y = 0 = y x x y + x B F (x, y ) = y x x y + x B
por lo que:

a = y b = x c = x B
Note que el coeciente necesitamos

b resulta negativo. Note adems que el valor de B

es desconocido

(se puede calcular, porque conocemos al menos dos puntos de la recta), pero como no

c,

no representa ningn problema para nuestro propsito.

Tenemos entonces:

E =

F (xi+1 , yi ) F (xi , yi ) = a

= y

NE = F (xi+1 , yi+1 ) F (xi , yi ) = a + b = y x 1 dinicial = a+ 1 = y x 2b 2


Pero esto nos fuerza a usar coma otante slo por el valor inicial de valores sucesivos son enteros...

d.

Todos los dems

Entonces, replanteamos la ecuacin inicial, de la siguiente manera:

y x+B x y 2y = 2 x + 2B x 2x y = 2y x + 2x B y = F (x, y ) = 2y x 2x y + 2x B
por lo que ahora: (3.9)

88

3.4 Algoritmo de lnea de punto medio

a = 2y b = 2x c = 2x B
y volvemos a calcular los valores que nos interesan . Llegamos a:

E = dinicial =
Con esto hemos resuelto

F (xi+1 , yi ) F (xi , yi ) = a
1 a+ 2 b

= 2y = 2y x d.

NE = F (xi+1 , yi+1 ) F (xi , yi ) = a + b = 2y 2x

2 el problema del valor inicial otante para

Ahora, el algoritmo usa exclusivamente artimtica entera, sin el costoso tiempo para hacer operaciones en coma otante. El cdigo en lenguaje C es:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

Listing 3.2: Algoritmo de lnea de punto medio en la mitad de un cuadrante

void linea_punto_medio_1 ( int x0 , int y0 , int x1 , int y1 ) { int delta_x , delta_y , delta_E , delta_NE , d , x , y ; /* Supone que x0 < x1 , y0 < y1 y que delta_y <= delta_x . x vara de x0 a x1 en incrementos unitarios */ delta_x = x1 - x0 ; delta_y = y1 - y0 ; d = delta_y * 2 - delta_x ; delta_E = delta_y * 2; delta_NE = ( delta_y - delta_x ) * 2; x = x0 ; y = y0 ; marcarPixel (x , y ) ; // marcar el primero while ( x < x1 ) { if ( d <= 0) { d += delta_E ; } else { d += delta_NE ; y ++; } x ++;
1 2
Basta con repetir el proceso con la nueva ecuacin Note que el valor de

sigue siendo negativo

89

3 Discretizacin de Primitivas Grcas


23 24 25

marcarPixel (x , y ) ;

3.4.1. Simetra del algoritmo de lnea de punto medio


El problema de generalizar el algoritmo de punto medio para pendientes arbitrarias tiene dos componentes:

(a) permitir pendientes mayores que 1, y (b) poder hacer el recorrido

hacia atrs, para poder aceptar los puntos en cualquier orden sin tener que intercambiar
las variables de entrada. El primer problema se resuelve, averiguando la relacin de orden de a eso, se decide hacer el recorrido sobre

y .

En base

o sobre

y.

As:

Listing 3.3: Algoritmo de lnea de punto medio de un cuadrante (o dos mitades de cuad-

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

rante)

void linea_punto_medio_2 ( int x0 , int y0 , int x1 , int y1 ) { int delta_x , delta_y , delta_E , delta_NE , d , x , y ; delta_x = x1 - x0 ; delta_y =y1 - y0 ; x = x0 , y = y0 ; // Cuando ' horizontal ' es verdadero , el ciclo iterar a travs de x , si no , lo har a travs de y int horizontal = delta_y < delta_x ; // Inicializaciones if ( horizontal ) { d = ( delta_y *2) - delta_x ; delta_E = delta_y *2; delta_NE = ( delta_y - delta_x ) *2; } else { d = ( delta_x *2) - delta_y ; delta_E = delta_x *2; delta_NE = ( delta_x - delta_y ) *2; } // Dibujar el primer pixel marcarPixel (x , y ) ; if ( horizontal ) while ( x < x1 ) { // Iterar a travs de x if (d <=0) d += delta_E ; else { y ++; d += delta_NE ; }

90

3.4 Algoritmo de lnea de punto medio


31 32 33 34 35 36 37 38 39 40 41 42 43 44 45

else

x ++; marcarPixel (x , y );


Ahora bien, el otro problema es un poco ms complicado. Consideremos la gura 3.4 y repitamos el anlisis a partir de la ecuacin 3.9, pero haciendo las modicaciones pertinentes. Llegamos a lo siguiente :

while ( y < y1 ) { // Iterar a travs de y if (d <=0) d += delta_E ; else { x ++; d += delta_NE ; } y ++; marcarPixel (x , y ); }

d = F xp 1, yp O = SO = dinicial = 2y 2y + 2x 2y + x

1 2

= a (xp 1) + b yp = E = N E = dinicial

1 2

+c

Lo que signica que el convenio en este caso es: Si se elige el pixel

d 0,

se elige el pixel

O;

si

d < 0,
4

SO.

Aunque esto parezca bastante complicado, no hace falta darle muchas vueltas al asunto . Basta con tomar conciencia del siguiente razonamiento: Si el valor inicial de

y todos sus incrementos posibles son del mismo valor absoluto,

pero de signo opuesto, y el criterio se basa en el signo de dicha variable, signica que se pueden sustituir los valores manera que quede as: Si el pixel

O , SO

dinicial

por

E , N E
(sumar

dinicial

respectivamente

sin afectar gravemente el algoritmo. Basta con invertir el criterio de incremento, de tal

d > 0,

se elige el pixel

SO

N E );

si

d 0,

se elige

(sumar

E ).

De tal manera que con una ligera modicacin al cdigo 3.2 se

puedan considerar ambos casos, as:

3 4

La demostracin de esto se deja como ejercicio tal como lo hizo el autor de este libro

91

3 Discretizacin de Primitivas Grcas

Figura 3.4: Anlisis inverso de punto medio

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

Listing 3.4: Algoritmo de lnea de punto medio de dos mitades de cuadrantes opuestos

void linea_punto_medio_3 ( int x0 , int y0 , int x1 , int y1 ) { int delta_x , delta_y , delta_E , delta_NE , d , x , y , dir_x , dir_y ; /* x vara de x0 a x1 , o de x1 a x0 en incrementos unitarios */ delta_x = abs ( x1 - x0 ) ; delta_y = abs ( y1 - y0 ) ; x = x0 ; y = y0 ; d = delta_y * 2 - delta_x ; delta_E = delta_y * 2; delta_NE = ( delta_y - delta_x ) * 2; // Indican la direccin en que la lnea debe seguir dir_x = ( x1 - x0 ) >0 ? 1 : -1; dir_y = ( y1 - y0 ) >0 ? 1 : -1; marcarPixel (x , y ) ; // marcar el primero while ( x != x1 ) { if ( d <= 0) { d += delta_E ; } else { d += delta_NE ; y += dir_y ; }

92

3.4 Algoritmo de lnea de punto medio


26 27 28 29


Finalmente, el cdigo en lenguaje C para el algoritmo de lnea de punto medio genrico (que considera avance hacia atrs como el 3.4 y pendientes arbitrarias como el 3.3) es:

x += dir_y ; marcarPixel (x , y ) ;

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38

Listing 3.5: Algoritmo de lnea de punto medio genrico

void linea_punto_medio ( int x0 , int y0 , int x1 , int y1 ) { int delta_x , delta_y , delta_E , delta_NE , d , x , y , dir_x , dir_y ; delta_x = abs ( x1 - x0 ) ; delta_y = abs ( y1 - y0 ) ; x = x0 , y = y0 ; // Cuando ' horizontal ' es verdadero , el ciclo iterar a travs de x , sino , lo har a travs de y int horizontal = delta_y < delta_x ; // Indican la direccin en que la lnea debe seguir dir_x = ( x1 - x0 ) >0 ? 1 : -1; dir_y = ( y1 - y0 ) >0 ? 1 : -1; // Inicializaciones if ( horizontal ) { d =( delta_y *2) - delta_x ; delta_E = delta_y *2; delta_NE =( delta_y - delta_x ) *2; } else { d =( delta_x *2) - delta_y ; delta_E = delta_x *2; delta_NE =( delta_x - delta_y ) *2; } // Dibujar el primer pixel marcarPixel (x , y ); if ( horizontal ) while ( x != x1 ) { if ( d <= 0) { d += delta_E ; } else { d += delta_NE ; y += dir_y ; } x += dir_x ; marcarPixel (x , y ) ; } else

93

3 Discretizacin de Primitivas Grcas

Figura 3.5: Simetra de las circunferencias

39 40 41 42 43 44 45 46 47 48 49

while ( y != y1 ) { if ( d <= 0) { d += delta_E ; } else { d += delta_NE ; x += dir_x ; } y += dir_y ; marcarPixel (x , y ) ; }

3.5.

La simetra de la circunferencia

El crculo se divide en ocho partes iguales, y si se calculan los puntos de un octante, se pueden calcular, por simetra, los puntos de los dems octantes, como puede apreciarse en la gura 3.5 Podra implementarse un procedimiento como el siguiente para aprovechar este fenmeno:

1 2 3 4

Listing 3.6: Funcin de aprovechamiento de la simetra de las circunferencias

void punto_circunferencia_simetria ( int x , int y ) { marcarPixel ( x , y ) ; marcarPixel ( y , x ) ; marcarPixel ( y ,- x ) ;

94

3.6 Algunas ideas sobre circunferencias


5 6 7 8 9 10

marcarPixel ( x , - y ) ; marcarPixel ( -x , -y ) ; marcarPixel ( -y , -x ) ; marcarPixel ( -y , x ) ; marcarPixel ( -x , y ) ;

3.6.

Algunas ideas sobre circunferencias

Existen varias alternativas para dibujar un crculo. Entre ellas, dibujarla a partir de la ecuacin:

x2 + y 2 = R 2 y = R 2 x2
Pero esta alternativa provoca algunos efectos no deseables cuando

(3.10)

x R,

adems del

enorme consumo de recursos para ejecutar las multiplicaciones y la raz cuadrada. Otra alternativa, es dibujarla en su forma paramtrica: cas (que se realizan con series de potencias).

(r cos , r sin ), 0 2 . Esta

es menos ineciente, pero an demasiado, debido al clculo de las funciones trigonomtri-

3.7.

Algoritmo de circunferencia de punto medio

La alternativa ms eciente es el algoritmo de punto medio para circunferencia o algo-

ritmo de crculo de Bresenham . A continuacin se presentar la solucin bsica en el


caso de circunferencias centradas en el origen para comprender la idea general. En la gura 3.6 aparece un diagrama para explicar la idea, que es la misma del algortimo de punto medio para lneas. Para construir el cdigo se proceder de manera similar. Se usar la siguiente ecuacin del crculo, la ecuacin implcita:

F (x, y ) = x2 + y 2 R2 = 0
El valor de

(3.11)

es cero en el crculo, positivo fuera, y negativo dentro, siempre y cuando

los coecientes de

x2

y2

sean positivos.

Como se hizo con las lneas, la decisin se basa en la variable

d:

1 1 dviejo = F (M ) = F (xp + 1, yp ) = (xp + 1)2 + (yp )2 R2 2 2

95

3 Discretizacin de Primitivas Grcas

Figura 3.6: Algoritmo de circunferencia de punto medio

Si

dviejo < 0,

elegimos avanzar al pixel

E:

dE nuevo dE nuevo

= F

(xp + 1) + 1, yp

1 2

= dviejo + E = F ( ME ) F ( M ) 1 1 = F (xp + 2, yp ) F (xp + 1, yp ) 2 2 1 = (xp + 2)2 + (yp )2 R2 2 1 2 (xp + 1) (yp )2 + R2 2 = 2xp + 3

E = dE nuevo dviejo

Si

dviejo 0,

elegimos el pixel

SE :

96

3.7 Algoritmo de circunferencia de punto medio

dSE nuevo dSE nuevo

= F

(xp + 1) + 1, (yp 1)

1 2

= dviejo + SE = F (MSE ) F (M ) 3 1 = F (xp + 2, yp ) F (xp + 1, yp ) 2 2 3 = (xp + 2)2 + (yp )2 R2 2 1 2 (xp + 1) (yp )2 + R2 2 = (2xp + 3) + (2yp + 2)

SE = dSE nuevo dviejo

SE = 2xp 2yp + 5

Lo que falta es entonces, calcular la condicin inicial. Sabemos, que el punto inicial es

(0, R)

y por ende, el siguiente punto medio es

1 (1, R 2 ):

1 dinicial = F (1, R ) 2 1 2 = 1 + (R ) R2 2 1 = 1 + (R2 R + ) R2 4 5 dinicial = R 4

El algoritmo resultante en lenguaje C es:

1 2 3 4 5 6 7 8 9

Listing 3.7: Primera propuesta de algoritmo de crculo de punto medio

void circunferencia_punto_medio_1 ( int radio ) { int x , y ; float d ; x = 0; y = radio ; d = 5.0 / 4 - radio ; marcarPixel ( x , y ) ; marcarPixel ( y , x ) ;

97

3 Discretizacin de Primitivas Grcas


10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

marcarPixel ( y ,- x ) ; marcarPixel ( x ,- y ) ; marcarPixel ( -x , - y ); marcarPixel ( -y , - x ); marcarPixel ( -y , x ) ; marcarPixel ( -x , y ) ; while ( y > x ) { if ( d < 0) { d += x * 2.0 + 3; } else { d += ( x - y ) * 2.0 + 5; y - -; } x ++; marcarPixel ( x , y ) ; marcarPixel ( y , x ) ; marcarPixel ( y ,- x ) ; marcarPixel ( x ,- y ) ; marcarPixel ( -x , - y ); marcarPixel ( -y , - x ); marcarPixel ( -y , x ) ; marcarPixel ( -x , y ) ; }


Sin embargo, persiste el desagradable asunto de tener que operar con una variable de coma otante slo por el valor inicial de

d. Eso puede solucionarse de la siguiente manera:

Denimos una nueva variable de decisin:

en el cdigo 3.7. El valor es ahora

h = d 1 4 y sustituimos este valor en lugar de h = 1 R y la comparacin d < 0 es h < 1 4 . Pero

como como

en

h es incrementada nicamente en valores enteros, se puede dejar la comparacin h < 0 sin afectar el resultado. Y por razones de consistencia, se dejar la variable lugar de h.

El algoritmo resultante, usando slo aritmtica entera para dibujar crculos en lenguaje C es:

1 2 3 4 5 6 7 8 9

Listing 3.8: Algoritmo de circunferencia de punto medio slo con aritmtica entera

void circunferencia_punto_medio_2 ( int radio ) { int x , y , d ; x = 0; y = radio ; d = 1 - radio ; marcarPixel ( x , y ) ; marcarPixel ( y , x ) ; marcarPixel ( y ,- x ) ;

98

3.7 Algoritmo de circunferencia de punto medio


10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

marcarPixel ( x , - y ) ; marcarPixel ( -x , -y ) ; marcarPixel ( -y , -x ) ; marcarPixel ( -y , x ) ; marcarPixel ( -x , y ) ; while ( y > x ) { if ( d < 0) { d += x * 2 + 3; } else { d += ( x - y ) * 2 + 5; y - -; } x ++; marcarPixel ( x , y ) ; marcarPixel ( y , x ) ; marcarPixel ( y , - x ) ; marcarPixel ( x , - y ) ; marcarPixel ( -x , -y ) ; marcarPixel ( -y , -x ) ; marcarPixel ( -y , x ) ; marcarPixel ( -x , y ) ; }

3.7.1. Versin sin multiplicaciones


El algoritmo 3.8 se puede agilizar an ms si se considera que as como se han calculado

diferencias parciales de primer orden para


orden para los incrementos de d. As: Si elegimos

d,

se pueden calcular diferencias de primer

d,

es decir, las diferencias parciales de segundo orden de

en la iteracin actual, el punto de evaluacin se mueve de

(xp , yp )

(xp + 1, yp ).

Tenemos entonces el siguiente panorama:

Eviejo = 2xp + 3 Enuevo = 2(xp + 1) + 3 Enuevo Eviejo = 2 SEviejo = 2xp 2yp + 5


y tenemos adems:

SEnuevo = 2(xp + 1) 2yp + 5. SEnuevo SEviejo = 2


el punto de evaluacin se mueve de

Pero si elegimos a

SE en la iteracin actual, (xp + 1, yp 1). Tenemos lo siguiente:

(xp , yp )

99

3 Discretizacin de Primitivas Grcas


Eviejo = 2xp + 3 Enuevo = 2(xp + 1) + 3 Enuevo Eviejo = 2 SEviejo = 2xp 2yp + 5
y tenemos adems:

SEnuevo = 2(xp + 1) 2(yp 1) + 5. SEnuevo SEviejo = 4

De este anlisis, surge una tercera versin del algoritmo (mucho ms rpida que las anteriores, ya que slo contiene una multiplicacin):

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

Listing 3.9: Algoritmo de lnea de punto medio sin multiplicaciones

void circunferencia_punto_medio_3 ( int radio ) { int x , y , d , delta_E , delta_SE ; x = 0; y = radio ; d = 1 - radio ; delta_E = 3; delta_SE = 5 - radio * 2; marcarPixel ( x , y ) ; marcarPixel ( y , x ) ; marcarPixel ( y ,- x ) ; marcarPixel ( x ,- y ) ; marcarPixel ( -x , - y ); marcarPixel ( -y , - x ); marcarPixel ( -y , x ) ; marcarPixel ( -x , y ) ; while ( y > x ) { if ( d < 0) { d += delta_E ; delta_E += 2; delta_SE += 2; } else { d += delta_SE ; delta_E += 2; delta_SE += 4; y - -; } x ++; marcarPixel ( x , y ) ; marcarPixel ( y , x ) ; marcarPixel ( y ,- x ) ; marcarPixel ( x ,- y ) ; marcarPixel ( -x , - y ); marcarPixel ( -y , - x ); marcarPixel ( -y , x ) ;

100

3.7 Algoritmo de circunferencia de punto medio

Figura 3.7: Esquema para algorito de circunferencias con centro arbitrario

37 38 39

marcarPixel ( -x , y ) ;

3.7.2. Circunferencias con centro arbitrario


Para construir un algoritmo de dibujo de circunferencias con centro arbitrario, a partir del algoritmo de punto medio de esta seccin, bsicamente hay que hacer un desplazamiento vectorial de cada uno de los puntos a marcar. Consideremos la gura 3.7. En ella aparecen dos circunferencias del mismo radio. La diferencia vectorial entre cada uno de los puntos de la circunferencia centrada en el origen y la centrada en

(xc , yc ),

es precsamente el vector

C = (xc , yc ).

Otra forma de plantearlo es as: Sean centro en Entonces

(x , y )

los puntos de la circunferencia con

(xc , yc ). Sean (x, y ) los puntos de la circunferencia con centro en el origen. (x , y ) = (x + xc , y + yc ).

En base a este sencillo anlisis, presentamos el siguiente algoritmo de dibujo de circunferencias con centro arbitrario, basado en el algoritmo 3.8:

101

3 Discretizacin de Primitivas Grcas

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
Listing 3.10: Algoritmo de circunferencia de punto medio para centros arbitrarios

// Funcin auxiliar void marcarPixelesCicunferencia ( int x , int y , int xc , int yc ) { marcarPixel ( x +xc , y + yc ) ; marcarPixel ( x +xc , - y + yc ) ; marcarPixel ( - x + xc , y + yc ) ; marcarPixel ( - x + xc ,- y + yc ) ; marcarPixel ( y +xc , x + yc ) ; marcarPixel ( y +xc , - x + yc ) ; marcarPixel ( - y + xc , x + yc ) ; marcarPixel ( - y + xc ,- x + yc ) ; } void circunferencia_punto_medio ( int xc , int yc , int radio ) { int x ,y , d ; x =0; y = radio ; d =1 - radio ; marcarPixelesCicunferencia (x ,y , xc , yc ) ; while (y > x ) { if (d <0) { d += x * 2 + 3; } else { d += ( x - y ) * 2 + 5; y - -; } x ++; marcarPixelesCicunferencia (x ,y , xc , yc ) ; } }

3.8.

1 2 3 4 5 6 7 8 9 10 11 12

Relleno de rectngulos

Veamos primero un par de algoritmos de dibujo de rectngulos huecos antes de pasar al de relleno:

void dibujar_rectangulo_1 ( int x1 , int y1 , int x2 , int x2 ) { /* Se asume que x1 < x2 y y1 < y2 */ int i; if ( x1 > x2 ) { i = x1 ; x1 = x2 ; x2 = x1 ; } for ( i = x1 ; i <= x2 ; i ++) { marcarPixel (i , y1 ) ; marcarPixel (i , y2 ) ;

102

3.9 Relleno de circunferencias


13 14 15 16 17 18 19 20 21 22 23 1 2 3 4 5 6 7 8 9 10 11

} if ( y1 > y2 ) { i = y1 ; y1 = y2 ; y2 = y1 ; } for ( i = y1 ; i <= y2 ; i ++) { marcarPixel ( x1 , i ) ; marcarPixel ( x2 , i ) ; }

void dibujar_rectangulo_2 ( int x1 , int y1 , int ancho , int alto ) { int i ; for ( i = x1 ; i < x1 + ancho ; i ++) { marcarPixel (i , y1 , ); marcarPixel (i , y1 + alto -1) ; } for ( i = y1 ; i < y1 + alto ; i ++) { marcarPixel ( x1 , i); marcarPixel ( x1 + ancho -1 , i ) ; } }

El primer algoritmo recibe como parmetros dos puntos opuestos del rectngulo (ntese que no importa el orden en que se especiquen). El segundo recibe el punto ms cercano al origen y el ancho y alto del rectngulo (ntese que tanto positivos). Una de las alternativas ms usuales entre las bibliotecas grcas, es ofrecer al programador una funcin de relleno de rectngulos que requiere del punto inicial del rectngulo y su ancho y su alto. Esta es una propuesta de implementacin en lenguaje C:

ancho

como

alto,

deben ser

1 2 3 4 5 6

Listing 3.11: Relleno de rectngulos

void dibujar_rectangulo_relleno ( int x1 , int y1 , int ancho , int alto ) { int i , j ; for ( i = x1 ; i < x1 + ancho ; i ++) for ( j = y1 ; j < y1 + alto ; j ++) marcarPixel (i , j ) ; }

3.9.

Relleno de circunferencias

Para el relleno de circunferencias, tambin existen diferentes propuestas. Una de ellas es aprovechar el algoritmo de punto medio para circunferencias y la simetra propia de

103

3 Discretizacin de Primitivas Grcas

Figura 3.8: Relleno de circunferencias

esta hermosa gura geomtrica. La idea bsica es hacer una modicacin al algoritmo de punto medio para circunferencias de la siguiente manera: Cuando se haga un avance hacia el pixel marcar todos los pixeles desde

SE , aprovechar para

(0, y )

hasta

(x, y )

(y al mismo tiempo aprovechar para

marcar los pixeles correspondientes con la misma idea de la simetra de ocho octantes). A continuacin se presenta el cdigo fuente de dicha solucin : Listing 3.12: Algoritmo de relleno de circunferencias de centro arbitrario (basado en el

1 2 3 4 5 6 7 8 9 10 11 12 13 14

cdigo 3.10 y 3.11)

void circunferencia_rellena ( int x0 , int y0 , int radio ){ int r_x , r_y , r_ancho , r_alto ; int x ,y ,d , i ; x =0; y = radio ; d =1 - radio ; marcarPixelesCicunferencia (x ,y , x0 , y0 ) ; while (y > x ) { if (d <0) { d += x * 2 + 3; x ++; marcarPixelesCicunferencia (x ,y , x0 , y0 ) ; } else { d += ( x - y ) * 2 + 5;
5
comparar con el cdigo de la funcin

circunferencia_punto_medio de la subseccin en la pgina 101

104

3.10 Ejercicios
15 16 17 18 19 20 21 22 23 24 25 26 27 28

} } r_x = x0 - x ; r_y = y0 - y ; r_ancho = 2* x ; r_alto = 2* y ; dibujar_rectangulo_relleno ( r_x , r_y , r_ancho , r_alto ) ;

y - -; x ++; for ( i =0; i <= x ; i ++) { marcarPixelesCicunferencia (i ,y , x0 , y0 ) ; marcarPixelesCicunferencia (y ,i , x0 , y0 ) ; }

3.10.

Ejercicios

1. Construya un algoritmo que use la idea del DDA (cdigo 3.1 en la pgina 83) para pendientes arbitrarias. 2. Describa cmo funciona el algoritmo de dibujo de lneas conocido como algoritmo de lnea de punto medio. 3. Implementar un programa de dibujo usando la ecuacin 3.10 en la pgina 95. 4. En la cuadrcula de la gura 3.9, marque (sombree, tache, repinte, etc.) los pixeles que encendera el algoritmo de lnea 3.5, al unir a los puntos (2,2) y (9,5) y a los puntos (0,2) y (3,9). El centro geomtrico de los pixeles est en el centro de los cuadritos negros. Las lneas grices son slo guas. 5. En la cuadrcula de la gura 3.9, marque (sombree, tache, repinte, etc.) los pixeles que encendera el algoritmo de crculo 3.10 en la pgina 102, con centro (4,4) y radio 4. El centro geomtrico de los pixeles est en el centro de los cuadritos negros. Las lneas grices son slo guas. 6. Modicar el algoritmo 3.9 en la pgina 100 para dibujar crculos con centro arbitrario. 7. Modicar el algoritmo del ejercicio anterior para implementar el dibujo de crculos rellenos. 8. Considere los cdigos siguientes para rellenar crculos y explique por qu el primero es preferible frente al segundo:

1 2

void marcarPixelesCicunferencia ( int x , int y ) { marcarPixel ( x , y ) ;

105

3 Discretizacin de Primitivas Grcas

Figura 3.9: Cuadrcula de prctica de primitivas grcas

3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

marcarPixel ( x ,- y ) ; marcarPixel ( -x , y ) ; marcarPixel ( -x , - y) ; marcarPixel ( y , x ) ; marcarPixel ( y ,- x ) ; marcarPixel ( -y , x ) ; marcarPixel ( -y , - x) ;

void dibujarCirculoRelleno1 ( int radio ) { int x ,y ,d , i ; x =0; y = radio ; d =1 - radio ; marcarPixelesCicunferencia (x , y ) ; while (y > x ) { if (d <0) { d += x * 2 + 3; x ++; marcarPixelesCicunferencia (x , y ) ; } else { d += (x - y ) * 2 + 5; y - -;

106

3.10 Ejercicios
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

} } dibujar_rectangulo_relleno ( -x , -y , 2* x ,2* y ) ;

x ++; for ( i =0; i <= x ; i ++) { marcarPixelesCicunferencia (i , y ) ; marcarPixelesCicunferencia (y , i ) ; }

void dibujarCirculoRelleno2 ( int radio ) { int i ; for ( i =0; i <= radio ; i ++) dibujarCircunferencia ( i ) ; }

9. Considere los cdigos siguientes y explique por qu el primero es ms eciente que el segundo:

1 2 3 4 5 6 7 8 9 10 11 12

void rectanguloRelleno1 ( int x , int y , int ancho , int alto ) { int i , j , xmax = x + ancho , ymax = y + alto ; for ( i= x ; i < xmax ; i ++) { for ( j= y ; j < ymax ; j ++) marcarPixel (i , j) ; }

void rectanguloRelleno2 ( int x , int y , int ancho , int alto ) { int i , xmax = x + ancho , ymax = y + alto ; for ( i= x ; i < xmax ; i ++) linea_punto_medio (i , y , i , ymax ) ; }

10. Implementar una funcin de dibujo de rectngulos que reciba uno de los vrtices del rectngulo y su ancho y alto. Pero el alto y el ancho debe aceptarlos con signo negativo, lo que signica que el rectngulo debe dibujarse hacia atrs en esa dimensin. 11. Implementar una funcin de relleno de rectngulos que reciba como parmetros, dos puntos opuestos del mismo. 12. Implemente la siguiente funcin en lenguaje C estndar

void diujroligono@int xDint yDint nuntosAY


Esta funcin debe dibujar un polgono cerrado, formado por los puntos dados. Por

polgono cerrado, nos referimos a que el ltimo punto se une con el primero aunque
no sean iguales. 13. Construir una funcin de dibujo de elipses, dados su centro y sus dos radios.

107

3 Discretizacin de Primitivas Grcas


14. Construir una funcin de relleno de elipses, dados su centro y sus dos radios. 15. Construir una funcin de dibujo de elipses, dado el rectngulo que la circunscribe.

108

4 Marcos de Referencia y Cambio de Coordenadas


Es usual que se trabaje en los programas de aplicacin, con marcos de referencia diferentes del marco de referencia establecido por la biblioteca grca a utilizar, principalmente para proveer al cdigo la abstraccin necesaria para que la aplicacin sea portable y coherente al modelo que se est gracando. Adems, ayuda mucho a la comprensin por parte de otros programadores. Este captulo contempla una introduccin al tema de cambio de coordenadas entre diversos marcos de referencia. Y la herramienta fundamental que se usar ser el lgebra vectorial, por lo que se recomienda hacer un buen repaso de dichos temas, si es que no se tienen ya a la mano.

4.1.

Notacin

Para recorrer el presente captulo con tranquilidad, vamos a establecer la siguiente nomenclatura:

Sea

(R) p

el vector del punto

en el marco de referencia

R,

y se lee el vector

en

R.
Las componentes rectangulares de y

(R ) p

(R) p y , y se leen la componente x del vector p en R p en R respectivamente.

se representarn en este libro como y la componente

(R) px

del vector

Sea

(R)

una distancia

en la escala del marco de referencia

R,

y se lee  en

R.

Recordemos que un punto cualquiera, puede existir simultaneamente en una cantidad innita de marcos de referencia. Por ejemplo, veamos la gura 4.1.

109

4 Marcos de Referencia y Cambio de Coordenadas

Figura 4.1: Un mismo punto en dos marcos de referencia diferentes

En ella aparece el punto

en el marco de referencia

V,

y en el marco de referencia

R.

En cada uno de ellos, el punto tendr sus coordenadas medidas en una escala diferente, apropiada para cada marco. Usualmente trabajaremos con dos marcos de referencia, uno Virtual, propio de la lgica de nuestra aplicacin, y uno Real, propio de la API grca que estemos utilizando (tpicamente coincidente con los pixeles fsicos del monitor). El problema a analizar en este captulo es bsicamente el siguiente:

Disponemos de claro.

(V ) p ,

y deseamos conocer

(R) p

(y/o viceversa) para que nuestra

manipulacin programtica sea congruente y nuestro cdigo fuente sea ordenado y

4.2.

Anlisis vectorial del cambio de coordenadas

Habiendo hecho la respectiva presentacin de la nomenclatura, pasamos al tema que nos interesa: Cmo cambiar las coordenadas de un punto (u objeto) de un marco de

referencia a otro.
Bueno, el problema puede abordarse de muy diversas maneras. Una de ellas es la perspectiva vectorial.

110

4.2 Anlisis vectorial del cambio de coordenadas

Figura 4.2: Transformacin vectorial de coordenadas

Veamos la gura 4.2. En ella aparece el mismo punto

p,

visto de otra manera:

(R) p = T RV
. Veamos estos nuevos elementos:

(R ) (V ) = V + E R V p

(V ) p

T RV V

es una funcin vectorial que transforma el vector al

de entrada, del marco de referencia el vector del origen de

en

R. E RV

(esta es la que buscamos denir).

(R ) V

es

es una funcin vectorial que cambia la escala de

las componentes del vector de entrada, del marco de referencia El vector

al

R.

(R) V

usualmente se conoce, o puede calcularse de acuerdo a la propia distribu-

cin grca de nuestra aplicacin. La funcin

E R V

no es algo dado, normalmente, sin

embargo puede denirse de la siguiente manera: Antes que nada, necesitamos las coordenadas de dos puntos en ambos marcos de referencia. Llammoslos Ntese que

B.

Consideremos la gura 4.3.

(R) By

(R) x = (R) (V ) Ay y y

Bx Ax , x = Bx Ax , del mismo modo que y = (V ) (V ) = By Ay . Es importante mantener los signos tal cuales. Con

(R)

(R)

(V )

(V )

(V )

(R)

este panorama, es fcil deducir que:

E RV

(V ) p =

x x

(R)

(V )

(V ) y (V ) p x , (V ) py y

(R)

111

4 Marcos de Referencia y Cambio de Coordenadas

Figura 4.3: Cambio de escala de vectores

As, la forma completa para la ecuacin vectorial de transformacin de marcos de referencia es:

(R ) p = T R V

(R) (V ) p = V +

x x

(R)

(V )

(V ) y (V ) p x , (V ) py y

(R)

(4.1)

Si necesitaramos invertir el proceso, es decir, calcular de forma inversa:

(V ) (R) , procedemos p , dado un p

(V ) x (V ) (R) (R) y (R) p = T V R p = R + p x , (R) py (R) x y

(V )

(V )

(4.2)

4.2.1. Ejemplo
Planteemos un escenario en el que es necesario el cambio de marco de referencia: Tenemos una aplicacin grca como en la gura 4.4. La aplicacin tiene su propio marco de referencia establecido por la API grca que estamos utilizando. Esta API establece (y la mayora lo hace de la misma manera) que el origen est en la esquina superior izquierda del interior de la ventana. Todas las coordenadas delimitadas por parntesis pertenecen al sistema de coordenadas de la API. Este marco ser

R.

Dentro de nuestra aplicacin grca tenemos un cuadro (en este caso, punteado) a travs del cual mostramos un juego que tiene su propio marco de referencia, que establece que

112

4.2 Anlisis vectorial del cambio de coordenadas

Figura 4.4: Situacin de ejemplo

el origen est en la esquina inferior izquierda del cuadro, y que las coordenadas visibles para el usuario se extienden hacia la esquina superior derecha hasta la coordenada

[1000, 1000],
llamaremos

independientemente del tamao en pixeles del subcuadro. Todas las co-

ordendadas delimitadas por corchetes estn en el marco de referencia del juego, que

V.

Ntese que los ejes verticales avanzan en direccin opuesta, uno del otro. Y que por convencin en este texto, las coordenadas en el marco de referencia virtual se delimitan con corchetes: con parntesis:

V [ ]. Y que las coordenadas en el marco de referencia real, se delimitan R ( ).


(V ) p =

Se nos plantea entonces un problema: El juego necesita marcar el punto

[500, 500].

Pero nuestra API grca no funciona con ese marco de referencia. Necesi-

tamos indicarle el pixel especco que deseamos marcar. Cmo hacerlo?

El problema se resuelve de la siguiente manera: Tenemos Podemos ver que

(R) V = (100, 300).

(V ) p

y necesitamos

(R) p .

Procedemos entonces a buscar nuestros puntos y

B.

Podemos tomar estos de las esquinas inferior izquierda y superior derecha del

cuadro del juego. Entonces

A(R) = (100, 300), B (R) = (350, 50), A(V ) = [0, 0]

B (V ) =

[1000, 1000].

Ahora sustitumos:

113

4 Marcos de Referencia y Cambio de Coordenadas

Figura 4.5: Caso particular de pantalla completa

(R) p =

T RV (R) V +

(V ) p

(V ) y (V ) p x , (V ) py (V ) x y 350 100 50 300 = (100, 300) + 500, 500 1000 0 1000 0 = (100, 300) + (125, 125)

(R)

(R)

(R) p = (225, 175)

Ahora sabemos que si en el juego se necesita marcar el punto referencia interno, debemos marcar el pixel

[500, 500]

en el marco de

(225, 175)

de la aplicacin.

4.3.

Simplicacin escalar para ventana completa

Hay un caso particular que es muy usual en aplicaciones de corte acadmico: Aquel en el el que toda la pantalla (o al menos toda la ventana) de la aplicacin, se usa para el despliegue grco en un marco de referencia con origen en la esquina inferior izquierda, tal como puede verse en la gura 4.5. En este caso, tal como en el ejemplo de la seccin 4.4, los puntos a elegir como referencias, son las esquinas inferior izquierda y superior derecha del marco virtual en el marco real. Podemos ver que

A(V ) = [0, 0]

(R) V = (0, AltoR). Adems A(R) = (0, AltoR), B (R) = (AnchoR, 0), B (V ) = [XmaxV, Y maxV ] y sustituimos:

114

4.4 Transformacin de distancias


T RV (R) V +

(R) p =

(V ) p

(R) p

( V ) y (V ) p x , (V ) py (V ) x y AnchoR 0 0 AltoR (V ) (V ) = (0, AltoR) + px , py XmaxV 0 Y maxV 0 AnchoR AltoR (V ) (V ) = p x , AltoR py XmaxV Y maxV (V ) py AnchoR (V ) p x , AltoR 1 = XmaxV Y maxV

(R )

(R)

Lo que signica que:

(R) (V ) px px = AnchoR XmaxV


y

(R) (V ) py py =1 AltoR Y maxV

4.4.

Transformacin de distancias

Eventualmente aparece tambin el problema de transformar no slo coordenadas, sino tambin distancias entre diferentes marcos de referencia. El problema se resuelve intuitivamente haciendo una regla de tres con las distancias as:

x (V ) x

(R)

dx (V ) para las magnitudes en dx

(R)

xy

(R)

(V ) y

dy

(R)

(V ) dy

para las magnitudes en

y . De hecho,

es el mismo razonamiento usado para denir la ecuacin 4.1 en la pgina 112. Es de hacer notar que de esta manera, las distancias quedan direccionadas. Es decir, no slo se traduce la magnitud de la distancia, sino tambin su sentido. Si el propsito es traducir slo la magnitud de la distancia, habr que ignorar (o eliminar) el signo resultante.

4.5.

Aplicacin: Simulador de campo elctrico bidimensional

A continuacin se expone brevemente una sencila aplicacin grca interactiva que simula el campo elctrico producido por un conjunto de cargas puntuales en un espacio

115

4 Marcos de Referencia y Cambio de Coordenadas


continuo bidimensional .

4.5.1. Campo Elctrico


Si tenemos una carga puntual punto

en el punto

Q,

entonces el campo elctrico

en el

es:

E =

1 q QP 3 4 0 QP

(4.3)

Si tenemos una serie de cargas puntuales

i = 0, 1, . . . , n,

entonces el campo

qi en los puntos Qi respectivamente, elctrico E en el punto P es: 1 4 0


n i=0

con

E =

qi Qi P 3 Qi P

(4.4)

Los siguientes dos archivos proporcionan la funcionalidad bsica para manipular campo elctrico.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

Listing 4.1: Archivo de cabecera de funciones de campo elctrico

/* c04 / campos vectoriales / campo . h * */ # define EPSILON_0 8.854 e -12 # define _1_4PIe0 8.988 e9 /* Vector genrico bidimensional , se puede usar para posicin , velocidad , etc . */ typedef struct { double x , y ; } Vector ; /* < valor > puede ser negativo . */ typedef struct {
1
Para ms detalles del dicho fenmeno fsico, consltese cualquier texto introductorio de Electromagnetismo

116

4.5 Aplicacin: Simulador de campo elctrico bidimensional


18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41

Vector posicion ; double valor ; } CargaElectrica ; /* Calcula el vector de Campo Elctrico en el punto < punto > , por efecto de la carga < carga >. El resultado se almacena en < campo >. */ void campoElectrico ( CargaElectrica * carga , Vector * punto , Vector * campo ) ; /* * Calcula el vector de Campo Elctrico * en el punto < punto > , por efecto del * arreglo de cargas < cargas >. * El resultado se almacena en < campo > * */ void campoElectricoCargas ( CargaElectrica * cargas , int cantidad , Vector * punto , Vector * campo ) ; /* * Calcula la magnitud de un vector * */ double magnitudVector ( Vector * v ) ;

Listing 4.2: Cdigo fuente de funciones de campo elctrico

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

/* c04 / campos vectoriales / campo . c * */ # include < math .h > # include " campo . h " void campoElectrico ( CargaElectrica * carga , Vector * punto , Vector * campo ) { Vector distancia ; double magnitudDistancia3 ; distancia . x = punto - > x - carga - > posicion . x ; distancia . y = punto - > y - carga - > posicion . y ; magnitudDistancia3 = pow ( magnitudVector (& distancia ) ,3.0) ; campo - > x = _1_4PIe0 * carga - > valor * distancia . x / magnitudDistancia3 ; campo - > y = _1_4PIe0 * carga - > valor * distancia . y / magnitudDistancia3 ; } void campoElectricoCargas ( CargaElectrica * cargas , int cantidad , Vector * punto , Vector * campo ) { int k ; Vector temp ; campo - > x = campo - > y = 0.0;

117

4 Marcos de Referencia y Cambio de Coordenadas


20 21 22 23 24 25 26 27 28 29

for ( k =0; k < cantidad ; k ++) { campoElectrico ( cargas +k , punto , & temp ) ; campo - > x += temp .x ; campo - > y += temp .y ; }

double magnitudVector ( Vector * v ) { return sqrt (v - > x *v - > x + v - > y *v - > y ) ; }


Ntese que el cdigo de este ltimo archivo es altamente suceptible de ser optimizado para mejorar el rendimiento general de la aplicacin.

4.5.2. Uso de colores para SDL_gfxPrimitives


Los siguientes dos archivos proporcionan funcionalidad bsica para manipular colores en el formato que la biblioteca SDL_gfxPrimitives requiere para funcionar correctamente.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

Listing 4.3: Archivo de cabecera para el formato de color de gfx

/* c04 / campos vectoriales / colores . h * */ # include < SDL / SDL .h > // color slido # define ALFA 0 x000000FF # define # define # define # define # define /* ROJO (0 xFF000000 | ALFA ) VERDE (0 x00FF0000 | ALFA ) AZUL (0 x0000FF00 | ALFA ) BLANCO ( ROJO | VERDE | AZUL ) NEGRO 0 x000000FF

Las funciones ____Color de SDL_gfx requieren el color en un entero de 32 bis as : 0 xRRGGBBAA , no acepta el color en ningn otro formato . Adems , utiliza las transparencias por defecto , siempre hay que especificar la opacidad del color .

*/ Uint32 colorGfx ( Uint8 r , Uint8 g , Uint8 b ) ;

Uint32 colorGfxA ( Uint8 r , Uint8 g , Uint8 b , Uint8 a ) ;

118

4.5 Aplicacin: Simulador de campo elctrico bidimensional


28 29

// Borra la pantalla rellenando con el color especificado void borrarPantalla ( SDL_Surface * pantalla , Uint32 relleno ) ;

Listing 4.4: Cdigo fuente de colores para gfx

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

/* c04 / campos vectoriales / colores . c * */ # include " colores . h " Uint32 colorGfx ( Uint8 r , Uint8 g , Uint8 b ) { return r << 24 | g << 16 | b << 8 | 255; // este valor es la opacidad del color // y debe ser mxima para que el color // sea slido } Uint32 colorGfxA ( Uint8 r , Uint8 g , Uint8 b , Uint8 a ) { return r << 24 | g << 16 | b << 8 | a; } void borrarPantalla ( SDL_Surface * pantalla , Uint32 relleno ) { SDL_FillRect ( pantalla , NULL , SDL_MapRGB ( pantalla - > format , ( relleno & ROJO ) > >24 , ( relleno & VERDE ) > >16 , ( relleno & AZUL ) > >8) ) ; }

4.5.3. Las escalas y sus conversiones


Los siguientes dos archivos proporcionan la capa de abstraccin que independiza las dos escalas usadas: La escala interna del modelo, que un universo bidimensional continuo, virtualmente ilimitado donde slo existen cargas elctricas puntuales jas. Y la escala de la biblioteca grca usada, que, como suele suceder, es una matriz discreta y nita de pixeles.

1 2 3

Listing 4.5: Archivo de cabecera de funciones de escala

/* c04 / campos vectoriales / escala . h * Esta es una versin particular del problema * de la transformacin de escalas .

119

4 Marcos de Referencia y Cambio de Coordenadas


4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42

* No es una solucin general para cualquier caso . * */ # include < stdio .h > /* * Estructura para manipular escala * de pantalla completa * */ typedef struct { int AnchoR , AltoR ; double XminV , YminV ; double XmaxV , YmaxV ; } Escala ; /* * Funciones de transformacin vectorial * entre el marco de referencia Real y el Virtual * * La primera funcin se lee : * " Transformacin a Real , dado el Virtual en x " * */ int tR_Vx ( double xV , Escala * e ) ; int tR_Vy ( double yV , Escala * e ) ; double tV_Rx ( int xR , Escala * e ) ; double tV_Ry ( int yR , Escala * e ) ; /* * Funciones de tranformacin de distancias * entre el marco de referencia Real y el Virtual * * La primera funcin se lee : * " Magnitud en Real , dado el Virtual en x " * */ int mR_Vx ( double deltaxV , Escala * e ) ; int mR_Vy ( double deltayV , Escala * e ) ; double mV_Rx ( int deltaxR , Escala * e ); double mV_Ry ( int deltayR , Escala * e );

void imprimirEscala ( FILE * salida , Escala * e ) ;

Listing 4.6: Cdigo fuente de funciones de escala

1 2 3 4 5 6 7 8

/* c04 / campos vectoriales / escala . c * */ # include " escala . h " int tR_Vx ( double xV , Escala * e ) { return ( int ) (( xV - e - > XminV ) * e - > AnchoR / (e - > XmaxV - e - > XminV ) ) ; }

120

4.5 Aplicacin: Simulador de campo elctrico bidimensional


9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41

int tR_Vy ( double yV , Escala * e ) { return ( int ) (( yV - e - > YmaxV ) * -e - > AltoR / (e - > YmaxV - e - > YminV ) ); } double tV_Rx ( int xR , Escala * e ) { return e - > XminV + xR * (e - > XmaxV -e - > XminV ) /e - > AnchoR ; } double tV_Ry ( int yR , Escala * e ) { return e - > YmaxV - (e - > YmaxV -e - > YminV ) * yR /e - > AltoR ; } int mR_Vx ( double deltaxV , Escala * e ) { return deltaxV *e - > AnchoR /( e - > XmaxV -e - > XminV ) ; } int mR_Vy ( double deltayV , Escala * e ) { return deltayV * -e - > AltoR /( e - > YmaxV -e - > YminV ) ; } double mV_Rx ( int deltaxR , Escala * e) { return deltaxR *( e - > XmaxV -e - > XminV ) /e - > AnchoR ; } double mV_Ry ( int deltayR , Escala * e) { return deltayR *( e - > YmaxV -e - > YminV ) / -e - > AltoR ; } void imprimirEscala ( FILE * salida , Escala * e ) { fprintf ( salida , " Datos de escala :\ n " ) ; fprintf ( salida , " AnchoR :\ t %d \ nAltoR :\ t %d \ n " , e - > AnchoR , e - > AltoR ) ; fprintf ( salida , "[ %f, %f]-[ %f, %f ]\ n " , e - > XminV , e - > YminV , e - > XmaxV , e - > YmaxV ) ; }

4.5.4. Programa principal


A continuacin se presenta la aplicacin principal que controla los eventos producidos por el usuario. El programa tiene la funcionalidad de que el tamao de la ventana grca es independiente de la escala del modelo. Los comandos de interaccin son los siguientes:

clic izquierdo

Sobre una carga no tiene efecto. Sobre el campo tiene el efecto de imprimir

en consola el vector de campo y su magnitud medido en

N/C .

121

4 Marcos de Referencia y Cambio de Coordenadas

clic izquierdo sostenido clic derecho clic medio


en la macro

Sobre una carga tiene el efecto desplazar esa carga a otro punto

del plano. Sobre el campo no tiene ningn efecto. Agrega una nueva carga elctrica puntual de

1C

(dicho valor est indicado

geqesxsgsev)

si es que an no se ha alcanzado el mximo nmero

de cargas permitidas (indicado en la macro

wegeqe).

Sobre una carga, tiene el efecto de cambiarle el signo a su valor. Sobre el

campo no tiene ningn efecto.

rueda del mouse arriba rueda del mouse abajo Shift + echa arriba Shift + echa abajo

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

Sobre una carga tiene el efecto de aumentar su valor en

vez (el valor de incremento est denido en la macro el campo provoca un alejamiento del

1 C cada sxgiwixygeqe). Sobre 1C sxgiwixygeqe).

10 %

del plano virtual.

Sobre una carga tiene el efecto de decrementar su valor en

cada vez (el valor de decremento est denido en la macro Sobre el campo provoca un acercamiento del

10 %

del plano virtual.

Aumenta el nmero de echas de campo en la pantalla sin afectar

la escala del modelo ni el tamao de la ventana. Disminuye el nmero de echas de campo en la pantalla sin afectar

la escala del modelo ni el tamao de la ventana. Listing 4.7: Programa principal del simulador de campo elctrico

/* c04 / campos vectoriales / main . c * */ # include < SDL / SDL .h > # include < SDL / SDL_gfxPrimitives .h > # include < stdio .h > # include < math .h > # include " colores . h " # include " campo . h " # include " escala . h " # define # define # define # define MAX_CARGAS 20 CARGA_INICIAL 1.0 INCREMENTO_CARGA 1.0 MIN_CANT_CUADROS 5

struct configuracion { int numCuadrosH ; int numCuadrosV ; Uint32 Uint32 Uint32 Uint32 Uint32 colorFondo ; colorFlecha ; colorPuntaFlecha ; colorCargaPositiva ; colorCargaNegativa ;

122

4.5 Aplicacin: Simulador de campo elctrico bidimensional


26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74

CargaElectrica cargas [ MAX_CARGAS ]; int cantidadActualCargas ; Escala e ; double radioCarga ; // en el marco virtual } conf ; int profundidad_color ; const SDL_VideoInfo * info ; void configurar ( void ) { conf . numCuadrosH = conf . numCuadrosV = 30; conf . colorFondo = BLANCO ; conf . colorFlecha = VERDE ; conf . colorPuntaFlecha = ROJO | AZUL ; conf . colorCargaPositiva = ROJO ; conf . colorCargaNegativa = NEGRO ; conf . e . AnchoR = 600; conf . e . AltoR = 600; conf . e . XminV = conf . e . YminV = -10.0; conf . e . XmaxV = conf . e . YmaxV = 10.0; conf . radioCarga = 1.0; conf . cantidadActualCargas = 0; } // Dibuja los campos en el buffer , no en la pantalla directamente void actualizar ( SDL_Surface * pantalla ) { int i , j ; Vector campo ; Vector punto ; borrarPantalla ( pantalla , conf . colorFondo ) ; // dibujar las cargas for ( i =0; i < conf . cantidadActualCargas ; i ++) { filledEllipseColor ( pantalla , tR_Vx ( conf . cargas [ i ]. posicion .x ,& conf . e ) , tR_Vy ( conf . cargas [ i ]. posicion .y ,& conf . e ) , abs ( mR_Vx ( conf . radioCarga ,& conf . e ) ) , abs ( mR_Vy ( conf . radioCarga ,& conf . e ) ) , ( conf . cargas [ i ]. valor >0.0) ? conf . colorCargaPositiva : conf . colorCargaNegativa ) ; } // dibujar el campo if ( conf . cantidadActualCargas >0) for ( i =0; i < conf . numCuadrosV ; i ++) { // el centro de cada cuadrito punto . y = tV_Ry (( conf . e . AltoR / conf . numCuadrosV ) *(2* i +1) /2 , & conf .

123

4 Marcos de Referencia y Cambio de Coordenadas


e);
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116

for ( j =0; j < conf . numCuadrosH ; j ++) { punto . x = tV_Rx (( conf . e . AnchoR / conf . numCuadrosH ) *(2* j +1) /2 , & conf . e ) ; campoElectricoCargas ( conf . cargas , conf . cantidadActualCargas , & punto , & campo ) ; // dibujar las lneas aalineColor ( pantalla , tR_Vx ( punto .x , & conf . e ) , tR_Vy ( punto .y , & conf . e ) , // ********** tR_Vx ( punto .x , & conf . e ) + ( int ) (( campo . x / magnitudVector (& campo ) ) *( conf . e . AnchoR / conf . numCuadrosH -1) ) , tR_Vy ( punto .y , & conf . e ) - ( int ) (( campo . y / magnitudVector (& campo ) ) *( conf . e . AltoR / conf . numCuadrosV -1) ) , conf . colorFlecha ) ; // dibujar puntas filledCircleColor ( pantalla , tR_Vx ( punto .x , & conf . e ) + ( int ) (( campo . x / magnitudVector (& campo ) ) *( conf . e . AnchoR / conf . numCuadrosH -1) ) , tR_Vy ( punto .y , & conf . e ) - ( int ) (( campo . y / magnitudVector (& campo ) ) *( conf . e . AltoR / conf . numCuadrosV -1) ) , 1, conf . colorPuntaFlecha ) ;

void configurarVideo ( SDL_Surface ** pantalla ) { if ( SDL_Init ( SDL_INIT_VIDEO ) < 0) { printf ( " Error al iniciar SDL : %s \ n " , SDL_GetError () ) ; exit (1) ; } atexit ( SDL_Quit ) ; // Este if es importante para poder usar SDL_gfx info = SDL_GetVideoInfo () ; if ( info - > vfmt - > BitsPerPixel > 8 ) { profundidad_color = info - > vfmt - > BitsPerPixel ; // printf (" %d \ n " , profundidad_color ) ; } else { profundidad_color = 16; } * pantalla = SDL_SetVideoMode ( conf . e . AnchoR , conf .e . AltoR , profundidad_color , SDL_SWSURFACE | SDL_RESIZABLE ) ;

124

4.5 Aplicacin: Simulador de campo elctrico bidimensional


117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159

if (* pantalla == NULL ) { printf ( " Error al inicializar el modo de video : ' %s '\ n " , SDL_GetError () ) ; exit (2) ; } } SDL_WM_SetCaption ( " Simulador de campos elctricos " , NULL ) ;

/* * Dado un arreglo de cargas elctricas , * y una coordenada en el marco Real , * retorna el ndice del arreglo que se corresponde * con la carga seleccionada . * Si no coincide con ninguna , retorna -1 * * */ int seleccionarCarga ( CargaElectrica * cargas , int cantidad , int xR , int yR ){ int i ; int x1 , x2 , y1 , y2 ; for ( i =0; i < cantidad ; i ++) { x1 = tR_Vx ( cargas [ i ]. posicion .x ,& conf . e ) - mR_Vx ( conf . radioCarga ,& conf . e ) ; x2 = tR_Vx ( cargas [ i ]. posicion .x ,& conf . e ) + mR_Vx ( conf . radioCarga ,& conf . e ) ; y1 = tR_Vy ( cargas [ i ]. posicion .y ,& conf . e ) + mR_Vy ( conf . radioCarga ,& conf . e ) ; y2 = tR_Vy ( cargas [ i ]. posicion .y ,& conf . e ) - mR_Vy ( conf . radioCarga ,& conf . e ) ; if ( x1 <= xR && xR <= x2 && y1 <= yR && yR <= y2 ) return i ; } return -1; } int main ( int argc , char * argv []) { SDL_Surface * pantalla = NULL ; SDL_Event evento ; int corriendo = 1; int seleccion = -1; configurar () ; configurarVideo (& pantalla ) ; // hacer primer dibujo stringColor ( pantalla ,0 , conf . e . AltoR /2 , " Haga clic derecho para agregar una carga elctrica " , conf . colorFlecha ) ;

125

4 Marcos de Referencia y Cambio de Coordenadas


160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193

// volcar el buffer en la pantalla SDL_Flip ( pantalla ) ; while ( corriendo ) { while ( SDL_PollEvent (& evento ) ) { switch ( evento . type ) { case SDL_VIDEORESIZE : { conf . e . AnchoR = evento . resize . w ; conf . e . AltoR = evento . resize . h ; // redimensionar la pantalla , no hace falta liberar la anterior . pantalla = SDL_SetVideoMode ( conf . e . AnchoR , conf . e . AltoR , profundidad_color , SDL_SWSURFACE | SDL_RESIZABLE ) ; if ( pantalla == NULL ) { printf ( " Error al redimensionar la pantalla : ' %s '\ n " , SDL_GetError () ) ; exit (1) ; } // La pantalla nueva aparece en blanco // o mejor dicho , en negro , // por lo que hay que volver a dibujar stringColor ( pantalla , 0 , conf . e . AltoR /2 , " Haga clic derecho para agregar una carga elctrica " , conf . colorFlecha ) ; actualizar ( pantalla ) ; // vuelca el buffer en la pantalla : SDL_Flip ( pantalla ) ;

} break ;

194 195 196 197 198 199 200 201

case SDL_MOUSEBUTTONDOWN :{ // Seleccionar / evaluar campo if ( evento . button . button == SDL_BUTTON_LEFT ) { seleccion = seleccionarCarga ( conf . cargas , conf . cantidadActualCargas , evento . button .x , evento . button . y ) ; if ( seleccion == -1) { Vector punto ; Vector campo ; punto . x = tV_Rx ( evento . button .x ,& conf . e ) ; punto . y = tV_Ry ( evento . button .y ,& conf . e ) ; campoElectricoCargas ( conf . cargas , conf . cantidadActualCargas , & punto , & campo ) ; printf ( " Campo electrico : ( %f, %f ) , magnitud : %f \ n " , campo .x , campo .y , magnitudVector (& campo ) ) ; }

126

4.5 Aplicacin: Simulador de campo elctrico bidimensional


202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220

} // Agregar una carga else if ( evento . button . button == SDL_BUTTON_RIGHT ) { if ( conf . cantidadActualCargas < MAX_CARGAS ) { conf . cargas [ conf . cantidadActualCargas ]. valor = CARGA_INICIAL ; conf . cargas [ conf . cantidadActualCargas ]. posicion . x = tV_Rx ( evento . button .x ,& conf . e ) ; conf . cargas [ conf . cantidadActualCargas ]. posicion . y = tV_Ry ( evento . button .y ,& conf . e ) ; conf . cantidadActualCargas ++; actualizar ( pantalla ) ; SDL_Flip ( pantalla ) ; } else printf ( " Ya se alcanz el mximo nmero de cargas permitidas \ n" ) ; } else if ( evento . button . button == SDL_BUTTON_MIDDLE ) { int s ; if (( s = seleccionarCarga ( conf . cargas , conf . cantidadActualCargas , evento . button .x , evento . button . y ) ) > -1) { conf . cargas [ s ]. valor = - conf . cargas [ s ]. valor ; actualizar ( pantalla ) ; SDL_Flip ( pantalla ) ; } } else if ( evento . button . button == SDL_BUTTON_WHEELUP ) { int s ; if (( s = seleccionarCarga ( conf . cargas , conf . cantidadActualCargas , evento . button .x , evento . button . y ) ) > -1) { conf . cargas [ s ]. valor += INCREMENTO_CARGA ; printf ( " Nuevo valor de la carga : %fC \ n " , conf . cargas [ s ]. valor ) ; } else { // alejarse un 10 % double incrementoAncho , incrementoAlto ; incrementoAncho = abs ( conf . e . XmaxV - conf . e . XminV ) *0.10; incrementoAlto = abs ( conf . e . YmaxV - conf . e . YminV ) *0.10; conf . e . XminV -= incrementoAncho /2; conf . e . XmaxV += incrementoAncho /2; conf . e . YminV -= incrementoAlto /2; conf . e . YmaxV += incrementoAlto /2; }

221 222 223 224 225 226 227 228 229

230 231 232 233 234 235 236 237 238 239 240 241

127

4 Marcos de Referencia y Cambio de Coordenadas


242 243 244 245 246 247 248

actualizar ( pantalla ) ; SDL_Flip ( pantalla ) ;

249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285

else if ( evento . button . button == SDL_BUTTON_WHEELDOWN ) { int s ; if (( s = seleccionarCarga ( conf . cargas , conf . cantidadActualCargas , evento . button .x , evento . button . y ) ) > -1) { conf . cargas [ s ]. valor -= INCREMENTO_CARGA ; printf ( " Nuevo valor de la carga : %fC \ n " , conf . cargas [ s ]. valor ) ; } else { // acercarse un 10 % double decrementoAncho , decrementoAlto ; decrementoAncho = abs ( conf . e . XmaxV - conf . e . XminV ) *0.10; decrementoAlto = abs ( conf . e . YmaxV - conf . e. YminV ) *0.10; conf . e . XminV += decrementoAncho /2; conf . e . XmaxV -= decrementoAncho /2; conf . e . YminV += decrementoAlto /2; conf . e . YmaxV -= decrementoAlto /2; } actualizar ( pantalla ) ; SDL_Flip ( pantalla ) ; } } break ; // asegurar que se deselecciona la carga case SDL_MOUSEBUTTONUP :{ seleccion = -1; } break ; // desplazar la carga seleccionada case SDL_MOUSEMOTION :{ if ( seleccion > -1) { conf . cargas [ seleccion ]. posicion . x = tV_Rx ( evento . button .x ,& conf . e ) ; conf . cargas [ seleccion ]. posicion . y = tV_Ry ( evento . button .y ,& conf . e ) ; actualizar ( pantalla ) ; SDL_Flip ( pantalla ) ; } } break ; case SDL_KEYDOWN :{

128

4.5 Aplicacin: Simulador de campo elctrico bidimensional


286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318

} break ;

if ( evento . key . type == SDL_KEYDOWN && evento . key . keysym . mod & KMOD_SHIFT ) switch ( evento . key . keysym . sym ) { case SDLK_UP : { conf . numCuadrosH ++; conf . numCuadrosV ++; actualizar ( pantalla ) ; SDL_Flip ( pantalla ) ; } break ; case SDLK_DOWN : { if ( conf . numCuadrosH > MIN_CANT_CUADROS ) conf . numCuadrosH - -; if ( conf . numCuadrosV > MIN_CANT_CUADROS ) conf . numCuadrosV - -; actualizar ( pantalla ) ; SDL_Flip ( pantalla ) ; } break ; }

// cuando el usuario quiera cerrar la ventana , la aplicacin debe terminar case SDL_QUIT : corriendo = 0; break ; } // fin del switch } // fin de while PollEvent } // fin de while corriendo SDL_Quit () ; return 0;

4.5.5. El Makefile
Finalmente, este es el archivo compilacin :

wkefile

necesario para automatizar todo el proceso de

1 2 3

Listing 4.8: Archivo Makele para el simulador

# c04 / campos vectoriales / Makefile LDFLAGS = $ ( shell sdl - config -- cflags )


2
Slo se requiere ejecutar:

$ make

129

4 Marcos de Referencia y Cambio de Coordenadas


4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

LDLIBS = $ ( shell sdl - config -- libs ) GFX = - lSDL_gfx # # RM = / bin / rm -f

# Esto indica que los siguientes identificadores , # no son archivos , sino comandos de make : . PHONY : limpiar . PHONY : limpiartodo . PHONY : all # se puede por ejemplo ejecutar en la consola # lo siguiente : # ' make limpiartodo ' , etc . # Nombre del programa ejecutable : PROG = simulador # Un '*.o ' por cada '*.c ' OBJ = main . o escala . o colores . o campo . o # Cuando se ejecuta ' make ' , se ejecuta esto : all : limpiartodo $ ( PROG ) limpiar # Esto compila todo el cdigo y lo enlaza : $ ( PROG ) : $ ( OBJ ) $ ( RM ) $ ( PROG ) gcc -o $ ( PROG ) $ ( OBJ ) $ ( LDLIBS ) $ ( LDFLAGS ) $ ( GFX ) # Borra todos los archivos intermedios y de copia de seguridad limpiar : $ ( RM ) *~ $ ( OBJ ) $ ( PRG ) # Borra todos los archivos intermedios , de copia de seguridad # y el programa ejecutable , si es que existe limpiartodo : make limpiar $ ( RM ) $ ( PROG )

4.6.

Ejercicios
int tx@doule int ty@doule doule tx@int doule ty@int xAY yAY xAY yAY

1. Considerando la gura 4.6, construya las funciones

para convertir puntos del marco de referencia virtual al real y viceversa. Asuma

130

4.6 Ejercicios

Figura 4.6: Diagrama para el ejercicio 1

que los smbolos mostrados en la gura son constantes. 2. Modique el programa de la seccin 4.5 en la pgina 115 para representar la diferencia de intensidad del campo en cada punto. 3. Agregar la funcionalidad de eliminar cargas al programa de la seccin 4.5. 4. Modique el programa de la seccin 4.5 para que la cantidad de cargas puntuales aceptadas sea ilimitada. 5. Modique el programa de la seccin 4.5 para que el aumento/acercamiento tenga como centro la posicin del cursor del ratn en ese momento en lugar del centro del espacio visible como sucede con la versin presentada. 6. Modique el programa de la seccin 4.5 para que slo se pueda seleccionar una carga al hacer clic dentro de la supercie de la carga (ya que en la versin actual, la seleccin se logra con clic en el rectngulo circunscrito a la carga). 7. Realice todas las optimizaciones posibles al cdigo de la seccin 4.5, para lograr el mejor rendimiento . 8. Modique el programa de la seccin 4.5 para que el aumento de la cantidad de echas de campo se logre con (en la versin actual es con

hiftCueds del rtn sobre hiftCpleh rriGjo).

el campo elctrico

9. Modique el programa de la seccin 4.5 para permitir que las conguraciones iniciales (ver la estructura

onfigurion

del programa principal) sean cargadas

un buen lugar para empezar es en el archivo  campo.c, y el siguiente lugar, es el programa principal

131

4 Marcos de Referencia y Cambio de Coordenadas


desde un archivo y que las conguraciones al momento de cerrarse la aplicacin se guarden en el mismo archivo.

132

5 Transformaciones Geomtricas Bidimencionales


En este captulo analizaremos los procedimientos estndares para realizar transformaciones geomtricas bidimensionales sobre objetos grcos inmersos en algn marco de referencia especco (real o virtual). Todo el anlisis girar en torno a la representacin matricial de las coordenadas, por lo que se recomienda hacer un concienzudo repaso de operaciones con matrices, ya que el estudio de este captulo depende transversalmente de dichas habilidades. Adems, es preferible recordar algunos tpicos bsicos de trigonometra analtica para poder tener una lectura ms uda.

5.1.

Operaciones geomtricas bsicas

Existen bsicamente tres operaciones o transformaciones geomtricas bsicas, a partir de las cuales se pueden realizar todas las dems. Estas transformaciones son la traslacicn, el escalamiento y la rotacin, todas respecto del origen.

5.1.1. Traslacin o Desplazamiento


La traslacin o desplazamiento se reere a mover un punto, un conjunto de puntos o un objeto compuesto, de su ubicacin original hacia una nueva ubicacin en su marco de referencia. La operacin tiene un parmetro: el vector de desplazamiento. Veamos un ejemplo grco: Como podemos ver en la gura 5.1, el cuadro que representaremos de manera abstracta como

ha sido trasladado en un valor de

(3, 1) (este es el parmetro de la operacin). (5, 2).


El mismo desplazamiento ha

En su nueva ubicacin lo llamaremos base: Su posicin original era

. Tomemos su esquina inferior izquierda como

(2, 3)

y la nueva es

afectado a todos los dems puntos del cuadro.

133

5 Transformaciones Geomtricas Bidimencionales

Figura 5.1: Ejemplo de traslacin simple

5.1.2. Escalamiento
El escalamiento es la operacin que nos permite agrandar o empequeecer un conjunto de puntos o un objeto compuesto. La operacin requiere de dos parmetros: el factor de escalamiento a aplicar en

y el factor de escalamiento a aplicar en

y.

La operacin

requiere adem, de un punto de referencia, tpicamente el origen del marco de referencia. En el caso de aplicar escalamiento bsico a un punto, se produce el efecto de acercarlo o alejarlo del punto de referencia. Veamos un ejemplo: En la gura 5.2, se ilustra el escalamiento de

tomando como referencia al origen y

1 con factores de escalamiento 3 en


son menores que ms en

que

y . El efecto, debido a que ambos factores 1, es que todos los puntos del cuadro P se han acercado al origen (pero en y ).

1 x, y 2 en

Veamos otro ejemplo de escalamiento: En la gura 5.3, se ilustra el escalamiento de escalamiento

(en la misma posicin que en el ejem-

plo anterior) tomando como referencia a su esquina inferior derecha y con factores de

1 2 en

x,

1 2 en

y.

134

5.1 Operaciones geomtricas bsicas

Figura 5.2: Ejemplo de escalamiento simple

Figura 5.3: Ejemplo de escalamiento compuesto

135

5 Transformaciones Geomtricas Bidimencionales

Figura 5.4: Ejemplo de rotacin simple

5.1.3. Rotacin
La rotacin es la ms compleja de las operaciones o transformaciones geomtricas bsicas. Consiste en girar un punto, un conjunto de puntos o un cuerpo compuesto, al rededor de un punto de referencia (el cetnro de rotacin), tpicamente el origen del marco de referencia. Veamos un ejemplo: En la gura 5.4, se ilustra la rotacin de rotacin es de

P,

que est en las coordenadas

6 en sentido contrario al de las agujas del reloj a partir del eje

(6, 2). La x+ . El

punto de referencia es el origen del marco de referencia. Las coordenadas del punto inferior izquierdo de Un ejemplo ms: En la gura 5.5, se ilustra la rotacin de

son

(4,1962, 4,7321)1 .

P,

que est en las mismas coordenadas del

ejemplo anterior. La rotacin es de 4 en el sentido de las agujas del reloj a partir del eje x . El punto de referencia es esquina superior derecha de P . Las coordenadas del punto
inferior izquierdo de

son

(5,1716, 4).

Invitamos amablemente al lector a comprobarlo con comps y transportador

136

5.2 Representacin matricial

Figura 5.5: Ejemplo de rotacin compuesta

5.2.

Representacin matricial

Aunque existen diversas maneras de representar las coordendas de los objetos grcos y de representar las transformaciones geomtricas que operan sobre ellos, vamos a elegir aqu la ms estndar y exible. Los puntos se representan como vectores columna de tamao

3 1:

x P = y 1
La operacin de traslacin bidimensional se representa como una matriz de

(5.1)

3 3:

1 0 dx T (dx , dy ) = 0 1 dy 0 0 1

(5.2)

La operacin de escalamiento bidimensional (con el origen como punto de referencia) se representa similar:

sx 0 0 S (sx , sy ) = 0 sy 0 0 0 1

(5.3)

137

5 Transformaciones Geomtricas Bidimencionales


La operacin de rotacin bidimensional (respecto del origen, en el sentido opuesto al de las manecillas del reloj) se representa as:

cos sin 0 R () = sin cos 0 0 0 1

(5.4)

De este modo, para efectuar transformaciones geomtricas bsicas, esas funciones en forma de matrices, deben premultiplicarse por los puntos que se pretende transformar, as:

P = T (dx , dy ) P P = S (sx , sy ) P P = R ( ) P

signica que signica que

P P P

es es

P P

con un desplazamiento de

(dx , dy ). sx
en

con factores de escalamiento

sy

en

tomando el origen como referencia. signica que

es

con una rotacin de

movimiento de las manecillas del reloj, a partir del eje al origen del marco de referencia. -

radianes en sentido opuesto al x+ , tomando como eje de rotacin

5.3.

Composicin de tranformaciones geomtricas

Con lo planteado hasta ahora, surge la necesidad de representar varias transformaciones geomtricas encadenadas, para producir resultados compuestos, que no se pueden lograr con las operaciones bsicas representadas por las ecuaciones 5.2, 5.3 y 5.4. Ejemplos de ello son las transformaciones realizadas en las guras 5.3 en la pgina 135 y 5.5 en la pgina anterior. Analicemos el caso de la gura 5.3: Para lograr tal transformacin, deberamos primero trasladar

de tal manera que su esquina inferior derecha quede en el origen. Luego,

hay que hacer el escalamiento con ambos factores a antes. Expresado de otra manera: Sea

1 2 . Luego, habra que re-desplazar el

cuadro transformado, a su posicin nal, con la esquina inferior derecha a donde estaba

formacin

Q = (8, 7) la esquina inferior derecha del cuadro original P . Si aplicamos la transT (8, 7) a todos los puntos de P , lo habremos movido de tal manera que Q 1 1 de P ha quedado en el origen. Posteriormente, aplicamos a P el escalamiento S 2, 2 para obtener P , que es un cuadro de ancho y alto igual a 1, con su esquina inferior derecha tambin en el origen. Despus, aplicamos la traslacin T (8, 7) a P , con lo que obtenemos P .

138

5.4 Atencin a la eciencia


La matriz de transformacin aplicada en conjunto es mos aplicarla a los puntos de pasos intermedios. Sigamos la secuencia de transformacin para el punto

para obtener los

T (8, 7) S puntos de P Q

1 1 2, 2

T (8, 7). Pode-

directamente, sin hacer

como ejemplo:

Q Q Q

= T (8, 7) Q 1 1 = S Q =S , 2 2

1 1 , 2 2

T (8, 7) Q 1 1 , 2 2 T (8, 7) Q

= T (8, 7) Q = T (8, 7) S

5.4.

Atencin a la eciencia
r11 r12 tx M = r21 r22 ty 0 0 1 x P = y 1

Sucede que todas las composiciones de matrices de transformacin resultan en matrices de la siguiente forma:

Por lo que su producto con un punto

requiere 9 multiplicaciones y 6 sumas.

Sin embargo, al realizar las operaciones, se puede apreciar que el resultado es previsible. El resultado siempre ser que

x = xr11 + yr12 + tx

y = xr21 + yr22 + ty .

Lo cual

muestra que nicamente son necesarias 4 multiplicaciones y 4 sumas. La importancia de esta consideracin es porque las transformaciones de este tipo son extensivamente usadas en la gracacin tridimensional. Y un gasto innecesario de tiempo de procesador para realizar operaciones de las que ya conocemos el resultado, hara disminuir el rendimiento sensiblemente.

5.5.

Versin matricial del cambio de coordenadas

A continuacin analizaremos otra perspectiva del cambio de coordenadas entre dos marcos de referencia. Esta vez usando transformaciones matriciales. Reconsideremos la gura 4.2 en la pgina 111 y la ecuacin 4.1 en la pgina 112. De ellas podemos concluir que lo primero que hacemos para hacer un cambio de coordenadas, es un escalamiento, y luego hacemos un desplazamiento.

139

5 Transformaciones Geomtricas Bidimencionales


Intuitivamente concluimos que la matriz de transformacin es:

M R V = T

(R ) (R ) V x ,V y

x x

(R)

, (V )

y y

(R)
(5.5)

(V ) (V ) p

As que la transformacin que habamos realizado como bin se puede realizar como:

(R) p = T RV

, tam-

(R) (V ) px px (R) (V ) p y = MRV py 1 1 ( R ) px (R) (R) R) = T V x ,V y p( y 1


(R ) x , (V ) x (R) y (V ) y

(V ) px (V ) py 1

(5.6)

Hay un punto muy importante que recalcar en este punto, y es que as como hemos denido ms as:

MRV ,

tambin podramos denir un encadenamiento de transformaciones como en la gura 5.6. Esto proporciona la

sucesivas para pasar de un marco de referencia a otro, pasando por intermedio de otros

MDA = MDC MC B MB A ,

capacidad extra, de agregar rotaciones a dichos cambios de coordenadas o cualquier otra transformacin aplicable. En la gura 5.7 podemos ver un caso hipottico en el que el marco de referencia virtual, est inclinado referencia

radianes en el sentido opuesto a la del movimiento de las agujas del

reloj. Signica que la versin matricial de la funcin de transformacin del marco de

V al R es: (R) (V ) px px (R) (V ) p y = MR V py 1 1 (R) px (R ) (R ) ,V p (R) = T V


y x y


(R) y x (V ) , (V ) x y (R)

(V ) px (V ) R () py 1

Desde la perspectiva vectorial, habra sido difcil ofrecer una solucin sencilla.

5.6.

Reversin de transformaciones geomtricas

En frecuentes ocaciones no slo necesitaremos efectuar transformaciones geomtricas a un conjunto de puntos, sino que tambin necesitaremos  destransformarlos . Para hacer

140

5.6 Reversin de transformaciones geomtricas

Figura 5.6: Cambio de coordenadas a travs de mltiples marcos de referencia

Figura 5.7: Cambio de coordenadas con rotacin

141

5 Transformaciones Geomtricas Bidimencionales


esto, recordemos las siguientes propiedades matriciales:

P M
1

= M P = M 1 M P = M 1 M P = I P

M
y

= P

(A B )1 = B 1 A1
Esto signica que si tenemos

P =T

(R) (R) V x ,V y

(R) y x (V ) , (V ) x y

(R)

P,

entonces:

T x x
(R )

(R) (R) V x ,V y y y
(R) 1

x x

(R)

(V )

y y

(R )

(V ) 1

= I3 P

, (V )

(V )

(R) (R) V x ,V y

= P

y entonces basta con encontrar las respectivas matrices inversas y obtendremos la matriz de retro-transformacin para

(o sea, para regresar de

P ).

Afortunadamente no es necesario calcular ninguna matriz inversa (ese algoritmo no es precisamente rpido ), ya que suceden los siguientes fenmenos (cuyas demostraciones se dejan como ejercicios):

T (dx , dy )1 = T (dx , dy ) 1 1 S (sx , sy )1 = S , sx sy R()1 = R()


Esto signica, segn la explicacin en la que estabamos, que:

(5.7) (5.8) (5.9)

aunque no completamente inviable

142

5.7 Ejercicios

x x

(R)

, (V )

y y

(R)

(V )

T y
(V )

(R) (R) V x ,V y

P P

= P = P

(V )

(R) x

(R) y

(R) (R) V x , V y

Por lo que la idea general es que para invertir una serie de transformaciones geomtricas, basta con aplicar las transformaciones inversas (siguiendo las ecuaciones 5.7, 5.8 y 5.9) en orden inveso .

5.7.

Ejercicios
sentada en la gura 5.5?

1. Cul es la matriz de transformacin necesaria para lograr la transformacin pre-

2. Realizar la transformacin de las otras aristas del cuadro analizado en la seccin 5.3. 3. Considere la gura 5.8. Rote el tringulo -45 respecto de su esquina inferior derecha segn la regla de la mano derecha, con la ayuda de una matriz de transformacin bidimensional

M1 . M1 ?

a ) Cul es la denicin de

b ) Cules son las coordenadas de los puntos transformados?


4. Considere la gura 5.8 de nuevo. Rote el tringulo -30 respecto de su esquina inferior derecha segn la regla de la mano derecha y despus, duplique su tamao manteniendo la coordenada de su punto inferior derecho. De nuevo haga esto con la ayuda de una matriz de transformacin bidimensional

M2 .

a ) Cul es la denicin de

M2 ?

b ) Cules son las coordenadas de los puntos transformados?


5. Considere la gura 5.8 otra vez. Triplique el tamao del tringulo, manteniendo la coordenada del punto inferior izquierdo, con la ayuda de una matriz de transformacin bidimensional

M3 . M3 ?

a ) Cul es la denicin de

b ) Cules son las coordenadas de los puntos transformados?


3
es cierto, parece un juego de palabras, pero as es

143

5 Transformaciones Geomtricas Bidimencionales

Figura 5.8: Ejercicio de transformacin bidimensional

6. Demuestre la ecuacin 5.7 en la pgina 142. 7. Demuestre la ecuacin 5.8. 8. Demuestre la ecuacin 5.9.

144

6 Transformaciones Geomtricas Tridimencionales


En este captulo se presentan los tpicos bsicos y las herramientas matemticas bsicas para realizar transformaciones geomtricas tridimensionales en marcos de referencia tridimensionales.

6.1.

Sistemas de referencia

La mayora de los curso de lgebra y clculo vectorial utilizan el sistema de referencia de la mano derecha, por lo que la mayora de los anlisis posteriores a eso, lo utilizan tambin. No seremos la excepcin, aunque conviene atender que algunas bibliotecas grcas utilizan por defecto el sistema de referencia de la mano izquierda por diversas razones. No ahondaremos ms en el asunto ya que se asume que el lector ha aprobado algn curso de lgebra y/o clculo vectorial. Baste mencionar que la matriz de transformacin para pasar del sistema de mano derecha al sistema de mano izquierda (y viceversa) es

S (1, 1, 1).

6.2.

Representacin Matricial de Transformaciones Geomtricas


4 1:

Los puntos en tres dimensiones se representan como vectores columna de tamao

x y P = z 1

(6.1)

145

6 Transformaciones Geomtricas Tridimencionales

Figura 6.1: Sistema de referencia de mano derecha

Figura 6.2: Sistema de referencia de mano izquierda

146

6.2 Representacin Matricial de Transformaciones Geomtricas

Figura 6.3: Otra manera de ver el sistema de referencia de mano izquierda

La operacin de traslacin tridimensional se representa como una matriz de

4 4:

1 0 T (dx , dy , dz ) = 0 0

0 1 0 0

0 dx 0 dy 1 dz 0 1

(6.2)

La operacin de escalamiento tridimensional (con el origen como punto de referencia) se representa similar:

sx 0 0 0 sy 0 S (sx , sy , sz ) = 0 0 sz 0 0 0

0 0 0 1

(6.3)

Las operaciones de rotacin (respecto de cada eje principal, siguiendo la regla de la mano derecha) se representa as:

1 0 0 0 cos sin Rx () = 0 sin cos 0 0 0

0 0 0 1

(6.4)

147

6 Transformaciones Geomtricas Tridimencionales


cos 0 Ry () = sin 0 0 sin 0 1 0 0 0 cos 0 0 0 1 0 0 1 0 0 0 0 1 R()

(6.5)

cos sin sin cos R z ( ) = 0 0 0 0


Ntese la similitud entre esta ltima ecuacin

(6.6)

Rz ()

y la ecuacin de

que es para

el caso bidimensional (vase la ecuacin 5.4 en la pgina 138).

6.3.

Composicin y reversin de Transformaciones Geomtricas Tridimensionales

Las transformaciones geomtricas tridimensionales, al igual que las bidimensionales, tambin pueden componerse e invertirse . La lgica matemtica es idntica, por lo que nos limitaremos en este captulo a presentar las inversas de dichas transformaciones:

T (dx , dy , dz )1 = T (dx , dy , dz ) 1 1 1 S (sx , sy , sz )1 = S , , sx sy sz Rx ()1 = Rx () Ry () Rz ()


1 1

(6.7) (6.8) (6.9) (6.10) (6.11)

= Ry () = Ry ()

A manera de prctica, proponemos el uso de la aplicacin

trnsformionesQhFjr1

que se encuentra en el material adjunto a esta obra. Esta

aplicacin permite, de una manera muy simple, practicar las transformaciones geomtricas tridimensionales y ver sus efectos sobre dos objetos con volumen y un objeto plano que pueden traslaparse. Para ejecutar la aplicacin, se requiere de la mquina virtual de java (se recomienda la versin 1.6), procediendo as:

Consltese el captulo A en la pgina 291 para ms detalles sobre la compilacin y ejecucin de aplicaciones J2SE.

148

6.3 Composicin y reversin de Transformaciones Geomtricas Tridimensionales


6 jv Ejr trnsformionesQhFjr
Con esto se iniciar una ventana grca y se inicia un intrprete de comandos en la consola donde se ejecut el comando anterior. La ventana grca muestra al inicio un cubo y un rectngulo tridimensionales en el centro de un marco de referencia tridimensional de mano derecha que se puede rotar con arrastre del ratn. Los ejes

x+ , y +

z+

son lineas de colores rojo, verde y azul

respectivamente (es un truco mnemotcnico que relaciona el modelo RGB imagen.

2 con los ejes

x-y-z y la regla de la mano derecha). Con la rueda del ratn se puede acercar y alejar la

A travs de la ventana grca no se pueden alterar los objetos mencionados, para eso es el intrprete de comandos en la consola (si escribe

yud y presiona `interb, se mostrar

una breve descripcin de los comandos disponibles):

Xyud

muestra una breve resea de los comandos permitidos. reinicia la aplicacin a su estado inicial por defecto. agrega un comentario sin efecto alguno sobre la aplicacin. muestra el objeto en cuestin (si ya est visible, no tiene efecto). oculta el objeto en cuestin (pero todava permite su transforma-

Xreiniir

X 7`omentriob

X`ojetob mostrr X`ojetob oultr


cin).

X`ojetob t `dxb `dyb `dzb


pecicado.

provoca un desplazamiento

T (dx , dy , dz )

en el objeto es-

X`ojetob e `sxb `syb `szb


cado.

provoca un escalamiento

S (sx , sy , sz ) en el objeto especien el objeto especica-

X`ojetob eu `sb
do.

provoca un escalamiento uniforme

S (s, s, s),

X`ojetob {rx|ry|rz} `thetb Xslir


termina la aplicacin.

provoca una rotacin (en grados sexagesimales) en el

eje especicado para el objeto especicado.

Los objetos vlidos para esta versin de la aplicacin son El cubo tiene una arista de largo 4, el plano es de

uo, plno, ojo

43

y el ojo

3 es una pirmide de 4

todos.

4 3 1. Los ejes tienen una longitud de 10. Si el comando introducido no es reconocido,


el programa muestra lo que el usuario escribi y la ayuda. El ojo est por defecto oculto .

2 3 4

en la subseccin 2.5.1 en la pgina 76 veremos su propsito en el siguiente captulo su utilidad no es relevante en este momento, pero se le puede considerar, por el momento, como una pirmide rectangular.

149

6 Transformaciones Geomtricas Tridimencionales


Podramos realizar cualquier combinacin de transformaciones, pero vamos a seguir en este texto, las siguientes (obviamente recomendamos que despus de cada instruccin introducida en la consola se examinen las transformaciones correspondientes en la ventana grca):

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

: plano ocultar : cubo t 2 0 0 : cubo eu 2 : cubo t 0 -4 -4 : cubo t -8 0 0 : cubo t 4 4 4 : cubo e 1 1 .5 : cubo e 1 .5 1 : cubo rx 30 : cubo rx -30 : cubo ry 60 : cubo ry 30 : cubo rz 45 : cubo e 1 1 .5 : cubo rz 45 : cubo ocultar :%%%%%%%%%%% : ojo mostrar : ojo ry 30 : ojo ry -30 : % prstese atencin a estas ltimas transformaciones : : ojo t 5 0 0 : ojo rz 30 : ojo rx 60 : ojo t 0 2 0 : ojo t 0 -2 0 : ojo rx -60 : ojo rz -30 : ojo t -5 0 0
Note que las ltimas cuatro transformaciones invirtieron exactamente las anteriores cuatro.

6.4.

Ejercicios

1. Demuestre las ecuaciones 6.7 en la pgina 148, 6.8 y 6.9.

150

7 Vista Tridimensional (de 3D a 2D)1


El acto que comunmente llamaramos convertir algo de tres dimensiones a dos dimensiones es, formalmente hablando, una proyeccin de un conjunto de objetos tridimensionales, sobre una supercie plana. Existe una gran cantidad de tipos de proyecciones, cada una con sus aplicaciones y particularidades matemticas; algunas aplicadas al arte digital, a la arquitectura, al diseo de piezas de maquinaria, etc. Abordarlas todas, excede el propsito de este libro, por lo que en el presente captulo describiremos brevemente las dos principales: las

proyecciones ortogonales y las proyecciones de perspectiva .


Recomendamos repasar en este momento a ms tardar, los sistemas de coordenadas rectangular, cilndrico y esfrico y las transformaciones entre ellos.

7.1.

Proyeccin Ortogonal

La proyeccin ortogonal consiste bsicamente en proyectar las guras sobre un plano de proyeccin en direccin ortogonal al plano. Veamos algunos ejemplos en la gura 7.1 en la pgina siguiente. En todas las imgenes podemos ver los ejes principales de colores rojo, verde y azul respectivamente . Los detalles geomtricos formales no son de nuestro inters en este momento ya que el propsito de este texto no es adentrarnos en el estudio imagenolgico de las proyecciones, sino entender su efecto visual desde la experiencia del usuario de las aplicaciones grcas tridimensionales. La principal caracterstica a resaltar, es que

x, y

representados por lneas

en este tipo de proyeccin

la reproduccin de la sensacin de profundidad o lejana .


1

NO existe

Como comentario personal del autor, es curioso que modelemos realidades tridimensionales en modelos de almacenamiento lineales (la memoria de la computadora es unidimensional) que a su vez, representamos en pantallas planas bidimensionales. igual que se explic en la seccin 6.3 en la pgina 148

151

7 Vista Tridimensional (de 3D a 2D)

Figura 7.1: Ejemplos de proyeccin ortogonal

152

7.2 Proyeccin en Perspectiva


Esto podemos verlo en el primer recuadro de la gura recin referenciada. La gura es una instantanea de la aplicacin utilizada en el captulo anterior habiendo ejecutado los siguientes comandos:

1 2 3 4

Listing 7.1: Alejamiento del plano

: plano : plano : plano : plano

ry 90 t -50 0 0 rz 25 ry -25

de lo que se puede deducir que el plano est bastante lejos.

7.2.

Proyeccin en Perspectiva

En la proyeccin en perspectiva se proyectan las guras sobre el mismo plano de proyeccin que en la proyeccin ortogonal, pero cada punto en una direccin oblicua al plano, alineada con un foco de proyeccin (ver la gura 7.3 en la pgina 158). Veamos algunos ejemplos en la gura 7.2 en la pgina siguiente. En todas las imgenes podemos ver los ejes principales x-y-z de la misma manera que en la gura 7.1 en la pgina anterior. De nuevo, los detalles geomtricos formales no son de nuestro inters en este momento. La principal caracterstica a resaltar, es que ver en la gura. Los recuadros se corresponden con los de la gura 7.1, con la diferencia que all aparecen en proyeccin ortogonal y aqu en perspectiva, con la evidente sensacin de que hay partes de los cuerpos que estn ms cerca de nosotros que otras.

en este tipo de proyeccin

la reproduccin de la sensacin de profundidad o lejana , tal como podemos

existe

7.3.

Portal de Visin
Cmo producir proyecciones de los tipos

Ahora bien, la pregunta importante es:

presentados en las secciones anteriores? Y a estas alturas del libro, una parte
de la respuesta es obvia: Segursimo que se logran con transformaciones geomtricas

tridimensionales .
La pregunta consecuente es:

Con qu secuencia de transformaciones? En la pre-

sente seccin trataremos de responder claramente a esta pregunta.

por que si no, no habra tenido sentido estudiar todo eso. . . verdad. . . ?

153

7 Vista Tridimensional (de 3D a 2D)

Figura 7.2: Ejemplos de proyeccin en perspectiva

154

7.3 Portal de Visin


Portal

Antes de avanzar, es necesario denir el concepto de Portal de Visin: El

de Visin de una aplicacin grca tridimensional es una ventana, un rectngulo otante (desplazable o no) en el espacio tridimensional de la aplicacin. Este representa la pantalla bidimensional a travs de la cual, el usuario (o los usuarios) perciben esta realidad virtual tridimensional.

El portal de visin, es nuestro plano de proyeccin y su descripcin resulta fundamental para construir aplicaciones grcas tridimensionales. Habiendo aclarado eso, empecemos con las proyecciones ortogonales, que son ms fciles de comprender. A continuacin haremos el siguiente ejercicio, apoyandonos en nuestra aplicacin

trnsformionesQhFjr:

Vamos a considerar el ojo como el portal de visin de una

aplicacin grca tridimensional imaginaria y vamos a realizar las transformaciones geomtricas necesarias para transformar todo el escenario de tal manera que el portal de visin quede sobre el plano de visin

4 es como si lo viramos ya proyectado sobre la pantalla imaginaria (ya que

xy .

Entonces, al ver exactamente desde atrs del portal

habremos obviado la coordenada

de todos los puntos de las guras).

Considere la siguiente secuencia de comandos para nuestra aplicacin

trnsformionesQhFjr

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

para realizar lo anteriormente descrito: Listing 7.2: Proyeccin ortogonal

: % Ocultamos momentaneamente al cubo y al plano : % para concentrarnos en el portal de visin . : cubo ocultar : plano ocultar : % Coloquemos el " ojo " en posicin : : ojo mostrar : ojo t 0 0 5 : ojo rz 90 : ojo ry 60 : ojo rz 30 : % Ahora el ojo est en posicin de : % representar el portal de visin . : % Volvamos a visibilizar el cubo y el plano : : cubo mostrar : plano mostrar : % Agreguemos un detalle : : plano t 0 0 2 : % Veamos la imgen desde el portal : : ojo ocultar
4
asumimos que la parte de atrs es la que tiene la punta

155

7 Vista Tridimensional (de 3D a 2D)


20 21 22 23 24 25 26 27 28

: % Procedemos a hacer la alineacin con 'xy ' : ojo mostrar : todos rz -30 : todos ry -60 : todos rz -90 : todos t 0 0 -5 : % Ahora podramos ocultar el ojo : % para verlo mejor , :) : ojo ocultar
5

Ahora, si efectivamente imaginamos que el ojo es una pantalla de computadora , y nos ubicamos exactamente detrs de l, veremos la imagen de la pantalla imaginaria. Ahora revisemos qu fue exactamente lo que hicimos: Primero colocamos el portal de visin en una posicin conocida, con su centro en la coordenada

r : 5, : 6, :

(en las lneas 7 a 10 del cdigo 7.2). Llammosla simplemente

(r, , ).

Luego aplicamos la siguiente transformacin (en las lneas 22 a 25) al objeto a

proyectar (en este caso el cubo y el plano):

T (0, 0, r) Rz

Ry () Rz () 2

(7.1)

Luego podramos alinear la esquina inferior izquierda del portal de visin con el origen, agregando

T (2, 1,5, 0).

Adems, podramos agregar escalamiento, etc.. . .

Lo importante de este ejercicio, es entender cmo lograr proyecciones ortogonales a travs de transformaciones geomtricas tridimensionales.

7.4.

Implementacin de proyecciones ortogonales

En este momento ya casi disponemos de los recursos tericos para proceder a disear una estrategia de sistematizacin de proyecciones ortogonales. Un detalle importante que falta es considerar que la ecuacin 7.1 no es la versin ms apropiada de las alternativas disponibles. A continuacin presentamos una secuencia de transformaciones que es ms apropiada:

M2D3D = T (0, 0, r) Rz

Ry ( ) Rz () 2
6

(7.2)

Esta transformacin es muy similar a la anteriormente presentada, pero presenta una importante mejora sin alterar signicativamente el tiempo de ejecusin : Coloca los

5 6

por eso su tamao es

43

Tiene una inversin de signo menos y una suma ms

156

7.4 Implementacin de proyecciones ortogonales


objetos que estn delante del portal de visin en el eje (lo que es usual en gracacin en computadoras). Veamos el mismo caso de proyeccin del cdigo 7.2, pero con la transformacin 7.2: Listing 7.3: Proyeccin ortogonal preferible

z+

y con el eje

y+

hacia abajo

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

: % Ocultamos momentaneamente al cubo y al plano : cubo ocultar : plano ocultar : % Coloquemos el " ojo " en posicin : : ojo mostrar : ojo t 0 0 5 : ojo rz 90 : ojo ry 60 : ojo rz 30 : % Ahora el ojo est en posicin . : % Volvamos a visibilizar el cubo y el plano : : cubo mostrar : plano mostrar : plano t 0 0 2 : % Procedemos a hacer la alineacin con 'xy ' , : % pero de otra manera : : todos rz -30 : todos ry 120 : todos rz -90 : todos t 0 0 5

Luego de hacer esta transformacin, convendr hacer una transformacin extra hacia el marco de referencia real, con la transformacin 5.5 en la pgina 140 o alguna equivalente que se ajuste a las necesidades, as :

P P

= MRV M2D3D P AnchoR AltoR = S (sx , sy , sz ) T , , 0 M2D3D P 2 2

(7.3)

Es en este punto donde la aplicacin

trnsformionesQhFjr implementa su funcionaloloruntodeision

idad de acercamiento y alejamiento. Sugerimos abrir el cdigo de dicha aplicacin para ver todo este proceso en accin. En particular, el cdigo que realiza la denicin de la matriz de transformacin est en la funcin del archivo

ortldeisionFjv.
7
recordando que el eje

y+

ya est hacia abajo

157

7 Vista Tridimensional (de 3D a 2D)

Figura 7.3: Proyeccin ortogonal y en perspectiva

7.5.

Implementacin de proyecciones en perspectiva

Antes que nada, veamos ahora algunos detalles geomtricos anteriormente postergados de los tipos de proyecciones estudiados. La gura 7.3 nos muestra la diferencia fundamental entre ambos tipos de proyecciones: En las proyecciones ortogonales las guras se aplastan contra el plano en direccin siempre perpendicular a este (lo que se logra simplemente ignorando las componentes en

despus de haber hecho la transformacin 7.3), mientras que en las proyecciones en

perspectiva, las guras se proyectan en direccin de un foco de proyeccin o centro de proyeccin. Tpicamente el centro de proyeccin est detrs del plano de proyeccin (como si fuera el ojo fsico del observador, en el cual tambin hay un foco de proyeccin) y los objetos a proyectar estn delante del plano (si los objetos estn detrs del foco, el efecto visual es bastante desagradable y confuso). Veamos a continuacin cmo implementar este fenmeno ptico: Utilicemos la gura 7.4 como referencia. En ella se presenta un punto proyectado sobre el plano ser

xy ,

que adems es el plano de proyeccin . El centro de

(x, y, z )

que es

proyeccin est a una distancia

del plano. El valor en

del punto proyectado debe

xper

sobre el plano. Para encontrar ese valor, hacemos un anlisis de tringulos

semejantes:

esto por simplicidad

158

7.5 Implementacin de proyecciones en perspectiva

Figura 7.4: Deduccin de proyeccin en perspectiva

xper d

xper =

x d+z d dx = x d+z d+z

(7.4)

de la misma manera (ntese que

d = 0): = y d+z dx d = y d+z d+z

yper d

yper =
y

(7.5)

zper = 0
Lo que signica que podra aplicarse la transformacin

(7.6)

d d d+z , d+z , 0

para realizar la

proyeccin, posterior a la transformacin 7.2 (recuerde las condiciones descritas al inicio de esta seccin). O sea:

Pper = S

d d d+z , d+z , 0

M2D3D P z
de cada punto a

El problema de este ltimo escalamiento es que depende del valor de

proyectar, por lo que no es viable para un procesamiento automatizado; es decir, no tiene sentido hacer una multiplicacin matricial por cada punto a proyectar. Sera demasiado costoso, por lo que vamos a emplear otra estrategia: 1. Construir la matriz 2. Hacer

M2D3D

(la matriz de transformacin ortogonal)

P = M2D3D P

para todos los puntos

a proyectar (esto equivale a hacer

proyeccin ortogonal)

159

7 Vista Tridimensional (de 3D a 2D)


3. Para cada punto

a proyectar, hacer

Px Py Pz

d Px d + Pz d = Py d + Pz = 0 =

4. Y despus, aplicar el escalamiento y traslacin nal para cada punto:

Pper = MRV P
Recomendamos, a manera de prctica, el uso de la aplicacin cosas que su hermana no ortogonal. Para ejecutar la aplicacin, tambin escrita en lenguaje java:

perspetivQhFjr9

que

se encuentra en el material adjunto a esta obra. Esta aplicacin permite hacer las mismas

trnsformionesQdFjr, pero la proyeccin es en perspectiva y

6 jv Ejr perspetivQhFjr
Con esto se iniciar una ventana grca, idntica a la de la otra aplicacin, y se inicia un intrprete de comandos con las mismas alternativas. Recomendamos que se haga una comparacin entre el archivo la aplicacin

perspetivQhFjr

y el de

ortldeisionFjv de trnsformionesQdFjr, en ellos podemos

ver una implementacin de estas tcnicas de proyeccin:

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 9

Listing 7.4: Fragmento del archivo PortaldeVision.java de transformaciones3d.jar

/* * Recibe el centro del portal de visin en coordenadas * esfricas (d , theta , phi ) */ public void colocarPuntodeVision ( Objeto3DSimple objetos [] , float theta , float phi , float distancia ) { this . theta = theta ; this . phi = phi ; this . distancia = distancia ; matriz . identidad () ; matriz . rotarZ ( - theta ) ; matriz . rotarY (180 - phi ) ; matriz . rotarZ ( -90) ; matriz . trasladar (0 , 0, distancia ) ;
Consltese el captulo A en la pgina 291 para ms detalles sobre la compilacin y compilacin de aplicaciones J2SE.

160

7.5 Implementacin de proyecciones en perspectiva


54 55 56 57 58 59 60 61 62 63 64 65 66 67 68

// la media del ancho y alto real entre // la media del ancho y alto virtual , por // un factor de aumento matriz . escalar (( float )( ( tamanhoR . width + tamanhoR . height ) /( AnchoV + AltoV ) / magnitud_aumento )); // traslamiento final : matriz . trasladar ( tamanhoR . width /2 , tamanhoR . height /2 , 0) ; for ( int i =0; i < objetos . length ; i ++) objetos [ i ]. transformarProyeccion ( matriz , this );

Listing 7.5: Fragmento del archivo PortaldeVision.java de perspectiva3D.jar

45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74

/* * Recibe el punto de visin en coordenadas esfricas ( distancia , theta , phi ) * * Asumimos que dicha distancia representa tanto la * distancia del origen virtual al centro del portal de visin * como a la distancia del centro de proyeccin * al centro del portal de visin . * Esto es por simplicidad en este cdigo * pero no necesariamente debe ser as siempre . */ public void colocarPuntodeVision ( Objeto3DSimple objetos [] , float theta , float phi , float distancia ) { this . theta = theta ; this . phi = phi ; this . distancia = this . distanciaProyeccion = distancia ; matriz . identidad () ; matriz . rotarZ ( - theta ) ; matriz . rotarY (180 - phi ) ; matriz . rotarZ ( -90) ; matriz . trasladar (0 , 0 , distancia ) ; for ( int i =0; i < objetos . length ; i ++) objetos [ i ]. transformarProyeccion ( matriz , this , true ) ; matriz . identidad () ; // la media del ancho y alto real entre // la media del ancho y alto virtual , por // un factor de aumento matriz . escalar (( float )(

161

7 Vista Tridimensional (de 3D a 2D)


75 76 77 78 79 80 81 82 83 84 85

( tamanhoR . width + tamanhoR . height ) /( AnchoV + AltoV ) // / 3 f / distancia )); // traslamiento final : matriz . trasladar ( tamanhoR . width /2 , tamanhoR . height /2 , 0) ; for ( int i =0; i < objetos . length ; i ++) objetos [ i ]. transformarProyeccion2 ( matriz , this ) ;

7.6.

Ejercicios
tiene una distancia ja de una unidad (ver archivo

1. En la aplicacin

trnsformionesQdFjr el portal de visin (es decir, su centro) ortldeisionFjv), lo que

quiere decir que eventualmente los objetos a proyectar se encuentran detrs del Portal de Visin de la aplicacin. Por qu la imgen no se distorciona como sucede con

perspetivQhFjr?

2. Explique qu pasa cuando el centro de proyeccin en perspectiva est muy lejano de los objetos proyectados. 3. Explique geomtricamente (o matemticamente) qu es lo que sucede cuando se realiza la proyeccin en perspectiva de un objeto que se encuentra detrs del centro de proyeccin. 4. Todo el anlisis realizado hasta ahora implica que el portal de visin ve siempre directamente al origen del universo virtual. Reexione cmo hacer para dirigirlo en direciones arbitrarias. Es decir, cmo hacer para que el usuario pueda ver en otras direcciones?

162

8 Interaccin
Este captulo no pretende ser un tratado completo sobre tcnicas de interaccin humanomquina, sino una breve reexin sobre la importancia de hacer aplicaciones grcas interactivas amigables, fciles de usar e intuitivas. La razn por la que es importante que nuestras aplicaciones sean amigables (y el grado de amigabilidad depender de los usuarios a los que est orientada) es porque gran parte del xito en la difusin de una aplicacin, ya sea un juego de video, una aplicacin educativa, de entretenimiento, un simulador de algn fenmeno fsico, social, econmico, etc. depender del hecho que el usuario logre sus objetivos y que no se frustre al intentar aprender a usarlas. El respeto de estndares ociales o de facto, es muy importante en estos das. Ya no estamos en los das en los que se podan denir arbitrariamente paradigmas de interaccin.

8.1.

Posicionamiento

En una aplicacin con varios objetos (en dos o tres dimensiones), el usuario debera poder ser capaz de abrise paso, de algn modo, para lograr posicionarse al alcance de algn objeto especco que quisiera ver o modicar. Como programadores, a veces olvidamos las reacciones lgicas de una persona normal , como por ejemplo, utilizar las teclas cursoras, la de

gin enterior

gin iguiente

ulin

y las teclas

para avanzar en una u otra direccin del espacio

de trabajo de las aplicaciones que hacemos. A veces se nos olvida programar esas teclas y otras combinaciones que son obvias para todo el mundo en estos das. Por ello es importante presentarle la aplicacin a usuarios no familiarizados con el desarrollo de la aplicacin (de preferencia personas normales) para evaluar objetivamente si la aplicacin no tiene un interaccin excesivamente compleja o confusa antes de darla por terminada. En el caso de aplicaciones bidimensionales, el problema de posicionamiento suele resolverse con barras de desplazamiento al rededor del espacio de trabajo. Y en las ocaciones

entindase por persona normal a una persona no informtica

163

8 Interaccin
en que se operan objetos que pueden traslaparse, es conveniente disponer funciones del tipo Enviar al fondo, Enviar atrs, Traer adelante y Traer al frente. En el caso de aplicaciones tridimensionales, el problema de posicionamiento representa comunmente un problema de navegacin espacial. En donde usualmente habr que disear una estrategia de limitar el volumen visible. Usualmente no todo lo que est en el universo virtual debera ser visible por el usuario en cada posicin especca. Este fenmeno puede verse en diversos juegos de video 3D de carreras, en las que el paisaje va apareciendo a medida que el jugador se va desplazando por la pista. Esto por supuesto se debe a dos factores: El primero es que requerira demasiado procesamiento para mostrar todos los detalles lejanos y el segundo es que sera demasiada informacin para que un usuario tpico la procese. Partes importantes del problema de navegacin, son el problema del cambio de direccin y la magnitud del desplazamiento lineal. El primero se reere a cmo cambiar la direccin de movimiento o la orientacin en general del portal de visin, y el segundo se reere a la cantidad de desplazamiento a realizar en una direccin especca o en general en cualquier direccin. En el caso de nuestras aplicaciones de ejemplo de los ltimos captulos, el problema de la navegacin espacial est limitado ya que el portal de visin siempre mira jamente en direccin del origen de coordenadas del universo virtual. Sin embargo, s existe el problema del cambio de direccin, resuelto con arrastre del ratn; y tambin existe el problema de la magnitud de desplazamiento lineal, resuelto con la rueda del ratn. La aplicacin

perspetivQhFjr

tiene un evidente problema con respecto a la lim-

itacin del volumen visible, y es que no limita el volumen visible, por lo que proyecta incluso objetos detrs del centro de proyeccin (lo cual genera una distorcin visual ).

8.2.

Seleccin

8.2.1. Seleccin por puntero


Una vez que el usuario ha logrado posicionarse al alcance del objeto de su inters, debera poder indicar que quiere operar con l y no slo verlo. En estos das, lo ms usual es hacer clic sobre el objeto. Cualquier aplicacin que no siga dicho paradigma, resulta extraa para un usuario nicamente acostumbrado a operaciones visuales. En el caso particular de aplicaciones tridimensionales, la seleccin directa por ratn implica resolver, entre otros, los problemas siguientes:

(a) Averiguar qu objeto est

ms cerca del usuario, es decir, del portal de visin. Esto es porque no debera tener

Recomendamos al lector vericar esto e intentar no marearse ni asustarse en el proceso

;-)

164

8.3 Transformacin
sentido seleccionar objetos que estn detrs del portal de visin, en el caso de proyeccin ortogonal, o detrs del centro de proyeccin, en el caso de proyeccin en perspectiva. Y

(b) Averguar a qu punto del universo virtual, corresponde el punto sobre el que el

usuario hizo

li.

Esto ltimo requiere al menos, de lograr la inversin de todas las

transformaciones geomtricas aplicadas. Ntese que si aplicamos las reglas de la seccin 7.5 para la proyeccin en perspectiva, la transformacin en conjunto se vuelve irreversible (debido a la ecuacin 7.6 en la pgina 159). Una estrategia simple es obviar ejecutar la ecuacin 7.6. Dado que las transformaciones que le siguen slo afectan las otras dos dimensiones, no habr efectos colaterales negativos, y por el contrario, la inversin ser posible (tambin como un proceso de tres faces).

8.2.2. Seleccin por nominacin


Otra manera de seleccionar objetos, es haciendo referencia a ellos por su nombre, seleccionndolos de una lista o escribindolos directamente en una lnea de comandos. Ese es el caso de varias aplicaciones especializadas en simulacin que permiten asignarle nombre a los objetos grcos (y tal vez tambin a los no grcos), como nuestras aplicaciones de estudio de los ltimos captulos. En ellas, la operacin de seleccin de objetos es a travs de lnea de comandos, amarrada a la operacin de transformacin .

8.3.

Transformacin

Las transformaciones sobre objetos bidimensionales y tridimensionales deberan incluir slo las transformaciones que tengan sentido para el propsito de la aplicacin. Estas podran incluir las transformaciones bsicas estudiadas en los captulos 5 y 6 y/u otras transformaciones compuestas, relevantes, de nuevo, para la lgica de la aplicacin. En el caso bidimensional, las transformaciones se logran resolver generalmente con cambios de marco de referencia (ver captulo 4), pero en el caso tridimensional, hay que utilizar estrategias cognitivamente ecaces para que el usuario no se sienta excesivamente perdido. A continuacin mencionamos algunos ejemplos: Para rotar una escena 3D, podra utilizarse la estrategia de asociar el arrastre del ratn en

con un cambio del valor

de la coordenada del centro del portal

de visin, y asociar el arrastre del ratn en

con un cambio del valor

de la

La aplicacin es as porque su propsito es practicar las tranformaciones geomtricas en trminos de multiplicaciones matriciales y no en trminos grcos.

165

8 Interaccin
coordenada del centro del portal. Esta tcnica es usada en las aplicaciones usadas en los ltimos captulos. Para realizar acercamiento o alejamiento en una escena 3D, puede usarse la ya tradicional rueda del ratn, tal vez en combinacin con la tecla

trl

shift.

Para el desplazamiento de objetos o de toda una escena 3D, o del origen del marco de referencia podra usarse arrastre del ratn combinado con alguna tecla como

trl

shift.

8.4.

Conclusin

Evidentemente, existen diversas (y tal vez ilimitadas) formas de interaccin entre los usuarios y las aplicaciones grcas interactivas, por lo que, lo que nalmente podemos decir a manera de recomendacin general, es seguir el principio que la magnitud de las acciones del usuario sea proporcional a la magnitud de las transformaciones que estas acciones realizan. Obviamente esta proporcin tambin debe ser ergonmica , apropiada para la naturaleza humana. A manera de ejemplo de esto ltimo, recomendamos examinar el archivo

gonfigQhFjv

de las aplicaciones usadas en los ltimos captulos. En ellos aparecen varias constantes que controlan el funcionamiento de las aplicaciones. Estos valores fueron denidos debido a que proporcionan un comportamiento ms cmodo que otros valores. En particular las constantes que controlan la relacin entre pixeles arrastrados con el ratn y el giro del universo virtual son:

41 42 43 44 45 46 47

Listing 8.1: Fragmento de cdigo que especica constantes ergonmicas

// para poder rotar con el mouse : int numPixelesparaMovimiento = 1; float factorRapidezGiro = 4 f ; // aumento del zoom por movimiento de la rueda del ratn : static final float MAGNITUD_AUMENTO = 0.1 f ; static final float MAGNITUD_AUMENTO_INICIAL = 5 f ;

En otras combinaciones, no resulta fcil manipular la aplicacin.

166

9 Curvas Paramtricas
9.1. Representacin algebrica de Curvas Paramtricas
Las curvas paramtricas suelen representarse de la siguiente manera:

r(t) = f (t) i + g (t) j r(t) = x(t) i + y (t) j

r(t) = (x(t), y (t))


de esta otra:

x = f (t) y = g (t)

restricciones

Obviamente, en cada caso se debern especicar las funciones paramtricas (f y y

g,

segn la notacin).

Veamos algunos ejemplos de curvas paramtricas: Una circunferencia con radio

se representa as:

r() = R cos i + R sin j, 0 2


al

Un segmento de lnea recta, desde el punto

(x1 , y1 )

(x2 , y2 )

as:

r(t) = ((1 t)x1 + tx2 ) i + ((1 t)y1 + ty2 ) j, 0 t 1


Una elipse con radios

en

en

y:

r() = a cos i + b sin j, 0 2


En la gura 9.1 en la pgina siguiente se presentan una circunferencia con segmento de lnea recta con

(x1 , y1 ) = (2, 1)

(x2 , y2 ) = (4, 5)

y una elipse con

R = 5, un a=2y

b = 3.

167

9 Curvas Paramtricas

Figura 9.1: Ejemplo de curvas paramtricas planas

168

9.2 Continuidad

Figura 9.2: Resorte en alrededor del eje

y,

generado con Octave

Este es un resorte innito con radio

R alrededor del x = R cos z = R sin y=

eje

y:

En la gura 9.2 se presenta un resorte con

R = 3 y 0 8 . Se realiz con el siguiente

1 2 3 4 5 6

scritp de Octave:

theta = [0: pi /100:8* pi ]; R = 3; x = R * cos ( theta ) ; z = R * sin ( theta ) ; y = theta ; plot3 (x ,y , z )

9.2.

Continuidad

9.2.1. Curvas suaves


Concepto de

curva continua segn [Henrquez 2001, p. 78]:

169

9 Curvas Paramtricas

Se dice que una funcin es continua en un nmero

si

x c

l m f (x) = f (c)

(Lo que a su vez se cumple cuando el lmite por la izquierda y por la derecha son iguales).

Concepto de

curva suave:

f (x)

es una curva suave en

[a, b]

si

f (x)

es continua en todos los puntos de

[a, b].

Curva paramtrica suave [Henrquez 2001, p. 229]:

en

r(t) es una ]a, b[.

curva suave en

[a, b]

si

r (t)

es continua en todos los puntos de

[a, b]

r =0

9.2.2. Tipos de continuidad


A continuacin presentamos un conjunto de conceptos importantes de referencia para conceptos posteriores, adaptados de [Foley et al., p. 374]:

Continuidad Geomtrica
Se dice que hay continuidad geomtrica de grado 0, denotada por curvas suaves

G0 ,

entre dos

S1

S2

en el punto

con

t = ,

si

S1 ( ) = S2 ( ).

Es decir, si las

curvas se unen en

p. G1 ,
entre dos

Se dice que hay continuidad geomtrica de grado 1, denotada por curvas suaves

S1

S2 p

en el punto

p con t =

, si hay continuidad G0 (en ese mismo S1 ( ) = k S2 ( ), k > 0. Es decir, si las S1


y

punto y con el mismo valor de parmetro) y curvas se unen en Ntese que

y los vectores tangentes tienen la misma direccin y sentido.

G1

no implica que la curva formada por la unin de

S2

sea suave.

Continuidad Paramtrica
Se dice que hay continuidad paramtrica de grado 1 (o de primer grado), denotada por

C 1 , entre dos curvas S1

S2

en el punto

p con t = , si hay continuidad G0

(en

170

9.2 Continuidad

Figura 9.3: Interpolacin

ese mismo punto y con el mismo valor de parmetro) y si las curvas se unen en

S1 ( ) = S2 ( ). G1
con

Es decir,

y los vectores tangentes son iguales. Tambin se puede

denir que hay continuidad Ntese que Ntese que su trayecto.

C1 C 1.

cuando hay continuidad

k = 1.

C 1 G1 , C1

pero

G1

implica que la curva formada por la unin de

S1

S2

es suave en todo

Se dice que hay continuidad paramtrica de grado n, denotada por curvas

C n,

entre dos

S1

S2

en el punto

con

t=

, si hay continuidad C n1 , en ese mismo (n) (n) S1 ( ) = S2 ( ).

punto y con el mismo valor de parmetro, y si Ntese que si los vectores

S1

S2

representan (como funcin seccionada) la posicin de

un mismo objeto en funcin del tiempo,

C1

en su punto de unin, garantiza movimiento

2 suave y velocidad continua, y C all mismo, garantiza velocidad suave y aceleracin


continua. Esto es muy deseable para representar desplazamientos sin irregularidades.

9.2.3. Interpolacin
Interpolacin segn [RAE]: Calcular el valor aproximado de una magnitud en un intervalo, cuando se conocen algunos de los valores que toma a uno intervalo.

y otro lado de dicho

171

9 Curvas Paramtricas
Veamos la gura 9.3. En ella, el intervalo mencionado en la denicin es que s son conocidos.

[a, b]. Se conocen

los puntos negros. Y el punto griz es un valor aproximado a partir de los puntos negros

9.3.

Trazadores interpolantes cbicos - Curvas

Spline

Se le conoce como Trazadores Interpolantes Cbicos a un conjunto de segmentos de curva polinomial de tercer grado que unen una serie de puntos en el espacio desconocidos al interior de dicha serie de puntos. Existen diversas maneras de construir trazadores interpolantes cbicos, cada una con sus peculiaridades matemticas y geomtricas. En este libro nos concentraremos en un tipo particular:

comunmente

llamados nodos entre s; y que adems, pueden ser utilizados para interpolar puntos

Las curvas Spline con frontera natural.

9.3.1. Curvas Spline y B-Spline


Con frecuencia, aparece en la literatura el trmino B-spline. Estas y las curvas Spline no son las mismas. Ambas consisten de segmentos de curvas polinomiales cbicas, pero la diferencia radica en dos detalles:

(1) Las curvas Spline interpolan todos los puntos (2) Todos los segmentos de las curvas Spline

de control o nodos y las B-spline no.

dependen simultaneamente de todos los puntos de control, mientras que los segmentos de las B-spline dependen slo de los puntos de control ms cercanos. Una tercera diferencia las ltimas.

incidental si se quiere

es que

(3) las B-spline, se calculan

mucho ms rpido que las Spline, debido a la alta complejidad del algoritmo para calcular

9.3.2. Descripcin geomtrica


El trmino Spline proviene de las largas tiras exibles de metal que usaban los constructores para establecer las supercies de aeroplanos, automviles y barcos. A estas tiras se le colocaban pesos o prensas que servan para jalarlas en determinadas direcciones para forzar la forma que adoptaran las tiras durante su vida til. Y a menos que se sometan a grandes tensiones, estas tiras mantienen continuidad las prensas. En la actualidad, los dibujantes y arquitectos usan tiras semirgidas de plstico para dibujar curvas suaves, necesitando a veces, que interpolen ciertos puntos preestablecidos. Es el mismo principio que con los fuselajes.

C2

una vez liberadas de

172

9.3 Trazadores interpolantes cbicos - Curvas

Spline

9.3.3. Descripcin matemtica


Denicin (adaptado de [Burden y Faires 2002]): Dada una funcin y un conjunto de nodos para 1.

denida en

a = x0 < x1 < . . . < xn = b,

un trazador interpolante cbico

[a, b] S

es una funcin que cumple con las condiciones siguientes: es una funcin seccionada, y en todos los casos, es un polinomio cbico,

S (x)

denotado por 2. 3. 4. 5.

Sj (x),

en el subintervalo

[xj , xj +1 ]

para cada

j = 0, 1, . . . , n 1;

S (xj ) = f (xj )

para cada

j = 0, 1, . . . , n; j = 0, 1, . . . , n 2; j = 0, 1, . . . , n 2; j = 0, 1, . . . , n 2;

Sj +1 (xj +1 ) = Sj (xj +1 ) Sj +1 (xj +1 ) = Sj (xj +1 ) Sj +1 (xj +1 ) = Sj (xj +1 )


a) b)

para cada para cada para cada

6. Una de las siguientes condiciones de frontera se satisface:

S (x0 ) = S (xn ) = 0 (frontera libre o natural); S (x0 ) = f (x0 ) y S (xn ) = f (xn ) (frontera sujeta). S
tiene la siguiente forma:

El trazador

S (x) =

S0 (x) S1 (x)

x0 x < x1 x1 x < x2

. . . . . . Sn1 (x) xn1 x xn para cada

donde Est

Sj (x) = aj + bj (x xj ) + cj (x xj )2 + dj (x xj )3 claro que Sj (xj ) = aj = f (xj ). x

j = 0, 1, . . . , n 1.

Hay varias cosas que hay que notar: Para un valor de garantizan continuidad los nodos.

concreto, se opera con el nodo

ms cercano por la izquierda. Por otro lado, es de recalcar que las condiciones 3, 4 y 5

C 2.

Adems, la condicin 2 implica que la curva interpola todos

9.3.4. Algoritmo de clculo de coecientes


Para construir el trazador interpolante cbico natural en los nmeros 146]):

x0 < x 1 < . . . < x n

y que satisface

S de la funcin f , que se dene S (x0 ) = S (xn ) = 0 se procede de

la siguiente manera (algoritmo adaptado del algoritmo 3.4 de [Burden y Faires 2002, p.

Valores de entrada:

173

9 Curvas Paramtricas

n; x0 , x1 , . . . , xn ; a0 = f (x0 ), a1 = f (x1 ), . . . , an = f (xn )


Algoritmo:
1. INICIO 2. PARA

i = 0, 1, ..., n 1

a)

hi = xi+1 xi i = 1, 2, ..., n 1
3 hi

3. PARA

a)

i =
1

(ai+1 ai )

3 hi1

(ai ai1 )

4. HACER :

l0 = 1 0 = 0 z0 = 0
5. PARA

i = 1, 2, ..., n 1

a ) HACER:

li = 2 (xi+1 xi1 ) hi1 i1 i i = h li i hi1 zi1 zi = li


6. HACER:

cn = 0
7. PARA

j = n 1, n 2, ..., 0

a ) HACER:

cj = zj j cj +1 a aj h (c +2cj ) bj = j +1 j j +1 hj 3 dj =
8. FIN

cj +1 cj 3hj

Valores de salida:

aj , bj , cj , dj
1

para

j = 0, 1, ..., n 1

Los pasos 4, 5, 6 y parte del 7 resuelven un sistema lineal tridiagonal descrito en el algoritmo 6.7 de [Burden y Faires 2002, p. 408]

174

9.3 Trazadores interpolantes cbicos - Curvas

Spline

9.3.5. Algoritmos de clculo de puntos interpolados

Clculo de un slo punto2


Este algoritmo recibe un valor de la variable independiente y calcula su imagen aproximada utilizando los trazadores. Valores de entrada: 1. INICIO 2. SI

x; n; x0 , x1 , . . . , xn

aj , bj , cj , dj

para

j = 0, 1, . . . , n 1

x x0 j=0 (j < n) (x > xj +1 )


1)

a)

b ) MIENTRAS

j =j+1

c ) SI

j<n y = aj + bj (x xj ) + cj (x xj )2 + dj (x xj )3
o bien con la regla de Horner:

1) HACER:

y = aj + (x xj ) (bj + (x xj ) (cj + (x xj )dj ))


2) FIN 3. ERROR: El valor de 4. FIN Valores de salida:

x,

est fuera del dominio de

S .

para obtener

(x, y ) S

ERROR!.

Clculo de todos los puntos


Este algoritmo recibe los trazadores y un valor espaciados, en

que indica el nmero de bloques in-

terpolados que se generarn (es decir que se calcularn

p+1

puntos uniformemente

[x0 , xn ]). p; n; x0 , x1 , . . . , xn
y

Valores de entrada: 1. INICIO 2. HACER: 3. PARA

aj , bj , cj , dj

para

j = 0, 1, . . . , n 1

i=1

k = 0, 1, . . . , p

a ) HACER: b)
2

xk = x0 + k p (xn x0 ) MIENTRAS (xk > xi )

Este algoritmo implementa bsqueda lineal, pero podra ser ms eciente con bsqueda binaria.

175

9 Curvas Paramtricas
1) HACER:

c ) HACER d ) HACER:

i=i+1 i=i1

yk = ai + bi (xk xi ) + ci (xk xi )2 + di (xk xi )3


o bien con la regla de Horner:

yk = ai + (xk xi ) (bi + (xk xi ) (ci + (xk xi )di ))


4. FIN Valores de salida:

x0 , x1 , . . . , xp

y0 , y1 , . . . , yp .

9.3.6. Extensin para curvas paramtricas multidimensionales

El caso bidimensional
Para conectar los puntos

(x0, y0 ) , (x1, y1 ) , . . . , (xn , yn ), en el orden dado, an cuando no formen una funcin, se usa una nueva variable t en un intervalo [t0, tn ] (tpicamente [t0 = 0, tn = 1]) con t0 < t1 < . . . < tn (la distribucin puede ser uniforme o no).
Se recomponen los pares ordenados dados como si fueran dos problemas diferentes, as:

(t0, x0 ), (t1, x1 ), ..., (tn , xn ) (t0, y0 ), (t1, y1 ), ..., (tn , yn )


Se construyen entonces dos trazadores interpolantes cbicos, As que los valores que interpolan los puntos originales son:

x = S (x) (t)

y = S (y) (t).

(x, y ) =

(x) (y ) S0 (t), S0 (t) S (x) (t), S (y) (t) 1 1


. . .

t0 t < t1 t1 t < t2
. . .

S (x) (t), S (y) (t) n1 n1


donde

tn1 t tn
(x)
y

Sj (t) = aj + bj (t tj ) + cj (t tj )2 + dj (t tj )3
(y ) (y ) (y ) (y )

(x)

( x)

(x)

(x)

Sj (t) = aj + bj (t tj ) + cj (t tj )2 + dj (t tj )3

(y )

para cada

j = 0, 1, ..., n 1.

El caso tridimensional
Para conectar los puntos una nueva variable

en un intervalo

(x0, y0 , z0 ) , (x1, y1 , z1 ) , . . . , (xn , yn , zn ), en el orden dado, se usa [t0, tn ] (de nuevo, tpicamente [t0 = 0, tn = 1]) con

t0 < t1 < ... < tn .

176

9.3 Trazadores interpolantes cbicos - Curvas

Spline

Se recomponen los pares ordenados dados como si fueran tres problemas diferentes, as:

(t0, x0 ), (t1, x1 ), ..., (tn , xn ) (t0, y0 ), (t1, y1 ), ..., (tn , yn ) (t0, z0 ), (t1, z1 ), ..., (tn , zn )
Se construyen entonces tres trazadores interpolantes cbicos,

x = S (x) (t), y = S (y) (t)

z=

S (z ) (t). As que los valores que interpolan los puntos originales son: (x) (y ) (z ) S0 (t), S0 (t), S0 (t) S (x) (t), S (y) (t), S (z ) (t) 1 1 1
. . .

t0 t < t1 t1 t < t2
. . .

(x, y, z ) =

S (x) (t), S (y) (t), S (z ) (t) n1 n1 n1


donde

tn1 t tn

Sj (t) = aj + bj (t tj ) + cj (t tj )2 + dj (t tj )3 ,
(y ) (y ) (y ) (y )
y para cada

(x)

(x)

(x)

(x)

(x)

Sj (t) = aj + bj (t tj ) + cj (t tj )2 + dj (t tj )3 Sj (t) = aj + bj (t tj ) + cj (t tj )2 + dj (t tj )3
(z ) (z ) (z ) (z ) (z )

(y )

j = 0, 1, ..., n 1.

Eciencia en el clculo de un slo punto


Los algoritmos usados para construir y calcular estos trazadores interpolantes cbicos paramtricos no deben, por razones de eciencia, realizarse de forma completamente independiente. En lugar de eso, se debe procurar aprovechar los clculos para maximizar la eciencia de tales procesos. Como ejemplo de ello, considere la siguiente versin del algorito de clculo de un punto interpolado, para el caso paramtrico con dos dimensiones: Valores de entrada:

t; n; t0 , t1 , . . . , tn ; aj , bj , cj , dj ;

(x)

(x)

(x)

(x)

aj , bj , cj , dj

(y )

(y )

(y )

(y )

para

j=

0, 1, ..., n 1
1. INICIO 2. SI

t t0 j=0 (j < n) (t > tj +1 )


1)

a)

b ) MIENTRAS

j =j+1

c ) SI

j<n

177

9 Curvas Paramtricas
1) HACER:

x = aj + bj (t tj ) + cj (t tj )2 + dj (t tj )3 y = aj + bj (t tj ) + cj (t tj )2 + dj (t tj )3
2) FIN 3. ERROR: El valor de 4. FIN Valores de salida:

(x)

(x) (y )

( x)

(x)

(y )

(y )

(y )

t,

est fuera del dominio de

S .

x; y ,

para obtener

(x, y ) S

ERROR!.

Eciencia en el clculo todos los puntos


As mismo, se muestra el algoritmo que calcula en

p+1
(x)

puntos uniformemente espaciados,

[t0 , tn ]): p; n; t0 , t1 , . . . , tn ; aj , bj , cj , dj
(x) (x) (x)
y

Valores de entrada:

aj , bj , cj , dj

(y )

(y )

(y )

(y )

para

j =

0, 1, . . . , n 1
1. INICIO 2. HACER: 3. PARA

i=1 = t0 + k p (tn t0 )

k = 0, 1, . . . , p

a ) HACER: tk

b ) MIENTRAS

c)

tk > ti 1) HACER: i = i + 1 HACER i = i 1

d ) HACER: (x) (x) (x) (x) xk = ai + bi (tk ti ) + ci (tk ti )2 + di (tk ti )3 (y ) (y ) (y ) (y ) yk = ai + bi (tk ti ) + ci (tk ti )2 + di (tk ti )3
4. FIN Valores de salida:

x0 , x1 , . . . , xp

y0 , y1 , . . . , yp .

9.3.7. Ejemplo de implementacin


A continucacin presentamos un sencillo programa en lenguaje c estndar que implementa la manipulacin de trazadores interpolantes cbicos paramtricos de dos dimensiones. La aplicacin simplemente muestra una pantalla con una serie de nodos conectados por una curva spline y permite mover los puntos (los nodos) al tiempo que la curva se adapta a la forma que los puntos exigen.

178

9.3 Trazadores interpolantes cbicos - Curvas

Spline

En los siguientes archivos se especican la estructura de datos usada y las funciones que la operarn (ntese que es de propsito general).

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45

Listing 9.1: Archivo de cabecera de funciones de Trazadores

// / c09 / trazadores / tic . h # include < stdio .h > # define # define # define # define # define # define # define # define # define # define # define # define # define /* TIC_COD_EXITO 0 TIC_MSG_EXITO " xito \ n" TIC_COD_ERROR_PARAMETROS -1 TIC_MSG_ERROR_PARAMETROS " Error de parmetro ( s ) \ n " TIC_COD_ERROR_DOMINIO -2 TIC_MSG_ERROR_DOMINIO " Error de dominio \ n " TIC_COD_ERROR_MEMORIA -3 TIC_MSG_ERROR_MEMORIA " Error de solicitud de memoria dinmica \ n " H ALFA L MIU Z 0 1 2 3 4

Contiene toda la informacin para calcular los trazadores . El clculo de los trazadores es el siguiente : S ( x ) = S[ j ]( x ) = a [ j ] + b[ j ]*( x - x [ j ]) + c [ j ]*( x - x [ j ]) ^2 + d [j ]*( x - x [ j ]) ^3 = a [ j ] + (x - x [ j ]) * ( b [ j] + (x - x [ j ]) * ( c [ j ] + (x - x [j ]) * d [ j ] ) ) para x [ j ] <= x <= x [ j +1] ( ntese que los clculos se hacen con el nodo ms cercano a 'x ' por la izquierda ) Se asume que : x [0] < x [1] < ... < x[ num_trazadores ]

*/ typedef struct trazadores {

// coordenadas de los nodos double * x ; // las imgenes 'y ' estn en el arreglo 'a ' double * y ; // coeficientes de los polinomios double * a ; double * b ; double * c ; double * d ;

179

9 Curvas Paramtricas
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95

// nmero de polinomios int num_trazadores ; // siempre debe cumplirse la siguiente relacin : // num_nodos = num_trazadores + 1 } trazadores ; /* Entradas : ( ' x ','y ') define los nodos ' num_nodos ' indica el nmero de nodos Salida : Se devuelve un puntero a la estructura creada o NULL si hubo error . */ trazadores * TIC_crear_trazadores ( double x [] , double y [] , int num_nodos ) ; /* Toma el valor 'x ' y calcula su imagen en funcin del trazador que le corresponda y la deposita en 'y ' */ int TIC_calcular ( trazadores * tr , double x , double * y ) ; /* Entradas : ( ' x ','y ') define los nodos Salida : Se devuelve un codigo de resultado . Se asume que los arreglos son de la misma longitud entre s y con el valor ' num_nodos ' usado para crear a ' tr ' */ int TIC_modificar_trazador ( trazadores * tr , double x [] , double y []) ; /*

Libera la memoria dinmicamente solicitada de la estructura */ void TIC_liberar_memoria ( trazadores * tr ) ; /* Imprime en la salida especificada los valores almacenados en la estructura . */ void TIC_imprimir ( trazadores * tr , FILE * f ) ; /* ************** Paramtricos 2 D **************************** */

180

9.3 Trazadores interpolantes cbicos - Curvas


96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144

Spline

typedef struct trazadoresP { // coordenadas de los nodos double * t ; // las imgenes 'x ' estn en el arreglo ' ax ' double * x ; // coeficientes de los polinomios en x double * ax ; double * bx ; double * cx ; double * dx ; // las imgenes 'y ' estn en el arreglo ' ay ' double * y ; // coeficientes de los polinomios en y double * ay ; double * by ; double * cy ; double * dy ; // nmero de polinomios int num_trazadores ; // la siguiente relacin siempre debe cumplirse : // num_nodos = num_trazadores + 1 } trazadoresP ; trazadoresP * TIC_crear_trazadoresP ( double x [] , double y [] , int num_nodos ) ; /* Toma el valor 't ' y calcula su imagen en funcin del trazador que le corresponda y la deposita en ( ' x ','y ') */ int TIC_calcularP ( trazadoresP * tr , double t , double *x , double * y ) ; /* Se asume que los arreglos son de la misma longitud entre s y con el valor ' num_nodos ' usado para crear ' tr ' */ int TIC_modificar_trazadorP ( trazadoresP * tr , double x [] , double y []) ; /* Cuando se han actualizado los valores de los nodos pero no se han agregado o quitado . Se recalculan los coeficientes .

181

9 Curvas Paramtricas
145 146 147 148 149 150

*/ int TIC_actualizar_trazadorP ( trazadoresP * tr ) ; void TIC_liberar_memoriaP ( trazadoresP * tr ) ;

void TIC_imprimirP ( trazadoresP * tr , FILE * f ) ;

Listing 9.2: Cdigo fuente de funciones de trazadores

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

// / c09 / trazadores / tic . c # include " tic . h " # include < stdio .h > # include < stdlib .h > trazadores * TIC_crear_trazadores ( double x [] , double y [] , int num_nodos ) { trazadores * tr = NULL ; int i; if (( tr = ( trazadores *) malloc ( sizeof ( trazadores ) ) ) == NULL ) { fprintf ( stderr , TIC_MSG_ERROR_MEMORIA ) ; return NULL ; } tr - > x = tr - > y = tr - > a = tr - > b = tr - > c = tr - > d = NULL ; /* 'x ', 'y ' y 'a ' requieren ' num_nodos ' elementos , 'b ', 'c ' y 'd ' slo ' num_nodos -1= num_trazadores ', pero si 'c ' tiene ' num_nodos ', se simplifica la parte final del algoritmo */ if (( tr - > x = ( double *) malloc (5 * num_nodos * sizeof ( double ) ) ) == NULL ) { fprintf ( stderr , TIC_MSG_ERROR_MEMORIA ) ; TIC_liberar_memoria ( tr ) ; free ( tr ) ; return NULL ; } for ( i =0; i < 5 * num_nodos ; i ++) tr - > x [ i ]=0.0; tr - > a = tr - > y tr - > x tr - > b = tr - > a tr - > c = tr - > b tr - > d = tr - > c = + + + + num_nodos ; num_nodos ; num_nodos ; num_nodos ;

tr - > num_trazadores = num_nodos - 1; if (! TIC_modificar_trazador ( tr , x , y ) ) return tr ;

182

9.3 Trazadores interpolantes cbicos - Curvas


41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88

Spline

else { TIC_liberar_memoria ( tr ) ; free ( tr ) ; return NULL ; }

int TIC_modificar_trazador ( trazadores * tr , double x [] , double y []) { int i ; int n ; double * tmp [5]; // Verificar integridad de los parmetros if ( tr == NULL || tr - > a == NULL || tr - > b == NULL || tr - > c == NULL || tr - > d == NULL || x == NULL || y == NULL || tr - > num_trazadores < 1) { fprintf ( stderr , TIC_MSG_ERROR_PARAMETROS ) ; return TIC_COD_ERROR_PARAMETROS ; } // Gestionar memoria temporal para el algoritmo if ( ( tmp [0] = ( double *) malloc (5 * tr - > num_trazadores * sizeof ( double ) )) == NULL ) { fprintf ( stderr , TIC_MSG_ERROR_MEMORIA ) ; return TIC_COD_ERROR_MEMORIA ; } for ( i =1; i <5; i ++) tmp [ i ] = tmp [0] + i * tr - > num_trazadores ; // Clculo de los trazadores n = tr - > num_trazadores ; for ( i =0; i <= n ; i ++) { tr - > x [ i ] = x [ i ]; tr - > a [ i ] = y [ i ]; // Copiar los valores } // paso 2... for ( i =0; i < n ; i ++) tmp [ H ][ i ] = tr - > x [ i +1] - tr - > x [ i ]; // paso 3... for ( i =1; i < n ; i ++) tmp [ ALFA ][ i ] = 3 * ( ( tr - > a [ i +1] - tr - > a [ i ]) / tmp [ H ][ i ] ( tr - > a [ i ] - tr - > a [i -1]) / tmp [ H ][ i -1]

183

9 Curvas Paramtricas
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136

); // paso 4... tmp [ L ][0] = 1; tmp [ MIU ][0] = 0; tmp [ Z ][0] = 0; // paso 5... for ( i =1; i < n ; i ++) { tmp [ L ][ i ] = 2 * ( tr -> x [ i +1] - tr - > x [i -1]) - tmp [ H ][ i -1]* tmp [ MIU ][ i -1]; tmp [ MIU ][ i ] = tmp [ H ][ i ] / tmp [ L ][ i ]; tmp [ Z ][ i ] = ( tmp [ ALFA ][ i] - tmp [ H ][ i -1]* tmp [ Z ][ i -1]) / tmp [L ][ i ]; } // paso 6... tr - > c [ n ] = 0; // paso 7... for ( i =n -1; i >=0; i - -) { tr - > c [ i ] = tmp [ Z ][ i ] - tmp [ MIU ][ i ] * tr - > c[ i +1]; tr - > b [ i ] = ( tr - > a [ i +1] - tr - > a [ i ]) / tmp [ H ][ i ] - tmp [ H ][ i ] * (tr - > c [ i +1] + 2* tr -> c [ i ]) / 3; tr - > d [ i ] = ( tr - > c [ i +1] - tr - > c [ i ]) / (3* tmp [ H ][ i ]) ; } // Liberar memoria temporal free ( tmp [0]) ; } return TIC_COD_EXITO ;

int TIC_calcular ( trazadores * tr , double x , double * y ) { if ( tr == NULL || y == NULL ) { fprintf ( stderr , TIC_MSG_ERROR_PARAMETROS ) ; return TIC_COD_ERROR_PARAMETROS ; } if ( x >= tr - > x [0]) { /* int j =0; while ( j < tr - > num_trazadores && x > tr - > x [j +1]) j ++; if ( j < tr - > num_trazadores ) { double tmpX = x - tr - > x [ j ]; * y = tr - > a [ j ] + tmpX * ( tr - > b [ j ] + tmpX * ( tr - > c[ j ] + tmpX * tr - > d [ j ] ) ) ; return TIC_COD_EXITO ; } */

184

9.3 Trazadores interpolantes cbicos - Curvas


137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184

Spline

// Esta alternativa es ms eficiente que la de arriba : int j =1; while ( j <= tr - > num_trazadores && x > tr - > x [j ]) j ++; if ( - - j < tr - > num_trazadores ) { double tmpX = x - tr - > x [ j ]; * y = tr -> a [ j ] + tmpX * ( tr - > b [ j ] + tmpX * ( tr - > c [ j] + tmpX * tr - > d [ j] ) ) ; return TIC_COD_EXITO ; }

// fprintf ( stderr , TIC_MSG_ERROR_DOMINIO ) ; return TIC_COD_ERROR_DOMINIO ; } void TIC_liberar_memoria ( trazadores * tr ){ free ( tr - > x ) ; } void TIC_imprimir ( trazadores * tr , FILE * f ) { int i ; fprintf (f , " \ nTrazadores :\ n ") ; fprintf (f , " i \ tx_i \ ty_i \ ta_i \ tb_i \ tc_i \ td_i \ n " ) ; for ( i =0; i <= tr - > num_trazadores ; i ++) fprintf (f , " %d \ t %3.2 g \ t %3.2 g\ t %3.2 g \ t %3.2 g \ t %3.2 g \t %3.2 g \ n " , i , tr - > x [ i ] , tr - >y [ i ] , tr - > a [ i ] , tr - > b [ i ] , tr - > c [ i ] , tr - > d [ i ]) ; } /* *** Paramtricos ******************** */ trazadoresP * TIC_crear_trazadoresP ( double x [] , double y [] , int num_nodos ) { trazadoresP * tr = NULL ; int i ; if (( tr = ( trazadoresP *) malloc ( sizeof ( trazadoresP ) ) ) == NULL ) { fprintf ( stderr , TIC_MSG_ERROR_MEMORIA ) ; return NULL ; } tr - > x = tr - > y = tr - > t = tr - > ax = tr - > bx = tr -> cx = tr - > dx = tr - > ay = tr - > by = tr -> cy = tr - > dy = NULL ; /* 'x ', 'y ' y 'a ' requieren ' num_nodos ' elementos , 'b ', 'c ' y 'd ' slo ' num_nodos -1= num_trazadores ',

185

9 Curvas Paramtricas
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233

pero si 'c ' tiene ' num_nodos ', se simplifica la parte final del algoritmo , por lo que todos tendrn el mismo tamao de ' num_nodos '. */ if (( tr - > t = ( double *) malloc (9 * num_nodos * sizeof ( double ) ) ) == NULL ) { fprintf ( stderr , TIC_MSG_ERROR_MEMORIA ) ; free ( tr ) ; return NULL ; } for ( i =0; i < 9 * num_nodos ; i ++) tr - > t [ i ]=0.0; tr - > ax = tr - > x = tr - > t + num_nodos ; tr - > bx = tr - > ax + num_nodos ; tr - > cx = tr - > bx + num_nodos ; tr - > dx = tr - > cx + num_nodos ; tr - > ay = tr - > y = tr - > dx + tr - > by = tr - > ay + tr - > cy = tr - > by + tr - > dy = tr - > cy + num_nodos ; num_nodos ; num_nodos ; num_nodos ;

tr - > num_trazadores = num_nodos - 1; if (! TIC_modificar_trazadorP ( tr , x , y ) ) return tr ; else { TIC_liberar_memoriaP ( tr ) ; free ( tr ) ; return NULL ; }

int TIC_calcularP ( trazadoresP * tr , double t , double *x , double * y ) { if ( tr == NULL || x == NULL || y == NULL ) { fprintf ( stderr , TIC_MSG_ERROR_PARAMETROS ) ; return TIC_COD_ERROR_PARAMETROS ; } if ( t >= tr - > t [0]) { int j =1; while ( j <= tr - > num_trazadores && t > tr - > t[ j ]) j ++; if ( - - j < tr - > num_trazadores ) { double tmpT = t - tr - > t [ j ];

186

9.3 Trazadores interpolantes cbicos - Curvas


234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278

Spline

* x = tr -> ax [ j ] + tmpT tmpT * tr - > dx [ j ] ) * y = tr -> ay [ j ] + tmpT tmpT * tr - > dy [ j ] ) return TIC_COD_EXITO ;

* ( tr - > bx [ j ] + tmpT * ( tr - > cx [ j ] + ); * ( tr - > by [ j ] + tmpT * ( tr - > cy [ j ] + );

// fprintf ( stderr , TIC_MSG_ERROR_DOMINIO ) ; return TIC_COD_ERROR_DOMINIO ; } int TIC_actualizar_trazadorP ( trazadoresP * tr ){ return TIC_modificar_trazadorP ( tr , tr - >ax , tr - > ay ) ; } int TIC_modificar_trazadorP ( trazadoresP * tr , double x [] , double y []) { int i ; int n ; double * tmp [5]; // Verificar integridad de los parmetros if ( tr == NULL || tr - > ax == NULL || tr - > bx == NULL || tr -> cx == NULL || tr - > dx == NULL || tr - > ay == NULL || tr - > by == NULL || tr -> cy == NULL || tr - > dy == NULL || x == NULL || y == NULL || tr - > num_trazadores < 1) { fprintf ( stderr , TIC_MSG_ERROR_PARAMETROS ) ; return TIC_COD_ERROR_PARAMETROS ; } // Gestionar memoria temporal para el algoritmo if ( ( tmp [0] = ( double *) malloc (5 * tr - > num_trazadores * sizeof ( double ) )) == NULL ) { fprintf ( stderr , TIC_MSG_ERROR_MEMORIA ) ; return TIC_COD_ERROR_MEMORIA ; } for ( i =1; i <5; i ++) tmp [ i ] = tmp [0] + i * tr - > num_trazadores ; // Clculo de los trazadores n = tr - > num_trazadores ;

187

9 Curvas Paramtricas
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327

// Distribuir uniformemente el parametro 't ' // y asignar los coeficientes 'a ' for ( i =0; i <= n ; i ++) { tr - > t [ i ] = i / ( float ) n ; tr - > ax [ i ] = x [ i ]; tr - > ay [ i ] = y [ i ]; } // paso 2... for ( i =0; i < n ; i ++) tmp [ H ][ i ] = tr - > t [ i +1] - tr - > t [ i ]; /* *** en x **************** */ // paso 3... for ( i =1; i < n ; i ++) tmp [ ALFA ][ i ] = 3 * ( ( tr - > ax [ i +1] - tr - > ax [ i ]) / tmp [ H ][ i ] ( tr - > ax [ i ] - tr - > ax [i -1]) / tmp [ H ][ i -1] ); // paso 4... tmp [ L ][0] = 1; tmp [ MIU ][0] = 0; tmp [ Z ][0] = 0; // paso 5... for ( i =1; i < n ; i ++) { tmp [ L ][ i ] = 2 * ( tr -> t [ i +1] - tr - > t [i -1]) - tmp [ H ][ i -1]* tmp [ MIU ][ i -1]; tmp [ MIU ][ i ] = tmp [ H ][ i ] / tmp [ L ][ i ]; tmp [ Z ][ i ] = ( tmp [ ALFA ][ i] - tmp [ H ][ i -1]* tmp [ Z ][ i -1]) / tmp [L ][ i ]; } // paso 6... // tmp [ L ][ n ] = 1; // tmp [ Z ][ n ] = 0; tr - > cx [ n ] = 0; // paso 7... for ( i =n -1; i >=0; i - -) { tr - > cx [ i ] = tmp [ Z ][ i ] - tmp [ MIU ][ i ] * tr - > cx [ i +1]; tr - > bx [ i ] = ( tr - > ax [ i +1] - tr - > ax [ i ]) / tmp [ H ][ i ] - tmp [ H ][ i ] * (tr - > cx [ i +1] + 2* tr - > cx [ i ]) / 3; tr - > dx [ i ] = ( tr - > cx [ i +1] - tr - > cx [ i ]) / (3* tmp [ H ][ i ]) ; } /* *** en y **************** */ // paso 3... for ( i =1; i < n ; i ++) tmp [ ALFA ][ i ] = 3 * ( ( tr - > ay [ i +1] - tr - > ay [ i ]) / tmp [ H ][ i ] ( tr - > ay [ i ] - tr - > ay [i -1]) / tmp [ H ][ i -1] ); // paso 4...

188

9.3 Trazadores interpolantes cbicos - Curvas


328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372

Spline

tmp [ L ][0] = 1; tmp [ MIU ][0] = 0; tmp [ Z ][0] = 0; // paso 5... for ( i =1; i < n ; i ++) { tmp [ L ][ i ] = 2 * ( tr - > t [ i +1] - tr - > t [i -1]) - tmp [ H ][ i -1]* tmp [ MIU ][ i -1]; tmp [ MIU ][ i ] = tmp [ H ][ i ] / tmp [ L ][ i ]; tmp [ Z ][ i ] = ( tmp [ ALFA ][ i ] - tmp [ H ][ i -1]* tmp [ Z ][ i -1]) / tmp [ L ][ i ]; } // paso 6... // tmp [ L ][ n ] = 1; // tmp [ Z ][ n ] = 0; tr - > cy [ n ] = 0; // paso 7... for ( i =n -1; i >=0; i - -) { tr - > cy [ i ] = tmp [ Z ][ i ] - tmp [ MIU ][ i ] * tr - > cy [ i +1]; tr - > by [ i ] = ( tr - > ay [ i +1] - tr - > ay [ i ]) / tmp [ H ][ i ] - tmp [ H ][ i ] * ( tr - > cy [ i +1] + 2* tr -> cy [ i ]) / 3; tr - > dy [ i ] = ( tr - > cy [ i +1] - tr - > cy [ i ]) / (3* tmp [ H ][ i ]) ; } /* ******************* */ // Liberar memoria temporal free ( tmp [0]) ; } return TIC_COD_EXITO ;

void TIC_liberar_memoriaP ( trazadoresP * tr ) { free ( tr - > t ) ; } void TIC_imprimirP ( trazadoresP * tr , FILE * f ) { int i ; fprintf (f , " \ nTrazadores Paramtricos :\ n " ) ; fprintf (f , " i t_i \ tx_i \ ty_i \ tax_i \ tbx_i \ tcx_i \ tdx_i \ tay_i \ tby_i \ tcy_i \ tdy_i \ n " ) ; for ( i =0; i <= tr - > num_trazadores ; i ++) fprintf (f , " %d - >\ t %3.2 g \ t %3.2 g \ t %3.2 g \ n \ t %3.2 g \ t %3.2 g \ t %3.2 g \ t %3.2 g \ n \ t %3.2 g \t %3.2 g \ t %3.2 g \ t %3.2 g \ n" , i , tr - > t [ i ] , tr - >x [ i ] , tr - > y [ i ] , tr - > ax [ i ] , tr - > bx [ i ] , tr - > cx [ i] , tr - > dx [ i ] , tr - > ay [ i ] , tr - > by [ i ] , tr - > cy [ i] , tr - > dy [ i ]) ; }

Este es el programa principal, que utiliza el cdigo de los dos archivos anteriores. No

189

9 Curvas Paramtricas
es nada espectacular, ni aplicado. Es simplemente para comprender la naturaleza geomtrica de las curvas spline.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44

Listing 9.3: Programa principal

// / c09 / trazadores / main . c # include < SDL / SDL .h > # include " tic . h " # include < SDL / SDL_gfxPrimitives .h > # include < stdio .h > # define ANCHO 640 # define ALTO 480 # define ANCHO_RECTANGULO 10 # define # define # define # define # define XMIN 0.0 XMAX 15.0 YMIN 0.0 YMAX 3.0 TPASO 0.005

# define TAM 21 static inline int x_real ( double x_virtual ) { return ( int ) ( ANCHO * x_virtual / XMAX ) ; } static inline int y_real ( double y_virtual ) { return ( int ) ( ALTO * (1.0 - y_virtual / YMAX ) ) ; } static inline double x_virtual ( int x_real ) { return ( XMAX * x_real ) / ANCHO ; } static inline double y_virtual ( int y_real ) { return YMAX * (1.0 - y_real / ( double ) ALTO ) ; } // Variables globales double x [ TAM ] = {0.9 , 1.3 , 1.9 , 2.1 , 2.6 , 3.0 , 3.9 , 4.4 , 4.7 , 5.0 , 6.0 , 7.0 , 8.0 , 9.2 , 10.5 , 11.3 , 11.6 , 12.0 , 12.6 , 13.0 , 13.3}; double y [ TAM ] = {1.3 , 1.5 , 1.85 , 2.1 , 2.6 , 2.7 , 2.4 , 2.15 , 2.05 , 2.1 , 2.25 , 2.3 , 2.25 , 1.95 , 1.4 , 0.9 , 0.7 , 0.6 , 0.5 , 0.4 , 0.25}; trazadoresP * tr = NULL ; int i , j , k , l ; double tvar , xvar , yvar ; Uint32 color_fondo , color1 , color2 ; Uint32 gfxColor ( Uint8 r , Uint8 g , Uint8 b ) { return r << 24 | g << 16 |

190

9.3 Trazadores interpolantes cbicos - Curvas


45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92

Spline

b << 8 255; }

| // este valor es la opacidad del color // y debe ser mxima para que sea slido

void dibujo ( SDL_Surface * pantalla ) { i = j = k = l =0; SDL_Rect rect ; // Borra la pantalla SDL_FillRect ( pantalla , NULL , color_fondo ); // dibujo de los rectngulos rect . w = rect . h = ANCHO_RECTANGULO ; for ( i =0; i <= tr - > num_trazadores ; i ++) { rect . x = x_real ( tr - > x [ i ]) - ANCHO_RECTANGULO /2; rect . y = y_real ( tr - > y [ i ]) - ANCHO_RECTANGULO /2; rect . h = rect . w = ANCHO_RECTANGULO ; rectangleColor ( pantalla , rect .x , rect .y , rect . x + rect .w , rect . y + rect .h , color2 ) ; } // dibujo de la curva tvar = 0.0; TIC_calcularP ( tr , tvar , & xvar , & yvar ) ; i = x_real ( xvar ) ; j = y_real ( yvar ) ; for ( tvar = TPASO ; tvar <= 1; tvar += TPASO ) { if (! TIC_calcularP ( tr , tvar , & xvar , & yvar ) ) { aalineColor ( pantalla , i , j , k = x_real ( xvar ) , l = y_real ( yvar ) , color1 ) ; i=k; j=l; } } TIC_calcularP ( tr , 1.0 , & xvar , & yvar ) ; aalineColor ( pantalla , i , j , k = x_real ( xvar ) , l = y_real ( yvar ) , color1 ) ; // vuelca el buffer en la pantalla : SDL_Flip ( pantalla ) ;

int main ( int argc , char * argv []) { SDL_Surface * pantalla = NULL ; SDL_Event evento ; int profundidad_color ; const SDL_VideoInfo * info ; int i ; int corriendo = 1; int seleccionado = 0;

191

9 Curvas Paramtricas
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139

if ( SDL_Init ( SDL_INIT_VIDEO ) < 0 ) { fprintf ( stderr , " No se puede iniciar SDL : %s \ n" , SDL_GetError () ) ; exit (1) ; } atexit ( SDL_Quit ) ; // Este if es importante para poder usar SDL_gfx info = SDL_GetVideoInfo () ; if ( info - > vfmt - > BitsPerPixel > 8 ) { profundidad_color = info - > vfmt - > BitsPerPixel ; // printf (" %d \ n " , profundidad_color ) ; } else { profundidad_color = 16; } pantalla = SDL_SetVideoMode ( ANCHO , ALTO , profundidad_color , SDL_SWSURFACE ) ; if ( pantalla == NULL ) { fprintf ( stderr , " No se puede establecer el modo de video %dx %d : % s\n", ANCHO , ALTO , SDL_GetError () ) ; exit (1) ; } SDL_WM_SetCaption ( " Trazadores Interpolantes Cbicos Paramtricos ! " , NULL ) ; color_fondo = SDL_MapRGB ( pantalla - > format ,0 ,0 ,0) ; color1 = gfxColor (255 ,255 ,255) ; color2 = gfxColor (128 ,128 ,128) ; if (( tr = TIC_crear_trazadoresP (x , y , TAM ) ) == NULL ) { fprintf ( stderr , " Error al crear los trazadores \ n " ) ; exit (1) ; } TIC_imprimirP ( tr , stderr ) ; dibujo ( pantalla ) ; while ( corriendo ) { while ( SDL_PollEvent (& evento ) ) { switch ( evento . type ) { case SDL_MOUSEMOTION :{ if ( seleccionado ) { // actualizar el punto tr - > x [ i ]= x_virtual ( evento . button .x ) ; tr - > y [ i ]= y_virtual ( evento . button .y ) ; TIC_actualizar_trazadorP ( tr ) ; dibujo ( pantalla ) ; }

192

9.3 Trazadores interpolantes cbicos - Curvas


140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171

Spline

break ; case SDL_MOUSEBUTTONDOWN :{ for ( i =0; i <= tr - > num_trazadores ; i ++) { if ( evento . button . button == SDL_BUTTON_LEFT && // si hace clic sobre un nodo ... (( evento . button . x > x_real ( tr - > x [ i ]) ANCHO_RECTANGULO /2) && ( evento . button . y > y_real ( tr - > y [ i ]) ANCHO_RECTANGULO /2) && ( evento . button . x < x_real ( tr - > x [ i ]) + ANCHO_RECTANGULO /2) && ( evento . button . y < y_real ( tr - > y [ i ]) + ANCHO_RECTANGULO /2) ) ){ // se selecciona y el ndice queda en 'i ' seleccionado = 1; // printf (" seleccionado \n ") ; break ; } } } break ; case SDL_MOUSEBUTTONUP : seleccionado = 0; break ; case SDL_QUIT : corriendo = 0; break ;


Listing 9.4: Archivo Makele para la aplicacin

SDL_Quit () ; return 0;

1 2 3 4 5 6 7 8 9 10

# / c09 / trazadores / Makefile # Esto es un comentario LDFLAGS = $ ( shell sdl - config -- cflags ) LDLIBS = $ ( shell sdl - config -- libs ) - lSDL_gfx # # RM = / bin / rm -f # Esto indica que los siguientes identificadores , # no son archivos , sino comandos de make : . PHONY : limpiar

193

9 Curvas Paramtricas
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

. PHONY : limpiartodo . PHONY : all # se puede por ejemplo ejecutar en la consola # lo siguiente : # ' make limpiartodo ' , etc . # Nombre del programa ejecutable : PROG = trazadores # Un '*.o ' por cada '*.c ' OBJ = tic . o main . o # Cuando se ejecuta ' make ' , se ejecuta esto : all : $ ( PROG ) limpiar $ ( PROG ) : $ ( OBJ ) gcc -o $ ( PROG ) $ ( OBJ ) $ ( LDLIBS ) $ ( LDFLAGS ) # Borra todos los archivos intermedios y de copia de seguridad limpiar : $ ( RM ) *~ $ ( OBJ ) # Borra todos los archivos intermedios , de copia de seguridad # y el programa ejecutable , si es que existe limpiartodo : make limpiar $ ( RM ) $ ( PROG )

9.3.8. Aplicacin para animaciones


Como se sabe, para realizar una secuencia animada, se necesita una serie de cuadros con pequeas variaciones entre s y tienen que mostrarse a una velocidad sucientemente alta como para que el ojo humano no note el cambio y el cerebro (tambin humano) reconstruya las partes faltantes. Las computadoras pueden usarse para que el dibujante/artista no deba hacer tantos cuadros sino slo unos cuantos, y sean estas quienes ayuden a completar los cuadros necesarios para que el ojo humano no note el cambio. La idea general es que el dibujante/artista slo realice algunos cuadros (mientras ms, mejor) y estos cuadros se unan por curvas Splines a travs del tiempo. El mecanismo es el siguiente: Todos los cuadros a fundir en una sla animacin deben tener la misma cantidad de puntos de control (por puntos de control nos referimos a puntos a partir de los cuales se puede construir toda la escena). Estos puntos deben ser coherentes entre cada cuadro. A cada cuadro se le asigna un valor de tiempo en

194

9.3 Trazadores interpolantes cbicos - Curvas

Spline

el cual debe ocurrir, a partir del inicio de la animacin. Luego se calcula una curva Spline paramtrica para cada punto de control, como se explica en la seccin 9.3.6 en la pgina 176, donde el parmetro adicional es el tiempo. Luego, pueden crearse tantos cuadros como se desee, aplicando un algoritmo basado en el algoritmo en la pgina 178. Un ejemplo aplicado, sencillo, puede estudiarse en la aplicacin se describe a continuacin:

editorplinesFpy

que

arrastre con clic izquierdo


mite moverlos.

Sobre los cuadros que representan los puntos de control, per-

Sobre el fondo blanco, mueve el marco virtual.

clic derecho Sobre un punto de control, agrega un puntos ms. ruedas del ratn+CTRL Aumento/disminucin del detalle/nura de la animacin. ruedas del ratn+SHIFT Aumento/disminucin del tamao de los cuadros de control. ruedas del ratn Implementa acercamiento/alejamiento. ruedas del ratn + tecla x/y Expande/Contrae el marco virtual horizontalmente/verticalmente.

clic central Sobre un punto de control, lo borra. CTRL+tecla Izquierda Se desplaza hacia el cuadro anterior. CTRL+tecla Derecha Se desplaza hacia el cuadro siguiente. Crea un nuevo cuadro a
partir del anterior cuando est en el ltimo.

CTRL+tecla L Alterna dibujo de las lneas rectas que unen los puntos de control. CTRL+tecla C Alterna dibujo de los rectngulos que marcan los puntos de control. CTRL+tecla G Guarda el archivo (interfaz de lnea de comandos). CTRL+tecla Arriba/Abajo Aumento/disminucin del grueso de los cuadros de control. tecla Espacio Inicia la animacin desde el primer cuadro hasta el ltimo y regresa al
cuadro en el que se encontraba antes de la animacin.

1 2 3 4 5 6 7 8 9

Listing 9.5: Programa para generar animaciones sencillas en Python

# !/ usr / bin / env python # coding : utf -8 " " " c09 / bezier_py / editorBezier . py - Programa para hacer animaciones con curvas Splines . Los datos se guardan en archivos xml . """ import pygame

195

9 Curvas Paramtricas
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59

import sys , os , random import splines PAUSA = 100 ANCHO_POR_DEFECTO = 800 ALTO_POR_DEFECTO = 500 SUPERIOR_DER = ( 10.0 , 10.0) INFERIOR_IZQ = ( -10.0 , -10.0) # valores por defecto __dibujarLineas = True __dibujarCuadros = True __dibujarCurvas = False BOTON_IZQ = 1 BOTON_CEN = 2 BOTON_DER = 3 RUEDA_ARRIBA = 4 RUEDA_ABAJO = 5 __colorLineas = (128 ,128 ,128) __colorCurva = (0 ,0 ,255) __colorCuadro = (0 ,0 ,0) __colorCuadroPrimero = (255 ,0 ,0) __colorFondo = (255 ,255 ,255) __gruesoLineas = 1 def __inicializarMatriz ( matriz ) : matriz . ponerEscala ( \ ( ANCHO_POR_DEFECTO , ALTO_POR_DEFECTO ) , \ ( -10.0 , -10.0) ,(10.0 ,10.0) ) def dibujar ( matriz , ixS , pantalla , actualizarCurvas = True ) : ' ' ' Se recibe una matriz de puntos , y se dibuja la fila ixS - sima en la pantalla especificada . ''' # Borrar la pantalla if actualizarCurvas : pantalla . fill ( __colorFondo ) # Dibujar la curva : if __dibujarCurvas and actualizarCurvas : pass # Dibujar los cuadrados : if __dibujarCuadros :

196

9.3 Trazadores interpolantes cbicos - Curvas


60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102

Spline

pygame . draw . rect ( pantalla , __colorCuadroPrimero , pygame . Rect ( matriz . e . vrx ( matriz . puntos [0][ ixS ][0]) - matriz . anchoRectangulo /2 , matriz . e . vry ( matriz . puntos [0][ ixS ][1]) - matriz . anchoRectangulo /2 , matriz . anchoRectangulo , matriz . anchoRectangulo ) , matriz . gruesoRectangulo ) for i in range (1 , len ( matriz . puntos ) ) : pygame . draw . rect ( pantalla , __colorCuadro , pygame . Rect ( matriz . e . vrx ( matriz . puntos [ i ][ ixS ][0]) - matriz . anchoRectangulo /2 , matriz . e . vry ( matriz . puntos [ i ][ ixS ][1]) - matriz . anchoRectangulo /2 , matriz . anchoRectangulo , matriz . anchoRectangulo ) , matriz . gruesoRectangulo ) # Dibujar las lneas : if __dibujarLineas and actualizarCurvas : for i in range ( len ( matriz . puntos ) -1) : pygame . draw . line ( pantalla , __colorLineas , matriz . e . vr ( matriz . puntos [ i ][ ixS ]) , matriz . e . vr ( matriz . puntos [ i +1][ ixS ]) , __gruesoLineas ) # Redibujar el buffer pygame . display . flip () def guardarMatriz ( matriz ): if len ( sys . argv ) >1: matriz . guardarArchivo ( sys . argv [1]) print ( " Archivo guardado " ) else : archivo = raw_input ( " Escriba el nombre del archivo a guardar ( si lo omite , su trabajo no se guardar ) : " ) if archivo : matriz . guardarArchivo ( archivo ) print ( " Archivo guardado " ) else : print ( " Archivo no guardado " ) def iniciarAnimacion ( matriz , pantalla ) : pygame . display . set_caption ( " Animacin en curso - Por favor espere ") listaPuntosAnimacion = [] for i in range ( matriz . numNodos () ) : listaPuntosAnimacion . append ( matriz . trazador ( i )) for j in range ( len ( listaPuntosAnimacion [0]) ) : # Borrar la pantalla

197

9 Curvas Paramtricas
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146

pantalla . fill ( __colorFondo ) # Dibujar los cuadrados : for i in range ( matriz . numNodos () ) : pygame . draw . rect ( pantalla , __colorCuadro , pygame . Rect ( matriz . e . vrx ( listaPuntosAnimacion [ i ][ j ][0]) - matriz . anchoRectangulo /2 , matriz . e . vry ( listaPuntosAnimacion [ i ][ j ][1]) - matriz . anchoRectangulo /2 , matriz . anchoRectangulo , matriz . anchoRectangulo ) , matriz . gruesoRectangulo ) # Dibujar las lneas : for i in range ( matriz . numNodos () -1) : pygame . draw . line ( pantalla , __colorLineas , matriz . e . vr ( listaPuntosAnimacion [ i ][ j ]) , matriz . e . vr ( listaPuntosAnimacion [ i +1][ j ]) , __gruesoLineas ) # Redibujar el buffer pygame . display . flip () # Hacer pausa pygame . time . delay ( PAUSA ) if __name__ == " __main__ " : pygame . init () matriz = splines . MatrizSpline () if len ( sys . argv ) >1: if os . path . exists ( sys . argv [1]) : matriz . cargarArchivo ( sys . argv [1]) else : __inicializarMatriz ( matriz ) else : __inicializarMatriz ( matriz ) pantalla = pygame . display . set_mode ( matriz . dimensiones () , pygame . RESIZABLE ) pygame . display . set_caption ( \ " Editor de Secuencia de puntos - " + \ ( len ( sys . argv ) >1 and sys . argv [1] or " SinNombre . bezier " ) ) ratonPresionado = 0 # cdigo del botn del ratn que ha sido presionado punto = None ixPunto = -1 ixSecuencia = 0

198

9.3 Trazadores interpolantes cbicos - Curvas


147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196

Spline

dibujar ( matriz , ixSecuencia , pantalla ) while True : for evento in pygame . event . get () : modificadores = pygame . key . get_mods () # Movimiento del ratn if evento . type == pygame . MOUSEMOTION : # Arrastrando punto if ratonPresionado == BOTON_IZQ and punto : punto [0] = matriz . e . rvx ( evento . pos [0]) punto [1] = matriz . e . rvy ( evento . pos [1]) dibujar ( matriz , ixSecuencia , pantalla ) # Desplazar el espacio virtual elif ratonPresionado == BOTON_IZQ and not punto : dxv = matriz . e . magnitudrvx ( evento . rel [0]) dyv = matriz . e . magnitudrvy ( evento . rel [1]) matriz . e . minxv -= dxv matriz . e . maxxv -= dxv matriz . e . minyv -= dyv matriz . e . maxyv -= dyv dibujar ( matriz , ixSecuencia , pantalla ) # Clic izquierdo - Identificar el punto para moverlo elif evento . type == pygame . MOUSEBUTTONDOWN \ and evento . button == BOTON_IZQ : ratonPresionado = evento . button xclic , yclic = evento . pos for i in range ( len ( matriz . puntos ) ) : px , py = matriz . e . vr ( matriz . puntos [ i ][ ixSecuencia ]) if xclic > px - matriz . anchoRectangulo /2 and \ yclic > py - matriz . anchoRectangulo /2 and \ xclic < px + matriz . anchoRectangulo /2 and \ yclic < py + matriz . anchoRectangulo /2 : punto = matriz . puntos [ i ][ ixSecuencia ] ixPunto = i dibujar ( matriz , ixSecuencia , pantalla ) break # Clic derecho - Agregar nuevo segmento elif evento . type == pygame . MOUSEBUTTONDOWN \ and not ratonPresionado \ and evento . button == BOTON_DER : xclic , yclic = evento . pos for i in range ( len ( matriz . puntos ) ) : px , py = matriz . e . vr ( matriz . puntos [ i ][ ixSecuencia ]) if xclic > px - matriz . anchoRectangulo /2 and \ yclic > py - matriz . anchoRectangulo /2 and \ xclic < px + matriz . anchoRectangulo /2 and \

199

9 Curvas Paramtricas
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239

yclic < py + matriz . anchoRectangulo /2 : l = [] if i == len ( matriz . puntos ) -1: # est al final for k in range ( len ( matriz . puntos [0]) ) : l . append ([ matriz . puntos [ i ][ k ][0] , # ponerlo sobre el anterior matriz . puntos [ i ][ k ][1] ]) else : for k in range ( len ( matriz . puntos [0]) ) : l . append ([ ( matriz . puntos [ i ][ k ][0] + matriz . puntos [ i +1][ k ][0]) /2 , ( matriz . puntos [ i ][ k ][1] + matriz . puntos [ i +1][ k ][1]) /2 ]) matriz . puntos . insert ( i +1 , l ) dibujar ( matriz , ixSecuencia , pantalla ) break # Ruedas del ratn elif evento . type == pygame . MOUSEBUTTONDOWN \ and not ratonPresionado \ and ( evento . button == RUEDA_ABAJO or evento . button == RUEDA_ARRIBA ) : # Aumentar o disminur el nmero de pasos if modificadores & pygame . KMOD_CTRL : if evento . button == RUEDA_ABAJO : np = matriz . numPasos () -1 if np >1: matriz . numPasos ( -1) else : matriz . numPasos (1) print ( " Nmero de pasos para la animacin : " + str ( matriz . numPasos () ) ) # Aumentar o disminur el tamao de los cuadros elif modificadores & pygame . KMOD_SHIFT : if evento . button == RUEDA_ABAJO : matriz . anchoRectangulo = matriz . anchoRectangulo -5 if matriz . anchoRectangulo <5: matriz . anchoRectangulo = 5 else : matriz . anchoRectangulo = matriz . anchoRectangulo +5 print ( " Ancho de los cuadrados : " + str ( matriz . anchoRectangulo ) ) # Hacer acercamiento o alejamiento

200

9.3 Trazadores interpolantes cbicos - Curvas


240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285

Spline

else : porcentajeDistanciaX = 0.05*( matriz . e . maxxv - matriz . e . minxv ) porcentajeDistanciaY = 0.05*( matriz . e . maxyv - matriz . e . minyv ) teclas = pygame . key . get_pressed () if evento . button == RUEDA_ABAJO : # acercamiento if not teclas [ pygame . K_x ] and not teclas [ pygame . K_y ]: matriz . e . minxv += porcentajeDistanciaX matriz . e . maxxv -= porcentajeDistanciaX matriz . e . minyv += porcentajeDistanciaY matriz . e . maxyv -= porcentajeDistanciaY elif teclas [ pygame . K_x ]: matriz . e . minxv += porcentajeDistanciaX matriz . e . maxxv -= porcentajeDistanciaX elif teclas [ pygame . K_y ]: matriz . e . minyv += porcentajeDistanciaY matriz . e . maxyv -= porcentajeDistanciaY else : # alejamiento if not teclas [ pygame . K_x ] and not teclas [ pygame . K_y ]: matriz . e . minxv -= porcentajeDistanciaX matriz . e . maxxv += porcentajeDistanciaX matriz . e . minyv -= porcentajeDistanciaY matriz . e . maxyv += porcentajeDistanciaY elif teclas [ pygame . K_x ]: matriz . e . minxv -= porcentajeDistanciaX matriz . e . maxxv += porcentajeDistanciaX elif teclas [ pygame . K_y ]: matriz . e . minyv -= porcentajeDistanciaY matriz . e . maxyv += porcentajeDistanciaY dibujar ( matriz , ixSecuencia , pantalla ) # Eliminar un punto elif evento . type == pygame . MOUSEBUTTONDOWN \ and not ratonPresionado \ and evento . button == BOTON_CEN : xclic , yclic = evento . pos for i in range ( len ( matriz . puntos ) ) : px , py = matriz . e . vr ( matriz . puntos [ i ][ ixSecuencia ]) if xclic > px - matriz . anchoRectangulo /2 and \ yclic > py - matriz . anchoRectangulo /2 and \ xclic < px + matriz . anchoRectangulo /2 and \ yclic < py + matriz . anchoRectangulo /2 : matriz . puntos . pop ( i ) dibujar ( matriz , ixSecuencia , pantalla ) break # Detener el movimiento

201

9 Curvas Paramtricas
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332

elif evento . type == pygame . MOUSEBUTTONUP : if evento . button == ratonPresionado == BOTON_IZQ : ratonPresionado = 0 punto = None ixPunto = -1 # Cuando se redimensiona la ventana elif evento . type == pygame . VIDEORESIZE : matriz . cambiarDimensiones ( evento . size ) pantalla = pygame . display . set_mode ( matriz . dimensiones () , pygame . RESIZABLE ) dibujar ( matriz , ixSecuencia , pantalla ) # Otras acciones elif evento . type == pygame . KEYDOWN and \ ( modificadores & pygame . KMOD_CTRL ) : if evento . key == pygame . K_LEFT : if ixSecuencia > 0: ixSecuencia -= 1 print ( " Cuadro {0}/{1} " . format ( ixSecuencia +1 , len ( matriz . puntos [0]) ) ) dibujar ( matriz , ixSecuencia , pantalla ) elif evento . key == pygame . K_RIGHT : if ixSecuencia == len ( matriz . puntos [0]) -1: for l in matriz . puntos : l . append ([ l [ -1][0] , l [ -1][1] ]) ixSecuencia += 1 print ( " Cuadro {0}/{1} " . format ( ixSecuencia +1 , len ( matriz . puntos [0]) ) ) dibujar ( matriz , ixSecuencia , pantalla ) elif evento . key == pygame . K_l : __dibujarLineas = not __dibujarLineas if __dibujarLineas : print ( " Dibujo de lneas encendido " ) else : print ( " Dibujo de lneas apagado " ) dibujar ( matriz , ixSecuencia , pantalla ) elif evento . key == pygame . K_c : __dibujarCuadros = not __dibujarCuadros if __dibujarCuadros : print ( " Dibujo de cuadros encendido " ) else : print ( " Dibujo de cuadros apagado ") dibujar ( matriz , ixSecuencia , pantalla ) elif evento . key == pygame . K_g : guardarMatriz ( matriz ) # Aumentar o disminur el grueso de los cuadros

202

9.3 Trazadores interpolantes cbicos - Curvas


333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357

Spline

elif evento . key == pygame . K_DOWN : matriz . gruesoRectangulo = matriz . gruesoRectangulo -1 if matriz . gruesoRectangulo <1: matriz . gruesoRectangulo = 1 print ( " Grueso de los cuadrados : " + str ( matriz . gruesoRectangulo ) ) dibujar ( matriz , ixSecuencia , pantalla ) elif evento . key == pygame . K_UP : matriz . gruesoRectangulo = matriz . gruesoRectangulo +1 print ( " Grueso de los cuadrados : " + str ( matriz . gruesoRectangulo ) ) dibujar ( matriz , ixSecuencia , pantalla ) elif evento . type == pygame . KEYDOWN and \ evento . key == pygame . K_SPACE : titulo = pygame . display . get_caption () [0] iniciarAnimacion ( matriz , pantalla ) pygame . display . set_caption ( titulo ) dibujar ( matriz , ixSecuencia , pantalla ) elif evento . type == pygame . KEYDOWN and \ evento . key == pygame . K_F1 : print ( " Ayuda ... " ) # Pendiente ... elif evento . type == pygame . QUIT : pygame . display . quit () # Cierra la ventana y apaga el subsistema de video guardarMatriz ( matriz ) sys . exit ()

La biblioteca principal, que contiene la implementacin de algunos algoritmos es esta:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

Listing 9.6: Biblioteca para Curvas Splines Bidimensionales en Python

# coding : utf -8 " " " c09 / trazadores_py / splines . py - Modulo para procesar un arreglo Spline . Los datos se guardan en archivos xml . """ import xml . dom . minidom __H __ALFA __L __MIU __Z A = 0 = = = = = 0 1 2 3 4

203

9 Curvas Paramtricas
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64

B = 1 C = 2 D = 3 def coeficientesTrazador ( independiente , dependiente ) : ' ' ' Devuelve la matriz de coeficientes de los 'n ' trazadores interpolantes cbicos paramtricos para la secuencia de nodos descritos en la matriz { independiente x dependiente }. __La salida tiene la forma : [[ a1 , a2 , ... , an ] , [ b1 , b2 , ... , bn ] , [ c1 , c2 , ... , cn ] , [ d1 , d2 , ... , dn ]] donde 'n +1 ' es el nmero de nodos que representan los dos vectores , dependiente e independiente . ''' if len ( independiente ) != len ( dependiente ) : raise Exception ( " __Los arreglos deben ser de igual longitud " ) numNodos = len ( independiente ) numTrazadores = len ( independiente ) - 1 def listaCeros () : l = [] for i in range ( numNodos ) : l . append (0.0) return l coef = [ 0 , listaCeros () , listaCeros () , listaCeros () ] tmp = [ listaCeros () , listaCeros () , listaCeros () , listaCeros () , listaCeros () ] # Clculo de los trazadores n = numTrazadores coef [ A ] = dependiente [:] # Copiar todo el arreglo # paso 2... for i in range ( n ) : tmp [ __H ][ i ] = independiente [ i +1] - independiente [ i ] # paso 3... for i in range (1 , n ): tmp [ __ALFA ][ i ] = 3 * ( ( coef [ A ][ i +1] - coef [ A ][ i ]) / tmp [ __H ][ i ] - \ ( coef [ A ][ i ] - coef [ A ][ i -1]) / tmp [ __H ][ i -1] ) # paso 4... tmp [ __L ][0] = 1 tmp [ __MIU ][0] = 0

204

9.3 Trazadores interpolantes cbicos - Curvas


65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110

Spline

tmp [ __Z ][0] = 0 # paso 5... for i in range (1 , n) : tmp [ __L ][ i ] = 2 * ( independiente [ i +1] - independiente [i -1]) - tmp [ __H ][ i -1]* tmp [ __MIU ][i -1] tmp [ __MIU ][ i ] = tmp [ __H ][ i ] / tmp [ __L ][ i ] tmp [ __Z ][ i ] = ( tmp [ __ALFA ][ i ] - tmp [ __H ][ i -1]* tmp [ __Z ][ i -1]) / tmp [ __L ][ i ] # paso 6... coef [ C ][ n ] = 0.0 # paso 7... for i in range (n -1 , -1 , -1) : coef [ C ][ i ] = tmp [ __Z ][ i ] - tmp [ __MIU ][ i ] * coef [ C ][ i +1] coef [ B ][ i ] = ( coef [ A ][ i +1] - coef [ A ][ i ]) / tmp [ __H ][ i] \ - tmp [ __H ][ i ] * ( coef [ C ][ i +1] + 2* coef [ C ][ i ]) / 3 coef [ D ][ i ] = ( coef [ C ][ i +1] - coef [ C ][ i ]) / (3* tmp [ __H ][ i ]) return coef class MatrizSpline () : ' ' ' Esta clase mantiene una matriz de puntos para modelar una matriz Spline . Cada lista principal de la matriz , es una secuencia independiente . ''' def __init__ ( self , escala = None ) : if escala : self . puntos = [ [ [ escala . minxv , escala . minyv ] ] , [ [( escala . minxv + escala . maxxv ) /2.0 , ( escala . minyv + escala . maxyv ) /2.0] ] , [ [ escala . maxxv , escala . maxyv ] ] ] else : self . puntos = [ [ [0 ,0] ] ,[ [0 ,0] ] ,[ [0 ,0] ] ] self . anchoRectangulo = 20 self . gruesoRectangulo = 1 self . e = escala self . __numPasos = 25 self . __t = None def numNodos ( self ) :

205

9 Curvas Paramtricas
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158

return len ( self . puntos ) def numCuadros ( self ) : return len ( self . puntos [0]) def numPasos ( self , valorIncremento =0) : ' ' ' Permite aumentar o disminur el nmero de lneas que conformarn una secuencia de segmentos de Bzier ''' if valorIncremento : self . __numPasos += valorIncremento if self . __numPasos == 0: self . __numPasos -= valorIncremento return self . __numPasos def dimensiones ( self ) : " " " Devuelve las dimensiones de la pantalla donde debe mostrarse la serie de puntos " " " return self . e . maxxr , self . e . maxyr def cambiarDimensiones ( self , tam ) : " cambiar las dimensiones de la escala real " self . e . maxxr , self . e . maxyr = tam def ponerEscala ( self , ( ANCHO , ALTO ) , ( minx , miny ) , ( maxx , maxy ) ) : " " " Configura la escala de visualizacin de esta matriz """ self . e = Escala ( \ ( ANCHO , ALTO ) , \ ( minx , miny ) , ( maxx , maxy ) ) def trazador ( self , i ) : ' ' ' Devuelve una lista de pares ordenados del tipo [x , y ]. La lista tiene self . __numPasos +1 pares que interpolan a la i - sima secuencia de esta matriz de Splines . ''' numTrazadores = self . numCuadros () -1 l = [] self . __t = [ float ( x) /( self . numCuadros () -1) for x in range ( self . numCuadros () )] coefX = coeficientesTrazador ( self . __t , \ [ p [0] for p in self . puntos [ i ]] ) coefY = coeficientesTrazador ( self . __t , \ [ p [1] for p in self . puntos [ i ]] ) ix_t = 1 for t in [ float ( x ) / self . __numPasos for x in range ( self . __numPasos +1) ]:

206

9.3 Trazadores interpolantes cbicos - Curvas


159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200

Spline

while t > self . __t [ ix_t ]: ix_t += 1 ix_t -= 1 tmpT = t - self . __t [ ix_t ] l . append ( [ \ coefX [ A ][ ix_t ] + tmpT * ( coefX [ B ][ ix_t ] + tmpT * ( coefX [ C ][ ix_t ] + tmpT * coefX [ D ][ ix_t ] ) ) , \ coefY [ A ][ ix_t ] + tmpT * ( coefY [ B ][ ix_t ] + tmpT * ( coefY [ C ][ ix_t ] + tmpT * coefY [ D ][ ix_t ] ) ) ] ) # for h in l : # print ( h ) return l def cargarArchivo ( self , nombreArchivo ) : documentoxml = xml . dom . minidom . parse ( nombreArchivo ) curvasxml = documentoxml . getElementsByTagName ( " curvas " ) [0] rectangulosxml = documentoxml . getElementsByTagName ( " rectangulos " ) [0] escalaxml = documentoxml . getElementsByTagName ( " escala " ) [0] secuenciasxml = documentoxml . getElementsByTagName ( " secuencias " ) [0] self . __numPasos = int ( curvasxml . getAttribute ( " pasos " ) ) self . __t = None self . anchoRectangulo = int ( rectangulosxml . getAttribute ( " ancho ") ) self . gruesoRectangulo = int ( rectangulosxml . getAttribute ( " grueso " ) ) self . e = Escala ( \ ( int ( escalaxml . getAttribute ( " maxxr ") ) , int ( escalaxml . getAttribute ( " maxyr " ) ) ) , \ ( float ( escalaxml . getAttribute ( " minxv " ) ) , float ( escalaxml . getAttribute ( " minyv " ) ) ) , \ ( float ( escalaxml . getAttribute ( " maxxv " ) ) , float ( escalaxml . getAttribute ( " maxyv " ) ) ) ) self . puntos = [] for nodos_xml in secuenciasxml . getElementsByTagName ( " nodos " ) : l = [] for nodoxml in nodos_xml . getElementsByTagName ( " nodo " ) : l . append ([ float ( nodoxml . getAttribute ( " x " ) ) , \ float ( nodoxml . getAttribute ( " y " ) ) ]) self . puntos . append ( l ) def guardarArchivo ( self , nombreArchivo ) : implementacionxml = xml . dom . minidom . getDOMImplementation ()

207

9 Curvas Paramtricas
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248

documentoxml = implementacionxml . createDocument ( None , " splines " , None ) raiz = documentoxml . documentElement curvas = documentoxml . createElement ( " curvas " ) curvas . setAttribute ( " pasos " , str ( self . __numPasos ) ) raiz . appendChild ( curvas ) rectangulos = documentoxml . createElement ( " rectangulos " ) rectangulos . setAttribute ( " ancho " , str ( self . anchoRectangulo ) ) rectangulos . setAttribute ( " grueso " , str ( self . gruesoRectangulo ) ) raiz . appendChild ( rectangulos ) escala = documentoxml . createElement ( " escala " ) escala . setAttribute ( " maxxr " , str ( self . e . maxxr ) ) escala . setAttribute ( " maxyr " , str ( self . e . maxyr ) ) escala . setAttribute ( " minxv " , escala . setAttribute ( " minyv " , escala . setAttribute ( " maxxv " , escala . setAttribute ( " maxyv " , raiz . appendChild ( escala ) str ( self . e . minxv ) ) str ( self . e . minyv ) ) str ( self . e . maxxv ) ) str ( self . e . maxyv ) )

secuenciasxml = documentoxml . createElement ( " secuencias " ) for lista in self . puntos : nodos_xml = documentoxml . createElement ( " nodos " ) for p in lista : nodoxml = documentoxml . createElement ( " nodo ") nodoxml . setAttribute ( " x " , str ( p [0]) ) nodoxml . setAttribute ( " y " , str ( p [1]) ) nodos_xml . appendChild ( nodoxml ) secuenciasxml . appendChild ( nodos_xml ) raiz . appendChild ( secuenciasxml ) f = open ( nombreArchivo , 'w ') f . write ( documentoxml . toprettyxml () ) f . close ()

class Escala () : " " " Clase de Escalas bidimensionales de ventana completa . Esta clase permite modelar un espacio bidimensional cuadrado con dos escalas diferentes : una Real y una Virtual . La escala Real est medida en pixeles en el dominio de los enteros . Su origen se encuentra en la esquina superior izquierda y en la esquina inferior derecha se encuentra la coordenada ( ANCHO -1 , ALTO -1) .

208

9.3 Trazadores interpolantes cbicos - Curvas


249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294

Spline

Se asume que ANCHO y ALTO son positivos . La escala Virtual tiene unidades arbitrarias en el dominio de los reales . En la esquina inferior izquierda se encuentra la coordenada [ minx , miny ] y en la esquina superior derecha [ maxx , maxy ]. Se asume que minx < maxx y que miny < maxy . """ def __init__ ( self , ( ANCHO , ALTO ) , ( minx , miny ) , ( maxx , maxy ) ) : # self . minxr = 0 # Siempre es cero # self . minyr = 0 # Siempre es cero self . maxxr = ANCHO self . maxyr = ALTO self . minxv = minx self . minyv = miny self . maxxv = maxx self . maxyv = maxy def rvx ( self , xr ) : ' ' ' Convierte un valor x Real en su correspondiente Virtual ' ' ' return ( self . maxxv - self . minxv ) * float ( xr ) / self . maxxr + self . minxv def rvy ( self , yr ) : ' ' ' Convierte un valor y Real en su correspondiente Virtual ' ' ' return ( self . minyv - self . maxyv ) * float ( yr ) / self . maxyr + self . maxyv def vrx ( self , xv ) : ' ' ' Convierte un return int ( ( xv ) def vry ( self , yv ) : ' ' ' Convierte un return int ( ( yv ) valor x Virtual en su correspondiente Real ' ' ' - self . minxv ) * self . maxxr /( self . maxxv - self . minxv ) valor y Virtual en su correspondiente Real ' ' ' - self . maxyv ) * self . maxyr /( self . minyv - self . maxyv )

def rv ( self , ( xr , yr )) : ' ' ' Convierte un par (x ,y ) Real en su correspondiente Virtual ' ' ' return self . rvx ( xr ) , self . rvy ( yr ) def vr ( self , ( xv , yv )) : ' ' ' Convierte un par (x ,y ) Virtual en su correspondiente Real ' ' ' return self . vrx ( xv ) , self . vry ( yv ) def magnitudvrx ( self , deltaxv ) : ' ' ' Convierte una ' distancia ' deltaxv en la escala Virtual a su correspondiente ' distancia ' en la escala Real ' ' ' return deltaxv * self . maxxr / ( self . maxxv - self . minxv ) def magnitudvry ( self , deltayv ) :

209

9 Curvas Paramtricas
295 296 297 298 299 300 301 302 303 304 305 306

' ' ' Convierte una ' distancia ' deltayv en la escala Virtual a su correspondiente ' distancia ' en la escala Real ' ' ' return deltayv * self . maxyr / ( self . minyv - self . maxyv ) def magnitudrvx ( self , deltaxr ) : ' ' ' Convierte una ' distancia ' deltaxr en la escala Real a su correspondiente ' distancia ' en la escala Virtual ' ' ' return deltaxr * ( self . maxxv - self . minxv ) / self . maxxr def magnitudrvy ( self , deltayr ) : ' ' ' Convierte una ' distancia ' deltayr en la escala Real a su correspondiente ' distancia ' en la escala Virtual ' ' ' return deltayr * ( self . minyv - self . maxyv ) / self . maxyr

9.4.

Curvas de

Bzier
3

Las curvas de Bzier fueron nombradas as en honor de Pierre Bzier , quien las utiliz para el diseo de carroceras de automviles en 1962 en la empresa Rnault.

9.4.1. Descripcin geomtrica


Estas curvas estn determinadas tpicamente por cuatro puntos ordenados, de los cuales el primero y el ltimo determinan el inicio y n de la curva, y los otros dos describen los vectores tangentes inicial y nal que controlan la trayectoria de la curva entre los puntos inicial y nal. Es importante recalcar que estas curvas que no pasan por todos los puntos de control.

no sirven para interpolar, ya

9.4.2. Descripcin matemtica


Dados cuatro puntos siguiente manera:

P0 , P1 , P2 , P3

(que pueden ser unidimensionales, bidimensionales,

tridimensionales, etc.), llamados puntos de control, se dene la curva de Bzier de la

B (t) = (1 t)3 P0 + 3t(1 t)2 P1 + 3t2 (1 t)P2 + t3 P3 , 0


es decir:

(9.1)

x(t) = (1 t)3 x0 + 3t(1 t)2 x1 + 3t2 (1 t)x2 + t3 x3 , 0 y (t) = (1


3

t t t

1, 1 1
y adems, (si es que aplica), etc...

t)3 y

+ 3t(1

t)2 y1

3t2 (1

t)y2 +

t3 y3

, 0

z (t) = (1 t)3 z0 + 3t(1 t)2 z1 + 3t2 (1 t)z2 + t3 z3 , 0


aunque fueron inventadas por Paul de Casteljau en 1959

210

9.4 Curvas de

Bzier

9.4.3. Polinomios de Bernstein


La teora bsica para calcular las curvas de Bzier se basa en la idea que cada punto

de la curva es un promedio ponderado de los puntos de control. Esto se consigue con


coecientes especialmente diseados para eso. Estos coecientes determinan el polinomio que forma la ecuacin de Bzier, y son conocidos como se denen as:

Polinomios de Bernstein, y

b(i, n, t) =
donde

n (1 t)ni ti i t 1. i

(9.2)

n i

n! i!(ni)! ,

n N, 0

es el grado del polinomio (para las curvas de Bzier normales, es 3.

es el ndice del

polinomio. As, los polinomios de Bernstein de grado 3 son:

b(0, 3, t) = (1 t)3 b(1, 3, t) = 3t(1 t)2 b(2, 3, t) = 3t2 (1 t) b(3, 3, t) = t3


Sus grcas pueden apreciarse en la gura 9.4 en la pgina siguiente. Pero su caracterstica ms importante, servir para generar un promedio ponderado puede apreciarse en la gura 9.5 en la pgina 213: El hecho de que su suma siempre resulte en 1 es lo que permite usarlos para clculos de promedios ponderados uniformemente espaciados.

9.4.4. Generalizacin de curvas de


como:

Bzier de grado n

En funcin de los Polinomios de Bernstein, se denen las curvas de Bzier de n-grado

Bn (t) =
i=0

b(i, n, t)Pi

(9.3)

De modo que podemos enunciar las siguientes curvas de Bzier de primero, segundo, tercer, cuarto y quinto grado (los coecientes numricos responden al Tringulo de Pascal):

B1 (t) = (1 t)P0 + tP1 , 0

1 t 1 t 1

B2 (t) = (1 t)2 P0 + 2t(1 t)P1 + t2 P2 , 0

B3 (t) = (1 t)3 P0 + 3t(1 t)2 P1 + 3t2 (1 t)P2 + t3 P3 , 0

211

9 Curvas Paramtricas

Figura 9.4: Grca de los Polinomios de Bernstein de grado 3

212

9.4 Curvas de

Bzier

Figura 9.5: Aporte de cada uno de los polinomios de Bernstein de grado 3

213

9 Curvas Paramtricas
B4 (t) = (1 t)4 P0 + 4t(1 t)3 P1 + 6t2 (1 t)2 P2 + 4t3 (1 t)P3 + t4 P4 , 0 t 1

B5 (t) = (1 t)5 P0 + 5t(1 t)4 P1 + 10t2 (1 t)3 P2 + 10t3 (1 t)2 P3 + 5t4 (1 t)P4 + t5 P5 , 0 t 1
Usar curvas de Bzier de grado muy alto no reportan signicativas ventajas respecto de los de tercer grado desde el punto de vista de la eciencia de los algoritmos necesarios para manipularlas, pero presentan un comportamiento muy interesante, como puede verse en la seccin Constructing Bzier Curves de [Wikipedia-Bzier Curve]. Debido a que en general slo se usan curvas de Bzier de tercer grado, el nombre de estas se ha generalizado, de tal forma que cuando uno se reere sin ms, a Curvas de

Bzier, se reere implcitamente a Curvas de Bzier de Tercer Grado (a menos que se


especique lo contrario, por supuesto).

9.4.5. Unin de segmentos de Bzier


La gran utilidad de las curvas de Bzier es que son rpidas de calcular, pero tienen la limitante que tienen un nmero jo de puntos de control. A este grupo de puntos se les llama Segmentos de Curva de Bzier o simplemente Segmentos de Bzier. El problema del tamao jo de los segmentos de Bzier, se resuelve ensamblando una serie de segmentos para construir una secuencia de segmentos de Bzier. Obviamente es de nuestro inters, construir una serie de segmentos de Bzier que no slo sea continua, sino tambin suave. Para lograr esto, analicemos lo siguiente: Para que haya continuidad

G0

en la unin de dos curvas de Bzier,

Q,

es

necesario que el ltimo punto de

coincida con el primero de

Q:

P3 = Q0
Para que haya continuidad que el ltimo vector

G1 , es necesario que haya continuidad G0 , y es necesario tangente de P sea linealmente dependiente del primero de Q P2 P3 = k Q0 Q1 , k > 0

y adems tener la misma direccin:

Para que haya continuidad igual al primero de

C1

(muy deseable para la mayora de las aplicaciones),

es necesario adems de continuidad

G1 ,

que el ltimo vector tangente de

sea

Q:

P2 P3 = Q0 Q1

Para poder, entonces, tener una secuencia de segmentos de Bzier (de tercer grado) que generen una curva suave, es necesario garantizar que se cumplan estas tres caractersticas descritas.

214

9.4 Curvas de

Bzier

9.4.6. Ejemplos de implementacin


A continuacin se presentan dos aplicaciones sencillas, ilustrativas de manipulacin de curvas de Bzier que se incluyen en el material adjunto a este libro:

ezierI y ezierP.

El primero permite manipular un solo segmento de Bzier, para familiarizarse con su naturaleza. El segundo permite manipular tres segmentos de Bzier encadenados.

bezier1
Los siguientes archivos presentan una implementacin de segmentos independientes de Bzier:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

Listing 9.7: Archivo de cabecera de operaciones de segmentos de Bzier

// / c09 / bezier / bezier1 . h # include < stdio .h > # define # define # define # define # define # define # define # define n" /* BEZIER_COD_EXITO 0 BEZIER_MSG_EXITO " xito \ n " BEZIER_COD_ERROR_PARAMETROS -1 BEZIER_MSG_ERROR_PARAMETROS " Error de parmetro ( s ) \ n " BEZIER_COD_ERROR_DOMINIO -2 BEZIER_MSG_ERROR_DOMINIO " Error de dominio \ n" BEZIER_COD_ERROR_MEMORIA -3 BEZIER_MSG_ERROR_MEMORIA " Error de solicitud de memoria dinmica \

Contiene los cuatro puntos de control para los segmentos de Bezier . El clculo de lo puntos internos del segmento es el siguiente : B ( t ) = Bx ( t ) i + By ( t ) j Bz ( t ) k , Bx ( t ) = (1 - t ) ^3 * x [0] + 3 t (1 - t ) ^2 * x [1] + 3t ^2(1 - t ) * x [2] + t ^3 * x [3] By ( t ) = (1 - t ) ^3 * y [0] + 3 t (1 - t ) ^2 * y [1] + 3t ^2(1 - t ) * y [2] + t ^3 * y [3] Bz ( t ) = (1 - t ) ^3 * z [0] + 3 t (1 - t ) ^2 * z [1] + 3t ^2(1 - t ) * z [2] + t ^3 * z [3] para 0 <= t <= 1

*/ typedef struct { // coordenadas de los cuatro nodos double x [4]; double y [4]; double z [4]; } segmentoBezier ;

215

9 Curvas Paramtricas
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53

/*

*/ int BEZIER_calcular ( segmentoBezier * sb , double t , double *x , double *y , double * z ) ; /* Dado un valor del parmetro 't ', devuelve los valores del punto calculado del segmento de Bzier . Esta versin asume que los puntos son coplanares en xy .

Dado un valor del parmetro 't ', devuelve los valores del punto calculado del segmento de Bzier . Asume que el segmento es tridimensional .

*/

int BEZIER_calcular2d ( segmentoBezier * sb , double t , double *x , double * y ) ; /* Imprime en la salida especificada los valores almacenados en la estructura .

*/ void BEZIER_imprimir ( segmentoBezier * sb , FILE * f ) ;

Listing 9.8: Cdigo fuente de funciones de segmentos de Bzier

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

// / c09 / bezier / bezier1 . c # include " bezier1 . h " int BEZIER_calcular ( segmentoBezier * sb , double t , double *x , double *y , double * z ) { if ( sb == NULL || x == NULL || y == NULL || z == NULL ) return BEZIER_COD_ERROR_PARAMETROS ; if (t <0.0 || t >1.0) return BEZIER_COD_ERROR_DOMINIO ; double double double double double *x = + *y = + *z = + _1_t1 = 1 - t ; _1_t2 = _1_t1 * _1_t1 ; _1_t3 = _1_t2 * _1_t1 ; t2 = t* t ; t3 = t2 * t ; * sb - > x [0] + 3* t * _1_t2 * sb - > x [1] + 3* t2 * _1_t1 * sb - > x [2] sb - > x [3]; * sb - > y [0] + 3* t * _1_t2 * sb - > y [1] + 3* t2 * _1_t1 * sb - > y [2] sb - > y [3]; * sb - > z [0] + 3* t * _1_t2 * sb - > z [1] + 3* t2 * _1_t1 * sb - > z [2] sb - > z [3];

_1_t3 t3 * _1_t3 t3 * _1_t3 t3 *

216

9.4 Curvas de
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46

Bzier

return BEZIER_COD_EXITO ;

int BEZIER_calcular2d ( segmentoBezier * sb , double t , double *x , double * y ) { if ( sb == NULL || x == NULL || y == NULL ) return BEZIER_COD_ERROR_PARAMETROS ; if (t <0.0 || t >1.0) return BEZIER_COD_ERROR_DOMINIO ; double double double double double _1_t1 = 1 - t ; _1_t2 = _1_t1 * _1_t1 ; _1_t3 = _1_t2 * _1_t1 ; t2 = t * t ; t3 = t2 * t ;

* x = _1_t3 * sb - > x [0] + 3* t * _1_t2 * sb -> x [1] + 3* t2 * _1_t1 * sb - > x [2] + t3 * sb - > x [3]; * y = _1_t3 * sb - > y [0] + 3* t * _1_t2 * sb -> y [1] + 3* t2 * _1_t1 * sb - > y [2] + t3 * sb - > y [3]; return BEZIER_COD_EXITO ;

void BEZIER_imprimir ( segmentoBezier * sb , FILE * f ) { int i ; fprintf (f , " \ nPuntos de control de Bzier :\ n " ) ; fprintf (f , " i \ tx_i \ ty_i \ tz_i \ n " ) ; for ( i =0; i <4; i ++) fprintf (f , " %d \ t %3.2 g \ t %3.2 g\ t %3.2 g \ n " , i +1 , sb - > x[ i ] , sb - > y [ i ] , sb -> z [ i ]) ; }

El siguiente cdigo es una sencilla aplicacin que usa el cdigo de los archivos anteriores para permitirle al usuario, como se mencion antes, manipular un solo segmento de Bzier:

1 2 3 4 5 6 7 8 9 10 11 12

Listing 9.9: Programa principal de manipulacin de un segmento de Bzier

// / c09 / bezier / main1 .c # include < SDL / SDL .h > # include " bezier1 . h " # include < SDL / SDL_gfxPrimitives .h > # define ANCHO 640 # define ALTO 480 # define ANCHO_RECTANGULO 15 # define XMIN 0.0 # define XMAX 15.0

217

9 Curvas Paramtricas
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62

# define YMIN 0.0 # define YMAX 15.0 # define TPASO 0.01 # define TAM 4 static inline int x_real ( double x_virtual ) { return ( int ) ( ANCHO * x_virtual / XMAX ) ; } static inline int y_real ( double y_virtual ) { return ( int ) ( ALTO * (1.0 - y_virtual / YMAX ) ) ; } static inline double x_virtual ( int x_real ) { return ( XMAX * x_real ) / ANCHO ; } static inline double y_virtual ( int y_real ) { return YMAX * (1.0 - y_real / ( double ) ALTO ) ; } Uint32 gfxColor ( Uint8 r , Uint8 g , Uint8 b ) { return r << 24 | g << 16 | b << 8 | 255; // este valor es la opacidad del color // y debe ser mxima para que sea slido } // Variables globales double x [ TAM ] = {1.3 , 3.0 , 6.0 , 13.0}; double y [ TAM ] = {1.3 , 5.0 , 7.4 , 14.2}; segmentoBezier sb ; int i , j , k , l ; double tvar , xvar , yvar ; Uint32 color_fondo , color1 , color2 , color3 ; void dibujo ( SDL_Surface * pantalla ) { i = j = k = l =0; SDL_Rect rect ; // Borra la pantalla SDL_FillRect ( pantalla , NULL , color_fondo ) ; for ( i =1; i <4; i ++) { lineColor ( pantalla , x_real ( sb . x [i -1]) , y_real ( sb . y [i -1]) , x_real ( sb . x [ i ]) , y_real ( sb . y [ i ]) , color3 ) ; } tvar = 0.0;

218

9.4 Curvas de
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110

Bzier

BEZIER_calcular2d (& sb , tvar , & xvar , & yvar ) ; i = x_real ( xvar ) ; j = y_real ( yvar ) ; for ( tvar = TPASO ; tvar <= 1.0; tvar += TPASO ) { if (! BEZIER_calcular2d (& sb , tvar , & xvar , & yvar ) ) { lineColor ( pantalla , i , j , k= x_real ( xvar ) , l = y_real ( yvar ) , color1 ) ; i=k; j=l; } } BEZIER_calcular2d (& sb , 1.0 , & xvar , & yvar ) ; lineColor ( pantalla , i , j , k= x_real ( xvar ) , l = y_real ( yvar ) , color1 ) ; // dibujar los rectngulos de control rect . w = rect . h = ANCHO_RECTANGULO ; for ( i =0; i <4; i ++) { rect . x = x_real ( sb . x [ i ]) - ANCHO_RECTANGULO /2; rect . y = y_real ( sb . y [ i ]) - ANCHO_RECTANGULO /2; rectangleColor ( pantalla , rect .x , rect .y , rect . x + rect .w , rect . y + rect .h , color2 ) ; } // vuelca el buffer en la pantalla : SDL_Flip ( pantalla ) ;

int main ( int argc , char * argv []) { SDL_Surface * pantalla = NULL ; SDL_Event evento ; int profundidad_color ; const SDL_VideoInfo * info ; Uint32 color ; int i ; int corriendo = 1; int seleccionado = 0; if ( SDL_Init ( SDL_INIT_VIDEO ) < 0 ) { fprintf ( stderr , " No se puede iniciar SDL : %s \ n " , SDL_GetError () ) ; exit (1) ; } atexit ( SDL_Quit ) ; // Este if es importante para poder usar SDL_gfx info = SDL_GetVideoInfo () ; if ( info - > vfmt - > BitsPerPixel > 8 ) { profundidad_color = info - > vfmt - > BitsPerPixel ; // printf (" %d \ n " , profundidad_color ) ; } else { profundidad_color = 16;

219

9 Curvas Paramtricas
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153

} pantalla = SDL_SetVideoMode ( ANCHO , ALTO , profundidad_color , SDL_SWSURFACE ) ; if ( pantalla == NULL ) { fprintf ( stderr , " No se puede establecer el modo de video %dx %d : % s\n", ANCHO , ALTO , SDL_GetError () ) ; exit (1) ; } SDL_WM_SetCaption ( " Segmento de Bezier " , NULL ) ; color_fondo = SDL_MapRGB ( pantalla - > format ,0 ,0 ,0) ; color1 = gfxColor (255 ,255 ,255) ; color2 = gfxColor (255 ,0 ,0) ; color3 = gfxColor (0 ,0 ,255) ; for ( i =0; i <4; i ++) { sb . x [ i ] = x [ i ]; sb . y [ i ] = y [ i ]; } // BEZIER_imprimir (& sb , stdout ) ; dibujo ( pantalla ) ; while ( corriendo ) { while ( SDL_PollEvent (& evento ) ) { switch ( evento . type ) { case SDL_MOUSEMOTION :{ if ( seleccionado ) { // actualizar el punto sb . x [ i ]= x_virtual ( evento . button . x ) ; sb . y [ i ]= y_virtual ( evento . button . y ) ; dibujo ( pantalla ) ; } } break ; case SDL_MOUSEBUTTONDOWN :{ for ( i =0; i <4; i ++) { if ( evento . button . button == SDL_BUTTON_LEFT && // si hace clic sobre un nodo ... (( evento . button . x > x_real ( sb . x [ i ]) ANCHO_RECTANGULO /2) && ( evento . button . y > y_real ( sb .y [ i ]) ANCHO_RECTANGULO /2) && ( evento . button . x < x_real ( sb .x [ i ]) + ANCHO_RECTANGULO /2) && ( evento . button . y < y_real ( sb .y [ i ]) + ANCHO_RECTANGULO /2) )

220

9.4 Curvas de
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180

Bzier

){

// se selecciona y el ndice queda en 'i ' seleccionado = 1; // printf (" seleccionado \n ") ; break ;

break ; case SDL_MOUSEBUTTONUP : seleccionado = 0; break ; case SDL_KEYDOWN : if ( evento . key . keysym . sym == SDLK_SPACE ) dibujo ( pantalla ) ; break ; case SDL_QUIT : corriendo = 0; break ;

SDL_Quit () ; return 0;

bezier2
Este programa, presenta una implementacin de una secuencia de tres segmentos de Bzier, con la funcionalidad de forzar continuidad

C1

entre los segmentos o mantener

0 slo continuidad G .
Veamos su uso:

arrastre con clic izquierdo

Sobre los cuadros que representan los nodos de control, per-

mite transformar la secuencia, manteniendo por defecto continuidad uniones de los segmentos.

C1

en las

letra l Alterna el dibujo de las lneas guas entre los nodos de control. letra c Alterna el dibujo de los cuadros que marca la posicin de los nodos de control. letra i Imprime en consola las coordenadas de los nodos de control. letra x Alterna entre forzar continuidad C 1 entre los segmentos (por defecto) o mantener
slo continuidad

G0 .

Cuando se activa la continuidad

C1

la posicin de algunos

nodos es forzada para garantizarla.

221

9 Curvas Paramtricas
Los siguientes archivos presentan una implementacin de una secuencia de segmentos de Bzier de longitud arbitraria:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45

Listing 9.10: Archivo de cabecera para operar secuencias de segmentos de Bzier

// / c09 / bezier / bezier2 . h # include " bezier1 . h " /* ********** Unin de segmentos de Bzier ************************ */ /* Nodo para una lista lineal doble con nodo de control para modelar una secuencia de segmentos de Bzier con al menos un segmento . Pero el nodo de control no es del mismo tipo de los nodos .

*/ typedef struct nodoBezier { // coordenadas de los tres nodos , // el ltimo coincide con el primero // del siguiente segmento . double x [3]; double y [3]; double z [3]; struct nodoBezier * ant ; struct nodoBezier * sig ; } nodoBezier ; /*

*/ typedef struct { // la coordenada del primer punto // del primer segmento : double x , y , z ;

Nodo de control para una secuencia de segmentos de Bzier .

nodoBezier primero ; int numNodos ; // representa el nmero de estructuras , no de puntos int continuidad ; // indica si se garantizar la continuidad de 3 er orden } listaBezier ; /* Imprime en la salida especificada los valores almacenados en la estructura .

*/ void BEZIER_imprimir2 ( listaBezier * lista , FILE * f ) ; /*

222

9.4 Curvas de
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90

Bzier

' numNodos ' representa el nmero de segmentos de Bzier deseados */ int BEZIER_crearLista ( listaBezier * lista , int numNodos , double valorInicial ) ; /* La longitud de los arreglos de entrada 'x ' y 'y ' se asume a '3* lista - > numNodos +1 '. Tambin se asume que los puntos son coplanares en xy . Se asume que la ' lista ' ya ha sido creada .

*/ int BEZIER_modificarLista2d ( listaBezier * lista , double *x , double * y ) ; /* La longitud de los arreglos de entrada 'x ', 'y ' y 'z ' se asume a '3* lista - > numNodos +1 '. Se asume que la ' lista ' ya ha sido creada .

*/ int BEZIER_modificarLista ( listaBezier * lista , double *x , double *y , double * z ) ; /* Libera la memoria utilizada para almacenar los ' nodoBezier '

*/ void BEZIER_liberarLista ( listaBezier * lista ) ; /* Dado un valor del parmetro 't ', devuelve los valores del punto calculado del segmento de Bzier . Asume que el segmento es tridimensional .

*/ int BEZIER_calculardeLista ( listaBezier * lista , double t , double *x , double *y , double * z ) ; /* Dado un valor del parmetro 't ', devuelve los valores del punto calculado del segmento de Bzier . Esta versin asume que los puntos son coplanares en xy .

*/ int BEZIER_calculardeLista2d ( listaBezier * lista , double t , double *x , double * y ) ; /* Dado un ndice de punto , actualizar el nodo de Bzier correspondiente */ int BEZIER_actualizarNodo ( listaBezier * lista , int ix , double x , double y , double z ) ;

223

9 Curvas Paramtricas
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137

/*

*/ int BEZIER_recuperarNodo2d ( listaBezier * lista , int ix , double *x , double *y); /* Dado un ndice , recuperar el punto en las direcciones de 'x ', 'y ' y 'z '.

Dado un ndice , recuperar el punto en las direcciones de 'x ' y 'y '.

*/ int BEZIER_recuperarNodo ( listaBezier * lista , int ix , double *x , double *y , double * z ) ; /* Copia los valores de todos los puntos a los arreglos 'x ', 'y ' y 'z '. Estos arreglos deben contener suficiente espacio .

*/ int BEZIER_recuperarNodos ( listaBezier * lista , double *x , double *y , double * z ) ; /*

*/ int BEZIER_recuperarNodos2d ( listaBezier * lista , double *x , double * y ) ; /* Garantiza que la curva sea continua , forzando algunos puntos . Tendrn prioridad los puntos previos .

Copia los valores de todos los puntos a los arreglos 'x ' y 'y '. Estos arreglos deben contener suficiente espacio . Asume que los puntos son coplanares en xy .

*/ int BEZIER_acomodarContinua ( listaBezier * lista ) ;

void BEZIER_activarContinuidad ( listaBezier * lista ) ; void BEZIER_desactivarContinuidad ( listaBezier * lista ) ; /* * Agrega un segmento nuevo de Bzier * al final de la Lista especificada * */ int BEZIER_agregarNodoNuevoaLista ( listaBezier * lista , int valorInicial ) ; /* * Agrega un segmento especificado de Bzier * al final de la Lista especificada

224

9.4 Curvas de
138 139

Bzier

* */ int BEZIER_agregarNodoaLista ( listaBezier * lista , nodoBezier * nb ) ;

Listing 9.11: Cdigo fuente de funciones de secuencias de segmentos de Bzier

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42

// / c09 / bezier / bezier2 . c # include " bezier2 . h " # include < stdlib .h > void BEZIER_imprimir2 ( listaBezier * lista , FILE * f ) { nodoBezier * nb = NULL ; int i , j ; if ( lista == NULL ) return ; fprintf (f , " \ nPuntos de control de Bzier :\ n " ) ; fprintf (f , " i \ tx_i \ ty_i \ tz_i \ n " ) ; // imprimir primer nodo fprintf (f , " %d \ t %3.2 g \ t %3.2 g\ t %3.2 g \ n " , i =1 , lista - >x , lista - >y , lista - > z ) ; // ' primero ' no es un puntero for ( nb = &( lista - > primero ) ; nb != NULL ; nb =nb - > sig ) { fprintf (f , " -- -\ n " ) ; for ( j =0; j <3; j ++) fprintf (f , " %d \ t %3.2 g \ t %3.2 g \ t %3.2 g \ n " , ++ i , nb - > x [ j ] , nb - > y [ j ] , nb - > z [j ]) ; }

int BEZIER_crearLista ( listaBezier * lista , int numNodos , double valorInicial ) { if ( lista == NULL || numNodos < 1) return BEZIER_COD_ERROR_PARAMETROS ; nodoBezier * temp = NULL , * temp2 = NULL ; lista - > numNodos = numNodos ; lista - > x = lista - > y = lista - > z = lista - > primero . x [0] = lista - > primero . x [1] = lista - > primero .x [2] = lista - > primero . y [0] = lista - > primero . y [1] = lista - > primero .y [2] = valorInicial ; lista - > continuidad = 1; lista - > primero . sig = lista - > primero . ant = NULL ; for ( numNodos - -; numNodos >0; numNodos - -) { if (!( temp = ( nodoBezier *) malloc ( sizeof ( nodoBezier ) ) ) ) return BEZIER_COD_ERROR_MEMORIA ; if ( temp2 == NULL ) { // es el primero creado dinmicamente lista - > primero . sig = temp2 = temp ;

225

9 Curvas Paramtricas
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91

} return BEZIER_COD_EXITO ;

temp - > sig = NULL ; temp - > ant = &( lista - > primero ) ; } else { // ya hay un nodo anterior en ' temp2 ' temp - > sig = NULL ; temp - > ant = temp2 ; temp2 - > sig = temp ; temp2 = temp ; } temp - > x [0]= temp - > x [1]= temp - > x [2]= temp - > y [0]= temp - > y [1]= temp - > y [2]= valorInicial ; temp = NULL ;

int BEZIER_modificarLista2d ( listaBezier * lista , double *x , double * y ) { if ( lista == NULL || x == NULL || y == NULL ) return BEZIER_COD_ERROR_PARAMETROS ; nodoBezier * temp = NULL ; int i =0 , j ; lista - > x = x [ i ]; lista - > y = y [ i ++]; lista - > z = 0.0; for ( temp = &( lista - > primero ) ; temp != NULL ; temp = temp - > sig ) for ( j =0; j <3; j ++) { temp - > x [ j ] = x [ i ]; temp - > y [ j ] = y [ i ++]; temp - > z [ j ] = 0.0; } if ( lista - > continuidad ) BEZIER_acomodarContinua ( lista ) ; return BEZIER_COD_EXITO ;

int BEZIER_modificarLista ( listaBezier * lista , double *x , double *y , double * z ) { if ( lista == NULL || x == NULL || y == NULL || z == NULL ) return BEZIER_COD_ERROR_PARAMETROS ; nodoBezier * temp = NULL ; int i =0 , j ; lista - > x = x [ i ]; lista - > y = y [ i ]; lista - > z = z [ i ++]; for ( temp = &( lista - > primero ) ; temp != NULL ; temp = temp - > sig ) for ( j =0; j <3; j ++) { temp - > x [ j ] = x [ i ];

226

9.4 Curvas de
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138

Bzier

} if ( lista - > continuidad ) BEZIER_acomodarContinua ( lista ) ; return BEZIER_COD_EXITO ;

temp - > y [ j ] = y [ i ]; temp - > z [ j ] = z [ i ++];

void BEZIER_liberarLista ( listaBezier * lista ) { if ( lista == NULL ) return ; nodoBezier * nb = lista - > primero . sig , * temp = NULL ; while ( nb ) { temp = nb - > sig ; free ( nb ) ; nb = temp ; } } int BEZIER_calculardeLista2d ( listaBezier * lista , double t , double *x , double * y ) { if ( lista == NULL || x == NULL || y == NULL ) return BEZIER_COD_ERROR_PARAMETROS ; if (t <0.0 || t >1.0) return BEZIER_COD_ERROR_DOMINIO ; nodoBezier * temp = NULL , * nb = NULL ; int i =0 , j ; // calcular el ndice del nodo ' nodoBezier ' // con el que debe calcularse este 't ' int ixBezier = ( int ) ( t * lista - > numNodos ) ; if ( ixBezier == lista - > numNodos ) ixBezier - -; t = lista - > numNodos * t - ixBezier ; double double double double double double t1 = t ; t2 = t * t ; t3 = t2 * t ; _1_t1 = 1 - t ; _1_t2 = _1_t1 * _1_t1 ; _1_t3 = _1_t1 * _1_t2 ;

nb = temp = &( lista - > primero ) ; if ( ixBezier ==0) { // es el primer ' nodoBezier ' * x = _1_t3 * lista - > x + 3* t1 * _1_t2 * nb - > x [0] + 3* t2 * _1_t1 * nb - > x [1] + t3 * nb - > x [2]; * y = _1_t3 * lista - > y + 3* t1 * _1_t2 * nb - > y [0] + 3* t2 * _1_t1 * nb - > y [1] + t3 * nb - > y [2]; } else { // no es el primer ' nodoBezier ' con el que se calcular 't '

227

9 Curvas Paramtricas
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182

} }

i ++; for ( temp = temp - > sig ; temp != NULL ; temp = temp - > sig ) if ( i == ixBezier ) { nb = temp ; break ; } else i ++; // aqu se asume que siempre lo encuentra ... * x = _1_t3 * nb - > ant - > x [2] + 3* t1 * _1_t2 * nb - > x [0] + 3* t2 * _1_t1 * nb - > x [1] + t3 * nb - > x [2]; * y = _1_t3 * nb - > ant - > y [2] + 3* t1 * _1_t2 * nb - > y [0] + 3* t2 * _1_t1 * nb - > y [1] + t3 * nb - > y [2];

return BEZIER_COD_EXITO ;

int BEZIER_calculardeLista ( listaBezier * lista , double t , double *x , double *y , double * z ) { if ( lista == NULL || x == NULL || y == NULL || z == NULL ) return BEZIER_COD_ERROR_PARAMETROS ; if (t <0.0 || t >1.0) return BEZIER_COD_ERROR_DOMINIO ; nodoBezier * temp = NULL , * nb = NULL ; int i =0 , j ; // calcular el ndice del nodo ' nodoBezier ' // con el que debe calcularse este 't ' int ixBezier = ( int ) ( t * lista - > numNodos ) ; if ( ixBezier == lista - > numNodos ) ixBezier - -; t = lista - > numNodos * t - ixBezier ; double double double double double double t1 = t; t2 = t* t ; t3 = t2 * t ; _1_t1 = 1 - t ; _1_t2 = _1_t1 * _1_t1 ; _1_t3 = _1_t1 * _1_t2 ;

nb = temp = &( lista - > primero ) ; if ( ixBezier ==0) { // es el primer ' nodoBezier ' * x = _1_t3 * lista - > x + 3* t1 * _1_t2 * nb - > x [0] x [1] + t3 * nb - > x [2]; * y = _1_t3 * lista - > y + 3* t1 * _1_t2 * nb - > y [0] y [1] + t3 * nb - > y [2]; * z = _1_t3 * lista - > z + 3* t1 * _1_t2 * nb - > z [0] z [1] + t3 * nb - > z [2]; } else { // no es el primer ' nodoBezier ' con el que

+ 3* t2 * _1_t1 * nb - > + 3* t2 * _1_t1 * nb - > + 3* t2 * _1_t1 * nb - > se calcular 't '

228

9.4 Curvas de
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227

Bzier

} }

i ++; for ( temp = temp - > sig ; temp != NULL ; temp = temp - > sig ) if ( i == ixBezier ) { nb = temp ; break ; } else i ++; // aqu se asume que siempre lo encuentra ... * x = _1_t3 * nb - > ant - > x [2] + 3* t1 * _1_t2 * nb - > x [0] + 3* t2 * _1_t1 * nb - > x [1] + t3 * nb - > x [2]; * y = _1_t3 * nb - > ant - > y [2] + 3* t1 * _1_t2 * nb - > y [0] + 3* t2 * _1_t1 * nb - > y [1] + t3 * nb - > y [2]; * z = _1_t3 * nb - > ant - > z [2] + 3* t1 * _1_t2 * nb - > z [0] + 3* t2 * _1_t1 * nb - > z [1] + t3 * nb - > z [2];

return BEZIER_COD_EXITO ;

int BEZIER_actualizarNodo2d ( listaBezier * lista , int ix , double x , double y){ return BEZIER_actualizarNodo ( lista , ix , x , y , 0.0) ; } int BEZIER_actualizarNodo ( listaBezier * lista , int ix , double x , double y , double z ) { if ( lista == NULL || ix < 0) return BEZIER_COD_ERROR_PARAMETROS ; nodoBezier * temp = NULL ; int i =0 , j ; if ( ix ==0) { lista - > x = x ; lista - > y = y ; lista - > z = z ; return BEZIER_COD_EXITO ; } i ++; for ( temp = &( lista - > primero ) ; temp != NULL ; temp = temp - > sig ) for ( j =0; j <3; j ++) if ( i == ix ) { // Mecanismo de coordinacin con los puntos adyacentes : if ( lista - > continuidad ) switch ( j ) { case 0:{ /* Desplazar el penltimo punto del nodo anterior , si hay nodo anterior . Esto es para garantizar la colinealidad

229

9 Curvas Paramtricas
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274

} else i ++;

} temp - > x [ j ]= x ; temp - > y [ j ]= y ; temp - > z [ j ]= z ; return BEZIER_COD_EXITO ;

} break ; case 1:{ /* Desplazar el primer punto del nodo siguiente , si hay nodo siguiente . Esto es para garantizar la colinealidad en las uniones de los segmentos de Bzier . */ if ( temp - > sig ) { temp - > sig - > x [0] = temp - > x [2] - (x - temp - > x [2]) ; temp - > sig - > y [0] = temp - > y [2] - (y - temp - > y [2]) ; temp - > sig - > z [0] = temp - > z [2] - (z - temp - > z [2]) ; } } break ; case 2:{ /* Desplazar el punto anterior y el siguiente , si es que hay un siguiente . */ if ( temp - > sig ) { temp - > x [1] += x - temp - > x [2]; temp - > y [1] += y - temp - > y [2]; temp - > z [1] += z - temp - > z [2]; temp - > sig - > x [0] += x - temp - > x [2]; temp - > sig - > y [0] += y - temp - > y [2]; temp - > sig - > z [0] += z - temp - > z [2]; } } break ;

en las uniones de los */ if ( temp - > ant ) { temp - > ant - > x [1] = - > x [2]) ; temp - > ant - > y [1] = - > y [2]) ; temp - > ant - > z [1] = - > z [2]) ; }

segmentos de Bzier . temp - > ant - > x [2] - (x - temp - > ant temp - > ant - > y [2] - (y - temp - > ant temp - > ant - > z [2] - (z - temp - > ant

230

9.4 Curvas de
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322

Bzier

return BEZIER_COD_ERROR_DOMINIO ;

int BEZIER_recuperarNodo2d ( listaBezier * lista , int ix , double *x , double *y){ if ( lista == NULL || ix < 0 || x == NULL || y == NULL ) return BEZIER_COD_ERROR_PARAMETROS ; nodoBezier * temp = NULL ; int i =0 , j ; if ( ix ==0) { * x = lista - > x ; * y = lista - > y ; return BEZIER_COD_EXITO ; } i ++; for ( temp = &( lista - > primero ) ; temp != NULL ; temp = temp - > sig ) for ( j =0; j <3; j ++) if ( i == ix ) { * x = temp - > x [ j ]; * y = temp - > y [ j ]; return BEZIER_COD_EXITO ; } else i ++; return BEZIER_COD_ERROR_DOMINIO ;

int BEZIER_recuperarNodo ( listaBezier * lista , int ix , double *x , double *y , double * z ) { if ( lista == NULL || ix < 0 || x == NULL || y == NULL || z == NULL ) return BEZIER_COD_ERROR_PARAMETROS ; nodoBezier * temp = NULL ; int i =0 , j ; if ( ix ==0) { * x = lista - > x ; * y = lista - > y ; * z = lista - > z ; return BEZIER_COD_EXITO ; } i ++; for ( temp = &( lista - > primero ) ; temp != NULL ; temp = temp - > sig ) for ( j =0; j <3; j ++) if ( i == ix ) { * x = temp - > x [ j ]; * y = temp - > y [ j ]; * z = temp - > z [ j ]; return BEZIER_COD_EXITO ;

231

9 Curvas Paramtricas
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371

} else i ++; return BEZIER_COD_ERROR_DOMINIO ;

int BEZIER_recuperarNodos ( listaBezier * lista , double *x , double *y , double * z ) { if ( lista == NULL || x == NULL || y == NULL || z == NULL ) return BEZIER_COD_ERROR_PARAMETROS ; nodoBezier * temp = NULL ; int i =0 , j ; x [ i ] = lista - > x ; y [ i ] = lista - > y ; z [ i ++]= lista - > z ; for ( temp = &( lista - > primero ) ; temp != NULL ; temp = temp - > sig ) for ( j =0; j <3; j ++) { x [ i ] = temp - > x [ j ]; y [ i ] = temp - > y [ j ]; z [ i ++]= temp - > z [ j ]; } return BEZIER_COD_EXITO ;

int BEZIER_recuperarNodos2d ( listaBezier * lista , double *x , double * y ) { if ( lista == NULL || x == NULL || y == NULL ) return BEZIER_COD_ERROR_PARAMETROS ; nodoBezier * temp = NULL ; int i =0 , j ; x [ i ] = lista - > x ; y [ i ++]= lista - > y ; for ( temp = &( lista - > primero ) ; temp != NULL ; temp = temp - > sig ) for ( j =0; j <3; j ++) { x [ i ] = temp - > x [ j ]; y [ i ++]= temp - > y [ j ]; } return BEZIER_COD_EXITO ;

int BEZIER_acomodarContinua ( listaBezier * lista ) { if ( lista == NULL ) return BEZIER_COD_ERROR_PARAMETROS ; nodoBezier * temp = NULL ; for ( temp = (&( lista - > primero ) ) -> sig ; temp != NULL ; temp = temp - > sig ){ /*

232

9.4 Curvas de
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421

Bzier

} }

Desplazar el primer punto del nodo actual . Esto es para garantizar la colinealidad en las uniones de los segmentos de Bzier . */ temp - > x [0] = temp - > ant - > x [2] - ( temp - > ant - > x [1] - temp - > ant - >x [2]) ; temp - > y [0] = temp - > ant - > y [2] - ( temp - > ant - > y [1] - temp - > ant - >y [2]) ; temp - > z [0] = temp - > ant - > z [2] - ( temp - > ant - > z [1] - temp - > ant - >z [2]) ;

return BEZIER_COD_EXITO ;

void BEZIER_activarContinuidad ( listaBezier * lista ) { lista - > continuidad = 1; } void BEZIER_desactivarContinuidad ( listaBezier * lista ) { lista - > continuidad = 0; } int BEZIER_agregarNodoNuevoaLista ( listaBezier * lista , int valorInicial ) { if ( lista == NULL ) return BEZIER_COD_ERROR_PARAMETROS ; nodoBezier * temp = NULL , * temp2 = NULL ; if (!( temp = ( nodoBezier *) malloc ( sizeof ( nodoBezier ) ) ) ) return BEZIER_COD_ERROR_MEMORIA ; for ( temp2 =&( lista - > primero ) ; temp2 - > sig ; temp2 = temp2 - > sig ) ; temp - > ant = temp - > sig = NULL ; temp2 - > sig = temp ; temp - > ant = temp2 ; lista - > numNodos ++; temp - > x [0]= temp - > x [1]= temp - > x [2]= temp - > y [0]= temp - > y [1]= temp - > y [2]= valorInicial ; } return BEZIER_COD_EXITO ;

int BEZIER_agregarNodoaLista ( listaBezier * lista , nodoBezier * nb ) { if ( lista == NULL || nb == NULL ) return BEZIER_COD_ERROR_PARAMETROS ; nodoBezier * temp2 = NULL ;

233

9 Curvas Paramtricas
422 423 424 425 426 427 428 429 430 431

for ( temp2 =&( lista - > primero ) ; temp2 - > sig ; temp2 = temp2 - > sig ) ; nb - > ant = nb - > sig = NULL ; temp2 - > sig = nb ; nb - > ant = temp2 ; lista - > numNodos ++; } return BEZIER_COD_EXITO ;


El siguiente archivo permite la manipulacin de una unin de tres segmentos de Bzier, permitiendo alternar la continuidad en sus puntos de unin, entre

C1

G0 :

Listing 9.12: Programa principal de manipulacin de una secuencia de segmentos de

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

// / c09 / bezier / main2 . c # include < SDL / SDL .h > # include " bezier2 . h " # include < SDL / SDL_gfxPrimitives .h > # define ANCHO 640 # define ALTO 480 # define ANCHO_RECTANGULO 15 # define # define # define # define # define XMIN 0.0 XMAX 15.0 YMIN 0.0 YMAX 15.0 TPASO 0.01

Bzier

# define TAM 10 // Debe cumplirse la siguiente relacin : // numPuntos = 3* numNodos +1; double x [ TAM ] = {1.3 , 3.0 , 6.0 , 5.0 , 4.0 , 5.0 , 6.0 , 8.0 , 10.0 , 9.0}; double y [ TAM ] = {1.3 , 5.0 , 7.4 , 9.2 , 2.0 , 3.0 , 4.0 , 5.0 , 7.0 , 4.0}; listaBezier lista ; static inline int x_real ( double x_virtual ) { return ( int ) ( ANCHO * x_virtual / XMAX ) ; } static inline int y_real ( double y_virtual ) { return ( int ) ( ALTO * (1.0 - y_virtual / YMAX ) ) ; } static inline double x_virtual ( int x_real ) { return ( XMAX * x_real ) / ANCHO ; }

234

9.4 Curvas de
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79

Bzier

static inline double y_virtual ( int y_real ) { return YMAX * (1.0 - y_real / ( double ) ALTO ) ; } Uint32 gfxColor ( Uint8 r , Uint8 g , Uint8 b ) { return r << 24 | g << 16 | b << 8 | 255; // este valor es la opacidad del color // y debe ser mxima para que sea slido } // Variables globales int i , j , k , l ; double tvar , xvar , yvar ; Uint32 color_fondo , color_curva , color_lineas , color_union_nodos , color_union ; int mostrar_lineas , mostrar_cuadrados ; void dibujo ( SDL_Surface * pantalla ) { i = j = k = l =0; SDL_Rect rect ; // Borra la pantalla SDL_FillRect ( pantalla , NULL , color_fondo ); // dibujar los rectngulos de control rect . w = rect . h = ANCHO_RECTANGULO ; if ( mostrar_cuadrados ) for ( i =0; i < TAM ; i ++) { rect . x = x_real ( x [i ]) - ANCHO_RECTANGULO /2; rect . y = y_real ( y [i ]) - ANCHO_RECTANGULO /2; if ( lista . continuidad ) switch ( i %3){ case 0: rectangleColor ( pantalla , rect .x , rect .y , rect . x + rect .w , rect . y + rect .h , color_union_nodos ) ; break ; default : rectangleColor ( pantalla , rect .x , rect .y , rect . x + rect .w , rect . y + rect .h , color_union ) ; break ; } else rectangleColor ( pantalla , rect .x , rect .y , rect . x + rect .w , rect . y + rect .h , color_union_nodos ) ; }

235

9 Curvas Paramtricas
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127

// dibujar lneas entre los nodos if ( mostrar_lineas ) for ( i =1; i < TAM ; i ++) { lineColor ( pantalla , x_real ( x [i -1]) , y_real ( y [i -1]) , x_real ( x [ i ]) , y_real ( y [ i ]) , color_lineas ) ; } // calcular los puntos de la curva tvar = 0.0; BEZIER_calculardeLista2d (& lista , tvar , & xvar , & yvar ) ; i = x_real ( xvar ) ; j = y_real ( yvar ) ; for ( tvar = TPASO ; tvar <= 1.0; tvar += TPASO ) { if (! BEZIER_calculardeLista2d (& lista , tvar , & xvar , & yvar ) ) { lineColor ( pantalla , i , j , k = x_real ( xvar ) , l = y_real ( yvar ) , color_curva ) ; i=k; j=l; } } BEZIER_calculardeLista2d (& lista , tvar , & xvar , & yvar ) ; lineColor ( pantalla , i , j , k = x_real ( xvar ) , l = y_real ( yvar ) , color_curva ); // vuelca el buffer en la pantalla : SDL_Flip ( pantalla ) ;

int main ( int argc , char * argv []) { SDL_Surface * pantalla = NULL ; SDL_Event evento ; int profundidad_color ; const SDL_VideoInfo * info ; Uint32 color ; int i; int corriendo = 1; int seleccionado = 0; mostrar_cuadrados = 1; mostrar_lineas = 1; if ( SDL_Init ( SDL_INIT_VIDEO ) < 0 ) { fprintf ( stderr , " No se puede iniciar SDL : %s \n " , SDL_GetError () ) ; exit (1) ; } atexit ( SDL_Quit ) ; // Este if es importante para poder usar SDL_gfx info = SDL_GetVideoInfo () ;

236

9.4 Curvas de
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174

Bzier

if ( info - > vfmt - > BitsPerPixel > 8 ) { profundidad_color = info - > vfmt - > BitsPerPixel ; // printf (" %d \ n " , profundidad_color ) ; } else { profundidad_color = 16; } pantalla = SDL_SetVideoMode ( ANCHO , ALTO , profundidad_color , SDL_SWSURFACE ) ; if ( pantalla == NULL ) { fprintf ( stderr , " No se puede establecer el modo de video %dx %d : % s\n", ANCHO , ALTO , SDL_GetError () ) ; exit (1) ; } SDL_WM_SetCaption ( " Segmentos de Bezier " , NULL ) ; color_fondo = SDL_MapRGB ( pantalla - > format ,0 ,0 ,0) ; color_curva = gfxColor (255 ,255 ,255) ; // lnea color_lineas = gfxColor (0 ,0 ,255) ; // unin puntos color_union_nodos = gfxColor (255 ,0 ,0) ; // unin de nodos color_union = gfxColor (255 ,255 ,0) ; // puntos adyacentes BEZIER_crearLista (& lista , ( TAM -1) /3 ,0.0) ; BEZIER_modificarLista2d (& lista , x , y ) ; BEZIER_recuperarNodos2d (& lista , x , y ) ; BEZIER_imprimir2 (& lista , stdout ) ; dibujo ( pantalla ) ; while ( corriendo ) { while ( SDL_PollEvent (& evento ) ) { switch ( evento . type ) { case SDL_MOUSEMOTION :{ if ( seleccionado ) { // actualizar el punto x [ i ]= x_virtual ( evento . button . x ) ; y [ i ]= y_virtual ( evento . button . y ) ; BEZIER_actualizarNodo2d (& lista , i , x [ i ] , y [ i ]) ; BEZIER_recuperarNodos2d (& lista , x , y ) ; dibujo ( pantalla ) ; } } break ; case SDL_MOUSEBUTTONDOWN :{ for ( i =0; i < TAM ; i ++) { if ( evento . button . button == SDL_BUTTON_LEFT && // si hace clic sobre un nodo ... (( evento . button . x > x_real ( x [ i ]) -

237

9 Curvas Paramtricas
ANCHO_RECTANGULO /2) && ( evento . button . y > y_real ( y [ i ]) ANCHO_RECTANGULO /2) && ( evento . button . x < x_real ( x [ i ]) + ANCHO_RECTANGULO /2) && ( evento . button . y < y_real ( y [ i ]) + ANCHO_RECTANGULO /2) ) // se selecciona y el n d i c e queda en 'i ' seleccionado = 1; // printf (" seleccionado \ n ") ; break ;

175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220

){

break ; case SDL_MOUSEBUTTONUP : seleccionado = 0; break ; case SDL_KEYDOWN : switch ( evento . key . keysym . sym ) { case SDLK_i : { BEZIER_imprimir2 (& lista , stdout ) ; } break ; case SDLK_l : { mostrar_lineas = ! mostrar_lineas ; dibujo ( pantalla ) ; } break ; case SDLK_c : { mostrar_cuadrados = ! mostrar_cuadrados ; dibujo ( pantalla ) ; } break ; case SDLK_r : { dibujo ( pantalla ) ; } break ; case SDLK_x : { lista . continuidad = ! lista . continuidad ; if ( lista . continuidad ) { BEZIER_acomodarContinua (& lista ) ; BEZIER_recuperarNodos2d (& lista , x , y ) ; } dibujo ( pantalla ) ; } break ; }

238

9.4 Curvas de
221 222 223 224 225 226 227 228 229 230 231 232

Bzier

break ; case SDL_QUIT : corriendo = 0; break ;

SDL_Quit () ; return 0;

editorBezier.py
Este programa fue implementado con el doble propsito de servir como ejemplo para este captulo y para hacer la parte principal de la cartula de este libro. Sirve para crear una serie de Secuencias de segmentos de Bzier. Las opciones de interaccin son:

arrastre con clic izquierdo


mite moverlos.

Sobre los cuadros que representan los puntos de control, per-

Sobre el fondo blanco, mueve el marco virtual de todas las secuencias.

arrastre con clic izquierdo+CTRL

Sobre algn punto de control, mueve toda la secuen-

cia a la que pertenece el punto.

ruedas del ratn Implementa acercamiento/alejamiento. ruedas del ratn + tecla x/y Expande/Contrae el marco virtual horizontalmente/verticalmente.

CTRL+tecla L Alterna dibujo de las lneas rectas que unen los puntos de control. CTRL+tecla C Alterna dibujo de los rectngulos que marcan los puntos de control. CTRL+tecla A Agrega una nueva Secuencia de segmentos de Bzier de manera aleatoria.

CTRL+tecla X

Alterna entre forzar continuidad

C1

entre los segmentos (por defecto)

0 o mantener slo continuidad G . Cuando se activa la continuidad

C1

la posicin

de algunos nodos es forzada para garantizarla. Igual que en la aplicacin anterior.

CTRL+tecla G Guarda el archivo (interfaz de lnea de comandos). clic derecho Sobre un punto de control, agrega tres puntos ms a partir de ah. clic central Sobre un punto de control, borra todo un segmento de una secuencia.
239

9 Curvas Paramtricas

ruedas del ratn+CTRL Aumento/disminucin del grosor de la curva. ruedas del ratn+SHIFT Aumento/disminucin del tamao de los cuadros de control. CTRL+tecla Arriba/Abajo Aumento/disminucin del grueso de los cuadros de control. SHIFT+tecla Arriba/Abajo Aumento/disminucin del detalle/nura de las curvas

9.5.

Splines vs. Bzier

Las curvas de Bzier no requieren de clculos previos para el clculo de los puntos que la componen, a diferencia de las curvas splines. Adems, el clculo de cada punto no requiere una bsqueda como en el caso de splines. Esto permite que las curvas de Bzier sean ms atractivas para el uso de diseo interactivo que las splines. Aunque hay que considerar que los puntos de control de Bzier, no pertenecen todos a la curva generada, y en consecuencia, su uso es menos natural que sus contrapartes splines. Adems, el usuario tendr que distinguir cules puntos de control s interpolan la curva y cules controlan los vectores tangentes. Para poder lograr esto, habr que enriquecer ms la interfaz, lo que requiere clculos adicionales. Para que un usuario no matemtico modele algo usando splines, slo tendra que aprender a agregar y eliminar puntos de control de la curva, mientras que si usa Bzier, tendr que pasar por un proceso de aprendizaje mayor para entender los efectos de cambiar los puntos de control sobre las curvas o supercies.

9.6.

Ejercicios
2 (t) = 2t i+ 5 t 1 y sea (t) = (4t 2) i t4 + 3t2 3 2 t j para 0 2 j 5 para 1 t 2. Observe que (1) = 2, 2 = (1), de manera que ambas curvas 0 se unen con continuidad C .
a ) Graque b) c)

1. Mencione las diferencias entre las curvas paramtricas Spline y Bzier. 2. Sea

(t) y (t) para 0 t 1 y 1 t 2 respectivamente. 1 Determine si (t) y (t) cumplen con continuidad G en el punto de unin. d d Recuerde que deber evaluar dt (t = 1) y dt (t = 1). 1 Determine si (t) y (t) cumplen con continuidad C en el punto de unin.
1 4 2 3 7 2 2t 9t + 6t 2 16 sin(2t) + 9
se unen en

3. Muestre que las dos curvas

f (t) =

g (t) = G1 cuando

5 3 2 +1 3t i + 3t t t + 12 i cos t j , con 1 3 3

3 18 3 3

j,

con

tienen continuidad

C1

f (1) = g (1).

240

9.6 Ejercicios
4. Modique el programa presentado en la subseccin 9.3.7 en la pgina 178, para que permita al usuario agregar y eliminar nodos de control. 5. Modique el programa presentado en el apartado bezier2 de la subseccin 9.4.6 en la pgina 221, para que adems de permitir elegir entre mantener continuidad y

C1

G0

tambin permita mantener continuidad

G1

(sin

C 1,

obviamente).

241

9 Curvas Paramtricas

242

10 Supercies paramtricas
10.1. Representacin algebrica de Supercies paramtricas

De manera anloga a las curvas paramtricas, las supercies paramtricas se pueden representar de las siguientes maneras:

r(s, t) = x(s, t) i + y (s, t) j + z (s, t)k r(u, v ) = x(u, v ) i + y (u, v ) j + z (u, v )k x = x(s, t) y = y (s, t) , restricciones z = z (s, t)
Veamos algunos ejemplos de supercies paramtricas: Cilindro hueco de altura

y radio

R: x = R cos z = R sin y=v

0 2 0vH

En la gura 10.1 en la pgina siguiente se presenta un cilindro con generado con el comando

HD TAY

R = 4 y H = 6, plotQd@RBos@thADvDRBsin@thAD thD HD PB 7piD vD

de Maxima.

Toroide con radio central

R y radio lateral r (ver gura 10.2): x = (r cos + R) cos 0 2 y = r sin , 0 2 z = (r cos + R) sin

243

10 Supercies paramtricas

Figura 10.1: Cilindro generado con Maxima

Figura 10.2: Toroide

244

10.2 Ejercicios

Figura 10.3: Cinta de Mbius

Cinta de Mbius con radio de la cinta

R y un ancho de la cinta A: u x = Av sin 2 + R cos u 0 u 2 , y = Av sin u 1 1 2 + R sin u 2 v 2 u z = Av cos 2 + R A=2


y

En la gura 10.3 se presenta una cinta de Mbius con

R = 5,

generada con el

1 2 3 4 5 6

script de Maxima:

A: 2$ R: 5$ x : ( A * v * sin ( u /2) + R ) * cos ( u ) $ y : ( A * v * sin ( u /2) + R ) * sin ( u ) $ z : A * v * cos ( u /2) + R$ plot3d ([ x ,y , z ] , [u , 0 ,2* % pi ] , [v , -1/2 , 1/2] , [ ' grid , 60 ,20]) ;

Supercie paramtrica suave

r(r, t) es una curva suave en {[a, b] [c, d]} si la derivada direccional Du r (s, t) es continua en todos los puntos de {[a, b] [c, d]} para cualquier direccin u .

10.2.

Ejercicios

1. Escriba la ecuacin paramtrica de un sorbete que est formado por la mitad superior de una bola con centro en el origen y radio 5, y un cono circular con la

245

10 Supercies paramtricas
base en el plano

xy

y la punta en

(0, 0, 15).

Sugerencia 1: Hacer la parametrizacin en coordenadas cilndricas. Sugerencia 2: Es una ecuacin seccionada.

246

11 Mallas Poligonales
Una manera alternativa de modelar cuerpos tridimensionales arbitrarios, es aproximando sus superces con lo que se conoce como Mallas Poligonales. Las mallas poligonales son conjuntos de puntos, aristas (lneas) y polgonos relacionados entre s con el objetivo de aproximar un cuerpo o una supercie. Existen diversas maneras de implementar tales estructuras de datos. Algunas requieren ms memoria que otras, algunas requieren algoritmos ms sosticados para operarlas que otras, algunas posibilitan ciertos anlisis que otras no. Todo depender de las necesidades concretas de la aplicacin a desarrollar. En este punto es conveniente volver a tener frescos los conceptos de geometra analtica vectorial relacionados con planos. Tambin recomendamos leer el captulo 14 en la pgina 283 para poder comprender mejor las descripcines de las estructuras de datos. A continuacin se presentarn algunas representaciones genricas diferentes de mallas poligonales (las primeras tres, adaptadas de [Foley et al., p. 366-367]):

11.1.

Representacin explcita

En esta representacin, los objetos tridimensionales se representan como una lista de polgonos; y cada polgono se representa como una lista propia de los puntos que lo conforman. El diagrama de clases correspondiente puede apreciarse en la gura 11.1. Los objetos tridimensionales se modelan como una composicin de uno o ms polgonos. Y cada polgono se modela como una secuencia lineal de puntos. Obviamente estos son los vrtices que delimitan cada polgono. Esta representacin tiene el problema de la redundancia de informacin. Vemoslo con un ejemplo: En la gura 11.2 se presenta un objeto tridimensional de cuatro vrtices dispuestos en dos polgonos adyacentes. Su representacin en diagrama de objetos (de acuerdo al diagrama de clases de la gura 11.1) est en la gura 11.3.

247

11 Mallas Poligonales

Figura 11.1: Diagrama de clases de una malla poligonal en representacin explcita

Figura 11.2: Objeto tridimensional simple de ejemplo

Figura 11.3: Diagrama de objetos del objeto de la gura 11.2

248

11.2 Apuntadores a una lista de vrtices

Figura 11.4: Diagrama de clases de una malla poligonal en representacin de apuntadores a una lista de vrtices

Es de notar que las coordenadas de los puntos

o vrtices v 2 y v 4 estn repetidos en

ambos polgonos, provocando una redundancia innecesaria. Imagine tal redundacia en un objeto altamente complejo, como el toroide de la gura 10.2 en la pgina 244. Adems, como efecto colateral, se genera un grave problema en el caso de querer trasladar un punto, puesto que la bsqueda del punto se hara por igualdad entre tres pares de otantes, lo cual es altamente riesgoso.

11.2.

Apuntadores a una lista de vrtices

En esta representacin, los objetos tridimensionales se representan como una lista de polgonos y una lista de vrtices. Cada vrtice del objeto est slo una vez en la lista de vrtices, y cada polgono contiene una lista de apuntadores a los vrtices que lo conforman. El diagrama de clases correspondiente puede apreciarse en la gura 11.4. Los objetos tridimensionales se modelan como una composicin de uno o ms polgonos y al mismo tiempo como una composicin de varios vrtices nicos. Cada polgono se modela como una secuencia lineal de referencias a vrtices. Esta representacin resuelve el problema de la redundancia de informacin, pero presenta el problema siguiente: El proceso de dibujado sera recorrer todos los polgonos y dibujar todas las lineas de unin; pero las lineas

o aristas

que son compartidas por dos

polgonos, se dibujaran dos veces (en realidad tantas veces como polgonos unan un mismo par de vrtices). Entonces, para una gura compleja, habra que ejecutar el cdigo de dibujo de lneas una alta cantidad de veces sin necesidad, porque ya habran

249

11 Mallas Poligonales

Figura 11.5: Otro diagrama de objetos del objeto de la gura 11.2

sido dibujadas. De hecho, la cantidad de lneas innecesariamente dibujadas ronda la mitad de todas las lneas. Veamos en la gura 11.5 la representacin en diagrama de objetos (de acuerdo al diagrama de clases de la gura 11.4) del objeto de la gura 11.2 en la pgina 248. En este caso, al trasladar un punto actualizados.

o alterarlo de cualquier manera

se hace slo

una modicacin y automticamente todos los polgonos que incluyen tal punto estarn

11.3.

Apuntadores a una lista de aristas

En esta representacin, los objetos tridimensionales se representan como una lista de polgonos, una lista de vrtices y una lista de aristas. Cada polgono se representa como una lista de referencias a las aristas que lo conforman, y cada arista es un par de referencias a los vrtices que unen. El diagrama de clases correspondiente puede apreciarse en la gura 11.6.

250

11.3 Apuntadores a una lista de aristas

Figura 11.6: Diagrama de clases de una malla poligonal en representacin de apuntadores a una lista de aristas

Figura 11.7: Objeto tridimensional de ejemplo para representacin de apuntadores a una lista de aristas

Los objetos tridimensionales se modelan como una composicin de uno o ms polgonos, como una composicin de varias aristas nicas y como una composicin de varios vrtices nicos. Cada polgono se modela como una secuencia lineal de referencias a las aristas que lo conforman. Cada arista contiene una referencia a los dos vrtices que une y contiene una referenia a los polgonos a los que pertenece. Cada vrtice por supuesto es nico. Vemos un ejemplo: En la gura 11.7 se presenta el mismo objeto de la gura 11.2 en la pgina 248 pero con informacin sobre las aristas. Su representacin en diagrama de objetos (de acuerdo al diagrama de clases de la gura 11.6) est en la gura 11.8. En este caso, existe la manera de garantizar que cada lnea

o arista

ser dibujada

slo una vez, ya que el recorrido del algoritmo de dibujo puede hacerse sobre la lista de aristas y no sobre la de polgonos. Adems no hay redundancia de vrtices. Por otro

251

11 Mallas Poligonales

Figura 11.8: Diagrama de objetos del objeto de la gura 11.7

252

11.4 Apuntadores slo a una lista de aristas


lado, gracias a la referencia a los polgonos a los que pertenecen las aristas, es posible discriminar algunos polgonos para que no sean dibujados, haciendo siempre el recorrido sobre la lista de aristas. En la gura en la pgina siguiente se presenta un ejemplo con los detalles de implementacin de este tipo de representacin. En ese caso, las listas se implementan como listas circulares dobles con nodo de control.

11.4.

Apuntadores slo a una lista de aristas

Como se dijo al principio del captulo, existen diversas maneras de modelar cuerpos tridimensionales y que el modelo a implementar depende de las necesidades concretas de las aplicaciones. As, la estructura de datos usada para implementar las aplicaciones

trnsformionesQhFjr y perspetivQhFjr de los captulos 6 y 7 no es de ninguno


de los tipos presentados anteriormente. Dado que para esas aplicaciones no es relevante el concepto de polgono (ya que cada objeto tridimensional es simplemente una agrupacin de vrtices y aristas), tal clase de objetos no existe. En la gura 11.10 se presenta la estructura de datos usada en ese caso. A continuacin se presenta el cdigo (en lenguaje java) que implementa dicha estructura de datos:

1 2 3 4 5 6 7 8 9 10 11 12 13

Listing 11.1: Cdigo de Objeto3DSimple.java

/* c06 / transformaciones3d / Objeto3DSimple . java * Clases 3 D bsicas */ public class Objeto3DSimple implements Config3D { VerticeSimple puntos []; AristaSimple aristas []; boolean visible = true ; // Transformar todos los vrtices mediante una matriz // para ser proyectados en un portal de visin public void transformarProyeccion ( Matriz3d m , PortaldeVision portal ) { for ( int i =0; i < puntos . length ; i ++) { puntos [ i ]. puntoProyectado . x = m . e [0][0] * puntos [ i ]. puntoReal . x + m. e [0][1] * puntos [i ]. puntoReal . y + m . e [0][2] * puntos [ i ]. puntoReal . z + m . e [0][3]; puntos [ i ]. puntoProyectado . y = m . e [1][0] * puntos [ i ]. puntoReal . x + m. e [1][1] * puntos [i ]. puntoReal . y + m . e [1][2] * puntos [ i ]. puntoReal . z + m . e [1][3]; puntos [ i ]. puntoProyectado . z = m . e [2][0] * puntos [ i ]. puntoReal . x + m. e [2][1] * puntos [i ]. puntoReal . y + m . e [2][2] * puntos [ i ]. puntoReal . z + m . e [2][3]; }

14

15

16

253

11 Mallas Poligonales

Figura 11.9: Detalles de implementacin de una malla poligonal en representacin de apuntadores a una lista de aristas 254

11.4 Apuntadores slo a una lista de aristas

Figura 11.10: Diagrama de clases de las aplicaciones

trnsformionesQhFjr

perspetivQhFjr

17 18 19 20 21 22 23 24

} // Transformar todos los vrtices mediante una matriz // para ser modificados en su universo virtual public void transformar ( Matriz3d m ) { float nx , ny , nz ; for ( int i =0; i < puntos . length ; i ++) { nx = m . e [0][0] * puntos [ i ]. puntoReal . x + m . e [0][1] * ]. puntoReal . y + m. e [0][2] * puntos [i ]. puntoReal . z [0][3]; ny = m . e [1][0] * puntos [ i ]. puntoReal . x + m . e [1][1] * ]. puntoReal . y + m. e [1][2] * puntos [i ]. puntoReal . z [1][3]; nz = m . e [2][0] * puntos [ i ]. puntoReal . x + m . e [2][1] * ]. puntoReal . y + m. e [2][2] * puntos [i ]. puntoReal . z [2][3]; puntos [ i ]. puntoReal . x = nx ; puntos [ i ]. puntoReal . y = ny ; puntos [ i ]. puntoReal . z = nz ; } } } class VerticeSimple { Punto3d puntoReal , puntoProyectado ; public VerticeSimple () { puntoProyectado = new Punto3d (0 f ,0 f ,0 f ) ; } } class AristaSimple { VerticeSimple punto1 , punto2 ;

puntos [ i + m.e puntos [ i + m.e puntos [ i + m.e

25

26

27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42

255

11 Mallas Poligonales
43 44

java . awt . Color color ;

11.5.

Ejercicios

1. Construya un algoritmo que genere una supercie de malla poligonal cilndrica (Obviamente hay que elegir la representacin que tendr). 2. Construya un algoritmo que genere una supercie de malla poligonal esfrica (se sugiere considerar una divisin en meridianos y paralelos). La nura de la malla deber ser controlada por parmetros de entrada al algoritmo.

256

12 Introduccin a los Fractales


En este captulo no haremos un estudio formal de la matemtica involucrada con los fractales, sino ms bien un breve recorrido por algunas familias de fractales que son

relativamente fciles de gracar.

12.1.

Caractersticas de los fractales


1

En realidad no existe una denicin especca de Fractal, sino ms bien, un conjunto de caractersticas asociadas a tal denicin. De tal manera que cuando algo tiene algunas de esas caractersticas , se dice que ese algo es un fractal. Las principales caractersticas son las siguientes: Tener una intrincada (y aparentemente sosticada) geometra, tal que no puede ser descrito en trminos de geometra euclideana normal. Poseer el mismo nivel de detalle a cualquier escala. Tener una descripcin geomtrica recursiva. Tener autosimilitud, determinstica o probabilstica en su apariencia. O sea, que el todo sea igual o muy parecido a una de sus partes. Tener una dimensin de Hausdor-Besicovitch mayor que la propia dimensin topolgica. Veamos algunos ejemplos de objetos con esas caractersticas en la naturaleza, en las guras 12.1 , 12.2 y 12.3. Ahora veamos algunas guras fractales generadas por computadora. La 12.4 muestra un helecho que efectivamente parece real. Las guras 12.5 conjunto de Mandelbrot.

3 y 12.64 muestran perfectamente

la caracterstica de la autosimilitud. La 12.7 muestra relmpagos fractales a partir del

1 2 3 4

al menos dos de ellas Ruta completa: http://www.ubcbotanicalgarden.org/potd/2006/02/brassica_oleracea_botrytis_group_romanesco.php Fuente: http://commons.wikimedia.org/wiki/User:Wolfgangbeyer Realizado con el programa libre XaoS: http://xaos.sf.net/

257

12 Introduccin a los Fractales

Figura 12.1: Fractal natural: Brassica oleracea, un Romanescu fresco, cortesa del programa Botany Photo of the Day de http://www.ubcbotanicalgarden.org/.

Figura 12.2: Las ramas de los rboles siguen leyes fractales de distribuin volumtrica.

258

12.1 Caractersticas de los fractales

Figura 12.3: Las hojas de casi todos los helechos tienen la caracterstica de la autosimilitud nita.

Figura 12.4: Helecho fractal

259

12 Introduccin a los Fractales

Figura 12.5: Espiral de satlites con islas de Julia, cortesa del Dr. Wolfgang Beyer

Figura 12.6: Acercamiento del conjunto de Mandelbrot realzado por el autor de este libro con el programa XaoS.

260

12.1 Caractersticas de los fractales

Figura 12.7: Otro acercamiento del conjunto de Mandelbrot realizado con la aplicacin

mndelrotFout

presentada ms adelante en este captulo.

261

12 Introduccin a los Fractales

Figura 12.8: Copo de nieve de von Koch

12.2.

El copo de nieve de

von Koch 5

El copo de nieve de

Niels Fabian Helge von Koch, es una gura sencilla que exhibe

las caractersticas de estar geomtricamente denido por un algoritmo recursivo y por que su dimensin de Hausdor-Besicovitch es mayor que su dimensin topolgica. La explicacin formal de esto ltimo est fuera del alcance actual de esta obra, pero podemos enunciar la consecuencia directa de tal formalismo: La longitud de la curva es innita,

pero el rea que encierra es nita.

Podemos apreciar una representacin hecha con la aplicacin XaoS mencionada anteriormente en la gura 12.8.

lase fon koj

262

12.3 El tringulo de

Sierpiski

12.3.

El tringulo de

Sierpiski 6
Wacaw Sierpiski, el tringulo de

Presentamos brevemente las famosas guras de

Sierpiski y la carpeta de Sierpiski. Estas pueden verse en las guras 12.9 y 12.10. Las ideas bsicas de estas guras planas se pueden extender para guras tridimensionales, como vemos en la gura 12.11 en la pgina 266.

12.4.

Los Conjuntos

Julia-Fatou

12.4.1. Denicin
Llamados as, en honor de

Gaston Julia y Pierre Fatou, los conjuntos Julia no

son guras concretas, sino una familia de guras fractales, con todo el esplendor de la expresin. Se obtienen al analizar el acotamiento de ciertas funciones recursivas en el dominio de (s, de los nmeros complejos).

El conjunto Julia de una funcin conjunto de todos los valores

fc (z )

con semilla

c C,

denotado por

Jc (f ),

es el

z,

tales que la siguiente sucesin sea acotada:

z0 = z zn+1 = fc (zn )

Tpicamente se calculan los conjuntos Por otro lado, el conjunto Fatou, Es decir,

Jc (f )

con

fc (z ) = z + c.

Fc (f ) es el complemento de Jc (f ), Fc (f ) = C Jc (f ). Fc (f ) contiene todos los z para los que la sucesin antes descrita, no es acotada. | zn | > 2
entonces

Ya encaminndonos a la implementacin, se puede demostrar que si la sucesin no es acotada y

z / Jc (f ).

Ese es el critero a usar para saber si la sucesin

diverge. Y si no es acotada, no se llegar al valor de 2, por lo que debe haber un nmero mximo de iteraciones a evaluar. Si el valor de

llega a un cierto lmite sin pasar de 2,

consideraremos que dicha sucesin es acotada. Vale recalcar que mientras mayor sea el

mximo, ms nos acercaremos al conjunto real (el cual, por supuesto, es imposible de

alcanzar). A continuacin presentamos algunas imgenes del programa las ventanas aparecen los valores 12.14 y 12.15.

juliFout.

En el ttulo de

usados como semilla. Son las guras 12.12, 12.13,

lase sierpiski

263

12 Introduccin a los Fractales

Figura 12.9: Tringulo de Sierpiski

264

12.4 Los Conjuntos

Julia-Fatou

Figura 12.10: Carpeta de Sierpiski

265

12 Introduccin a los Fractales

Figura 12.11: Pirmide de Sierpiski

266

12.4 Los Conjuntos

Julia-Fatou

Figura 12.12: Imgen del programa

juliFout

267

12 Introduccin a los Fractales

Figura 12.13: Segunda imgen del programa

juliFout

268

12.4 Los Conjuntos

Julia-Fatou

Figura 12.14: Tercera imgen del programa

juliFout

269

12 Introduccin a los Fractales

Figura 12.15: Cuarta imgen del programa

juliFout

270

12.4 Los Conjuntos

Julia-Fatou

12.4.2. Implementacin
En el material adjunto a este libro se encuentra la aplicacin cionamiento se describe a continuacin:

juliFout,

cuyo fun-

clic izquerdo clic derecho

El primer clic, dene una de las esquinas de un rectngulo que ser usado

como rea de aumento. El segundo clic, dene la esquina opuesta del rectngulo y se efecta el aumento correspondiente. Cambia la semilla usada para calcular el conjunto y regresa la escala a

sus valores por defecto. Actualmente el programa contiene 12 semillas diferentes (algunas no se aprecian bien con algunos esquemas de color). El valor de la semilla usada se muestra en la barra de ttulo de la ventana.

rueda del ratn

Cambia el algoritmo de coloreado para el conjunto actual con la escala

actual. Por el momento hay 6 algoritmos de coloreado. El ndice del algoritmo aparece en la barra de ttulo de la ventana grca con el formato  olor

iGT.

Debido a la alta complejidad del algoritmo que decide si cada pixel pertenece o no al conjunto, la respuesta de la aplicacin no es inmediata. Dependiendo del procesador en el que se ejecute, la aplicacin puede ser un poco lenta para responder a la rueda del ratn. A continuacin se presenta una de las funciones de dibujo de la aplicacin mencionada (tomada del archivo

lulosF):

165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185

Listing 12.1: Funcin de dibujo de Conjunto de Julia

void dibujarFractalJulia6 ( SDL_Surface * pantalla , complejo *c , escala * e ) { int i , j ; complejo z0 , z ; int numPasos ; for ( i =0; i <e - > anchoReal ; i ++) { z0 . i = tV_Ry (e , i) ; for ( j =0; j <e - > altoReal ; j ++) { z0 . r = tV_Rx (e , j ) ; z = z0 ; numPasos = 0; while ( numPasos < MAXPASOS && ( sq ( z . r ) + sq ( z . i ) < LIMITEMODULO2 ) ) { CX_suma ( CX_cuadrado (& z ) , c ) ; numPasos ++; } if ( numPasos == MAXPASOS ) { pixelColor ( pantalla , j , i , COLOR_ADENTRO ) ; } else {

271

12 Introduccin a los Fractales


186 187 188 189 190 191 192 193

pixelColor ( pantalla , j , i , colorGfx ( ( MAXPASOS - numPasos ) *256/ MAXPASOS , ( MAXPASOS - numPasos ) *256/ MAXPASOS , ( MAXPASOS - numPasos ) *256/ MAXPASOS )) ;

12.5.

Conjunto de

Mandelbrot

12.5.1. Denicin
En honor a

Benot Mandelbrot, es un conjunto especco, bien denido, estrechamente

relacionado con los conjuntos Julia.

El conjunto Mandelbrot, denotado por

M,

es el conjunto de todos los valores

cC

tales que la siguiente sucesin sea acotada:

z0 = 0 + 0 i zn+1 = zn + c

Y se utilizan los mismos criterios de seleccin que para los conjuntos Julia. Presentamos la forma clsica de este famoso conjunto fractal en la gura 12.16 y una versin estilizada con XaoS en la gura 12.17.

12.5.2. Implementacin
En el material adjunto a este libro se encuentra la aplicacin funcionamiento se describe a continuacin:

mndelrotFout,

cuyo

clic izquerdo clic derecho

El primer clic, dene una de las esquinas de un rectngulo que ser usado

como rea de aumento. El segundo clic, dene la esquina opuesta del rectngulo y se efecta el aumento correspondiente. Cambia el algoritmo de coloreado para el conjunto actual con la escala

actual. Por el momento hay 8 algoritmos de coloreado. El ndice del algoritmo aparece en la barra de ttulo de la ventana grca con el formato  olor

iGV.

272

12.5 Conjunto de

Mandelbrot

Figura 12.16: Forma clsica del conjunto Mandelbrot, generado con

mndelrotFout

273

12 Introduccin a los Fractales

Figura 12.17: Conjunto Mandelbrot con suavizacin de color interna y externa, generado con la aplicacin XaoS

274

12.6 Ejercicios
El algoritmo que decide si cada pixel pertenece o no al conjunto de Mandelbrot es de la misma complejidad que el del caso de los conjuntos de Julia, por lo que la respuesta de la aplicacin tiene en general, la misma velocidad. A continuacin se presenta una de las funciones de dibujo de la aplicacin mencionada (tomada del archivo

lulosF):

371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398

Listing 12.2: Funcin de dibujo de Conjunto de Mandelbrot

void dibujarFractalMandelbrot7 ( SDL_Surface * pantalla , escala * e ) { int i , j ; complejo z ={0.0 , 0.0} , c ; int numPasos ; for ( i =0; i <e - > anchoReal ; i ++) { c . i = tV_Ry (e , i ) ; for ( j =0; j <e - > altoReal ; j ++) { c . r = tV_Rx (e , j ) ; numPasos = 0; z . r = z .i = 0.0; while ( numPasos < MAXPASOS && ( sq ( z . r ) + sq ( z . i ) < LIMITEMODULO2 ) ) { CX_suma ( CX_cuadrado (& z ) , & c ) ; numPasos ++; } if ( numPasos == MAXPASOS ) { pixelColor ( pantalla , j , i , COLOR_ADENTRO ) ; } else pixelColor ( pantalla , j , i , colorGfx ( numPasos *256/ MAXPASOS , ( MAXPASOS - numPasos ) *256/ MAXPASOS , numPasos *256/ MAXPASOS ) ) ;

12.6.

Ejercicios
M
y

1. Investigue cul es la relacin entre

Jc (f ).

2. Construya un algoritmo que dibuje el fractal de la gura 12.18. 3. Construya un algoritmo que dibuje el fractal de la gura 12.19.

275

12 Introduccin a los Fractales

Figura 12.18: Fractal de ejercicio

Figura 12.19: Fractal de otro ejercicio

276

Parte II

Otras Yerbas

277

13 Compilacin desde Mltiples archivos fuente (en lenguaje C)


Supongamos que tenemos nuestro cdigo separado en diversos archivos fuente, tal como el siguiente ejemplo:

1 2 3 4 5 6 7 8

Listing 13.1: Programa principal

/* c13 / principal . c * */ # include " otro . h " int main ( int argc , char * argv []) { funcion () ; return 0; }
Listing 13.2: Cabecera de otro cdigo

1 2 3

/* c13 / otro . h * */ int funcion ( void ) ;


Listing 13.3: Otro cdigo fuente

1 2 3 4 5 6 7 8 9

/* c13 / otro . c * */ # include " otro . h " # include < stdio .h > int funcion ( void ) { printf ( " hola a todos y todas \ n " ) ; return 0; }

El programa es muy simple, su salida es completamente previsible, por lo que es perfecto para ilustrar cmo podemos apoyarnos en la utilera automtica: Debemos crear en ese mismo directorio un archivo

mke

para compilarlo de forma

wkefile

para orientar a

mke.

El

archivo de ayuda a la compilacin debera contener algo parecido a lo siguiente:

279

13 Compilacin desde Mltiples archivos fuente (en lenguaje C)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
Listing 13.4: Makele para varios archivos fuente

# c13 / Makefile # Esto es un comentario # El comando para borrar archivos RM = / bin / rm -f # Un '*.o ' por cada '*.c ' que pertenezca al proyecto OBJS = principal . o otro . o # Nombre del programa ejecutable : PROG = programa # Esto indica que los siguientes identificadores , # no son archivos , sino comandos de make : . PHONY : limpiar . PHONY : limpiartodo . PHONY : all # se puede , por ejemplo , ejecutar en la consola # lo siguiente : # '$ make limpiartodo ' , etc . # Cuando se ejecuta '$ make ' , se evalan # las reglas '$ ( PROG ) ' y ' limpiar ': all : $ ( PROG ) limpiar # Esta regla compila todo el cdigo y lo enlaza : $ ( PROG ) : $ ( OBJS ) gcc -o $ ( PROG ) $ ( OBJS ) # Esta regla borra todos los archivos intermedios # y de copia de seguridad : limpiar : $ ( RM ) *~ $ ( OBJS ) # Esta regla borra todos los archivos intermedios # y el programa ejecutable , si es que existe limpiartodo : make limpiar $ ( RM ) $ ( PROG )

La presencia de dicho archivo y su contenido nos permiten ejecutar las siguientes rdenes en esa carpeta:

6 mke limpir
Borra los archivos de copia de seguridad que se hayan creado y tambin borra los archivos de cdigo objeto intermedios (los

BFo)

que se crean al compilar los

280

respectivos archivos de cdigo fuente (los

BF).

6 mke limpirtodo
Invoca la instruccin anterior y adems borra el programa ensamblado o ejecutable (su nombre depende de lo que hayamos puesto en la variable

yq.

6 mke
Se ejecuta lo que hayamos indicado en la regla

yq, que a su vez invoca la compilacin individual de cada archivo fuente (los BF) y posteriormente invoca el ensamblaje de estos con el comando g. Posteriormente invoca la regla limpir.
Esta pequea gua no pretende ser altamente exhaustiva. Simplemente pretende orientar para la compilacin asistida en proyectos de programacin en lenguaje C estndar de mediana escala en ambientes tipo UNIX. Si se necesita mayor detalle o explicacin, por favor rerase el lector a la documentacin apropiada. Por ejemplo:

ll.

En este caso, invoca la regla

6 mn mke
O en los sitios siguientes: http://www.chuidiang.com/clinux/herramientas/makele.php http://www.calcifer.org/documentos/make/makele.html http://atc1.aut.uah.es/lsotm/Makele.htm http://en.wikipedia.org/wiki/Make_(software) http://www.opussoftware.com/tutorial/TutMakele.htm

281

13 Compilacin desde Mltiples archivos fuente (en lenguaje C)

282

14 Diagramas de Clases y Diagramas de Objetos (una untadita de UML)


Haremos en este captulo un breve resumen de la notacin UML (Unied Modeling Language) para diagramas de clases y objetos.

14.1.

Notacin de clases

En UML una clase se representa como un rectngulo con tres espacios. En el primero va el nombre de la clase, en el segundo sus atributos y en el tercero sus operaciones. Veamos un ejemplo en la gura 14.1. La clase se llama

Clase, tiene dos atributos y dos operaciones. Los atributos son i, un

entero, y h, un otante con valor por defecto de 4.0. El atributo i es pblico y h es privado. Eso indican los signos que preceden a los nombres. Que un atributo sea privado signica que su mbito de acceso est limitado al interior del cdigo de la clase, y si es pblico, signica que se puede acceder a l desde cualquier mbito desde el que se pueda alcanzar una instancia de esta clase. La primera operacin es privada, se llama metodo, retorna void y recibe un parmetro llamado para1 de tipo

Clase (s, del mismo tipo). La segunda funcin se llama funcion,

retorna un String y recibe dos parmetros: semilla que es entero con valor por defecto de 3, y objeto de tipo Estructura.

Figura 14.1: Representacin de una clase

283

14 Diagramas de Clases y Diagramas de Objetos

(una untadita de UML)

Figura 14.2: Diagrama de clase ocultando sus atributos y operaciones

Los niveles de acceso pblico y privado son los ms usuales en los lenguajes de programacin orientados a objetos, pero algunos lenguajes denen otros niveles de acceso. En el caso de Java, tambin existen los niveles de acceso protegido y de paquete, representados por los signos  5 y  ~ respectivamente. Por otro lado, la informacin de los atributos y de las operaciones de las clases a veces no es relevante o no es conveniente mostrarla (generalmente por cuestiones de espacio), por lo que pueden omitirse esas secciones y mostrar nicamente un rectngulo con el nombre de la clase, como vemos en la gura 14.2.

14.2.

Notacin de relaciones o asociaciones

En la gura 14.3 se presenta un resumen de la notacin bsica de asociaciones en diagramas de clases. Veamos cada uno de ellos: 1. Indica simplemente que hay una relacin uno-a-uno entre una instancia de y una de

Clase1

Clase2. Clase1 tienen una instancia de la clase

2. Indica que hay una relacin uno-a-uno entre las instancias, pero agrega semntica a la relacin: indica que las instancias de

Clase2.
3. Indica que hay una relacin uno-a-uno entre las clases, y el nombre de la relacin, pero sin indicar la direccin de la relacin. 4. Indica que una instancia de muchas instancias de referencias a instancias de

Clase1 puede tener referencias a ningna, una o

Clase2 y que una instancia de Clase2 tiene forzosamente 2 Clase1. Clase1 debe tener al menos una referencia a instancias

Estos elementos que indican cantidad de referencias se llaman  multiplicidades . 5. Indica que una instancia de de a instancias de

Clase2 y que una instancia de Clase2 tiene forzosamente entre 2 y 4 referencias Clase1.

6. Indica que hay una relacin uno-a-uno, pero indicando que para las instancias de

Clase1, la referencia a la instancia de Clase2 se llama objetoUtil y es privada.


Dice adems, que para las instancias de

Clase2, la referencia a la instancia de

Clase1 se llama apuntado y es pblica.


Estos nombres se conocen como  roles .

284

14.2 Notacin de relaciones o asociaciones

Figura 14.3: Relaciones bidireccionales entre clases 285

14 Diagramas de Clases y Diagramas de Objetos

(una untadita de UML)

Figura 14.4: Otros tipos de relacin entre clases

7. Indica que las instancias de al menos 2 instancias de stancias de

Clase1 deben tener una coleccin de referencias a Clase2 tienen una coleccin de 5 referencias a in-

Clase2 y que esa coleccin se llama objetosUtiles. Dice

adems, que las instancias de dichas colecciones. 8. Indica que una instancia de y que las instancias de relacionadas de

Clase1 que se llama apuntados. Indica adems el nivel de acceso de Clase1 tiene 5 referencias a instancias de Clase2, Clase2 no tienen referencias a las instancias

Clase2 son referenciadas por 3 instancias de Clase1.

Tambin dice que las instancias de

Clase1.

Los diagramas de clase no obligan al diseador a especicar cmo, en concreto, se implementarn tales referencias o colecciones de referencias. Se podran implementar con arreglos, con listas, rboles, grafos, etc. En la gura 14.4 se presentan ms tipos de asociaciones en diagramas de clases: 1. Indica una agregacin, en la que las instancias de estas tienen una referencia a la instancia de

Clase1 estn formadas (entre Clase2, y

otras cosas) por una coleccin de referencias a algunas instancias de

Clase1 a la cual estn agregadas.

286

14.3 Notacin de objetos

Figura 14.5: Representacin de una instancia de una clase

Tambin indica que si la instancia agregada de stancias de

Clase1 deja de existir, las inClase1 estn formadas (entre Clase2, y Clase1, pero no contienen

Clase2 de las cuales estaba fomada, no tienen por qu dejar de existir.

2. Indica una agregacin en la que las instancias de

otras cosas) por una coleccin de entre 1 y 4 referencias a instancias de que estas conforman simultaneamente dos instancias de referencia a ellas.

3. Indica una composicin, que es una agregacin muy restrictiva, ya que indica que si la instancia compuesta de

Clase1 deja de existir, las instancias de Clase2 que

la componan, deben tambin dejar de existir. En consecuencia, las instancias de

Clase2 slo pueden componer una instancia de Clase1.


4. Esta es una composicin en la que las instancias de a la instancia de

Clase2 no tienen referencia

Clase1 que componen.

5. Indica que la clase s.

Clase2 hereda de Clase1. Algunos lenguajes de programacin 1 permiten herencia mltiple , otros no. Java no lo permite directamente, Python

Hay ms tipos de relaciones en la notacin UML, pero como el ttulo de este captulo dice, es slo una untadita.

14.3.

Notacin de objetos

Las instancias de una clase se representan como rectngulos con dos partes. La primera tiene el siguiente formato:

`nomresnstnibX `nomreglseb
La otra parte est reservada para los valores de sus atributos. Al igual que en el caso de los diagramas de clases, esta parte se puede obviar cuando no es necesaria. Podemos ver un ejemplo de un diagrama de objetos en la gura 14.5, en la que hay dos instancias de la clase

Clase.

es decir, que una clase pueda heredar de varias clases simultaneamente

287

14 Diagramas de Clases y Diagramas de Objetos

(una untadita de UML)

En los diagramas de objetos tambin se pueden incluir las referencias entre s, con o sin nombre y con o sin direccin, segn sea el caso, tal como se ve en las guras 11.3, 11.5 y 11.8.

288

Parte III

Apndices

289

A Plantilla de Aplicacin Grca en J2SE


A continuacin se presenta el esqueleto de una aplicacin grca en lenguaje java. Es la idea usada para las aplicaciones

trnsformionesQhFjr

perspetivQhFjr.

Se sigue la idea de un diseo Modelo-Vista-Controlador como en la gura A.1.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

Listing A.1: Clase controladora

/* cA / Controlador . java * Objeto controlador */ import java . awt . Graphics ; import java . awt . event .*; /* * Controlador de la aplicacin */ public class Controlador { /* * * Funcin principal independiente de instancias . */ public static void main ( String args []) { new Controlador ( args ) ;

Figura A.1: Modelo de diseo Modelo-Vista-Controlador

291

A Plantilla de Aplicacin Grca en J2SE


17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60

} Ventana vista ; Modelo modelo ; /* * Hilo principal */ public Controlador ( String args []) { vista = new Ventana ( this ) ; modelo = new Universo3D () ; // mostrar la ventana vista . setVisible ( true ) ; System . out . println ( " Iniciando aplicacin " ) ; // accin principal o // conjunto de acciones principales modelo . hacerAlgo () ; // cuando ya acabamos de hacer algo importante , // terminamos la aplicacin System . exit (0) ;

/* * * Simple delegacin del proceso * */ public void dibujar ( Graphics g ) { modelo . dibujar ( g ) ; } /* * * Implementar los eventos generados desde la vista . * Como el de cerrar la ventana : * */ public WindowAdapter AdaptadorVentana = new WindowAdapter () { public void windowClosing ( java . awt . event . WindowEvent evt ) { System . out . println () ; System . exit (0) ; } };

} // fin de la clase Controlador

Listing A.2: Clase del modelo

1 2 3

/* cA / Modelo . java * Objeto principal de la lgica * de la aplicacin

292

4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42

*/ import java . awt .*; public class Modelo { public Modelo () { /* * * Echar a andar todos * los mecanismos del modelo * y toda su lgica . * */ } public hacerAlgo () { /* * * Aqu debera estar el corazn * de la ejecucin del modelo . * */ } /* * * El modelo no tiene conciencia * de la procedencia del contexto * grfico en el que se le * est solicitando trabajar . * * Simplemente responde por delegacin . * */ public void dibujar ( Graphics g ) { /* * * Aqu hay que hacer lo propio . * * Aqu hay que dibujar lo que haya * que dibujar de acuerdo al estado * actual del modelo y de otros * factores relevantes . * */ }


Listing A.3: Clase vista

1 2 3 4 5 6 7 8

/* cA / Ventana . java * Objeto vista , * tpicamente la Ventana de la aplicacin */ import java . awt .*; public class Ventana extends javax . swing . JFrame implements Config3D {

293

A Plantilla de Aplicacin Grca en J2SE


9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56

/* * * Referencia al controlador para * delegarle la respuesta a los * eventos generados desde aqu . * */ private Controlador control ; /* * * Objeto especial para lograr * la delegacin de las solicitudes * de refrescamiento * */ private PanelEspecial panelprin ; public Ventana ( Controlador control ) { this . control = control ; inicializarComponentes () ; } /* * Inicializar los componentes de la interfaz * grfica de usuario si es que hay . * En el caso de Java con Swing siempre hay . * */ private void inicializarComponentes () { panelprin = new PanelEspecial ( control ) ; setDefaultCloseOperation ( javax . swing . WindowConstants . EXIT_ON_CLOSE ) ; setTitle ( " Ttulo de la aplicacin " ) ; /* * * Agregar todos los " escuchadores " , * que deberan estar implementados * en el controlador . * * Todas las respuestas a los eventos * diparados desde este objeto grfico * y sus includos , deberan ser respondidos * por el controlador con asistencia de * los datos del modelo . * */ addWindowListener ( control . AdaptadorVentana ) ; panelprin . setBorder ( new javax . swing . border . LineBorder ( new java . awt . Color (0 , 0 , 0) ) ) ; panelprin . setMinimumSize ( new java . awt . Dimension (400 , 300) ) ; // agregar el panel principal a la ventana

294

57 58 59 60 61 62 63 64 65 66 67 68

getContentPane () . add ( panelprin , java . awt . BorderLayout . CENTER ) ; // acomodar dinmicamente los objetos grficos pack () ; // centrar esta ventana en la pantalla : Dimension tamanjoForzado = new Dimension (400 , 300) ; java . awt . Dimension tamanjoPantalla = java . awt . Toolkit . getDefaultToolkit () . getScreenSize () ; setLocation (( tamanjoPantalla . width - tamanjoForzado . width ) /2 ,( tamanjoPantalla . height - tamanjoForzado . height ) /2) ;

} } // fin de la clase vista

Listing A.4: Clase especial para delegar el refrescamiento

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

/* cA / PanelEspecial . java * Un componente que puede ponerse en un contenedor ... * y mostrar las figuras */ import java . awt .*; import javax . swing .*; import java . awt . event .*; public class PanelEspecial extends JPanel { Controlador control ; public PanelEspecial ( Controlador control ) { this . control = control ; } /* * * Mtodo llamado cuando es necesario * redibujar la pantalla . * La tarea se delega de la vista al * controlador y de este al modelo . * */ public void paintComponent ( Graphics g) { control . dibujar ( g ) ; }

} // fin de la clase PanelEspecial

en lugar de ejecutarlo desde los archivos

Luego de este breve ejemplo, conviene mencionar cmo compilar el cdigo fuente y cmo ensamblarlo en un slo archivo

Fjr

Flss.

Bueno, la compilacin se realiza as:

6 jv gontroldorFjv

295

A Plantilla de Aplicacin Grca en J2SE


Se sobreentiende que el archivo de clase indicado es el que debe contener la funcin Esto provocar la compilacin en cascada de todos las clases necesarias para que el de

min. min

gontroldorFjv

se ejecute sin problemas.

Para ejecutar el programa se puede hacer:

6 jv gontroldor
Pero en lugar de dejar todos los archivos archivo jar luego de la compilacin, as:

Flss,

podra realizarse la generacin de un

6 jr Efe pliionFjr gontroldor BFlss


La opcin   indica que se desea crear un archivo archivos

Fjr,

Fjr (el comando sirve para manipular

no slo para crearlos).

La opcin f  indica que el siguiente parmetro debe ser el nombre del archivo a crear (o del archivo a operar). La opcin e indica que el siguiente parmetro (en este caso, despus del nombre del archivo a operar), es el nombre de la clase con el punto de entrada (donde est el main que queremos que se ejecute primero). Finalmente listamos todos los archivos que queremos incluir en el archivo creado (en este caso, todos los archivos

Flss

generados en el paso de compilacin.

Para mayor informacin sobre el comando

jr,

ver

http://java.sun.com/docs/books/tutorial/deployment/jar/ o ejecutar:

6 mn jr

296

B Referencias y manuales
B.1. SDL  Simple DirectMedia Layer
B.1.1. Sitios de recursos
http://www.libsdl.org http://www.libsdl.es http://www.javielinux.com http://www.agserrano.com/publi.html http://www.losersjuegos.com.ar/

B.1.2. Artculos
Por qu SDL? (en espaol): http://www.losersjuegos.com.ar/referencia/articulos/why_sdl/why_sdl.php http://es.wikipedia.org/wiki/Grcos_3D_por_computadora Artculo sobre juegos libres http://www.marevalo.net/creacion/unmundofeliz/1999_12_06_juegos_libres.html http://es.wikipedia.org/wiki/Desarrollo_de_videojuegos http://en.wikipedia.org/wiki/Game_programming http://www.losersjuegos.com.ar/referencia/articulos/articulos.php Game Programming Wiki http://wiki.gamedev.net/

297

B Referencias y manuales

B.2.

Python y pygame

http://inventwithpython.com/ http://en.wikibooks.org/wiki/Python_Programming http://openbookproject.net//thinkCSpy/ http://pyspanishdoc.sourceforge.net/ http://www.pygame.org/ http://vpython.wikidot.com/ http://www.vpython.org/ http://www.diveintopython.org/

B.3.

Java Me

B.3.1. Sitios de recursos


http://java.sun.com/javame/ http://www.agserrano.com/publi.html http://programacion.com/java/tutorial/ags_j2me/

298

Bibliografa
[Foley et al.] Foley, James D.; van Dam, Andries; Feiner, Steven K.; Hughes, John F.; Phillips, Richard L. 1996. [Burden y Faires 2002] Burden, Richard L.; Faires, J. Douglas.

Introduccin a la gra-

cacin por computador. Addison-Wesley Iberoamericana, Anlisis Numrico.

Sptima edicin, Thomson Learning, 2002. [Henrquez 1999] Henrquez, Mauro Hernn.

Clculo integral en una variClculo diferencial en una

able real. UCA editores, 1999.


[Henrquez 2001] Henrquez, Mauro Hernn.

variable real. UCA editores, 2001.


[RAE] RAE,

Diccionario de la real academia de la lengua esWikipedia - Bzier

paola. Vigsima segunda edicin 2001.


[Wikipedia-Bzier Curve] Comunidad de Wikipedia en Ingls.

Curve. Edicin del 26 de Febrero de 2010, 13:20 UTC. Revisado el 4 de Marzo de 2010 a las 8:42am, hora local de El Salvador. Enlace permanente: Bzier curve - oldid=346486604.

299

Você também pode gostar