Node Transform
Para situar, orientar y escalar los objetos según
nuestras necesidades dentro del entorno que estemos construyendo, debemos poder
aplicar las transformaciones geométricas básicas, que son: traslación, rotación
y escalado. También existen variantes de estas pero que no veremos aquí.
Para aplicar transformaciones en VRML se utiliza
el node Transform. Este node puede realizar
cualquiera de las tres operaciones o las tres a la vez. Para ver cómo funciona
iremos analizando ejemplos:
Traslaciones
Ejemplo1: Definiremos una
esfera roja en el origen de coordenadas y una esfera verde trasladada 10
unidades en el eje de las X.
Shape { # Esfera roja que nos marca el origen de coordenadas geometry Sphere { radius 2 } appearance Appearance { material Material { diffuseColor 1 0 0 } } } Transform { translation 10 0 0 children [ Shape { # Esfera verde trasladada 10 unidades en el eje X geometry Sphere { radius 2 } appearance Appearance { material Material { diffuseColor 0 1 0 } } } ] } |
Analicemos este código. La primera parte define
la esfera roja que nos servirá de referencia para ver claramente que la esfera
verde ha sido efectivamente trasladada. Como se puede observar, la esfera roja
no sufre ninguna transformación.
La segunda parte es la que realmente nos
interesa. Aquí definimos un node Transform. Este tiene un field
translation donde se define el desplazamiento que sufrirá el objeto que
deseamos trasladar. Así pues se define un desplazamiento de 10 unidades en el
eje X, 0 unidades en el eje Y y 0 unidades en el
eje Z. Las traslaciones son relativas y por lo tanto se suman a
la posición actual de los objetos.
Después del field translation
podemos ver el field children. Este field permite
dar una lista de objetos que serán afectados por la trasformación definida. En
este caso la lista de objetos se limita a uno solo: la esfera verde que
queremos situar a 10 unidades del origen en el eje X. (De hecho
este node es uno de los nodes agrupadores que
mencionábamos en el módulo Nodos, Campos y Eventos I).
(NOTA: Como referencia de tipos de fields,
el field children es de tipo MFNode, es decir, de
tipo multivaluado por el hecho que puede contener una lista de valores).
Compliquemos un poco más el
entorno:
Ejemplo2: Ahora queremos
añadir un cono azul sobre nuestra esfera verde.
Para hacer esto debemos
trasladar el cono 10 unidades en el eje de las X al igual que la
esfera verde, pero además, debemos trasladarlo 3 unidades en el eje de las Y
(en concordancia con las medidas de la esfera y el cono).
Shape { # Esfera roja que nos marca el origen de coordenadas geometry Sphere { radius 2 } appearance Appearance { material Material { diffuseColor 1 0 0 } } } Transform { translation 10 0 0 children [ Shape { # Esfera verde trasladada 10 unidades en el eje X geometry Sphere { radius 2 } appearance Appearance { material Material { diffuseColor 0 1 0 } } } ] } Transform { translation 10 3 0 children [ Shape { # Cono azul trasladado 10 unidades en el eje X y 3 en el eje Y geometry Cone { bottomRadius 2 height 2 } appearance Appearance { material Material { diffuseColor 0 0 1 } } }
]
} |
Rotaciones
Ejemplo3: Definiremos una caja
amarilla en el origen de coordenadas con las caras paralelas a los planos
coordenados, y otra caja azul (más estrecha y más alta) rotada 45 grados
respecto al eje Y.
Shape { # Caja amarilla que nos señala la rotación cero geometry Box { size 4 2 4 } appearance Appearance { material Material { diffuseColor 1 1 0 } } } Transform { rotation 0 1 0 0.7854 # 45 grados en radianes
children [ Shape { # Caja azul que es rotada 45 grados respecto al eje Y geometry Box { size 2 4 2 } appearance Appearance { material Material { diffuseColor 0 0 1 } } } ] } |
Analicemos este fragmento de código. La primera
parte define la caja amarilla que servirá de referencia para ver claramente que
la caja azul ha sido rotada. Como se puede observar, la caja amarilla no sufre
ninguna transformación y por lo tanto mantiene sus caras paralelas a los planos
coordenados.
En la segunda parte definimos un node
Transform. Este tiene un field rotation donde se define
la rotación que sufrirá el objeto a modificar. Así pues, una rotación de 45
grados (0.7854 radianes) respecto al eje Y. Esto en VRML se
define de una forma poco común.
El field rotation tiene en realidad
dos partes. Los tres primeros valores determinan un vector en el espacio 3D que
corresponde a la orientación del eje de rotación de nuestra transformación. El
eje de rotación coincide con los ejes coordenados cuando:
- eje rotación = (1 0 0) -> equivale a X
- eje rotación = (0 1 0) -> equivale a Y
- eje rotación = (0 0 1) -> equivale a Z
Este sistema de definir un eje de rotación de la
transformación es muy flexible y genérico, pero resulta compleja cuando el eje
de rotación no es paralelo a un eje coordenado.
La segunda parte es el ángulo de rotación que
queremos aplicar (expresado en radianes) respecto al eje definido en la primera
parte. Al estar en un sistema de ejes a derechas, los ángulos positivos
resultan en rotaciones en sentido antihorario (y los ángulos negativos, en
rotaciones en sentido horario).
Después del field translation
podemos ver el field children que, como antes, engloba la lista
de objetos que serán afectados por la rotación. En este caso, la lista de
objetos es, de nuevo, un solo objeto en nuestro ejemplo: la caja azul que
deseamos rotar 45 grados respecto al eje Y.
Compliquemos un poco más el
entorno:
Ejemplo4: Ahora queremos que
la caja azul también rote 45 grados respecto al eje X.
Shape { # Caja amarilla que nos marca la rotación cero geometry Box { size 4 2 4 } appearance Appearance { material Material { diffuseColor 1 1 0 } } } Transform { rotation 1 0 0 0.7854 # Rotación eje X
children [ Transform { rotation 0 1 0 0.7854 # Rotación eje Y
children [ Shape { # Caja azul geometry Box { size 2 4 2 } appearance Appearance { material Material { diffuseColor 0 0 1 } } }
]
}
]
} |
La comprensión de esta variante se deja al
lector.
Escalados
Por lo que refiere a los
escalados se deben tener en cuenta dos tipos distintos: los escalados uniformes
y los no uniformes (aunque los uniformes sean un caso concreto de los no
uniformes). Los uniformes agrandan o reducen un objeto en todas las direcciones
por igual, mientras que los no uniformes tan solo modifican la talla en una dirección.
Por lo tanto los escalados uniformes mantienen las proporciones originales de
los objetos, mientras que los no uniformes deforman los objetos.
Ejemplo5: Definiremos un
escalado no uniforme sobre un cubo.
En concreto, escalaremos el cubo al doble
de su tamaño en la dirección de las X.
Transform { scale 2 1 1 children [ Shape { # Cubo naranja geometry Box { size 1 1 1 } appearance Appearance { material Material { diffuseColor 1 0.5 0 } } } ] } |
Analicemos este código. Se define un node
Transform que tiene un field scale donde se define el
escalado no uniforme que sufrirá el objeto que deseamos modificar. Para definir
el escalado, damos el tanto por uno que queremos agrandar/reducir el objeto en
cada una de las direcciones que marcan los ejes coordenados.
Como el escalado no uniforme que definimos es de
[2 1 1], el cubo de 1 unidad por lado ya no aparece como tal si no que aparece
como una caja alargada horizontalmente de exáctamente 2 unidades de ancho, 1 de
alto y 1 de fondo.
Si queremos aplicar un escalado uniforme, solo
tenemos que poner el mismo valor de escalado para las tres direcciones.
Encadenamiento de Transformaciones
Cuando se desea aplicar más
de una transformación a un objeto, no es necesario definir un node
Transform para cada una, sinó que podemos definir las tres bajo un
mismo node.
Ejemplo6: Definiremos un cubo
de 1 unidad y le aplicaremos una traslación de [5 4 0], una rotación de 30
grados respecto el eje X y un escalado de [0.5 0.5 2].
Transform { translation 5 4 0
rotation 1 0 0 0.5236 # 30 grados scale 0.5 0.5 1
children [
Shape { # Cubo verde obscuro geometry Box { size 1 1 1 } appearance Appearance { material Material { diffuseColor 0 0.5 0.2 } } } ] } |
Analizando el código vemos que lo que hemos
definido no es tan claro como parece ya que no obtenemos el mismo resultado si
aplicamos las tres operaciones en ordenes distintos.
Por definición, VRML define un orden de
preferencia de operaciones de transformación que es el siguiente:
- En primer lugar se aplica el escalado.
- En segundo lugar se aplica la rotación.
- En tercer lugar se aplica la traslación.
Por lo tanto el código de arriba
es totalmente equivalente al siguiente, realizando las transformaciones por
separado:
Transform { translation 5 4 0 children [ Transform { rotation 1 0 0 0.5236 # 30 grados
children [ Transform { scale 0.5 0.5 1 children [ Shape { # Cubo verde obsuro geometry Box { size 1 1 1 } appearance Appearance { material Material { diffuseColor 0 0.5 0.2 } } }
]
}
]
}
]
} |
Como podemos comprobar el resultado es el mismo
que antes.
No se debe caer en el error de pensar que esto se
lee de arriba a abajo. Este código se lee de dentro hacia fuera y por lo tanto
el orden en que se aplican las operaciones es empezando desde el objeto e ir
saliendo hacia fuera: cubo > escalado > rotación > traslación.
Para aplicar las
transformaciones en el orden que hemos dicho inicialmente deberíamos invertir
la secuencia de la siguiente forma:
Transform { scale 0.5 0.5 1 children [ Transform { rotation 1 0 0 0.5236 # 30 grados
children [ Transform { translation 5 4 0 children [ Shape { # Cubo verde obscuro geometry Box { size 1 1 1 } appearance Appearance { material Material { diffuseColor 0 0.5 0.2 } } } ] } ] } ] } |