Edición en 3D (Tridimensional)

Programación Pascal. 3D Studio. VR. Polígonos. Vectorización

  • Enviado por: Agustín Caballero
  • Idioma: castellano
  • País: España España
  • 28 páginas
publicidad
publicidad

3DEDIT es un programa para la edici¢n de escenas tridimensionales

en formato B-rep (boundary representation), es decir, mediante definici¢n

de los pol¡gonos que forman el contorno de los diferentes objetos.

El programa permite la creaci¢n de escenas complejas a partir de una

serie de figuras b sicas, como esferas, conos, cubos, etc. mediante la edici¢n

de las mismas a trav s de cuatro vistas disponibles. Las cuatro vistas est n

en modo isom trico, y vienen predefinidas al arrancar el programa como planta,

alzado, perfil y tridimensional. No obstante, se pueden modificar las vistas

mediante la rotaci¢n y traslaci¢n de las c maras.

Para los que est is interesados en usar los ficheros generados por

3DEDIT, paso a explicar el formato que he utilizado; recordad que una

expresi¢n A* indica la repetici¢n de A 0 ¢ m s veces.

escena = objeto*

objeto = N§V rtices V rtice* N§Pol¡gonos Pol¡gono*

v rtice = x y z

pol¡gono = N§Referencias Referencia*

La escena se guarda en el fichero colocando consecutivamente cada uno

de los objetos que la forman; cada objeto se compone a su vez de una lista de

v rtices y otra de pol¡gonos. La lista de v rtices se especifica mediante un

valor (tipo word) que indica el n£mero de v rtices, y las coordenadas de cada

v rtice (tres valores tipo real) de forma consecutiva. De igual forma, la

lista de pol¡gonos se guarda como el n£mero de pol¡gonos (un word) y las

listas de referencias a cada uno de los v rtices. Cada lista de referencias

se almacena de la misma forma, es decir, una cabecera indicando el n£mero de

referencias (que equivale al n£mero de v rtices del pol¡gono). Cada una de

estas referencias indica el n£mero de v rtice (tipo word) dentro de la lista

anteriormente descrita. Este galimat¡as lo puedes resolver f cilmente viendo

c¢mo se graba y carga los ficheros en el listado fuente.

En cuanto a posibles ampliaciones, he pensado a¤adir un sistema de

c maras que utilicen perspectiva, con lo que se podr¡a usar directamente las

escenas para introducirlas en alg£n iluminador. Tambi n se podr¡a implementar

la ocultaci¢n de pol¡gonos a nivel general, ya que de momento s¢lamente se

ocultan los pol¡gonos posteriores de cada objeto por separado (el tiempo era

oro al final del curso pasado).

El lenguaje utilizado ha sido Turbo Pascal (s¡, ya lo se, deber¡a

haberlo hecho en C, pero no estoy excesivamente puesto en el mismo, y de todas

formas ¨qu tiene casi todo el mundo contra el -maravilloso, intuitivo, simple

pero potente- Pascal?), en su versi¢n 7.0 (al compilar con la versi¢n 6.0 da

error, ya que ste no incluye algunas instrucciones como continue, break, etc.).

Todo el proyecto lo he realizado mediante un 486/66MHz con 4Mb de RAM

(todav¡a no me puedo comprar un Pentium, snif...), con lo que he intentado que

el programa funcione medianamente bien en esta m quina. No obstante, el que

tenga un bicho m s potente, tanto mejor para l, ya que el modo gr fico usado

hace que el redibujado de las escenas, por ejemplo al mover objetos, sea

bastante lento (no es culpa m¡a: el 3D Studio usa el mismo formato gr fico,

y a la hora de mover objetos lo hace colocando una caja que lo representa para

no dar la sensaci¢n de lentitud).

Puedes hacer con este programa lo que te d la gana, con la condici¢n

de que incluyas todos los ficheros que se suministran; tambi n puedes extraer

parte del programa fuente, si es que logras entender algo (lo siento pero no

tuve tiempo de arreglarlo y mucho menos de ponerle comentarios; contacta

conmigo si quieres tener detalles sobre alg£n aspecto y quieres tener el

manual t cnico que redact para la pr ctica). Naturalmente, te pido que tengas

la cortes¡a de citarme en aqu llos trabajos para los que te apoyes en el

presente programa.

Para comenzar a operar con el programa te sugiero que saques una

copia del cutre-manual (fichero MANUAL.TXT adjunto), y comiences a trastear por tu

cuenta. Tambi n tienes algunos ejemplos ya construidos (ficheros con extensi¢n

.ACB). Lamento que el manual no sea m s exhaustivo, pero la revisi¢n de esta

pr ctica se hizo "in situ", por lo que ¨para qu escribir un manual de

cincuenta folios cuando sab¡a que probablemente el profesor no lo iba a leer?.

En cuanto a posibles ampliaciones, estoy pensando en a¤adir las

siguientes caracter¡sticas; cuando tenga tiempo (espero que antes del 2.000)

me pondr con ellas.

- Optimizaci¢n del c¢digo, haci ndolo m s r pido; he detectado

bastantes puntos que se pueden mejorar aplicando algoritmos distintos.

- Nuevas primitivas, como semiesferas, figuras irregulares, etc. En

realidad esto es bastante simple y creo que no me llevar¡a m s que unos pocos

d¡as implementarlo.

- Operar con vol£menes; ste es uno de los aspectos m s £tiles en un

editor de escenas, pero no est  implementado de momento ya que es un punto

bastante complicado.

- Animaciones; en realidad es bastante sencillo su realizaci¢n,

simplemente manejando una lista de escenas en lugar de una sola. De hecho, ya

tengo hechas algunas pruebas respecto a esto y funciona bastante bien; el

problema es la memoria, ya que una escena medianamente compleja es bastante

voluminosa, y se requiere la utilizaci¢n de memoria expandida (no se -todav¡a-

c¢mo se usa en Pascal).

- Introducci¢n de vistas con distinta perspectiva; en el programa he

usado visi¢n isom trica, ya que es la m s sencilla de implementar. No obstante,

se pueden introducir perspectiva; de hecho, esto lo ten¡a casi acabado, pero

no funcionaba al cien por cien, por lo que decid¡ entregar la pr ctica con una

vista m s simple.

- Ocultaci¢n de pol¡gonos total; es decir, hacer que unos objetos se

superpongan a otros. De momento s¢lo se ocultan los posteriores.

Edición en 3D (Tridimensional)

Práctica de Infografía

Sistema de diseño 3D

Manual de usuario

El programa arranca con la instrucción 3dedit, y requiere para su uso de un ratón. Al arrancar, se comprueba si el ratón está efectivamente instalado y en funcionamiento; si hay algún problema, se le pedirá que solucione el problema y pruebe de nuevo. Si todo ha ido bien, aparece la pantalla siguiente:

Edición en 3D (Tridimensional)

Podemos distinguir tres zonas en esta pantalla:

- Zona de visualización. Esta zona está dividida en cuatro cuadrantes, cada una de las cuales representan una de las posibles vistas que se pueden presentar simultáneamente. Inicialmente, el primer cuadrante representa la planta, el segundo el alzado, el tercero el perfil, y el cuarto una vista tridimensional en proyección isométrica. Esquemáticamente, la configuración inicial de los cuadrantes sería la siguiente:

1 Y 2 Z

X Y

2 Z 3 Z

Y

X

X

- Zona de mensajes. Está situada en la parte inferior de la pantalla; en esta zona irán apareciendo todos los mensajes que el programa emita, tanto los de ayuda como los que requieran algún tipo de entrada por parte del usuario.

- Zona de menús. En la parte derecha de la pantalla tenemos una serie de opciones divididas en varios submenús según a qué entidad estén referidos (vista, objeto, vértice ó escena).

El manejo del programa se realiza enteramente mediante el ratón (excepto las entradas que requieran datos numéricos o alfanuméricos, lógicamente), utilizándose los botones izquierdo y derecho.

En este momento, la vista seleccionada es la superior izquierda (primer cuadrante); esto se indica porque el marco que rodea a dicho cuadrante está en color rojo. Esta vista será sobre la que se ejecuten todos los comandos; si deseamos seleccionar cualquier otra, bastará con pulsar sobre la vista correspondiente.

Para ejecutar uno de los comandos del menú, basta con desplazar el ratón sobre la opción correspondiente, y pulsar uno de los botones. En general, el botón izquierdo se utiliza para ejecutar un comando sobre una única entidad, mientras que el derecho indica la ejecución múltiple, cuando esta opción esté disponible.

A continuación veamos el propósito de cada uno de los comandos.

Menu Vista

Acercar. Realiza un acercamiento del punto de vista hacia la escena, con lo que se produce el efecto de un zoom de ampliación de la escena.

Alejar. Aleja la posición del observador de la escena, por lo que la escena se hará más pequeña.

Desplazar. Permite desplazar el punto de observación paralelamente al plano de proyección. Para ello, tras pulsar sobre la opción, podremos realizar dicho desplazamiento simplemente moviendo el raton. Para terminar, simplemente hay que pulsar un botón.

Centrar. Ajusta la escena de forma que se visualice toda ella en la vista determinada.

Max/Min. Como antes comentamos, inicialmente aparecen cuatro cuadrantes en pantalla; esto puede ser modificado con este comando, haciendo que la vista seleccionada ocupe toda la zona de visualización, o haciendo que vuelva a su modo inicial.

Rotar. Realiza una rotación del punto de observación alrededor de la escena, manteniendo la dirección de observación. Desplazando el ratón horizontalmente, giramos respecto del eje Z, mientras que si el movimiento es vertical, la rotación se hace inclinando más o menos el plano de proyección hacia la posición del observador.

Restaurar. Restablece las características iniciales de las vistas, restaurando la posición de la cámara, los ejes, etc. Esto es útil cuando queramos regresar al esquema tradicional de planta, perfil y alzado.

Ejes. Hace que sean visibles u ocultos los ejes X, Y y Z. Estos ejes saldrán en línea discontinua y en color amarillo.

Las opciones Acercar, Alejar, Centrar, Restaurar y Ejes admiten actuación múltiple, es decir, si ejecutamos uno de estos comandos pulsando el botón derecho del ratón, la acción se realizará sobre las cuatro vistas.

Menú Objeto

Nuevo. Permite la creación de un nuevo objeto, siendo éste de uno de los siguientes tipos predefinidos: esfera, pirámide, tronco, toroide y caja; se solicita al usuario que elija uno de estos tipos. Todos los objetos se crearán colocándolo sobre el plano de proyección. Veamos cómo se procede para crear un objeto de una clase:

1. Esfera. El usuario debe introducir por teclado el número de vértices que tendrá cada uno de los “meridianos” de la esfera; posteriormente, se debe dar gráficamente el tamaño del radio de la esfera. El objeto creado consiste en una malla con forma esférica compuesta de polígonos de cuatro aristas en general y de tres aristas para aquéllos a los que pertenece cada uno de los “polos”. Por ejemplo, el aspecto tridimensional de una esfera de 20 vértices por circunferencia es la siguiente:

Edición en 3D (Tridimensional)

2. Pirámide. En este caso, el valor numérico que se pide al usuario es el número de vértices que componen la base, introduciendo el radio gráficamente. El objeto creado es una pirámide que tendrá por defecto una altura igual al diámetro de la base. El aspecto para el caso de 10 vértices sería el siguiente:

Edición en 3D (Tridimensional)

3. Tronco. Un tronco es un objeto resultado de eliminar de una pirámide la parte superior. Así pues, además de el número de vértices de las bases superior e inferior (lógicamente será el mismo), se deben determinar gráficamente sus respectivos radios. Veamos cómo queda un objeto de este tipo con 6 vértices en cada base:

Edición en 3D (Tridimensional)

4. Toroide. Un toroide es el resultado de realizar el recorrido de un polígono sobre el camino marcado por otro; así pues, debemos determinar la forma de ambos polígonos. Primero se introduce el número de vértices del polígono que marcará el camino, y posteriormente el del polígono que forma la sección. Gráficamente determinamos los radios máximo y mínimo del toroide, de tal forma que el diámetro del polígono de la sección será la diferencia entre ambos radios. Veamos un ejemplo de toroide con número de vértices igual a 16 para el exterior y 12 para la sección:

Edición en 3D (Tridimensional)

5. Cubo. La forma de este tipo de objetos es la obvia; el único parámetro necesario es el tamaño del mismo, que se introducirá gráficamente.

Al crear un objeto, si aumentamos el número de vértices éste se parecerá más a una figura perfecta. Así, la esfera se aproximará a una esfera perfecta, la pirámide a un cono, el tronco a un cono truncado y el toroide a un anillo circular con sección también circular. No obstante, este número no puede ser todo lo grande que se desee, por cuestiones de espacio en memoria. Para delimitar este número se realiza un cálculo aproximado de la memoria que precisará el objeto, y si no hay bastante disponible se da un mensaje por pantalla para que se disminuya el valor.

Seleccion. Permite la selección de uno o varios objetos; en caso de ser selección simple (botón izquierdo), cada vez que pulsemos el botón izquierdo seleccionaremos el objeto más cercano, entendiendo por éste aquél que contiene al vértice más cercano al puntero; para terminar se pulsa el botón derecho. Si hemos optado por selección múltiple (botón derecho), ésta se hace creando una ventana, y serán seleccionados todos los objetos que tengan algún vértice dentro de la misma. Todos los objetos seleccionados aparecerán en pantalla en color verde hasta que sea deseleccionado.

Deselec. Este comando es el opuesto al anterior, y funciona de forma completamente idéntica.

Eliminar. Con este comando, en modo sencillo iremos indicando con el botón izquierdo los objetos que se van eliminando, finalizando con el derecho. En modo múltiple, serán eliminados directamente todos los objetos que estén seleccionados.

Mover. Si estamos en modo sencillo, elegimos los objetos a mover y los desplazamos con el ratón, depositándolos con el botón izquierdo; para terminar, se pulsa el botón derecho. En modo múltiple se moverán de forma conjunta los objetos seleccionados. Señalar que todos los movimientos se realizan lógicamente de forma paralela al plano de proyección.

Escalar. Se utiliza esta opción para cambiar el tamaño de un objeto o conjunto de objetos; este escalado se hace respecto del punto central de la caja que encierra al objeto u objetos correspondientes.

Rotar. Realiza una rotación de un objeto u objetos sobre el plano de proyección; al igual que el caso anterior, el punto respecto del cual se rota es el centro de la caja que encierra al conjunto.

Copiar. Usamos esta opción para realizar un duplicado de un objeto u objetos, permitiendo que la copia creada sea trasladada de lugar para evitar que se solape con el original.

Unir. Este comando se utiliza cuando deseamos que una serie de objetos pasen a ser únicamente uno y sean tratados como tal. En el caso del modo sencillo, se supone que queremos unir dos objetos, con lo cual los indicamos mediante el ratón y se ejecuta la acción; en caso múltiple, la unión se realiza entre todos los objetos seleccionados.

Menú Vértice

Seleccion. Al igual que en el caso de los objetos, podemos seleccionar vértices individualmente o mediante una ventana; en el primer caso, el vértice seleccionado es el más cercano al puntero del ratón, y en el segundo se seleccionan todos aquellos que caigan dentro de dicha ventana. Un vértice seleccionado se distingue porque aparece un pequeño círculo rojo alrededor de él.

Deselec. Realiza la acción contraria a la anterior, de forma completamente análoga.

Mover. Permite el movimiento de un vértice ó conjunto de vértices, de idéntica forma a la vista hasta ahora: bien eligiendo uno de forma individual o bien moviendo todos los seleccionados conjuntamente.

Escala. Realiza un escalado de los vértices de forma similar a la de un objeto, es decir, tomando como punto de referencia el centro del conjunto de vértices. Esta opción, como es lógico, se realiza sobre el conjunto de vértices seleccionados.

Menú Escena

Cargar. Lee un fichero de disco y carga la escena en memoria. Para ello, se solicita el nombre del fichero a cargar y se saca por pantalla la escena. Respecto al nombre del fichero, éste puede tener cualquier nombre válido para M.S.-D.O.S.; se puede especificar cualquier extensión e incluso la ruta del fichero. En el manual técnico se explica en profundidad el formato de los ficheros.

Grabar. Salva la escena actual a disco, pidiendo por teclado el nombre destino; en caso de que el fichero ya exista, se pide confirmación para sobreescribirlo.

Nueva. Elimina de memoria la escena devolviendo el programa al estado inicial, pidiendo previamente confirmación por teclado.

Mezclar. Realiza la misma función que cargar, con la diferencia de que no elimina la escena actual, sino que la mantiene y une ambas escenas. Esta opción es muy útil cuando queremos diseñar una escena descomponiéndola en varias subescenas más pequeñas.

Cuando entramos en una opción que permite movimiento, rotación, etc. mediante el ratón, existe la posibilidad de fijar el movimiento del mismo. Esto se puede variar pulsando la tecla Ctrl; cada vez que se pulse, aparecerá en el centro del cuadrante un pequeño icono que indica el modo actual de funcionamiento. Veamos el significado de cada uno de ellos:

Movimiento libre

Movimiento vertical. El movimiento horizontal

está bloqueado.

Movimiento horizontal. El movimiento vertical

está bloqueado.

Movimiento en diagonal. El movimiento se hace

igual en horizontal y en vertical.

El estado inicial del ratón es el de movimiento libre, y sucesivamente se va pasando cada uno de los siguientes. Esta limitación del ratón se puede utilizar cuando por ejemplo queremos mover un objeto en horizontal. La utilidad de la última opción se manifiesta cuando cambiamos de tamaño un objeto y queremos que mantengan la proporción entre el eje horizontal y el vertical.

Manual técnico

La práctica realizada consiste en un editor tridimensional de escenas compuestas por un conjunto de sólidos. Cada objeto se crea a partir de una serie de primitivas, que concretamente son esferas, pirámides, troncos de pirámide, toroides y cubos. Cada una de estas figuras básicas admite una serie de transformaciones hasta darle la forma deseada; se pueden desplazar, rotar, duplicar, unir, etc.

La edición se realiza a partir de cuatro vistas distintas ,cada una de las cuales representa la proyección paralela de la escena desde un determinado punto, y que inicialmente serán las típicas de los programas de diseño (planta, alzado, perfil y una vista tridimensional), y que podrán ser variadas a base de desplazar el punto de observación o rotando el plano de proyección.

Veamos a continuación una serie de cuestiones básicas respecto de la práctica, tal y como se plantearon al comienzo de la realización de la misma.

Representación interna

El modelo más adecuado para la representación de sólidos es el conocido bajo el nombre de B-rep (Boundary Representation). Éste consiste en guardar para cada sólido los datos de los polígonos que forman su contorno.

Por tanto para cada objeto tendremos que guardar, además de las coordenadas de los vértices que lo componen, una lista de polígonos que indique qué vértices forman cada uno de los mismos.

Ya que el lenguaje en el que está implementada la práctica es el Turbo Pascal, las listas de objetos, polígonos y vértices se crean mediante listas enlazadas a base de punteros. Podemos representar una escena mediante el siguiente esquema.

Escena

Objeto 1 Objeto 2 Objeto N

Así, una escena será una lista enlazada de objetos; cada objeto está compuesto a su vez de listas: una de vértices, en la que cada nodo almacena la posición en el espacio de dicho vértice, y otra de polígonos en la que cada polígono es a su vez otra lista enlazada de apuntadores a los vértices que forman el polígono.

La implementación de los tipos en el programa es la siguiente:

- Lista de vértices:

lista_vertices=^tlvert;

tlvert=record

pos : vector;

sig : lista_vertices;

end;

- Lista de referencias a un vértice en un polígono:

l_vertpoli=^tp;

tp=record

vert: p_vertice;

sig: l_vertpoli;

end;

- Lista de polígonos:

lista_poligonos=^lpol;

lpol=record

lvert: l_vertpoli;

sig: lista_poligonos;

end;

- Lista de objetos:

lista_objetos=^lo;

lo=

record

lvert: lista_vertices;

lpoli: lista_poligonos;

sig: lista_objetos;

end;

Este tipo de representación hace que cuando queremos realizar una transformación baste con modificar la posición de los vértices.

Especificación de las vistas

En este apartado vamos a ver la forma en que se define cada una de las vistas que se pueden representar en los cuatro cuadrantes operativos en el programa. Para esto he optado en la práctica por la utilización de los siguientes parámetros.

- Ejes que definen el plano de proyección. Estos ejes nos dan un nuevo sistema de referencia, y los denominaremos en lo subsiguiente u, v y n. Los dos primeros son dos vectores unitarios contenidos en el plano de proyección, y el tercero es un vector también unitario perpendicular al plano de proyección.

- Centro de la pantalla. Especifica, en coordenadas respecto del sistema de referencia anteriormente comentado, la posición del plano de proyección que coincidirá con el centro de la pantalla (o de la vista).

- Escala. Es un valor numérico que indica cuántos pixels equivalen a una unidad de medida en coordenadas reales.

Hay que señalar que, por estar en un sistema de proyección paralela, no precisamos de un origen para el sistema de referencia, puesto que no es relevante la posición de dicho plano, sólamente su orientación; por ello, podemos suponer que éste pasa por el origen.

Todo esto podemos verlo teniendo en cuenta la configuración inicial de las vistas. Gráficamente podemos verlo de esta forma:

1 Y 2 Z

X Y

2 Z 3 Z

Y

X

X

El primer cuadrante representará inicialmente la planta, por lo que el eje u será paralelo al eje x, el v paralelo al y y el n paralelo al z. Sucede algo parecido con los cuadrantes segundo y tercero. El cuarto es especial, ya que inicialmente representa una vista isométrica; por tanto, el eje v será paralelo a la bisectriz de los ejes x e y, y como el eje n debe ser paralelo a (1,-1,1), obtendremos el que nos falta, que es el v, mediante el producto vectorial de los anteriores. Por otra parte, la vista estará centrada en todos los casos sobre el punto (0,0,0); por tanto, podemos obtener la siguiente tabla:

1

2

3

4

Eje u

( 1, 0, 0)

( 0, 1, 0)

( 1, 0, 0)

( 1/"2, 1/"2, 0)

Eje v

( 0, 1, 0)

( 0, 0, 1)

( 0, 0, 1)

(-1/"6, 1/"6, 2/"6)

Eje n

( 0, 0, 1)

( 1, 0, 0)

( 0,-1, 0)

( 1/"3,-1/"3, 1/"3)

Configuración de las ventanas

El usuario tiene a su disposición cuatro cuadrantes sobre los que definir cada una de las cuatro posibles vistas. Cada ventana viene determinada por el cuadro que las enmarca, es decir, por cuatro parámetros: cmin, fmin,cmax y fmax, que representan las columnas y filas máximas y mínimas que acotan a la ventana.

Inicialmente, el tamaño de las cuatro ventanas es idéntico; podemos ver esto mediante el siguiente gráfico:

2 278 282 558

2

228

232

458

Disponemos de una variable global, lim_cuad, que indica en todo momento la disposición de cada cuadrante. También existe un vector, lim_cuad_ini, que contiene la configuración inicial, y otro, maxima, que hace que uno de los cuadrantes ocupe todo el espacio. Esto se utiliza al maximizar la pantalla.

Generación de las figuras primitivas

Como se ha mencionado anteriormente, la construcción de objetos se realiza modificando una serie de figuras básicas, por lo que debemos conocer la forma de generar cada una y almacenarla en memoria de la forma en que antes se menciona; veámos esto una por una.

- Esfera.- La forma de una esfera puede ser aproximada mediante una malla de puntos unidos por aristas, que al ser desplegada adopta una forma como la de la figura abajo representada. En ella vemos que la figura generada es parecida a la forma de meridianos y paralelos en un globo terráqueo.

La posición de cada vértice vendrá dada por un ángulo en horizontal y otro en vertical, de tal modo que dividimos los 360 grados en partes iguales y vamos calculando las posiciones e insertando los vértices en una lista.

(N/2)-1

Por la forma en que se han calculado las posiciones de los vértices, éstos se encuentran dentro de la lista de derecha a izquierda y de abajo hacia arriba, teniendo en cuenta la malla. Con esto deducimos que cada polígono estará compuesto por un vértice, su sucesor, y los dos que se diferencian de éstos en (N/2)-1 posiciones dentro de la lista, con lo cual podemos crear directamente la lista de polígonos. Además hay que crear los polígonos de la parte superior e inferior, lo que se hace relacionando los vértices del “polo sur” con el primer vértice de la lista, y a partir de ahí avanzando (N/2)-1 posiciones. Se procede análogamente con el “polo norte”, comenzando por el vértice superior izquierdo.

- Pirámide.- La forma de la malla resultado de una pirámide es obvia; en este caso, primero creamos los vértices de la base, conectándolos entre sí. Posteriormente, simplemente se conecta cada uno de estos vértices también con su sucesor en la base y con el que forma la cima de la pirámide; así generamos los polígonos triangulares de cada una de las caras laterales.

- Tronco.- En este caso, la malla que vamos a crear es de la siguiente forma (recordemos que N es el número de vértices por circunferencia y que los vértices de la derecha conectan con los de la izquierda).

N

Para generar la lista de vértices, vamos insertando en ella alternativamente un vértice de cada base, aumentando progresivamente el ángulo en horizontal respecto del centro de las dos bases. De forma parecida a la esfera, en este caso cada polígono está formado por un vértice y sus tres sucesores.

- Toroide.- Recordemos que un toroide tiene dos valores para el número de vértices: uno para el perímetro y otro para la sección. Los llamaremos N1 y N2, respectivamente. De esta manera, la forma de la malla al ser desplegada es la de la figura siguiente, teniendo en cuenta que en este caso los nodos de la derecha conectan con los de la izquierda y los de arriba con los de abajo.

N2

N1

La posición de los vértices se calcula mediante dos ángulos, uno que expresa el desplazamiento respecto del perímetro y otro dentro de la sección. Teniendo en cuenta la disposición de la lista de vértices, tenemos que un vértice estará en el mismo polígono que su sucesor en la lista, y los dos que disten de éstos N2 posiciones dentro de la misma. La diferencia respecto de la esfera es que en este caso hay que considerar la conectividad de la parte superior de la malla con la parte inferior.

- Cubo.- Aunque este tipo de figura aparece como primitiva en el programa, en realidad es generada a partir de un tronco de cuatro vértices y el mismo radio en cada base.

Hay que señalar que en todas las creaciones de objetos, se ha tenido en cuenta el sentido de definición de los polígonos; en este caso, el criterio ha sido el de seguir el sentido de las agujas del reloj, con lo que el vector normal de los polígonos siempre va hacia fuera del sólido.

Selecciones

Durante la realización de esta práctica, se hizo evidente la necesidad que hay en cualquier programa de edición de poder seleccionar una serie de entidades para realiza operaciones con ellas sin tener que hacerlas individualmente sobre cada uno de sus componentes. En este caso, se manifiesta en el manejo de grupos de objetos y de vértices.

La solución adoptada consiste en tener en todo momento una lista de objetos y otra de vértices seleccionados, de tal manera que cada nodo de la misma apunte a un objeto o vértice. De esta forma, todas las rutinas se han diseñado para actuar sobre estas selecciones; en el caso de que deseemos realizar una acción sobre un elemento concreto, con el fin de poder utilizar la misma rutina, simplemente creamos una selección temporal que sólo contendrá a dicho elemento.

El tipo de datos construido para la lista de objetos seleccionados es la siguiente:

l_obj_sel=^los;

los=record

obj: objeto;

sig: l_obj_sel;

end;

El tipo de datos para la lista de vértices es exactamente igual que el de una lista de referencias, que es la que compone cada polígono, ya que contiene punteros a una serie de vértices.

Formato externo

La forma en que se graban las escenas en disco intenta seguir aproximadamente la organización que se sigue en memoria. Para cada objeto, tendremos un conjunto de vértices, especificando su posición, y una lista de polígonos, que harán referencia a esos vértices. Ahora bien, la forma de referirse a esos vértices debe ser distinta; en este caso, a cada vértice se le asigna un número según la posición que ocupa dentro de la lista correspondiente, con lo que la referencia consiste precisamente en ese número.

Veamos más en concreto el formato; los objetos se colocan uno detrás de otro dentro del fichero, teniendo cada uno la siguiente estructura.

Número de vértices

Vértice 1

Vértice 2

...

Vértice V

Número de Polígonos=P

Número de referencias polígono 1

Posición del vértice

...

Posición del vértice

...

Número de referencias polígono P

Posición del vértice

...

Posición del vértice

Supongamos que tenemos un tetraedro, que está formado por cuatro polígonos cada uno compuesto de tres vértices, con esta forma:

A

D

C B

Lista de vértices: A B C D

Lista de polígonos: P1: A B C

P2: A D B

P3: A C D

P4: B D C

La representación en el fichero de este objeto sería la siguiente:

4 (vértices)

Ax Ay Az

Bx By Bz

Cx Cy Cz

Dx Dy Dz

4 (polígonos)

3 (el polígono P1 tiene tres vértices)

1 2 3 (vértices A, B y C)

3 (el polígono P2 tiene tres vértices)

1 4 2 (vértices A, D y B)

3 (el polígono P2 tiene tres vértices)

1 3 4 (vértices A, C y D)

3 (el polígono P2 tiene tres vértices)

2 4 3 (vértices B, D y C)

Si la escena tuviera más objetos, éstos se dispondrían uno detrás de otro sin tener que incluir ninguna información extra.

Por último debemos mencionar los tipos utilizados para representar cada dato en el fichero; en la tabla se da tanto el tipo como el tamaño en bytes del mismo.

Tipo de dato

Tamaño en bytes

Nº de vértices

Word

2

Posición vértice

Vector

18

Nº de polígonos

Word

2

Nº vert del políg.

Word

2

Referencia a vért.

Word

2

Funciones auxiliares

El programa incluye una serie de rutinas que no están directamente relacionadas con la base de la práctica, pero que han sido utilizadas para la implementación de la misma, como son funciones matemáticas, manejo de mensajes, etc. Veamos simplemente lo que hace cada una de ellas.

- Procedure Mensaje(x,y: integer;s: string)

Presenta el mensaje s en la posición (x,y) de la pantalla.

- Procedure Info(s: string)

Presenta el mensaje s en la parte inferior de la pantalla.

- Procedure Limita_raton

Limita el movimiento del ratón a la zona de pantalla demarcada por la vista activa.

- Procedure Libera_raton

Permite el movimiento del ratón por toda la pantalla.

- Procedure Poner_menus

Presenta en la parte izquierda pantalla todos los menús de acciones.

- Procedure Cambia_opcion(op,nop: byte)

Desactiva la opción op y activa la nop.

- Function Cad_Ent(s: string): integer

Transforma la cadena de caracteres s a entero.

- Function Cad_Real(s: string): real

Transforma la cadena de caracteres s a número real.

- Function Leer_cadena(men: string): string

Lee una cadena por teclado, apareciendo los sucesivos caracteres en la zona inferior de la pantalla.

- Procedure Inicia_graf

Inicializa la pantalla a modo gráfico de 640x480 y 16 colores.

- Procedure Escala_vector( v1: vector; esc: real;

var v2: vector);

Multiplica el vector v1 por el escalar esc, devolviendo el resultado en v2; es decir, v2 = esc·v1.

- Procedure Dif_vectores(A,B: vector; var C: vector);

Realiza la operación vectorial C = A - B.

- Procedure Suma_vectores( A,B: vector; var C: vector);

Realiza la operación vectorial C = A + B.

- Function Prod_escalar(a,b: vector): real;

Devuelve el producto escalar de los vectores a y b.

- Procedure Prod_matrices(a,b: matriz; var c: matriz);

Multiplica las matrices a y b, devolviendo el resultado en la matriz c.

- Procedure MatrizXvector(M: matriz; vi: vector;

var vf: vector)

Multiplica la matriz M por el vector vi.

- Procedure VectorXmatriz(vi: vector; M: matriz;

var vf: vector)

Multiplica el vector vi por la matriz M. Sería la operación vf = vitM

- Procedure Intro_coord(var A: vector;c1,c2,c3: real)

Da a las coordenadas del vector A los valores c1, c2 y c3.

- Function Distancia_3d(A,B: vector): real

Calcula la distancia entre los puntos dados por los vectores A y B.

- Function Distancia_2d(A,B: pixel): real

Calcula la distancia entre los pixels cuya posición determinan A y B.

- Function Minimo(a,b: real): real

Devuelve el valor mínimo entre a y b.

Rutinas principales

A continuación vamos a analizar las rutinas que intervienen en la representación gráfica de la escena, transformación de los objetos, etc. No entraremos en demasiados detalles respecto de la implementación final de las rutinas, sino que nos quedaremos en una descripción general de las mismas.

Conversión entre sistemas de referencia

En el sistema de diseño actual, disponemos de tres sistemas de referencia distintos: el mundo real, el plano de proyección y la pantalla. Disponemos de una matriz llamada ejes para cada vista que contiene los vectores unitarios que determinan los ejes u, v y n. El hecho de definirlo como matriz nos permite acceder a ella de forma global y también de forma individual a cada uno de los vectores que la forman. Además, se han definido unas constantes u, v y n que equivalen respectivamente a 1, 2 y 3, con lo que nos podremos referir a un eje concreto como ej[u], ej[v] y ej[n], ganándose así en claridad. Sucede igual con x, y y z.

Estos tres sistemas admiten transformaciones de unos a otros, existiendo una serie de rutinas implementadas para cumplir con dicha función:

Trans_wc_pp: Convierte un vector de coordenadas reales a coordenadas respecto del plano de proyección. Para realizar esta conversión, se calcula la proyección del vector sobre cada uno de los ejes del sistema de referencia final. Esto equivale a realizar el producto escalar del vector por los vectores u, v y n correspondientes, o dicho de otra forma, multiplicar el vector por la matriz ejes asociada a la vista seleccionada.

Trans_pp_wc: Realiza la función contraria a la anterior, partiendo de un vector en coordenadas respecto al plano de proyección. En este caso habrá que multiplicar cada coordenada del vector a convertir por el vector correspondiente de los ejes u, v y n. Dicho de otro modo, llamando pp al vector original y wc al resultado:

wc = pp[u]·u + pp[v]·v + pp[n]·n

Esto es equivalente a multiplicar por delante el vector pp por la matriz de los ejes.

Trans_pp_2d: Convierte un vector de coordenadas respecto del plano de proyección a coordenadas respecto de la ventana activa. Para aclarar esto veamos la siguiente figura:

Puesto que el centro de la ventana está situado en el punto indicado por CW (contenido en una matriz del mismo nombre), debemos calcular primero las coordenadas del punto respecto de dicho centro, multiplicar dicha diferencia por la escala de la vista y sumar el resultado al centro de la ventana en coordenadas de pantalla. Las coordenadas de pantalla se expresan mediante el tipo pixel, que contiene un número de fila y otro de columna; puesto que las filas crecen en sentido opuesto al eje v, este término debe apaecer con signo negativo.

Trans_2d_pp. Realiza la función opuesta a la anterior, y su deducción es simple si se deshacen las operaciones realizadas en el punto anterior, es decir, calculamos las coordenadas respecto del centro de la ventana en pantalla y dividimos por la escala y sumamos al vector CW. Naturalmente, la coordenada n no puede ser deducida, por lo que se le da un valor arbitrario igual a cero.

Trans_wc_2d. Esta rutina consiste tan sólo en la composición de Trans_wc_pp y Trans_pp_2d.

Dibujar_una escena

Para sacar por pantalla una escena, lo que se hace es recorrer toda la lista de objetos, transformando las coordenadas de cada vértice a coordenadas de pantalla y dibujando la correspondiente línea.

El color de la línea varía según el objeto esté seleccionado o no; para ello, se busca el objeto en la lista de objetos seleccionados y se pone el color blanco ó verde.

También puede cambiar el tipo de línea, ya que cuando el polígono es posterior se dibuja una línea discontinua. Para ello, se utiliza el método del vector normal, tomando tres vértices del polígono y calculando dicho vector. Si el producto escalar de dicho vector por el vector normal al plano es menor que cero, el polígono será posterior, en caso contrario será visible. Esto es así porque el vector normal va hacia fuera del sólido y además el vector de proyección es opuesto a la dirección de n.

N1 N2

n Dp

N1·n<0 --> Posterior

N2·n>0 --> Visible

Operaciones sobre vistas

Existe una serie de transformaciones que se pueden hacer sobre una vista; veamos cuáles son y en qué consisten:

Acercar o alejar. Puesto que estamos en proyección paralela, la forma de acercar o de alejar el punto de observación a la escena es aumentar o disminuir la escala. Esto se hace mediante la rutina Zoom_vista, multiplicando la escala por un factor determinado.

Desplazar. Se utiliza para cambiar la posición del punto desde el que queremos ver la escena. Esto se consigue en el procedimiento desplaza_camara, que simplemente va leyendo la variación que se produce en la posición del ratón y realiza el cambio en las componentes u y v sumándole la variación horizontal y vertical, respectivamente.

Centrar. Para hacer que la escena coincida con los bordes de la ventana, lo primero que se hace es delimitar las coordenadas u y v máxima y mínima de la misma, transformando todos los puntos a coordenadas del plano de proyección. Posteriormente, se calcula la escala dividiendo el tamaño de la ventana entre el tamaño en el plano de proyección de la escena, y se fija el centro de la vista en las coordenadas del plano que dan el centro de la escena.

Maximizar y minimizar. En todo momento disponemos de una variable llamada maximizado que indica si la ventana actual ocupa todo el espacio de visualización. De este modo, sabemos si debemos restaurar los límites originales de los cuadrantes o si debemos cargar los datos del vector maxima (antes comentado).

Rotar. Esta función tiene como objetivo el poder ofrecer una visión de la escena desde cualquier punto de vista; al mover el ratón horizontalmente, generamos una rotación sobre el eje z, mientras que si lo hacemos verticalmente, estamos cambiando la inclinación del plano de proyección. En el primer caso, usamos una matriz de transformación típica mediante el seno y el coseno del ángulo de rotación. En el segundo, debemos rotar respecto del eje u, con lo que en lugar de multiplicar por detrás multiplicamos por delante. Este razonamiento es análogo al usado al transformar entre sistemas de referencia, solo que en lugar de transformar un vector lo hacemos con tres vectores, es decir, con una matriz. Todo esto está implementado en el procedimiento Rotar_camara.

Restaurar. Se utiliza para recuperar el estado inicial de las vistas; para esto, tan sólo hay que hacer que los vectores lim_cuad, cw y ejes contengan los valores iniciales, definidos en la sección de constantes.

Ejes. Para ocultar ó hacer visibles los ejes, se cambia el valor del vector ej_vis, que cuando es true indica que los ejes son visibles; este vector se usa en el proceso de representar la escena para dibujar los ejes x, y y z.

Operaciones sobre objetos

Para realizar las transformaciones, el criterio seguido ha sido el del cálculo vectorial, en lugar del clásico de usar matrices cuadradas de tamaño 4. Esto es debido a que, una vez implementado este último método, se pone de manifiesto que la mayoría de las casillas de las matrices usadas están a cero, con lo que estamos haciendo cálculos innecesarios. Por ejemplo, para desplazar un vértice, sólo necesitaríamos los valores de la última columna, pero aun así estamos haciendo gran cantidad de productos (recordemos que el producto de matrices tiene complejidad de orden n3). De aquí se deduce que el uso de matrices de este tipo está más justificado en el caso de transformaciones muy complejas.

Por ello, he preferido deducir analíticamente las operaciones vectoriales y aplicarlas a los puntos a transformar.

Crear un objeto. La rutina Nuevo_objeto permite introducir un nuevo objeto en la escena, mediante la elección de uno de los tipos básicos y ejecutando los procedimientos esfera, piramide, tronco y toroide, que crean un objeto del tipo correspondiente, tal y como se comentó anteriormente.

En este procedimiento usamos el procedimiento polígono, que dibuja un polígono regular de un número determinado de lados. Con esto permitimos que al introducir gráficamente el tamaño de un objeto se vea la forma del mismo.

Elegir un objeto o vértice. La rutina Seleccionar incluye estas dos posibilidades, ya que devuelve el vértice más cercano al puntero del ratón y el objeto al que este vértice pertenece. El proceso es simple: primero se lee la posición del ratón, y a continuación se consultan todos los vértices de la escena, transformándolos a coordenadas de pantalla; en cada momento recordamos el vértice más cercano y el objeto en el que está incluido éste. Hay que señalar que esta rutina no modifica la lista de objetos ni de vértices seleccionados, simplemente señala a los más cercanos. Serán las acciones más complejas las que la utilicen (por ejemplo, antes de mover un objeto lo seleccionamos).

Seleccionar un objeto o vértice. En el caso de un objeto, basta con insertar el objeto en la lista obj_sel, que contiene los objetos seleccionados; esto se hace con la rutina selec_objeto. El caso de un vértice es idéntico, pero con la lista vert_sel, usando selec_vert.

Deseleccionar un objeto o vértice. Para conseguir esto basta con eliminar de la lista de objetos ó vértices seleccionados el objeto en cuestión.

Consulta sobre selección de un objeto ó vértice. Para saber si un objeto ó vértice está seleccionado, recorremos la lista hasta que lo encontramos. Esto se implementa en Esta_selec_obj y Esta_selec_vert.

Seleccionar un conjunto de objetos ó vértices. Todas las acciones descritas anteriormente se utilizan para seleccionar un único elemento; pero existe otro modo de hacer selecciones: mediante cuadro, con la rutina Selec_cuadro. En este caso, se leen las esquinas del cuadro, y se comienza a recorrer la lista de objetos; todos los vértices que caigan dentro del cuadro y los objetos a los que éstos pertenecen son candidatos. Entonces, según la acción que queramos realizar (seleccionar/deseleccionar objeto/vértice), se aplica la rutina correspondiente, teniendo cuidado de no seleccionar un objeto ya seleccionado y de no deseleccionar uno que no lo esté.

Eliminar un objeto. La eliminación de un objeto consiste en recorrer todas las listas que forman su estructura e ir liberando la memoria correspondiente.

Las siguientes acciones sobre objetos actuan siempre sobre una lista de objetos seleccionados; recordemos que cuando se actúa sobre un sólo objeto se habrá creado anteriormente una selección virtual conteniendo sólamente a ese objeto.

Mover un conjunto de objetos. Cuando movemos un objeto en una vista determinada queremos que lo haga en paralelo con el plano de proyección, por lo que el movimiento del ratón lo convertimos a un vector en coordenadas respecto a dicho plano, y al convertirlo a coordenadas reales, tendremos el desplazamiento real. Para modificar los objetos, recorremos la lista de objetos seleccionados y desplazamos cada vértice de cada objeto. Todo este proceso se realiza dentro de Mover_selec.

Escalar un conjunto de objetos. Para la realización de esta transformación disponemos de dos rutinas: Escalar_selec, que lee el ratón y determina el grado de escalado respecto de cada eje, y Escalar_objeto, que realiza el escalado del objeto propiamente dicho.

En la primera rutina se procede de forma parecida al anterior apartado, es decir, se capta la variación en el ratón, y la variación horizontal se traduce como un escalado en el eje u y la vertical en el eje v. Tras convertir este vector a coordenadas reales, se recorre la lista de objeto seleccionados y se aplica Escalar_objeto sobre cada uno de ellos. Este escalado se realiza calculando la posición de cada vértice respecto del punto de referencia, y multiplicando cada coordenada del vector resultado por el vector de escalado.

Rotar un conjunto de objetos. Básicamente la estructura es la misma que el caso anterior, con la salvedad de que el movimiento del ratón es interpretad ahora como el ángulo en que se desea rotar el objeto.

Para rotar un objeto, puesto que esta rotación es sobre el plano de proyección, se debe expresar cada vértice en relación a dicho sistema de referencia, calcular su posición respecto del centro de rotación, y realizar la transformación sobre el vector obtenido. Una vez rotado el vector lo volvemos a convertir a coordenadas reales.

Copiar un conjunto de objetos. Este proceso consiste en recorrer la lista de objetos seleccionados y realizar un clon del cada objeto. Para esto, en la rutina Copiar_selec se recorre la lista de vértices y se hace un calco de la misma. Posteriormente, para cada polígono, se busca los vértices a los que referencia el original; la posición que ocupe un vértice en la lista original nos dará la posición que ocupa su imagen en la nueva vista, con lo que el nuevo polígono debe contener a este nuevo vértice en su lista de referencias.

Unir un conjunto de objetos. La unión básica se hace entre dos objetos, en el proceso Unir_objetos; el proceso es bastante sencillo: basta con unir las dos listas de vértices en una única lista, y unir de la misma forma las de polígonos, además de borrar uno de los objetos.

Existe otra rutina, Unir seleccion, que une un conjunto de objetos seleccionados. Para ello, simplemente va uniendo todos los objetos con el primero de ellos. Además debe borrar de la selección todos los objetos excepto el primero, ya que éstos han dejado de existir como tales.

Operaciones sobre una escena

Las operaciones relativas a escenas son las siguientes:

Nueva escena. Antes de eliminar la escena actual, se pide confirmación, para posteriormente utilizar la rutina Eliminar_escena, que consiste en recorrer la lista de objetos e ir eliminándolos.

Cargar_escena. Siguiendo el esquema antes comentado del formato de un fichero, carga en memoria una escena, eliminándo la actual, previa confirmación. Lógicamente se comprueba que el fichero a cargar realmente existe, abriéndolo y consultando el error devuelto por el sistema operativo. El procedimiento asociado es Cargar_escena.

Grabar_escena. Se pide por teclado el nombre del fichero al que se desea volcar la escena, y si este no existe, se procede a su grabación siguiendo el formato anteriormente explicado.

Mezclar. Utiliza también el procedimiento Cargar_escena, con la diferencia de que no elimina la escena actual, sino que los objetos se van incorporando a la misma conforme son leidos.

{$M 65000,0,655350}

Program Infografia;

uses graph,crt,mouse,drivers,dos;

const x=1;y=2;z=3;

u=1;v=2;n=3;

type pixel=

record

c,f: integer;

end;

vector=array[1..3] of real;

matriz=array[1..3] of vector;

lista_vertices=^tlvert;

p_vertice=lista_vertices;

tlvert=record

pos : vector;

sig : lista_vertices;

end;

l_vertpoli=^tp;

tp=record

vert: p_vertice;

sig: l_vertpoli;

end;

lista_poligonos=^lpol;

lpol=record

lvert: l_vertpoli;

sig: lista_poligonos;

end;

lista_objetos=^lo;

objeto=lista_objetos;

lo=

record

lvert: lista_vertices;

lpoli: lista_poligonos;

sig: lista_objetos;

end;

l_obj_sel=^los;

los=record

obj: lista_objetos;

sig: l_obj_sel;

end;

l_vert_sel=l_vertpoli;

type num_cuadrante=0..3;

t_lim_cuad=

record

cmin,fmin,cmax,fmax: integer;

end;

tlimites=array[num_cuadrante] of t_lim_cuad;

tejes=array[num_cuadrante] of matriz;

torig=array[num_cuadrante] of vector;

tescala=array[num_cuadrante] of real;

tej_vis=array[num_cuadrante] of boolean;

modo_raton=(libre,horiz,vertic,isomet);

alfabeto=set of char;

var izq,der: boolean;

escena: lista_objetos;

maximizado: boolean;

obj_sel: l_obj_sel;

vert_sel: l_vert_sel;

ejes: tejes;

cw: torig;

escala: tescala;

lim_cuad: tlimites;

ej_vis: tej_vis;

cuad_act: num_cuadrante;

mraton: modo_raton;

const raiz2_1=0.707106781186548;

raiz3_1=0.577350269189626;

raiz6_1=0.408248290463863;

const ejes_ini: tejes=((( 1, 0, 0), ( 0, 1, 0), ( 0, 0, 1)),

(( 0, 1, 0), ( 0, 0, 1), ( 1, 0, 0)),

(( 1, 0, 0), ( 0, 0, 1), ( 0,-1, 0)),

(( raiz2_1, raiz2_1, 0),

(-raiz6_1, raiz6_1, 2*raiz6_1),

(raiz3_1, -raiz3_1, raiz3_1)));

orig_ini: torig=(( 0, 0, 0),( 0, 0, 0),( 0, 0, 0),( 0, 0, 0));

lim_cuad_ini: tlimites=

((cmin: 2; fmin: 2; cmax: 278; fmax: 228),

(cmin: 282; fmin: 2; cmax: 558; fmax: 228),

(cmin: 2; fmin: 232; cmax: 278; fmax: 458),

(cmin: 282; fmin: 232; cmax: 558; fmax: 458));

Maxima: t_lim_cuad=(cmin: 2; fmin: 2; cmax: 558; fmax: 458);

escala_ini: tescala=( 100, 100, 100, 100);

ej_vis_ini: tej_vis=(false, false, false, false);

maxreal: real=1.7e38;

cos30=0.866025403784439;

sen30=0.5;

coef_zoom= 0.8;

final_lista=nil;

color_oselec=green;

color_vselec=brown;

color_nosel=white;

color_ejes=yellow;

modo2D=true;

modo3D=false;

caracteres=[#0..#255];

digitos=['0'..'9'];

const numitems=27;

const menu: array[1..numitems] of

record

texto: string[10];

ayuda: string[60];

fila: byte;

end=

((texto:'Acercar'; ayuda:'Acercar al observador a la escena'; fila: 1),

(texto:'Alejar' ; ayuda:'Alejar al observador de la escena'; fila: 2),

(texto:'Desplazar'; ayuda:'Desplazar el punto de observaci¢n'; fila: 3),

(texto:'Centrar'; ayuda:'Centrar la escena en la vista seleccionada'; fila: 4),

(texto:'Max/Min'; ayuda:'Maximizar o minimizar la vista seleccionada'; fila: 5),

(texto:'Rotar'; ayuda:'Realizar una rotaci¢n de la c mara'; fila: 6),

(texto:'Nuevo'; ayuda:'Crear un nuevo objeto'; fila:10),

(texto:'Seleccion'; ayuda:'Seleccionar un objeto'; fila:11),

(texto:'Deselec'; ayuda:'Deseleccionar un objeto'; fila:12),

(texto:'Eliminar'; ayuda:'Eliminar un objeto/conjunto existentes'; fila:13),

(texto:'Mover'; ayuda:'Mover un objeto/conjunto'; fila:14),

(texto:'Escala 2D'; ayuda:'Cambiar el tama¤o en 2D de un objeto/conjunto'; fila:15),

(texto:'Escala 3D'; ayuda:'Cambiar el tama¤o en 3D de un objeto/conjunto'; fila:16),

(texto:'Rotar'; ayuda:'Rotar sobre s¡ mismo un objeto/conjunto'; fila:17),

(texto:'Copiar'; ayuda:'Realizar un duplicado de un objeto/conjunto'; fila:18),

(texto:'Unir'; ayuda:'Unir dos ¢ m s objetos para formar uno s¢lo'; fila:19),

(texto:'Seleccion'; ayuda:'Seleccionar un v rtice'; fila:21),

(texto:'Mover'; ayuda:'Mover un v rtice/conjunto de v rtices'; fila:23),

(texto:'Cargar'; ayuda:'Cargar una escena almacenada en un fichero'; fila:26),

(texto:'Grabar'; ayuda:'Salvar la escena en un fichero'; fila:27),

(texto:'Nueva'; ayuda:'Crear una escena nueva'; fila:28),

(texto:'Restaurar'; ayuda:'Restaurar la vista inicial'; fila: 7),

(texto:'Deselec'; ayuda:'Deseleccionar un objeto'; fila:22),

(texto:'Escalar'; ayuda:'Escala v rtices respecto del centro'; fila:24),

(texto:'Ejes'; ayuda:'Muestra u oculta los ejes X, Y y Z'; fila: 8),

(texto:'Mezclar'; ayuda:'Mezcla la escena cargada con la actual'; fila:29),

(texto:'Salir'; ayuda:'Salir del programa'; fila:30));

const acer=1;alej=2;desp=3;cent=4;mxmn=5;roca=6;

nuev=7;slob=8;dsob=9;elim=10;mvob=11;es2D=12;es3D=13;rota=14;copi=15;unio=16;

slvt=17;mvvt=18;carg=19;grab=20;nues=21;rest=22;dsvt=23;esvt=24;eje=25;mezc=26;

salir=27;

Function Leertecla: char;

begin

mem[$40:$1A]:= mem[$40:$1C];

Leertecla:= readkey;

end;

Procedure Mensaje(x,y: integer;s: string);

var antcol: byte;

vp: viewporttype;

begin

getviewsettings(vp);

setviewport(0,0,getmaxx,getmaxy,clipon);

antcol:= getcolor;

setcolor(yellow);

setfillstyle(1,lightblue);

bar(x,y,x+textwidth(s),y+textheight(s));

outtextxy(x,y,s);

setcolor(antcol);

setviewport(vp.x1,vp.y1,vp.x2,vp.y2,clipon);

end;

Procedure Info(s: string);

var vp: viewporttype;

begin

getviewsettings(vp);

setviewport(0,0,639,479,clipon);

setfillstyle(1,lightblue);

bar(0,465,550,479);

Mensaje(5,465,s);

setviewport(vp.x1,vp.y1,vp.x2,vp.y2,clipon);

end;

Procedure Limita_raton;

var vp: viewporttype;

begin

getviewsettings(vp);

with vp do begin

Situarraton((x1+x2) div 2,(y1+y2) div 2);

zonaraton(x1,y1,x2,y2);

end;

end;

Procedure Libera_raton;

begin

zonaraton(0,0,639,479);

end;

Procedure Poner_menus;

var i: byte;

vp: viewporttype;

begin

getviewsettings(vp);

setviewport(0,0,639,479,clipon);

setcolor(yellow);

outtextxy(562,12,'Vista');

setcolor(white);

for i:= 1 to numitems do

outtextxy(565,10+menu[i].fila*15,menu[i].texto);

setcolor(yellow);

outtextxy(562,147,'Objeto');

outtextxy(562,312,'V rtice');

outtextxy(562,387,'Escena');

setviewport(vp.x1,vp.y1,vp.x2,vp.y2,clipon);

end;

Procedure Cambia_opcion(op,nop: byte);

var vp: viewporttype;

begin

punterooff;

getviewsettings(vp);

setviewport(0,0,639,479,clipon);

if op>0 then begin

setfillstyle(1,lightblue);

bar(563,9+menu[op].fila*15,639,20+menu[op].fila*15);

setcolor(white);

outtextxy(565,10+menu[op].fila*15,menu[op].texto);

end;

if nop>0 then begin

setfillstyle(1,white);

bar(563,10+menu[nop].fila*15,639,20+menu[nop].fila*15);

setcolor(blue);

outtextxy(565,12+menu[nop].fila*15,menu[nop].texto);

setviewport(vp.x1,vp.y1,vp.x2,vp.y2,clipon);

info(menu[nop].ayuda)

end

else

info('');

punteroon;

end;

Function Cad_Ent(s: string): integer;

var i: integer;

c: integer;

begin

val(s,i,c);

Cad_Ent:= i;

end;

Function Cad_Real(s: string): real;

var i: real;

c: integer;

begin

val(s,i,c);

Cad_Real:= i;

end;

Function Leer_cadena(men: string; conjcar: alfabeto): string;

var car: char;

x,y: integer;

vp: viewporttype;

s: string;

begin

getviewsettings(vp);

setviewport(0,0,getmaxx,getmaxy,clipon);

setcolor(yellow);

info(men);

x:= textwidth(men);

y:= 465;

s:= '';

car:= #0;

while car<>#13 do begin

car:= Leertecla;

if car in ['0'..'9','A'..'Z','a'..'z','.','\',':'] then begin

s:= s+car;

outtextxy(x,y,s);

end;

if (car=#8) and (s<>'') then begin

s[0]:= chr(length(s)-1);

info(men);

outtextxy(x,y,s);

end;

end;

Leer_cadena:= s;

setviewport(vp.x1,vp.y1,vp.x2,vp.y2,clipon);

end;

Procedure Inicia_graf;

var gd,gm: integer;

begin

gd:= detect;

initgraph(gd,gm,'');

end;

Procedure Escala_vector( v1: vector; esc: real; var v2: vector);

var i: 1..3;

begin

for i:= 1 to 3 do

v2[i]:= v1[i]*esc;

end;

Procedure Dif_vectores(A,B: vector; var C: vector);

var i: 1..3;

begin

for i:= 1 to 3 do

C[i]:= A[i]-B[i];

end;

Procedure Suma_vectores( A,B: vector; var C: vector);

var i: 1..3;

begin

for i:= 1 to 3 do

C[i]:= A[i]+B[i];

end;

Function Prod_escalar(a,b: vector): real;

begin

Prod_escalar:= a[1]*b[1]+a[2]*b[2]+a[3]*b[3];

end;

Procedure Prod_matrices(a,b: matriz; var c: matriz);

var i,j,k: 1..3;

begin

for i:= 1 to 3 do

for j:= 1 to 3 do begin

c[i,j]:= 0;

for k:= 1 to 3 do

c[i,j]:= c[i,j]+a[i,k]*b[k,j];

end;

end;

Procedure Trans_PP_2d(pp: vector; var pix: pixel; cuad: num_cuadrante);

var c,f: real;

begin

with lim_cuad[cuad] do begin

c:= (cmax-cmin)/2 + (pp[u] - cw[cuad][u])/escala[cuad];

f:= (fmax-fmin)/2 - (pp[v] - cw[cuad][v])/escala[cuad];

{ if c>cmax-cmin then c:= cmax-cmin+1;

if c<0 then c:= -1;

if f>fmax-fmin then f:= fmax-fmin+1;

if f<0 then f:= -1;}

pix.c:= round(c);

pix.f:= round(f);

end;

end;

Procedure Trans_2d_PP(pix: pixel; var pp: vector; cuad: num_cuadrante);

var cv: pixel;

begin

with lim_cuad[cuad] do begin

pp[u]:= cw[cuad][u]+(pix.c - (cmax-cmin)/2)*escala[cuad];

pp[v]:= cw[cuad][v]-(pix.f - (fmax-fmin)/2)*escala[cuad];

pp[n]:= 0;

end;

end;

Procedure MatrizXvector(M: matriz; vi: vector; var vf: vector);

begin

vf[1]:= Prod_escalar(M[1],vi);

vf[2]:= Prod_escalar(M[2],vi);

vf[3]:= Prod_escalar(M[3],vi);

end;

Procedure VectorXmatriz(vi: vector; M: matriz; var vf: vector);

begin

vf[1]:= vi[1]*M[1,1]+vi[2]*M[2,1]+vi[3]*M[3,1];

vf[2]:= vi[1]*M[1,2]+vi[2]*M[2,2]+vi[3]*M[3,2];

vf[3]:= vi[1]*M[1,3]+vi[2]*M[2,3]+vi[3]*M[3,3];

end;

Procedure Trans_PP_WC(pp: vector; var wc: vector; cuad: num_cuadrante);

begin

VectorXMatriz( pp, ejes[cuad], wc);

end;

Procedure Trans_WC_PP(wc: vector; var pp: vector; cuad: num_cuadrante);

var esc: real;

begin

MatrizXvector( ejes[cuad], wc, pp);

end;

Procedure Trans_WC_2d(wc: vector; var pix: pixel; cuad: num_cuadrante);

var pp: vector;

begin

Trans_WC_PP(wc,pp,cuad);

Trans_PP_2d(pp,pix,cuad);

end;

Procedure Intro_coord(var A: vector;c1,c2,c3: real);

begin

A[1]:= c1; A[2]:= c2; A[3]:= c3;

end;

Function Distancia_3d(A,B: vector): real;

var o,p,q,temp: real;

begin

o:= sqr(B[1]-A[1]);

p:= sqr(B[2]-A[2]);

q:= sqr(B[3]-A[3]);

temp:= o+p+q;

Distancia_3d:= sqrt(temp);

end;

Function Distancia_2d(A,B: pixel): real;

var o,p,temp: real;

dc,df: real;

begin

dc:= B.c-A.c; o:= sqr(dc);

df:= B.f-A.f; p:= sqr(df);

temp:= o+p;

Distancia_2d:= sqrt(temp);

end;

Function Minimo(a,b: real): real;

begin

if a<=b then Minimo:= a

else Minimo:= b;

end;

Procedure Activa_cuadrante(cuad: num_cuadrante);

begin

setviewport(lim_cuad[cuad].cmin,lim_cuad[cuad].fmin,

lim_cuad[cuad].cmax,lim_cuad[cuad].fmax,clipon);

end;

Function Esta_selec_vert(vert: p_vertice): boolean;

var p_vs: l_vert_sel;

begin

p_vs:= vert_sel;

while (p_vs<>nil) and (p_vs^.vert<>vert) do

p_vs:= p_vs^.sig;

Esta_selec_vert:= p_vs<>nil;

end;

Procedure Selec_vertice(vert: p_vertice; var vert_s: l_vert_sel);

var l_vs: l_vert_sel;

begin

new(l_vs);

l_vs^.vert:= vert;

l_vs^.sig:= vert_s;

vert_s:= l_vs;

end;

Procedure Desel_vertice(vert: p_vertice; var vert_s: l_vert_sel);

var l_vs: l_vert_sel;

cabeza: l_vert_sel;

temp: l_vert_sel;

begin

new(cabeza);

cabeza^.vert:= nil;

cabeza^.sig:= vert_s;

l_vs:= cabeza;

while (l_vs^.sig^.vert<>vert) do

l_vs:= l_vs^.sig;

temp:= l_vs^.sig;

l_vs^.sig:= l_vs^.sig^.sig;

dispose(temp);

vert_s:= cabeza^.sig;

dispose(cabeza);

end;

Function Esta_selec_obj(obj: objeto): boolean;

var p_os: l_obj_sel;

begin

p_os:= obj_sel;

while (p_os<>nil) and (p_os^.obj<>obj) do

p_os:= p_os^.sig;

Esta_selec_obj:= p_os<>nil;

end;

Procedure Selec_objeto(obj: objeto; var obj_s: l_obj_sel);

var l_os: l_obj_sel;

begin

new(l_os);

l_os^.obj:= obj;

l_os^.sig:= obj_s;

obj_s:= l_os;

end;

Procedure Desel_objeto(obj: objeto;var obj_s: l_obj_sel);

var l_os: l_obj_sel;

cabeza: l_obj_sel;

temp: l_obj_sel;

begin

new(cabeza);

cabeza^.obj:= nil;

cabeza^.sig:= obj_s;

l_os:= cabeza;

while (l_os^.sig^.obj<>obj) do

l_os:= l_os^.sig;

temp:= l_os^.sig;

l_os^.sig:= l_os^.sig^.sig;

dispose(temp);

obj_s:= cabeza^.sig;

dispose(cabeza);

end;

Procedure Eliminar_objeto(obj: objeto);

var pobj,pobj2: objeto;

pvert1,pvert2: p_vertice;

plvert1,plvert2: l_vertpoli;

pol1,pol2: lista_poligonos;

begin

pobj:= obj;

pvert1:= obj^.lvert;

while pvert1<>nil do begin

pvert2:= pvert1;

pvert1:= pvert1^.sig;

if esta_selec_vert(pvert2) then

desel_vertice(pvert2,vert_sel);

dispose(pvert2);

end;

pol1:= obj^.lpoli;

while pol1<>nil do begin

pol2:= pol1;

plvert1:= pol1^.lvert;

while plvert1<>nil do begin

plvert2:= plvert1;

plvert1:= plvert1^.sig;

dispose(plvert2);

end;

pol1:= pol1^.sig;

dispose(pol2);

end;

if obj=escena then begin

escena:= obj^.sig;

dispose(obj);

end

else begin

pobj2:= escena;

while pobj2^.sig<>pobj do

pobj2:= pobj2^.sig;

pobj2^.sig:= pobj2^.sig^.sig;

dispose(obj);

end;

end;

Procedure Eliminar_escena(var esc: lista_objetos);

var ob1,ob2: objeto;

begin

while obj_sel<>nil do

Desel_objeto(obj_sel^.obj, obj_sel);

while vert_sel<>nil do

Desel_vertice(vert_sel^.vert, vert_sel);

ob1:= escena;

while ob1<>nil do begin

ob2:= ob1^.sig;

Eliminar_objeto(ob1);

ob1:= ob2;

end;

end;

Procedure Ins_obj_esc(var o: objeto);

begin

new(o);

o^.lvert:= final_lista;

o^.lpoli:= final_lista;

o^.sig:= escena;

escena:= o;

end;

Procedure Ins_vert_obj(var pv: p_vertice;var obj: objeto; c: vector);

begin

new(pv);

pv^.sig:= obj^.lvert;

obj^.lvert:= pv;

pv^.pos:= c;

end;

Procedure Ins_vert_poli(var pv: p_vertice; var pol: l_vertpoli);

{A¤adimos el v rtice pv al principio de la lista de v rtices}

var lv: l_vertpoli;

begin

new(lv);

lv^.vert:= pv;

lv^.sig:= pol;

pol:= lv;

end;

Procedure Ins_poli_obj(var lvert: l_vertpoli; var obj: objeto);

var l_poli: lista_poligonos;

begin

new(l_poli);

l_poli^.lvert:= lvert;

l_poli^.sig:= obj^.lpoli;

obj^.lpoli:= l_poli;

end;

Procedure Centro_caja(l_os: l_obj_sel; var cc: vector);

var minx,miny,minz,maxx,maxy,maxz: real;

p_os: l_obj_sel;

pv: p_vertice;

begin

minx:= maxreal;miny:= maxreal;minz:= maxreal;

maxx:= -maxreal;maxy:= -maxreal;maxz:= -maxreal;

p_os:= l_os;

while p_os<>nil do begin

pv:= p_os^.obj^.lvert;

while pv<>nil do begin

minx:= Minimo(minx,pv^.pos[x]);maxx:= -Minimo(-maxx, -pv^.pos[x]);

miny:= Minimo(miny,pv^.pos[y]);maxy:= -Minimo(-maxy, -pv^.pos[y]);

minz:= Minimo(minz,pv^.pos[z]);maxz:= -Minimo(-maxz, -pv^.pos[z]);

pv:= pv^.sig;

end;

p_os:= p_os^.sig;

end;

cc[x]:= (minx+maxx)/2; cc[y]:= (miny+maxy)/2; cc[z]:= (minz+maxz)/2;

end;

Procedure Dibuja_escena( cuad: num_cuadrante);

var p1,p2: pixel;

obj: objeto;

pol: lista_poligonos;

vertpol: l_vertpoli;

i: integer;

colant: byte;

Norm: vector;

v1,v2,v3: vector;

ej: vector;

begin

punterooff;

setfillstyle(1,0);

setcolor(color_nosel);

Activa_cuadrante(cuad);

clearviewport;

obj:= escena;

while obj<>final_lista do begin

if Esta_selec_obj(obj) then

setcolor(color_oselec)

else

setcolor(color_nosel);

pol:= obj^.lpoli;

while pol<>final_lista do begin

vertpol:= pol^.lvert;

v1:= vertpol^.vert^.pos;

v2:= vertpol^.sig^.vert^.pos;

v3:= vertpol^.sig^.sig^.vert^.pos;

Dif_vectores( v2, v1, v1);

Dif_vectores( v3, v2, v2);

Norm[x]:= v1[y]*v2[z]-v1[z]*v2[y];

Norm[y]:= v1[z]*v2[x]-v1[x]*v2[z];

Norm[z]:= v1[x]*v2[y]-v1[y]*v2[x];

if Prod_escalar(ejes[cuad][n],Norm)>0 then setlinestyle(0,0,1)

else setlinestyle(3,0,1);

Trans_WC_2d(vertpol^.vert^.pos,p1,cuad);

moveto(p1.c,p1.f);

vertpol:= vertpol^.sig;

while vertpol<>nil do begin

trans_WC_2d(vertpol^.vert^.pos,p2,cuad);

Lineto(p2.c,p2.f);

if Esta_selec_vert(vertpol^.vert) then begin

colant:= getcolor;

setcolor(color_vselec);

pieslice(p2.c,p2.f,0,360,2);

setcolor(colant);

end;

vertpol:= vertpol^.sig;

end;

lineto(p1.c,p1.f);

pol:= pol^.sig;

end;

obj:= obj^.sig;

end;

if ej_vis[cuad] then begin

setlinestyle(1,0,0);

setcolor(color_ejes);

ej[x]:= 0;ej[y]:= 0;ej[z]:= 0;

Trans_wc_2d(ej,p1,cuad);

ej[x]:= 5000;ej[y]:= 0;ej[z]:= 0;

Trans_wc_2d(ej,p2,cuad);

Line(p1.c,p1.f,p2.c,p2.f);

ej[x]:= 0;ej[y]:= 5000;ej[z]:= 0;

Trans_wc_2d(ej,p2,cuad);

Line(p1.c,p1.f,p2.c,p2.f);

ej[x]:= 0;ej[y]:= 0;ej[z]:= 5000;

Trans_wc_2d(ej,p2,cuad);

Line(p1.c,p1.f,p2.c,p2.f);

end;

punteroon;

setlinestyle(0,0,1);

end;

Procedure Dibuja_todo( cuad: num_cuadrante);

var i: byte;

begin

if not maximizado then

for i:= 0 to 3 do

Dibuja_escena(i)

else

Dibuja_escena(cuad);

Activa_cuadrante(cuad);

end;

Procedure Sentido_raton;

begin

setcolor(red);

setlinestyle(0,0,3);

with lim_cuad[cuad_act] do

moveto((cmax-cmin) div 2,(fmax-fmin) div 2);

if mraton in [libre,horiz] then begin

linerel(-15,0); linerel(5,-3);linerel(-5,3);

linerel(5,3);linerel(-5,-3);

linerel(30,0); linerel(-5,-3);linerel(5,3);

linerel(-5,3);linerel(5,-3);

linerel(-15,0);

end;

if mraton in [libre,vertic] then begin

linerel(0,-15); linerel(-3,5);linerel(3,-5);

linerel(3,5);linerel(-3,-5);

linerel(0,30); linerel(-3,-5);linerel(3,5);

linerel(3,-5);linerel(-3,5);

linerel(0,-15);

end;

if mraton=isomet then begin

linerel(-10,-10); linerel(5,0);linerel(-5,0);

linerel(0,5);linerel(0,-5);

linerel(20,20); linerel(-5,0);linerel(5,0);

linerel(0,-5);linerel(0,5);

end;

setlinestyle(0,0,1);

end;

Procedure Zoom_vista(cuad: num_cuadrante; coef: real);

begin

escala[cuad]:= escala[cuad]/coef;

end;

Procedure Varraton(var dc,df: integer; var izq,der: boolean);

var nposrat: pixel;

begin

Libera_raton;

Situarraton(100,100);

repeat

leerraton(nposrat.c,nposrat.f,izq,der);

if mem[$40:23] and 04<>0 then begin {Cambiamos el modo del raton}

if mraton=isomet then mraton:= libre

else mraton:= succ(mraton);

sentido_raton;

while mem[$40:23] and 04<>0 do;

dibuja_escena(cuad_act);

end;

if keypressed then

case Leertecla of

'+': begin

Zoom_vista(cuad_act,1/coef_zoom);

dibuja_escena(cuad_act);

end;

'-': begin

Zoom_vista(cuad_act,coef_zoom);

dibuja_escena(cuad_act);

end;

end;

until (nposrat.c<>100) or (nposrat.f<>100) or izq or der;

dc:= nposrat.c-100;

df:= nposrat.f-100;

case mraton of

horiz : df:= 0;

vertic : dc:= 0;

isomet : begin

dc:= dc+df;

df:= dc;

end;

end;

Limita_raton;

end;

Function Pos_vert(pv: p_vertice; lvert: lista_vertices): word;

{Devuelve la posici¢n de un v rtice dentro de una lista}

var n: word;

punt: p_vertice;

begin

n:= 1;

punt:= lvert;

while punt<>pv do begin

n:= n+1;

punt:= punt^.sig;

end;

pos_vert:= n;

end;

Function Vert_pos(n: word; lvert: lista_vertices): p_vertice;

var punt: p_vertice;

i: word;

begin

punt:= lvert;

for i:= 1 to n-1 do

punt:= punt^.sig;

Vert_pos:= punt;

end;

Procedure Copiar(l_os: l_obj_sel);

var pv,npv,ultvert: p_vertice;

nlvert: lista_vertices;

ultpol,nlpol,npol,pol: lista_poligonos;

ultref,nlref,ref,nref: l_vertpoli;

ob: objeto;

begin

while l_os<>nil do begin

nlvert:= nil;

pv:= l_os^.obj^.lvert;

while pv<>nil do begin

new(npv);

npv^.pos:= pv^.pos;

if nlvert=nil then nlvert:= npv

else ultvert^.sig:= npv;

ultvert:= npv;

pv:= pv^.sig;

end;

ultvert^.sig:= nil;

nlpol:= nil;

pol:= l_os^.obj^.lpoli;

while pol<>nil do begin

new(npol);

nlref:= nil;

ref:= pol^.lvert;

while ref<>nil do begin

new(nref);

nref^.vert:= Vert_pos( Pos_vert(ref^.vert,l_os^.obj^.lvert), nlvert);

if nlref=nil then nlref:= nref

else ultref^.sig:= nref;

ultref:= nref;

ref:= ref^.sig;

end;

ultref^.sig:= nil;

npol^.lvert:= nlref;

if nlpol=nil then nlpol:= npol

else ultpol^.sig:= npol;

ultpol:= npol;

pol:= pol^.sig;

end;

ultpol^.sig:= nil;

new(ob);

ob^.lvert:= nlvert;

ob^.lpoli:= nlpol;

ob^.sig:= escena;

escena:= ob;

l_os:= l_os^.sig;

end;

end;

Procedure Escalar_selec(var l_os: l_obj_sel; ref: vector; cuad: num_cuadrante; modo: boolean);

var posrat: pixel;

dc,df,dp: integer;

p_os: l_obj_sel;

pvert: p_vertice;

posrel: vector;

pp: vector;

begin

punterooff;

Posraton(posrat.c,posrat.f);

Trans_WC_PP( ref, ref, cuad);

repeat

dibuja_escena(cuad);

varraton(dc,df,izq,der);

if izq or der then begin

situarraton(posrat.c,posrat.f);

punteroon;

exit;

end;

p_os:= l_os;

while p_os<>nil do begin

pvert:= p_os^.obj^.lvert;

while pvert<>final_lista do begin

Trans_WC_PP( pvert^.pos, pp, cuad);

Dif_vectores( pp, ref, posrel);

if modo=modo2D then

dp:= 0

else begin

df:= dc;

dp:= dc;

end;

posrel[u]:= posrel[u]*(1+dc/100);

posrel[v]:= posrel[v]*(1+df/100);

posrel[n]:= posrel[n]*(1+dp/100);

Suma_vectores( ref, posrel, pp);

Trans_PP_WC( pp, pvert^.pos, cuad);

pvert:= pvert^.sig;

end;

p_os:= p_os^.sig;

end;

until false;

end;

Procedure Escalar_vertices(l_vs: l_vert_sel; cuad: num_cuadrante);

var dc,df: integer;

vrel: vector;

minx,maxx,miny,maxy,minz,maxz: real;

ref: vector;

p_vs: l_vert_sel;

begin

minx:= maxreal; miny:= maxreal; minz:= maxreal;

maxx:= -maxreal; maxy:= -maxreal; maxz:= -maxreal;

p_vs:= l_vs;

while p_vs<>nil do begin

with p_vs^.vert^ do begin

minx:= Minimo(minx,pos[x]);maxx:= -Minimo(-maxx, -pos[x]);

miny:= Minimo(miny,pos[y]);maxy:= -Minimo(-maxy, -pos[y]);

minz:= Minimo(minz,pos[z]);maxz:= -Minimo(-maxz, -pos[z]);

end;

p_vs:= p_vs^.sig;

end;

ref[x]:= (minx+maxx)/2;

ref[y]:= (miny+maxy)/2;

ref[z]:= (minz+maxz)/2;

Trans_wc_pp(ref,ref,cuad);

repeat

Dibuja_escena(cuad);

varraton(dc,df,izq,der);

p_vs:= l_vs;

while p_vs<>nil do begin

Trans_wc_pp(p_vs^.vert^.pos, vrel, cuad);

Dif_vectores(vrel, ref, vrel);

vrel[u]:= vrel[u]*(1+dc/100);

vrel[v]:= vrel[v]*(1+df/100);

Suma_vectores(vrel, ref, vrel);

Trans_pp_wc(vrel, p_vs^.vert^.pos,cuad);

p_vs:= p_vs^.sig;

end;

until izq;

end;

Procedure Rotar_objeto(var o: objeto; ref: vector;alfa: real; cuad: num_cuadrante);

var pvert: p_vertice;

coseno,seno: real;

npp,pp: vector;

begin

coseno:= cos(alfa*Pi/180);

seno:= sin(alfa*Pi/180);

Trans_WC_PP( ref, ref, cuad);

pvert:= o^.lvert;

while pvert<>final_lista do begin

Trans_WC_PP( pvert^.pos, pp,cuad);

Dif_vectores( pp, ref, pp);

Intro_coord( npp, pp[u]*coseno-pp[v]*seno, pp[u]*seno+pp[v]*coseno, pp[n]);

Suma_vectores( npp, ref, npp);

Trans_PP_WC( npp, pvert^.pos,cuad);

pvert:= pvert^.sig;

end;

end;

Procedure Rotar_selec(var l_os: l_obj_sel; ref: vector; cuad: num_cuadrante);

var posrat: pixel;

alfa: real;

pvert: p_vertice;

dc,df: integer;

p_os: l_obj_sel;

begin

alfa:= 0;

punterooff;

Posraton(posrat.c,posrat.f);

repeat

dibuja_escena(cuad);

Varraton(dc,df,izq,der);

alfa:= dc/5;

p_os:= l_os;

while p_os<>nil do begin

Rotar_objeto(p_os^.obj,ref,alfa,cuad);

p_os:= p_os^.sig;

end;

until izq or der;

situarraton(posrat.c,posrat.f);

punteroon;

end;

Procedure Unir_objetos(var ob1,ob2: objeto);

var pv: p_vertice;

pol: lista_poligonos;

pob: objeto;

begin

pv:= ob1^.lvert;

while pv^.sig<>nil do

pv:= pv^.sig;

pv^.sig:= ob2^.lvert;

pol:= ob1^.lpoli;

while pol^.sig<>nil do

pol:= pol^.sig;

pol^.sig:= ob2^.lpoli;

if escena=ob2 then begin

escena:= ob2^.sig;

dispose(ob2);

end

else begin

pob:= escena;

while pob^.sig<>ob2 do

pob:= pob^.sig;

pob^.sig:= ob2^.sig;

dispose(ob2);

end;

end;

Procedure Unir_selec(l_os: l_obj_sel);

var p_os,temp: l_obj_sel;

begin

p_os:= l_os^.sig; {Unimos todos los objetos con el primero}

while p_os<>nil do begin

unir_objetos(l_os^.obj,p_os^.obj);

p_os:= p_os^.sig;

end;

p_os:= l_os^.sig; {Deseleccionamos todos los objetos excepto}

while p_os<>nil do begin {el primero }

temp:= p_os^.sig;

desel_objeto(p_os^.obj,obj_sel);

p_os:= temp;

end;

end;

Procedure Rotar_camara(cuad: num_cuadrante);

var nposrat,posrat: pixel;

dc,df: integer;

alfa,beta: real;

coseno,seno: real;

rot: matriz;

orwc: vector;

begin

punterooff;

repeat

dibuja_escena(cuad);

Trans_pp_wc(cw[cuad],orwc,cuad);

varraton(dc,df,izq,der);

alfa:= -dc*pi/180;

coseno:= cos(alfa);

seno:= sin(alfa);

Intro_coord(rot[x], coseno, -seno, 0);

Intro_coord(rot[y], seno, coseno, 0);

Intro_coord(rot[z], 0, 0, 1);

Prod_matrices(ejes[cuad], rot, ejes[cuad]);

beta:= -df*pi/180;

coseno:= cos(beta);

seno:= sin(beta);

Intro_coord(rot[u], 1, 0, 0);

Intro_coord(rot[v], 0, coseno, -seno);

Intro_coord(rot[n], 0, seno, coseno);

Prod_matrices(rot, ejes[cuad], ejes[cuad]);

Trans_wc_pp(orwc,cw[cuad],cuad);

until izq or der;

punteroon;

end;

Procedure Desplaza_camara(cuad: num_cuadrante);

var dc,df: integer;

begin

punterooff;

repeat

dibuja_escena(cuad);

varraton(dc,df,izq,der);

cw[cuad][u]:= cw[cuad][u]+dc*escala[cuad];

cw[cuad][v]:= cw[cuad][v]-df*escala[cuad];

until izq or der;

punteroon;

end;

Procedure Centrar_vista(cuad: num_cuadrante; l_obj: lista_objetos);

var pvert: p_vertice;

pp: vector;

pobj: objeto;

minu,minv,maxu,maxv: real;

eschor,escver: real;

begin

minu:= maxreal;maxu:= -maxreal;minv:= maxreal;maxv:= -maxreal;

pobj:= l_obj;

while pobj<>nil do begin

pvert:= pobj^.lvert;

while pvert<>final_lista do begin

Trans_WC_PP(pvert^.pos,pp,cuad);

minu:= Minimo( minu, pp[u]); maxu:= -Minimo( -maxu, -pp[u]);

minv:= Minimo( minv, pp[v]); maxv:= -Minimo( -maxv, -pp[v]);

pvert:= pvert^.sig;

end;

pobj:= pobj^.sig;

end;

with lim_cuad[cuad] do begin

eschor:= (maxu-minu)/(cmax-cmin-10);

escver:= (maxv-minv)/(fmax-fmin-10);

end;

escala[cuad]:= -Minimo( -eschor, -escver); {escala = maximo (hor,ver) }

cw[cuad][u]:= (maxu + minu)/2;

cw[cuad][v]:= (maxv + minv)/2;

end;

Procedure Restaurar_vista(cuad: num_cuadrante);

begin

escala[cuad]:= escala_ini[cuad];

ejes[cuad]:= ejes_ini[cuad];

cw[cuad]:= orig_ini[cuad];

end;

Procedure Desplaza_vertice(var pvert: p_vertice; desp: vector);

begin

Suma_vectores(pvert^.pos, desp, pvert^.pos);

end;

Procedure Mover_vertices(var l_vs: l_vert_sel; cuad: num_cuadrante);

var posrat: pixel;

dc,df: integer;

desp: vector;

pvs: l_vert_sel;

begin

punterooff;

Posraton(posrat.c,posrat.f);

repeat

dibuja_escena(cuad);

varraton(dc,df,izq,der);

Intro_coord( desp, dc*escala[cuad], -df*escala[cuad], 0);

Trans_pp_wc( desp, desp, cuad);

pvs:= l_vs;

while pvs<>nil do begin

Desplaza_vertice(pvs^.vert,desp);

pvs:= pvs^.sig;

end;

until izq or der;

situarraton(posrat.c,posrat.f);

punteroon;

end;

Procedure Mover_selec(var l_os: l_obj_sel; cuad: num_cuadrante);

var posrat: pixel;

pvert: p_vertice;

dc,df: integer;

desp: vector;

pos: l_obj_sel;

begin

punterooff;

Posraton(posrat.c,posrat.f);

repeat

dibuja_escena(cuad);

varraton(dc,df,izq,der);

pos:= l_os;

while pos<>nil do begin

pvert:= pos^.obj^.lvert;

while pvert<>nil do begin

Intro_coord( desp, dc*escala[cuad], -df*escala[cuad], 0);

Trans_pp_wc( desp, desp, cuad);

Desplaza_vertice(pvert,desp);

pvert:= pvert^.sig;

end;

pos:= pos^.sig;

end;

until izq or der;

punteroon;

end;

Procedure Cambia_cuadrante(var cuad: num_cuadrante);

var xr,yr: integer;

i: num_cuadrante;

begin

posraton(xr,yr);

for i:= 0 to 3 do begin

if (xr>lim_cuad[i].cmin) and (xr<lim_cuad[i].cmax) and

(yr>lim_cuad[i].fmin) and (yr<lim_cuad[i].fmax) then begin

cuad:= i;

exit;

end;

end;

end;

Procedure Selec_cuadro(cuad: num_cuadrante; op: byte);

var p1,p2: pixel;

ct,ft: integer;

obj: objeto;

pv: p_vertice;

pix: pixel;

begin

punteroon;

setcolor(white);

info('Sit£e esquina superior izquierda');

repeat

leerraton(p1.c,p1.f,izq,der);

until izq or der;

p1.c:= p1.c-lim_cuad[cuad].cmin;

p1.f:= p1.f-lim_cuad[cuad].fmin;

punterooff;

Evita_repet;

info('Sit£e esquina inferior derecha');

repeat

repeat

ct:= p2.c;

ft:= p2.f;

leerraton(p2.c,p2.f,izq,der);

p2.c:= p2.c-lim_cuad[cuad].cmin;

p2.f:= p2.f-lim_cuad[cuad].fmin;

until (ct<>p2.c) or (ft<>p2.f) or izq or der;

dibuja_escena(cuad);

setcolor(white);

rectangle(p1.c,p1.f,p2.c,p2.f);

until izq or der;

if p2.c<p1.c then begin

ct:= p1.c;

p1.c:= p2.c;

p2.c:= ct;

end;

if p2.f<p1.f then begin

ft:= p1.f;

p1.f:= p2.f;

p2.f:= ft;

end;

obj:= escena;

while obj<>nil do begin

pv:= obj^.lvert;

while pv<>nil do begin

Trans_WC_2d(pv^.pos,pix,cuad);

if (pix.c>p1.c) and (pix.c<p2.c) and (pix.f>p1.f) and (pix.f<p2.f) then begin

if (op=slob) and (not esta_selec_obj(obj)) then begin

selec_objeto(obj,obj_sel);

break;

end;

if (op=dsob) and (esta_selec_obj(obj)) then begin

desel_objeto(obj,obj_sel);

break;

end;

if (op=slvt) and (not esta_selec_vert(pv)) then

selec_vertice(pv,vert_sel);

if (op=dsvt) and (esta_selec_vert(pv)) then

desel_vertice(pv,vert_sel);

end;

pv:= pv^.sig;

end;

obj:= obj^.sig;

end;

end;

Procedure Selecciona(var pv: p_vertice; var obj: objeto;

cuad: num_cuadrante; var ok: boolean);

var dvert: real;

p: pixel;

pobj: objeto;

ppol: lista_poligonos;

pvert: l_vertpoli;

dminvert: real;

pmin: pixel;

xr,yr: integer;

p0: pixel;

nvert: byte;

pp: vector;

wc: vector;

begin

punteroon;

repeat

leerraton(xr,yr,izq,der);

until izq or der;

Evita_repet;

if der then begin

ok:= false;

exit;

end;

punterooff;

p0.c:= xr-lim_cuad[cuad].cmin; {Hacemos el rat¢n relativo a la vista}

p0.f:= yr-lim_cuad[cuad].fmin;

dminvert:= maxreal;

pobj:= escena;

while pobj<>nil do begin

ppol:= pobj^.lpoli;

while ppol<>nil do begin

pvert:= ppol^.lvert;

while pvert<>final_lista do begin

Trans_WC_2d(pvert^.vert^.pos,p,cuad);

dvert:= Distancia_2d(p,p0);

if dvert<dminvert then begin

pv:= pvert^.vert;

pmin:= p;

dminvert:= dvert;

obj:= pobj;

end;

pvert:= pvert^.sig;

end;

ppol:= ppol^.sig;

end;

pobj:= pobj^.sig;

end;

punteroon;

if escena=nil then ok:= false

else ok:= true;

end;

Procedure Presenta_cuadrantes(sel: num_cuadrante);

var i: num_cuadrante;

lim: t_lim_cuad;

begin

setviewport(0,0,639,479,clipon);

punterooff;

setcolor(red);

for i:= 0 to 3 do begin

lim:= lim_cuad[i];

setlinestyle(0,0,1);

setcolor(lightgray);

rectangle(lim.cmin-1,lim.fmin-1,lim.cmax+1,lim.fmax+1);

setcolor(0);

rectangle(lim.cmin-2,lim.fmin-2,lim.cmax+2,lim.fmax+2);

end;

setcolor(red);

rectangle(lim_cuad[sel].cmin-2,lim_cuad[sel].fmin-2,

lim_cuad[sel].cmax+2,lim_cuad[sel].fmax+2);

rectangle(lim_cuad[sel].cmin-1,lim_cuad[sel].fmin-1,

lim_cuad[sel].cmax+1,lim_cuad[sel].fmax+1);

Activa_cuadrante(sel);

punteroon;

end;

Procedure Max_Min(cuad: num_cuadrante);

var df,dc: integer;

e1,e2: real;

esc: real;

begin

e1:= (maxima.cmax-maxima.cmin)/(lim_cuad_ini[cuad].cmax-lim_cuad_ini[cuad].cmin);

e2:= (maxima.fmax-maxima.fmin)/(lim_cuad_ini[cuad].fmax-lim_cuad_ini[cuad].fmin);

esc:= Minimo( e1, e2);

if maximizado then esc:= 1/esc;

escala[cuad]:= escala[cuad]/esc;

if not maximizado then begin

lim_cuad[cuad]:= maxima;

Presenta_cuadrantes(cuad);

maximizado:= true;

Dibuja_escena(cuad);

end

else begin

lim_cuad[cuad]:= lim_cuad_ini[cuad];

clearviewport;

Presenta_cuadrantes(cuad);

maximizado:= false;

Dibuja_todo(cuad);

end;

end;

Procedure Toroide(cent: vector; R1,R2: integer; n1,n2: byte; cuad: num_cuadrante);

var alfa,beta: real;

i,j: byte;

pp: vector;

wc: vector;

ult,pv1,pv2: p_vertice;

cara: l_vertpoli;

cont: word;

pva1,pva2: p_vertice;

tor: objeto;

centpp: vector;

begin

Ins_obj_esc(tor);

Trans_WC_PP(cent,centpp,cuad);

for i:= 0 to n1-1 do begin

alfa:= 2*Pi*i/n1+Pi/n1;

for j:= 0 to n2-1 do begin

beta:= 2*Pi*j/n2+Pi/n2;

pp[u]:= centpp[u]+(r1+r2*cos(beta))*cos(alfa);

pp[v]:= centpp[v]+(r1+r2*cos(beta))*sin(alfa);

pp[n]:= centpp[n]+r2*sin(beta);

Trans_PP_WC(pp,wc,cuad);

Ins_vert_obj(pv1,tor,wc);

end;

end;

ult:= tor^.lvert;

while ult^.sig<>nil do

ult:= ult^.sig;

ult^.sig:= tor^.lvert;

pv1:= tor^.lvert;

pv2:= pv1;

for i:= 1 to n2 do

pv2:= pv2^.sig;

for i:= 1 to n1 do begin

pva1:= pv1;

pva2:= pv2;

for j:= 1 to n2 do begin

cara:= nil;

Ins_vert_poli(pv1,cara);

if j<n2 then begin

Ins_vert_poli(pv1^.sig,cara);

Ins_vert_poli(pv2^.sig,cara);

end

else begin

Ins_vert_poli(pva1,cara);

Ins_vert_poli(pva2,cara);

end;

Ins_vert_poli(pv2,cara);

Ins_poli_obj(cara,tor);

pv1:= pv1^.sig;

pv2:= pv2^.sig;

end;

end;

ult^.sig:= nil;

end;

Procedure Tronco(cent: vector; R1,R2: integer; h: integer; nv: byte; cuad: num_cuadrante);

var cara,base,tapa: l_vertpoli;

i: byte;

alfa: real;

npv,pv: p_vertice;

tro: objeto;

ptapa: l_vertpoli;

pp: vector;

wc: vector;

centpp: vector;

begin

Ins_obj_esc(tro);

Trans_WC_PP(cent,centpp,cuad);

tapa:= nil;

base:= nil;

for i:= 0 to nv-1 do begin

alfa:= 2*Pi*i/nv+Pi/nv;

pp[u]:= centpp[u]+r1*cos(alfa);

pp[v]:= centpp[v]+r1*sin(alfa);

pp[n]:= centpp[n];

Trans_PP_WC(pp,wc,cuad);

Ins_vert_obj(pv,tro,wc);

Ins_vert_poli(pv,base);

pp[u]:= centpp[u]+r2*cos(alfa);

pp[v]:= centpp[v]+r2*sin(alfa);

pp[n]:= centpp[n]+h;

Trans_PP_WC(pp,wc,cuad);

Ins_vert_obj(pv,tro,wc);

Ins_vert_poli(pv,tapa);

end;

cara:= nil;

while tapa<>nil do begin

ins_vert_poli(tapa^.vert,cara);

tapa:= tapa^.sig;

end;

ptapa:= tapa;

while ptapa<>nil do begin

tapa:= tapa^.sig;

dispose(ptapa);

ptapa:= tapa;

end;

tapa:= cara;

Ins_poli_obj(tapa,tro);

Ins_poli_obj(base,tro);

pv:= tro^.lvert;

while pv<>nil do begin

cara:= nil;

Ins_vert_poli(pv,cara);

Ins_vert_poli(pv^.sig,cara);

npv:= pv^.sig^.sig;

if npv<>nil then begin

Ins_vert_poli(npv^.sig,cara);

Ins_vert_poli(npv,cara);

end

else begin

Ins_vert_poli(tro^.lvert^.sig,cara);

Ins_vert_poli(tro^.lvert,cara);

end;

Ins_poli_obj(cara,tro);

pv:= npv;

end;

end;

Procedure Piramide(cent: vector; r: integer; h: integer; nv: byte; cuad: num_cuadrante);

var i: integer;

cara: l_vertpoli;

pp: vector;

wc: vector;

alfa: real;

pv: p_vertice;

base: l_vertpoli;

cima: p_vertice;

pol: lista_poligonos;

j: integer;

pir: objeto;

centpp: vector;

begin

Trans_WC_PP(cent,centpp,cuad);

Ins_obj_esc(pir);

for i:= nv-1 downto 0 do begin

alfa:= 2*Pi*i/nv+Pi/nv;

pp[u]:= centpp[u]+r*cos(alfa);

pp[v]:= centpp[v]+r*sin(alfa);

pp[n]:= centpp[n];

Trans_PP_WC(pp,wc,cuad);

Ins_vert_obj(pv,pir,wc);

end;

centpp[n]:= centpp[n]+h;

Trans_PP_WC(centpp,wc,cuad);

Ins_vert_obj(cima,pir,wc);

pv:= cima^.sig;

base:= nil;

while pv<>nil do begin

Ins_vert_poli(pv,base);

cara:= nil;

if pv^.sig<>nil then

Ins_vert_poli(pv^.sig,cara)

else

Ins_vert_poli(cima^.sig,cara);

Ins_vert_poli(pv,cara);

Ins_vert_poli(cima,cara);

Ins_poli_obj(cara,pir);

pv:= pv^.sig;

end;

Ins_poli_obj(base,pir);

end;

Procedure Esfera(cent: vector; r: integer; nv: byte; cuad: num_cuadrante);

var i,j,k: integer;

cosa,sena,cosb,senb: real;

alfa,beta: real;

pp: vector;

wc: vector;

pv,pv2: p_vertice;

lvert: l_vertpoli;

m: byte;

ult: p_vertice;

pol: lista_poligonos;

lv: l_vertpoli;

lvert1,lvert2: l_vertpoli;

pvi,pvs: p_vertice;

pvt1,pvt2: p_vertice;

esf: objeto;

centpp: vector;

begin

Trans_WC_PP(cent,centpp,cuad);

Ins_obj_esc(esf);

m:= (nv div 2) - 1;

for i:= 0 to nv-1 do begin

alfa:= 2*Pi*i/nv;

cosa:= cos(alfa);

sena:= sin(alfa);

for j:= 1 to m do begin

beta:= 2*Pi*j/nv-Pi/2;

cosb:= cos(beta);

senb:= sin(beta);

pp[u]:= r*cosb*cosa+centpp[u];

pp[v]:= r*cosb*sena+centpp[v];

pp[n]:= r*senb+centpp[n];

Trans_PP_WC(pp,wc,cuad);

Ins_vert_obj(pv,esf,wc);

end;

end;

pvt2:= esf^.lvert;

while pvt2^.sig<>nil do

pvt2:= pvt2^.sig;

ult:= esf^.lvert;

while ult^.sig<>nil do

ult:= ult^.sig;

ult^.sig:= esf^.lvert;

new(pvi);

pp[u]:= centpp[u];

pp[v]:= centpp[v];

pp[n]:= centpp[n]-r;

Trans_PP_WC(pp,wc,cuad);

pvi^.pos:= wc;

new(pvs);

pp[u]:= centpp[u];

pp[v]:= centpp[v];

pp[n]:= centpp[n]+r;

Trans_PP_WC(pp,wc,cuad);

pvs^.pos:= wc;

lvert:= nil;

pv:= esf^.lvert;

for i:= 0 to nv-1 do begin

pvt1:= pv;

for j:= 1 to m-1 do begin

lvert:= nil;

Ins_vert_poli(pv,lvert);

Ins_vert_poli(pv^.sig,lvert);

pv2:= pv;

for k:= 1 to m do

pv2:= pv2^.sig;

Ins_vert_poli(pv2^.sig,lvert);

Ins_vert_poli(pv2,lvert);

Ins_poli_obj(lvert,esf);

pv:= pv^.sig;

end;

lvert2:= nil;

ins_vert_poli(pvi,lvert2);

ins_vert_poli(pv,lvert2);

ins_vert_poli(pvt2,lvert2);

ins_poli_obj(lvert2,esf);

pvt2:= pv;

pv:= pv^.sig;

lvert1:= nil;

ins_vert_poli(pvt1,lvert1);

ins_vert_poli(pv,lvert1);

ins_vert_poli(pvs,lvert1);

ins_poli_obj(lvert1,esf);

end;

pvs^.sig:= esf^.lvert;

esf^.lvert:= pvs;

pvi^.sig:= esf^.lvert;

esf^.lvert:= pvi;

ult^.sig:= nil;

end;

Procedure Nuevo_objeto(cuad: num_cuadrante);

var centpp: vector;

centwc: vector;

op: char;

r: integer;

rm: integer;

df,dc: integer;

posrat,nposrat: pixel;

cent2d: pixel;

N,N2: longint;

pvert: p_vertice;

ratio: real;

membaja: boolean;

mem_nec: longint;

Procedure Poligono(cent: pixel; rad: integer; n: integer);

var alfa: real;

coseno,seno: real;

i: integer;

c,f,nc,nf: real;

begin

setcolor(white);

rad:= abs(rad);

alfa:= Pi*2/n;

coseno:= cos(alfa);

seno:= sin(alfa);

c:= rad*cos(alfa/2);

f:= rad*sin(alfa/2);

for i:= 1 to n do begin

nc:= c*coseno-f*seno;

nf:= c*seno+f*coseno;

line(cent.c+round(c),cent.f+round(f),cent.c+round(nc),cent.f+round(nf));

c:= nc;f:= nf;

end;

{ line(c.c+p.c,c.f+p.f,c.c+pini.c,c.f+pini.f);}

end;

begin

info('1. Esfera 2. Pir mide 3. Tronco 4. Toroide 5. Cubo 6. Cancelar');

repeat

op:= Leertecla;

until op in ['1'..'6'];

if op='6' then exit;

case op of

'1' : begin

repeat

N:= Cad_Ent(Leer_cadena('N£mero de v rtices por circunferencia (ò4) ',digitos));

mem_nec:= 31*N*N+2*N+56+10240;

membaja:= memavail<mem_nec;

if (N<4) or membaja then write(#7);

if membaja then begin

Info('No hay suficiente memoria: pruebe con menos v rtices. Pulse una tecla');

Leertecla;

end;

until (N>=4) and (not membaja);

end;

'2' : begin

repeat

N:= Cad_Ent(Leer_cadena('N£mero de v rtices de la base: ',digitos));

mem_nec:= 62*N+42+10240;

membaja:= memavail<mem_nec;

if (N<3) or membaja then write(#7);

if membaja then begin

Info('No hay suficiente memoria: pruebe con menos v rtices. Pulse una tecla');

Leertecla;

end;

until (N>=3) and (not membaja);

end;

'3' : begin

repeat

N:= Cad_Ent(Leer_cadena('N£mero de v rtices de las caras superior e inferior: ',digitos));

mem_nec:= 100*N+28+10240;

membaja:= memavail<mem_nec;

if (N<3) or membaja then write(#7);

if membaja then begin

Info('No hay suficiente memoria: pruebe con menos v rtices. Pulse una tecla');

Leertecla;

end;

until (N>=3) and (not membaja);

end;

'4' : begin

repeat

repeat

N:= Cad_Ent(Leer_cadena('N£mero de v rtices del per¡metro: ',digitos));

if N<3 then write(#7);

until N>=3;

repeat

N2:= Cad_Ent(Leer_cadena('N£mero de v rtices de la secci¢n: ',digitos));

if N2<3 then write(#7);

until N2>=3;

mem_nec:= 182*N*N2+12+10240;

membaja:= memavail<mem_nec;

if membaja then begin

write(#7);

Info('No hay suficiente memoria: pruebe con menos v rtices. Pulse una tecla');

Leertecla;

end;

until not membaja;

end;

'5' : begin

N:= 4;

mem_nec:= 100*N+28+10240;

membaja:= memavail<mem_nec;

if membaja then begin

Info('No hay suficiente memoria');

exit;

end;

end;

end;

info('Sit£e el centro de la figura');

Punteroon;

repeat

leerraton(posrat.c,posrat.f,izq,der);

until izq or der;

Evita_repet;

cent2d.c:= posrat.c-lim_cuad[cuad].cmin;

cent2d.f:= posrat.f-lim_cuad[cuad].fmin;

Trans_2d_PP(cent2d,centpp,cuad);

punterooff;

info('Determine el tama¤o base');

r:= 0;

repeat

Dibuja_escena(cuad);

Poligono(cent2d,r,N);

Varraton(dc,df,izq,der);

r:= r+dc;

until izq or der;

Evita_repet;

r:= abs(round(r*escala[cuad])); {Radio en el coordenadas reales}

Trans_pp_wc(centpp,centwc,cuad);

case op of

'1': Esfera(centwc,r,N,cuad);

'2': Piramide(centwc,r,round(2*r),N,cuad);

'3','4': begin

rm:= 0;

repeat

Dibuja_escena(cuad);

Poligono(cent2d,round(r/escala[cuad]),N);

Poligono(cent2d,rm,N);

Varraton(dc,df,izq,der);

rm:= rm+dc;

until izq or der;

Evita_repet;

rm:= abs(round(rm*escala[cuad]));

if op='3' then Tronco(centwc,r,rm,2*r,N,cuad)

else Toroide(centwc,round((r+rm)/2),round((r-rm)/2),N,N2,cuad);

end;

'5': Tronco(centwc,r,r,trunc(r*sqrt(2)),4,cuad);

end;

Dibuja_todo(cuad);

Libera_raton;

punteroon;

end;

Procedure Cargar_escena(var esc: lista_objetos);

var obj: objeto;

ultvert,vert: p_vertice;

nobj: word;

npol: word;

nvert: word;

f: file;

pos_nvert,pos_npol: longint;

i,j: word;

com_vert: longint;

punt: longint;

fic: string;

lvert: lista_vertices;

pol,ultpol,lpol: lista_poligonos;

vertpol,ultvertpol,lvertpol: l_vertpoli;

n: word;

error: byte;

begin

repeat

fic:= Leer_cadena('Nombre del fichero a cargar (Enter=cancelar): ',caracteres);

if fic='' then exit;

assign(f,fic);

{$I-}

reset(f,1);

{$I+}

error:= ioresult;

if error<>0 then begin

write(#7);

info('El fichero no existe. Pulse una tecla');

Leertecla;

end;

until error=0;

reset(f,1);

while not eof(f) do begin

Ins_obj_esc(obj);

lvert:= nil;

blockread(f,nvert,sizeof(nvert));

com_vert:= filepos(f);

for i:= 1 to nvert do begin

new(vert);

if lvert=nil then lvert:= vert

else ultvert^.sig:= vert;

ultvert:= vert;

blockread(f,vert^.pos,sizeof(vert^.pos));

end;

ultvert^.sig:= nil;

obj^.lvert:= lvert;

lpol:= nil;

blockread(f,npol,sizeof(npol));

for i:= 1 to npol do begin

new(pol);

if lpol=nil then lpol:= pol

else ultpol^.sig:= pol;

ultpol:= pol;

lvertpol:= nil;

blockread(f,nvert,sizeof(nvert));

for j:= 1 to nvert do begin

new(vertpol);

if lvertpol=nil then lvertpol:= vertpol

else ultvertpol^.sig:= vertpol;

ultvertpol:= vertpol;

blockread(f,n,sizeof(n));

vertpol^.vert:= vert_pos(n,lvert);

end;

ultvertpol^.sig:= nil;

pol^.lvert:= lvertpol;

end;

ultpol^.sig:= nil;

obj^.lpoli:= lpol;

end;

close(f);

for i:= 0 to 3 do

centrar_vista(i,esc);

end;

Procedure Grabar_escena(esc: lista_objetos);

var obj: objeto;

vertpol: l_vertpoli;

vert: p_vertice;

pol: lista_poligonos;

nobj: word;

npol: word;

nvert: word;

f: file;

pos_nvert,pos_npol: longint;

fic: string;

error: byte;

n: word;

begin

repeat

fic:= Leer_cadena('Nombre del fichero a grabar: ',caracteres);

assign(f,fic);

{$I-}

reset(f,1);

{$I+}

error:= ioresult;

if error=0 then begin

write(#7);

info('El fichero ya existe. ¨Desea reemplazarlo?');

if upcase(Leertecla)='S' then error:= 1;

end;

until error<>0;

rewrite(f,1);

obj:= esc;

while obj<>nil do begin

nvert:= 0;

pos_nvert:= filepos(f);

blockwrite(f,nvert,sizeof(nvert));

vert:= obj^.lvert;

while vert<>nil do begin

blockwrite(f,vert^.pos,sizeof(vert^.pos));

nvert:= nvert+1;

vert:= vert^.sig;

end;

seek(f,pos_nvert);

blockwrite(f,nvert,sizeof(nvert));

seek(f,filesize(f));

pos_npol:= filepos(f);

npol:= 0;

blockwrite(f,npol,sizeof(npol));

pol:= obj^.lpoli;

while pol<>nil do begin

pos_nvert:= filepos(f);

nvert:= 0;

blockwrite(f,nvert,sizeof(nvert));

vertpol:= pol^.lvert;

while vertpol<>nil do begin

nvert:= nvert+1;

n:= Pos_vert(vertpol^.vert,obj^.lvert);

blockwrite(f,n,sizeof(n));

vertpol:= vertpol^.sig;

end;

seek(f,pos_nvert);

blockwrite(f,nvert,sizeof(nvert));

seek(f,filesize(f));

npol:= npol+1;

pol:= pol^.sig;

end;

seek(f,pos_npol);

blockwrite(f,npol,sizeof(npol));

seek(f,filesize(f));

obj:= obj^.sig;

end;

end;

var i: byte;

obj,obj2: objeto;

opcion,nop: byte;

posrat,nposrat,postemp: pixel;

pvert: p_vertice;

ok: boolean;

cm: vector;

selo_temp: l_obj_sel;

selv_temp: l_vert_sel;

conj_vistas: set of num_cuadrante;

begin

writeln('3DEDIT. Realizado por Agust¡n Caballero Belda');

writeln;

Initevents;

if not mouseevents then begin

write(#7);

writeln;

writeln('Rat¢n no detectado');

writeln('Por favor, corrija el problema e intente de nuevo');

doneevents;

halt;

end;

inicia_graf;

setbkcolor(blue);

setfillstyle(1,lightblue);

bar(maxima.cmax,maxima.fmin,640,480);

bar(0,maxima.fmax,maxima.cmax,480);

punteroon;

maximizado:= false;

lim_cuad:= lim_cuad_ini;

ejes:= ejes_ini;

escala:= escala_ini;

cw:= orig_ini;

escena:= nil;

Poner_menus;

obj_sel:= nil;

vert_sel:= nil;

cuad_act:= 0;

Presenta_cuadrantes(cuad_act);

Activa_cuadrante(cuad_act);

{ for i:= 0 to 3 do

zoom_vista( i, 100);}

info('3DEDIT Programado por Agust¡n Caballero Belda');

repeat

setviewport(0,0,639,479,clipon);

libera_raton;

mraton:= libre;

Evita_repet;

punteroon;

repeat

leerraton(posrat.c,posrat.f,izq,der);

if posrat.c>560 then begin

nop:= 0;

for i:= 1 to numitems do

if (posrat.f-10) div 15 = menu[i].fila then

nop:= i;

end

else nop:= 0;

if (nop<>opcion) then

cambia_opcion(opcion,nop);

opcion:= nop;

until ( (izq or der) and ((opcion<>0) or ((posrat.c<560) and (posrat.f<460))));

Activa_cuadrante(cuad_act);

Evita_repet;

punterooff;

if (izq or der) then begin

if posrat.c>560 then begin {Si es men£}

posraton(posrat.c,posrat.f);

if ((escena=nil) and (not (opcion in [alej,acer,roca,desp,rest,eje,mxmn,nuev,carg,salir]))) then begin

info('Comando no ejecutable sobre una escena vac¡a');

write(#7);

end

else if (obj_sel=nil) and der and (opcion in [elim,mvob,es2D,es3D,rota,copi,unio]) then begin

info('Comando no ejecutable. No se ha seleccionado ning£n objeto');

write(#7);

end

else if (vert_sel=nil) and ((der and (opcion=mvvt)) or (opcion=esvt)) then begin

info('Comando no ejecutable. No se ha seleccionado ning£n v rtice');

write(#7);

end

else

case opcion of

acer,alej,cent,rest,eje: begin

if izq then conj_vistas:= [cuad_act]

else conj_vistas:= [0..3];

for i:= 0 to 3 do

if i in conj_vistas then begin

case opcion of

acer: Zoom_vista(i,1/coef_zoom);

alej: Zoom_vista(i,coef_zoom);

cent: Centrar_vista(i,escena);

rest: Restaurar_vista(i);

eje: ej_vis[i]:= not ej_vis[i];

end;

if maximizado then begin

if i=cuad_act then

dibuja_escena(cuad_act);

end

else

dibuja_escena(i);

end;

end;

mxmn: Max_min(cuad_act);

nuev: begin

Limita_raton;

Nuevo_objeto(cuad_act);

end;

esvt: Escalar_vertices( vert_sel, cuad_act);

mvvt: begin

Limita_raton;

if not der then

repeat

Evita_repet;

info('Izq: Elegir v rtice Der: Cancelar');

Selecciona(pvert,obj,cuad_act,ok);

if ok then begin

new(selv_temp);

selv_temp^.sig:= nil;

selv_temp^.vert:= pvert;

Mover_vertices(selv_temp,cuad_act);

Dibuja_todo(cuad_act);

Desel_vertice(pvert,selv_temp);

end

until not ok

else begin

Mover_vertices(vert_sel,cuad_act);

Dibuja_todo(cuad_act);

end;

end;

mvob,es2D,es3D,rota,copi,elim,slob,dsob : begin

Limita_raton;

if izq then

repeat

Evita_repet;

info('Izq: Elegir objeto Der: Cancelar');

Selecciona(pvert,obj,cuad_act,ok);

if ok then begin

new(selo_temp);

selo_temp^.sig:= nil;

selo_temp^.obj:= obj;

Centro_caja(selo_temp,cm);

case opcion of

mvob: Mover_selec(selo_temp,cuad_act);

es2D: Escalar_selec(selo_temp,cm,cuad_act,modo2D);

es3D: Escalar_selec(selo_temp,cm,cuad_act,modo3D);

rota: Rotar_selec(selo_temp,cm,cuad_act);

copi: begin

Copiar(selo_temp);

Mover_selec(selo_temp,cuad_act);

end;

elim: begin

Eliminar_objeto(obj);

if Esta_selec_obj(obj) then Desel_objeto(obj,obj_sel);

end;

slob: if not Esta_selec_obj(obj) then Selec_objeto(obj,obj_sel);

dsob: if Esta_selec_obj(obj) then Desel_objeto(obj,obj_sel);

end;

Desel_objeto(obj,selo_temp);

dibuja_todo(cuad_act);

end

until not ok

else begin

Centro_caja(obj_sel,cm);

case opcion of

mvob: Mover_selec(obj_sel,cuad_act);

es2D: Escalar_selec(obj_sel,cm,cuad_act,modo2D);

es3D: Escalar_selec(obj_sel,cm,cuad_act,modo3D);

rota: Rotar_selec(obj_sel,cm,cuad_act);

copi: begin

Copiar(obj_sel);

Mover_selec(obj_sel,cuad_act);

end;

elim: begin

info('Se eliminar n todos los objetos seleccionados. ¨Confirma?');

if upcase(Leertecla)='S' then

while obj_sel<>nil do begin

Eliminar_objeto(obj_sel^.obj);

Desel_objeto(obj_sel^.obj,obj_sel);

end;

end;

dsob,slob: Selec_cuadro(cuad_act,opcion);

end;

dibuja_todo(cuad_act);

end;

end;

unio: begin

if not der then

Repeat

Evita_repet;

Limita_raton;

info('Izq: Elegir primer objeto Der: Cancelar');

Selecciona(pvert,obj,cuad_act,ok);

if ok then begin

info('Izq: Elegir segundo objeto Der: Cancelar');

repeat

Selecciona(pvert,obj2,cuad_act,ok);

if obj=obj2 then

info('No se puede unir un objeto consigo mismo');

until (obj<>obj2) or (not ok);

if ok then Unir_objetos(obj,obj2);

end;

until not ok

else begin

Unir_selec(obj_sel);

end;

end;

slvt,dsvt: begin

Limita_raton;

if izq then

repeat

Evita_repet;

info('Izq: Elegir v rtice Der: Cancelar');

Selecciona(pvert,obj,cuad_act,ok);

if ok then begin

if (opcion=slvt) and (not Esta_selec_vert(pvert)) then

Selec_vertice(pvert,vert_sel);

if (opcion=dsvt) and (Esta_selec_vert(pvert)) then

Desel_vertice(pvert,vert_sel);

Dibuja_todo(cuad_act);

end;

until not ok

else

Selec_cuadro(cuad_act,opcion);

end;

carg: begin

info('La escena actual ser  eliminada. ¨Confirma?');

if (escena=nil) or (upcase(Leertecla)='S') then begin

eliminar_escena(escena);

for i:= 0 to 3 do

Restaurar_vista(i);

Cargar_escena(escena);

Dibuja_todo(cuad_act);

end;

end;

mezc: begin

Cargar_escena(escena);

Dibuja_todo(cuad_act);

end;

grab: Grabar_escena(escena);

nues: begin

info('La escena actual ser  eliminada. ¨Confirma?');

if upcase(Leertecla)='S' then begin

Eliminar_escena(escena);

end;

for i:= 0 to 3 do

Restaurar_vista(i);

dibuja_todo(cuad_act);

end;

desp: Desplaza_camara(cuad_act);

roca: rotar_camara(cuad_act);

salir: begin

info('¨Seguro que desea salir?');

if upcase(Leertecla)='S' then begin

Eliminar_escena(escena);

Doneevents;

restorecrtmode;

halt;

end;

end;

end;

Libera_raton;

situarraton(posrat.c,posrat.f);

end

else if not maximizado then

begin

Cambia_cuadrante(cuad_act);

Presenta_cuadrantes(cuad_act);

end;

end;

cambia_opcion(opcion,opcion);

until false;

end.

3D EDIT

Calasparra (Murcia)

acebe@ctv.es

Manual de usuario

El programa arranca con la instrucci¢n 3dedit, y

requiere para su uso de un rat¢n. Al arrancar, se comprueba

si el rat¢n est  efectivamente instalado y en funcionamiento;

si hay alg£n problema, se le pedir  que solucione el problema

y pruebe de nuevo. Si todo ha ido bien, aparece la pantalla

siguiente:

Podemos distinguir tres zonas en esta pantalla:

- Zona de visualizaci¢n. Esta zona est  dividida en

cuatro cuadrantes, cada una de las cuales representan una de

las posibles vistas que se pueden presentar simult neamente.

Inicialmente, el primer cuadrante representa la planta, el

segundo el alzado, el tercero el perfil, y el cuarto una

vista tridimensional en proyecci¢n isom trica.

- Zona de mensajes. Est  situada en la parte inferior de

la pantalla; en esta zona ir n apareciendo todos los mensajes

que el programa emita, tanto los de ayuda como los que

requieran alg£n tipo de entrada por parte del usuario.

- Zona de men£s. En la parte derecha de la pantalla

tenemos una serie de opciones divididas en varios submen£s

seg£n a qu entidad est n referidos (vista, objeto, v rtice ¢

escena).

El manejo del programa se realiza enteramente mediante

el rat¢n (excepto las entradas que requieran datos num ricos

o alfanum ricos, l¢gicamente), utiliz ndose los botones

izquierdo y derecho.

Inicialmente, la vista seleccionada es la superior

izquierda (primer cuadrante); esto se indica porque el marco

que rodea a dicho cuadrante est  en color rojo. Esta vista

ser  sobre la que se ejecuten todos los comandos; si deseamos

seleccionar cualquier otra, bastar  con pulsar sobre la vista

correspondiente.

Para ejecutar uno de los comandos del men£, basta con

desplazar el rat¢n sobre la opci¢n correspondiente, y pulsar

uno de los botones. En general, el bot¢n izquierdo se utiliza

para ejecutar un comando sobre una £nica entidad, mientras

que el derecho indica la ejecuci¢n m£ltiple, cuando esta

opci¢n est disponible.

A continuaci¢n veamos el prop¢sito de cada uno de los

comandos.

Menu Vista

Acercar. Realiza un acercamiento del punto de vista hacia la

escena, con lo que se produce el efecto de un zoom de

ampliaci¢n de la escena.

Alejar. Aleja la posici¢n del observador de la escena, por lo

que la escena se har  m s peque¤a.

Desplazar. Permite desplazar el punto de observaci¢n

paralelamente al plano de proyecci¢n. Para ello, tras pulsar

sobre la opci¢n, podremos realizar dicho desplazamiento

simplemente moviendo el raton. Para terminar, simplemente hay

que pulsar un bot¢n.

Centrar. Ajusta la escena de forma que se visualice toda ella

en la vista determinada.

Max/Min. Como antes comentamos, inicialmente aparecen cuatro

cuadrantes en pantalla; esto puede ser modificado con este

comando, haciendo que la vista seleccionada ocupe toda la

zona de visualizaci¢n, o haciendo que vuelva a su modo

inicial.

Rotar. Realiza una rotaci¢n del punto de observaci¢n

alrededor de la escena, manteniendo la direcci¢n de

observaci¢n. Desplazando el rat¢n horizontalmente, giramos

respecto del eje Z, mientras que si el movimiento es

vertical, la rotaci¢n se hace inclinando m s o menos el plano

de proyecci¢n hacia la posici¢n del observador.

Restaurar. Restablece las caracter¡sticas iniciales de las

vistas, restaurando la posici¢n de la c mara, los ejes, etc.

Esto es £til cuando queramos regresar al esquema tradicional

de planta, perfil y alzado.

Ejes. Hace que sean visibles u ocultos los ejes X, Y y Z.

Estos ejes saldr n en l¡nea discontinua y en color amarillo.

Las opciones Acercar, Alejar, Centrar, Restaurar y Ejes

admiten actuaci¢n m£ltiple, es decir, si ejecutamos uno de

estos comandos pulsando el bot¢n derecho del rat¢n, la acci¢n

se realizar  sobre las cuatro vistas.

Men£ Objeto

Nuevo. Permite la creaci¢n de un nuevo objeto, siendo ste de

uno de los siguientes tipos predefinidos: esfera, pir mide,

tronco, toroide y caja; se solicita al usuario que elija uno

de estos tipos. Todos los objetos se crear n coloc ndolo

sobre el plano de proyecci¢n. Veamos c¢mo se procede para

crear un objeto de una clase:

1. Esfera. El usuario debe introducir por teclado el

n£mero de v rtices que tendr  cada uno de los "meridianos" de

la esfera; posteriormente, se debe dar gr ficamente el tama¤o

del radio de la esfera. El objeto creado consiste en una

malla con forma esf rica compuesta de pol¡gonos de cuatro

aristas en general y de tres aristas para aqu llos a los que

pertenece cada uno de los "polos".

2. Pir mide. En este caso, el valor num rico que se pide

al usuario es el n£mero de v rtices que componen la base,

introduciendo el radio gr ficamente. El objeto creado es una

pir mide que tendr  por defecto una altura igual al di metro

de la base.

3. Tronco. Un tronco es un objeto resultado de eliminar

de una pir mide la parte superior. As¡ pues, adem s de el

n£mero de v rtices de las bases superior e inferior

(l¢gicamente ser  el mismo), se deben determinar gr ficamente

sus respectivos radios.

4. Toroide. Un toroide es el resultado de realizar el

recorrido de un pol¡gono sobre el camino marcado por otro;

as¡ pues, debemos determinar la forma de ambos pol¡gonos.

Primero se introduce el n£mero de v rtices del pol¡gono que

marcar  el camino, y posteriormente el del pol¡gono que forma

la secci¢n. Gr ficamente determinamos los radios m ximo y

m¡nimo del toroide, de tal forma que el di metro del pol¡gono

de la secci¢n ser  la diferencia entre ambos radios.

5. Cubo. La forma de este tipo de objetos es la obvia;

el £nico par metro necesario es el tama¤o del mismo, que se

introducir  gr ficamente.

Al crear un objeto, si aumentamos el n£mero de v rtices

ste se parecer  m s a una figura perfecta. As¡, la esfera se

aproximar  a una esfera perfecta, la pir mide a un cono, el

tronco a un cono truncado y el toroide a un anillo circular

con secci¢n tambi n circular. No obstante, este n£mero no

puede ser todo lo grande que se desee, por cuestiones de

espacio en memoria. Para delimitar este n£mero se realiza un

c lculo aproximado de la memoria que precisar  el objeto, y

si no hay bastante disponible se da un mensaje por pantalla

para que se disminuya el valor.

Selecci¢n. Permite la selecci¢n de uno o varios objetos; en

caso de ser selecci¢n simple (bot¢n izquierdo), cada vez que

pulsemos el bot¢n izquierdo seleccionaremos el objeto m s

cercano, entendiendo por ste aqu l que contiene al v rtice

m s cercano al puntero; para terminar se pulsa el bot¢n

derecho. Si hemos optado por selecci¢n m£ltiple (bot¢n

derecho), sta se hace creando una ventana, y ser n

seleccionados todos los objetos que tengan alg£n v rtice

dentro de la misma. Todos los objetos seleccionados

aparecer n en pantalla en color verde hasta que sea

deseleccionado.

Deselec. Este comando es el opuesto al anterior, y funciona

de forma completamente id ntica.

Eliminar. Con este comando, en modo sencillo iremos indicando

con el bot¢n izquierdo los objetos que se van eliminando,

finalizando con el derecho. En modo m£ltiple, ser n

eliminados directamente todos los objetos que est n

seleccionados.

Mover. Si estamos en modo sencillo, elegimos los objetos a

mover y los desplazamos con el rat¢n, deposit ndolos con el

bot¢n izquierdo; para terminar, se pulsa el bot¢n derecho. En

modo m£ltiple se mover n de forma conjunta los objetos

seleccionados. Se¤alar que todos los movimientos se realizan

l¢gicamente de forma paralela al plano de proyecci¢n.

Escalar. Se utiliza esta opci¢n para cambiar el tama¤o de un

objeto o conjunto de objetos; este escalado se hace respecto

del punto central de la caja que encierra al objeto u objetos

correspondientes. En modo 2D cambiamos el tama¤o de la

proyecci¢n seleccionada £nicamente, mientras que en 3D se

cambia el tama¤o en todas las dimensiones.

Rotar. Realiza una rotaci¢n de un objeto u objetos sobre el

plano de proyecci¢n; al igual que el caso anterior, el punto

respecto del cual se rota es el centro de la caja que

encierra al conjunto.

Copiar. Usamos esta opci¢n para realizar un duplicado de un

objeto u objetos, permitiendo que la copia creada sea

trasladada de lugar para evitar que se solape con el

original.

Unir. Este comando se utiliza cuando deseamos que una serie

de objetos pasen a ser £nicamente uno y sean tratados como

tal. En el caso del modo sencillo, se supone que queremos

unir dos objetos, con lo cual los indicamos mediante el rat¢n

y se ejecuta la acci¢n; en caso m£ltiple, la uni¢n se realiza

entre todos los objetos seleccionados.

Men£ V rtice

Selecci¢n. Al igual que en el caso de los objetos, podemos

seleccionar v rtices individualmente o mediante una ventana;

en el primer caso, el v rtice seleccionado es el m s cercano

al puntero del rat¢n, y en el segundo se seleccionan todos

aquellos que caigan dentro de dicha ventana. Un v rtice

seleccionado se distingue porque aparece un peque¤o c¡rculo

rojo alrededor de l.

Deselec. Realiza la acci¢n contraria a la anterior, de forma

completamente an loga.

Mover. Permite el movimiento de un v rtice ¢ conjunto de

v rtices, de id ntica forma a la vista hasta ahora: bien

eligiendo uno de forma individual o bien moviendo todos los

seleccionados conjuntamente.

Escala. Realiza un escalado de los v rtices de forma similar

a la de un objeto, es decir, tomando como punto de referencia

el centro del conjunto de v rtices. Esta opci¢n, como es

l¢gico, se realiza sobre el conjunto de v rtices

seleccionados.

Men£ Escena

Cargar. Lee un fichero de disco y carga la escena en memoria.

Para ello, se solicita el nombre del fichero a cargar y se

saca por pantalla la escena. Respecto al nombre del fichero,

ste puede tener cualquier nombre v lido para M.S.-D.O.S.; se

puede especificar cualquier extensi¢n e incluso la ruta del

fichero. En el manual t cnico se explica en profundidad el

formato de los ficheros.

Grabar. Salva la escena actual a disco, pidiendo por teclado

el nombre destino; en caso de que el fichero ya exista, se

pide confirmaci¢n para sobreescribirlo.

Nueva. Elimina de memoria la escena devolviendo el programa

al estado inicial, pidiendo previamente confirmaci¢n por

teclado.

Mezclar. Realiza la misma funci¢n que cargar, con la

diferencia de que no elimina la escena actual, sino que la

mantiene y une ambas escenas. Esta opci¢n es muy £til cuando

queremos dise¤ar una escena descomponi ndola en varias

subescenas m s peque¤as.

Cuando entramos en una opci¢n que permite movimiento,

rotaci¢n, etc. mediante el rat¢n, existe la posibilidad de

fijar el movimiento del mismo. Esto se puede variar pulsando

la tecla Ctrl; cada vez que se pulse, aparecer  en el centro

del cuadrante un peque¤o icono que indica el modo actual de

funcionamiento. Los modos son libre, vertical, horizontal y

diagonal. El estado inicial del rat¢n es el de movimiento

libre, y sucesivamente se va pasando cada uno de los

siguientes. Esta limitaci¢n del rat¢n se puede utilizar

cuando por ejemplo queremos mover un objeto en horizontal. La

utilidad de la £ltima opci¢n se manifiesta cuando cambiamos

de tama¤o un objeto y queremos que mantengan la proporci¢n

entre el eje horizontal y el vertical.

Lista de polígonos

Lista de vértices

N

6

4

2

5

3

1

v

CW

PP

u