Anda di halaman 1dari 31

1

VRML 2.0 con Java CAPÍTULO 14

Fractales y l-Systems
2

VRML 2.0 con Java CAPÍTULO 14

Contenido CAPÍTULO 1
• Fractales
• L-Systems
• La matriz de la clase

Fractales
3

VRML 2.0 con Java CAPÍTULO 14

Un fractal es la mejor idea de como se define recursivamente algoritmo para la


generación de formas. El tipo de algoritmo fractal estaremos examinando en
este capítulo se basa en sucesivas triángulo subdivisión y se adapta bien a la
generación automática de terreno.

Para comprender cómo los sucesivos triángulo subdivisión algoritmo, considere


el triángulo se muestra en la Figura 14.1.

Figura 14-1 Un triángulo básico

Cada paisaje generamos comenzará con un número de triángulos, y cada uno


de los triángulos se procesan de la misma manera. El primer paso en este
proceso es encontrar el punto medio de cada uno de los tres lados del
triángulo, como se muestra en la Figura 14.2. Tenga en cuenta que desde que
usamos para hacer referencia a el punto medio de la línea de A a B.

La cifra de 14,2 puntos medios de los lados del triángulo

El siguiente paso es conectar a estos tres nuevos vértices con segmentos de


línea, de manera efectiva subdividir nuestro triángulo original en cuatro más
pequeños, como se muestra en la Figura 14.3. Por ejemplo, uno de esos
triángulo se conectará a una ab a ca y de vuelta a a.

Figura 14.3 subdividir el triángulo


4

VRML 2.0 con Java CAPÍTULO 14

Cada uno de los cuatro triángulos resultantes de nuevo puede ser subdividido.
El resultado será un total de 16 triángulos, ya que los cuatro triángulos
resultantes de la primera subdivisión se produce cada cuatro subtriangles. La
próxima subdivisión producirá 64 triángulos, ya que cada uno de los 16 se
divide en cuatro triángulos más pequeños. Cada nueva "generación" produce
cuatro veces el número de triángulos en la generación anterior. Figura 14.4
muestra el resultado de cuatro generaciones de la subdivisión.

Figura 14.4 Cuatro generaciones de subdivisión triangular

La introducción de aleatoriedad

Hasta ahora, todos los que hemos hecho es un triángulo de la malla con un alto
grado de regularidad. Con el fin de producir un paisaje de aspecto natural,
tenemos que introducir un elemento de azar, después de todo, es el carácter
aleatorio de los procesos del mundo real que produce la variedad del terreno
que nos rodea.

Vamos a introducir la aleatoriedad que encontramos justo después de los


puntos medios de los bordes de un triángulo, pero antes de la próxima
subdivisión. La idea básica es desplazar cada uno de los nuevos vértices
generados a lo largo de un pseudoaleatorias tridimensional vector. Cada uno
de los tres puntos se moverá una pequeña cantidad en algunos azar dirección,
produciendo una irregularidad en el diseño. Desde esta aleatoriedad se
presenta en cada generación, que tienden a acumularse con el tiempo,
tenemos que limitar cuidadosamente la velocidad a la que esto ocurre, o se
convertirán en nuestros paisajes poco natural al azar.

El algoritmo específico que usaremos se basa en uno desarrollado por Steve ira
por su FRGEN (generador de fractales) programa (que se puede encontrar en la
página principal de Steve, http://www.hookup.net/ Sanger ~). Su técnica, que
será la adopción, es generar un "vector de ruido" que se añade a cada uno de
los vértices perturbar un poco. El ruido de vectores se escala por la longitud de
la banda a la que pertenece el vértice y se ve afectada por la profundidad de la
recursividad. Por lo tanto, las primeras generaciones tienen el mayor efecto en
la forma del terreno, y cada generación simplemente añade detalle sin producir
graves alteraciones.

Vamos a permitir que la persona que está construyendo el mundo para


5

VRML 2.0 con Java CAPÍTULO 14

controlar la magnitud de los vectores de ruido y su tasa de caída a lo largo de


cada eje. También vamos a proporcionar una manera para que el mundo-
constructor al sesgo el ruido vectores en una determinada dirección, lo que
simular el efecto de la erosión en los casos en que hay un preferido la dirección
del viento, por ejemplo. La magnitud de los vectores de ruido y su tasa de
caída de determinar si el terreno se compone de acantilados rocosos o suaves
colinas.
Paisajes continua y fija los bordes

Tenemos que tratar con el requisito de que el paisaje sea continuo, sin las
lagunas que aparecen entre triángulos. Considere la posibilidad de dos
triángulos adyacentes, como se muestra en la Figura 14.5. El punto x es un
punto medio de borde, pero es común a los dos triángulos. Si se desplaza x al
azar para cada uno de los dos triángulos, se lo terminan con dos nuevos
vértices (llamamos x 'y x "), que ya no están en el mismo lugar. La costura
entre los dos triángulos será discontinuo.

Figura 14.5 Dos triángulos adyacentes, compartir un borde y un punto medio


vértice

La solución a este problema es el ruido a base de vectores de la localización de


los vértices está perturbado, a fin de que a pesar de que el vector es al azar,
todavía podemos estar seguros de que x 'y x "están en el mismo lugar (ya que
su" aleatorio "los desplazamientos son a la vez sobre la base de x).

Otro problema es que a menudo quieren los bordes de nuestro paisaje de


quedarse, es decir, queremos que la limitan a la barra de bordes, y no
desplazar a los vértices que se encuentran en ellos. Hacemos esto al ofrecer
una "solución" bandera de cada borde, si que tiene un borde pabellón distinto
de cero, es fijo, aunque los bordes que puedan tocar en él son los bienes
muebles.

El Recursivo tri_fractal () de rutina

Puesto que está utilizando una gran cantidad de vectores en tres dimensiones,
utilizaremos la clase de utilidad Vec3 se introdujo de nuevo en el capítulo 14.
Esto nos ahorra un montón de esfuerzo adicional, siempre se debe buscar
6

VRML 2.0 con Java CAPÍTULO 14

oportunidades de reutilización de las clases que has escrito para otros


proyectos, ya que puede ahorrar una enorme cantidad de trabajo en el tiempo.

El algoritmo estaremos usando, por supuesto, ser recursivo. Una función


recursiva es aquella que las particiones de un problema en piezas más
pequeñas y, a continuación, se llama a sí mismo para manejar cada una de
esas piezas. En la tramitación de cada pieza, se llama a sí mismo de nuevo
para manejar incluso las piezas más pequeñas.

La rutina recursiva que hace todo el trabajo se llama tri_fractal (), y su


cabecera tiene este aspecto:

tri_fractal vacío (un Vec3, Vec3 b, c Vec3, boolean fix_ab,


fix_bc boolean boolean fix_ca, int nivel);

Los tres Vec3 del (a, b, yc) son los tres ángulos de un triángulo. Los tres
indicadores señalan que fijar los bordes deben permanecer fijos en un lugar
determinado, por ejemplo, si fix_ab es cierto, entonces el punto medio del
borde de A a B no debe ser perturbada por el ruido de vectores. Por último, el
nivel contador realiza el seguimiento de la profundidad de la recursividad,
cuando llega a cero, hemos golpeado el límite de recursión nuestra realidad y
debe generar un triángulo. El nivel pasó a la llamada inicial a tri_fractal () es el
número de generaciones que el usuario especifica.

Poner fin a la recursión

Evidentemente, no queremos que este recursión continúe indefinidamente, por


lo tanto, tenemos que tener alguna condición que indica cuando está
terminado. En el caso de tri_fractal (), vamos a estar utilizando el nivel de
contador, como se ha descrito anteriormente, cuando llega a cero, se habrá
terminado. Esto es lo que el código en tri_fractal () tiene el siguiente aspecto:

if (nivel <0) {
addTriangle (a, b, c);
retorno;
}

El addTriangle () método añade un triángulo a la salida de la estructura de


datos. Los tres vértices del triángulo son a, b, y c. Una vez que el triángulo se
ha añadido, simplemente de hacer un regreso sin más transformación; en tal
caso, tri_fractal () no va a llamar a sí mismo, y la recursión es completa.
7

VRML 2.0 con Java CAPÍTULO 14

Encontrar los puntos medios

A continuación, tri_fractal () considera los puntos medios de cada uno de los


bordes del triángulo:

Vec3 ab = new Vec3 ((a.add (b)). Escala (0.5F));


Vec3 aC = new Vec3 ((b.add (c)). Escala (0.5F));
Vec3 ca = new Vec3 ((c.add (a)). Escala (0.5F));

Al principio este código puede ser de tipo extraño, pero tiene sentido cuando lo
piensas. Para encontrar el punto medio, queremos encontrar el mismo
segmento de línea (b - a), dividir por 2, y añadir de nuevo a a. Sin embargo, (b
- a) / 2 es el mismo que (ba) / 2, que es lo que el fragmento de código anterior
hace. Añade a una b, escalas por la mitad, y lo asigna a ab (el punto medio de
la línea de a a b). Se hace lo mismo para los otros dos lados del triángulo.

Algunos hacer ruido

Ahora tenemos que generar el ruido de los vectores propios. Empezamos por la
búsqueda de la longitud de cada segmento de línea, ya que la magnitud del
ruido será proporcional a la longitud del segmento:

flotador ab_len = (a.sub (b)). magnético ();


flotador bc_len = (b.sub (c)). magnético ();
ca_len flotador = (c.sub (a)). magnético ();

Esto es simple: Reste el vértice de la b de un vértice para obtener la línea entre


ellos. La magnitud del resultado es la longitud del borde.

Utilizamos la función noise_vector () para generar el ruido vectores, vamos a


examinar en detalle esta función un poco más tarde. El real pide a noise_vector
() algo como esto:

Vec3 noise_ab = noise_vector (ab, profundidad - nivel). Escala (ab_len);


Vec3 noise_bc = noise_vector (BC, profundidad - nivel). Escala (bc_len);
Vec3 noise_ca = noise_vector (ca, profundidad - nivel). Escala (ca_len);

Nótese que cada base de ruido de vectores de la localización del punto medio,
como se describió anteriormente. También tomamos nota de que la longitud de
cada segmento se utiliza para ese segmento de la escala del punto medio de
desplazamiento. Y, por último, aviso que usamos la expresión profundidad - a
nivel de control de la magnitud del vector de ruido, lo que asegura que el ruido
disminuye con cada generación.
8

VRML 2.0 con Java CAPÍTULO 14

A continuación, encontramos el triángulo a la normal (es decir, el vector que


apunta perpendicular a la cara del triángulo), tomando la cruz producto de dos
bordes y normalizar el resultado. Usaremos este vector para anular la
perturbación de los bordes fijos, mediante la adopción de la cruz de producto
con el vector normal el ruido vector de una ventaja que se fija:

Vec3 norm = (b.sub (a)). Cruz (c.sub (a)). Normalizar ();


if (fix_ab) noise_ab = noise_ab.cross (norma);
if (fix_bc) noise_bc = noise_bc.cross (norma);
if (fix_ca) noise_ca = noise_ca.cross (norma);

Ahora que tenemos el ruido de los vectores, que se aplican a cada uno de los
tres vértices recién generado (AB, BC, y CA):

ab = ab.add (noise_ab);
BC = bc.add (noise_bc);
ca = ca.add (noise_ca);

Nosotros llamando recurrentemente

Por último, hacemos la recursión. El tri_fractal () la función se llama a sí mismo


cuatro veces, una para cada uno de los subtriangles, de la siguiente manera:

tri_fractal (a, ab, ca, fix_ab, falso, fix_ca, nivel-1);


tri_fractal (B, BC, AB, fix_bc, falso, fix_ab, nivel-1);
tri_fractal (C, CA, BC, fix_ca, falso, fix_bc, nivel-1);
tri_fractal (ab, bc, ca, falsas, falsa, falsa, nivel-1);

Observe que hemos cuidado de establecer nuestras banderas correctamente:


Todo el borde del común que a un borde de la matriz triángulo debe fijarse si su
padre borde es fijo, mientras que recién generado bordes nunca son fijas (por
lo que su revisión es falsa bandera). También tomamos nota de que disminuir
el nivel de uno cuando nos llamamos.

El noise_vector () Función

Todo lo que queda ahora es la función de generación de ruido, noise_vector (), que se
muestra en el Listado 14.1.

14.1 La lista noise_vector () de rutina

estático privado Vec3 noise_vector (Vec3 v, int nivel) (


9

VRML 2.0 con Java CAPÍTULO 14

rand.setSeed (semillas
^ (Int) (-23465 * v.getX ())
^ (Int) (17365 * v.getY ())
^ (Int) (5364 * v.getZ ()));

Xscale = nscale.getX doble ()


* Math.exp (nivel * Math.log (nfalloff.getX ()));
doble yscale = nscale.getY ()
* Math.exp (nivel * Math.log (nfalloff.getY ()));
doble zscale = nscale.getZ ()
* Math.exp (nivel * Math.log (nfalloff.getZ ()));

Vec3 randvec = new Vec3 {


2 * rand.nextFloat () - 1,
2 * rand.nextFloat () - 1,
2 * rand.nextFloat () - 1);

randvec = randvec.add (nbias);

return new Vec3 {


(float) Xscale randvec.getX * (),
(float) yscale * randvec.getY (),
(float) zscale * randvec.getZ ());

Lo primero que hacemos es la semilla el generador de números aleatorios basados en la


localización de los vértices nos perturba; los valores que aquí son los mismos que Steve
Enojo utilizado en el original FRGEN programa. A continuación, hacemos una caída
exponencial de los factores de escala que se especifica por la persona que la construcción
del mundo, haciendo uso de la nscale y nfalloff vectores que dieron. Generamos un vector
al azar, con cada uno de los componentes están en el rango -1 a 1, y agregue el usuario de la
parcialidad de vectores (nbias) a la misma. Nosotros la escala al azar por el vector de
factores que computa y regresar como el vector de ruido.

Creación de Prototipo

Ahora que tenemos el código que genera el fractal, vamos a crear un prototipo para un
fractal terreno nodo. Vamos a llamarlo TriFractal, y tendrá el siguiente formulario:

TriFractal (
SFInt32 recursionDepth 3
SFFloat randomSeed 0
SFVec3f sesgo 0 0 0
SFVec3f escala 0,1 0,1 0,1
SFVec3f caída 0 0 0
MFVec3f triángulos []
10

VRML 2.0 con Java CAPÍTULO 14

MFInt32 fixFlags []
SFBool ccw TRUE
SFBool sólidos FALSE
SFFloat creaseAngle 0,5
SFBool normalPerVertex FALSE
)

La mayoría de los campos ya se han descrito. El randomSeed es el valor de semillas que


usamos para el generador de números aleatorios en el noise_vector () función. Ser capaz de
configurar esta semilla es útil si tenemos que crear copias exactas de los terrenos al azar.
Los triángulos [] campo almacena el a, b, c y las coordenadas de cada uno de los triángulos,
si usted tiene cuatro triángulos, que habrá doce elementos en triángulos []. El campo
almacena la fixFlags fijar valores descritos anteriormente; usamos un MFInt32 VRML
porque carece de un tipo MFBool.

Por ejemplo, para crear unas sencillas terreno podemos hacer lo siguiente:

TriFractal {
recursionDepth 4
sesgo 0 0,55 0
escala 0,06 0,2 0,06
triángulos [
10 0 0, 0 0 10 -10 0 0, # primer triángulo
-10 0 0 0 0 -10, 10 0 0 # segundo triángulo
]
fixFlags [
1 1 0 # los dos primeros bordes del primer triángulo son fijos
1 1 0 # también lo son los dos primeros bordes de la segunda triángulo
]
}

El paisaje resultante se muestra en la Figura 14.6.

Figura 14.6 Una cordillera creado usando el nodo FractalTerrain

Para implementar el nodo, se inicia mediante la creación de un archivo VRML que contiene
el prototipo, como se muestra en el Listado de 14.2.

14.2 La lista TriFractal Prototipo

# VRML V2.0 utf8

PROTO TriFractal
[
11

VRML 2.0 con Java CAPÍTULO 14

SFInt32 recursionDepth campo 3


campo SFFloat randomSeed 0
campo SFVec3f sesgo 0 0 0
SFVec3f escala sobre el terreno 0,1 0,1 0,1
SFVec3f campo caída 0 0 0
campo MFVec3f triángulos []
campo MFInt32 fixFlags []
SFBool campo ccw TRUE
SFBool materia sólida FALSE
campo SFFloat creaseAngle 0,5
campo SFBool normalPerVertex FALSE
]
}
IFS IndexedFaceSet (DEF
coord coordenadas ()
coordIndex []
ccw ES ccw
TRUE convexo
creaseAngle ES creaseAngle
sólido es sólido
normalPerVertex ES normalPerVertex
}
Guión (DEF SC
url "TriFractal.class"
SFInt32 recursionDepth campo se recursionDepth
SFInt32 randomSeed campo se randomSeed
campo SFVec3f sesgo sesgo ES
campo SFVec3f escala escala
campo SFVec3f caída es caída
campo MFVec3f triángulos ES triángulos
campo MFInt32 fixFlags ES fixFlags
eventOut SFNode coord_changed
eventOut MFInt32 coordIndex_changed
}
VÍA SC.coord_changed A IFS.set_coord
VÍA SC.coordIndex_changed A IFS.set_coordIndex
}
}

El prototipo se parece como un nodo IndexedFaceSet, y por lo tanto puede ser usada en el
campo de la geometría de una forma nodo. Colores, un mapa de textura, apariencia y otras
propiedades se pueden añadir fácilmente a la forma nodo.

El TriFractal.java archivo (que se compila para crear el archivo TriFractal.class que se hace
referencia en la secuencia de comandos nodo) hace uso de la tri_fractal () y noise_vector ()
rutinas que ha descrito anteriormente y se muestra en el Listado 14.3.
12

VRML 2.0 con Java CAPÍTULO 14

14.3 La lista de archivos TriFractal.java

/ / Nodo generador fractal

/ / Escrito por Bernie Roehl, noviembre de 1996

/ / Inspirado por Steve ira del FRGEN

paquete fractales;

importación vrml .*;


importación vrml.field .*;
importación vrml.node .*;
importación java.io. *;
importación java.util .*;
utilidad de importación .*;

clase pública se extiende TriFractal Guión {


Vec3 nbias; / / ruido de los prejuicios de vectores
Vec3 nscale; / / ruido de escala de vectores
Vec3 nfalloff; / / ruido de caída de vectores
int profundidad; / / profundidad de recursión
int semilla; / / semilla de número aleatorio

Aleatorio rand = new Aleatorio ();

OutputVertices vector = new Vector (); / / nodo para Coordinar


OutputTriangles vector = new Vector (); / / de campo coordIndex

public void inicializar () {


profundidad = ((SFInt32) getField ( "recursionDepth")). getValue ();
semillas = ((SFInt32) getField ( "randomSeed")). getValue ();
nbias = convertVector ((SFVec3f) getField ( "sesgo"));
nscale = convertVector ((SFVec3f) getField ( "escala"));
nfalloff = convertVector ((SFVec3f) getField ( "caída"));

if (semilla! = 0)
rand.setSeed (semilla);

int nvertices = ((MFVec3f) getField ( "triángulos")). getSize ();


int ntriangles = nvertices / 3;

flotado [] [] = new vértices flotar [nvertices] [3];


((MFVec3f) getField ( "triángulos")). GetValue (vértices);
13

VRML 2.0 con Java CAPÍTULO 14

int [] = new int fixFlags [nvertices];


((MFInt32) getField ( "fixFlags")). GetValue (fixFlags);

for (int i = 0; i <ntriangles, i) {


= Vec3 un nuevo Vec3 (vértices [i * 3] [0],
vértices [i * 3] [1], vértices [i * 3] [2]);
Vec3 b = new Vec3 (vértices [i * 3 1] [0],
vértices [i * 3 1] [1], vértices [i * 3 1] [2]);
Vec3 c = new Vec3 (vértices [i * 3 2] [0],
vértices [i * 3 2] [1], vértices [i * 3 2] [2]);
boolean fix_ab = (fixFlags [i * 3 0]! = 0);
boolean fix_bc = (fixFlags [i * 3 1]! = 0);
boolean fix_ca = (fixFlags [i * 3 2]! = 0);
tri_fractal (a, b, c, fix_ab, fix_bc, fix_ca, profundidad);
}

StringBuffer coordBuff = new StringBuffer (1024);


coordBuff.append ( "punto de coordenadas ([\ n");
Enumeración en outputVertices.elements = ();
while (en.hasMoreElements ()) {
Vec3 v = (Vec3) en.nextElement ();
coordBuff.append ( "\ t" v "\ n");
}
coordBuff.append ( "]) \ n");

try {
SFNode coord_changed =
(SFNode) getEventOut ( "coord_changed");
coord_changed.setValue (getBrowser ().
createVrmlFromString (coordBuff.toString ()) [0]);
}
de capturas (InvalidVRMLSyntaxException ex) ()

int [] puntos = new int [outputTriangles.size () * 4];


en outputTriangles.elements = ();
for (int n = 0; en.hasMoreElements (); n = 4){
Triángulo tri = (Triángulo) en.nextElement ();
/ / En cuenta que invertir el orden de las agujas del reloj
puntos [n] = tri.getC ();
puntos [n 1] = tri.getB ();
puntos [n 2] = tri.getA ();
puntos [n 3] = -1; / / poner fin a la cara
}
14

VRML 2.0 con Java CAPÍTULO 14

MFInt32 coordIndex_changed =
(MFInt32) getEventOut ( "coordIndex_changed");
coordIndex_changed.setValue (puntos);

}
}

Después de leer los distintos ámbitos, la inicializar () método pasa por tres
vértices a la vez y pide tri_fractal () en cada triángulo. El tri_fractal () desarrolla
un método de producción de vectores de vértices y un vector de la producción
triángulos. Vértices obtener la salida se convirtió en un nodo utilizando VRML
Coordinar createVrmlFromString () y enviado a la coord_changed eventOut. El
vector de la producción triángulos se convirtió en una matriz de enteros, con
un -1 después de cada triángulo. Este es el enviado a la coordIndex_changed
eventOut.

El convertVector () sea el método utilizado para convertir una SFVec3f en un


Vec3:

protegidas Vec3 convertVector (SFVec3f sfvec) {


return new Vec3 (sfvec.getX (), sfvec.getY (), sfvec.getZ ());
}

El addTriangle () añade el método de tres vértices a la producción de vectores


de vértices. A continuación, toma los índices de los vértices y las agrega a la
producción de vectores de triángulos:

protegidas addTriangle vacío (un Vec3, Vec3 b, c Vec3) {


int a_index = -1, b_index = -1, c_index = -1;
int i;
Enumeración en outputVertices.elements = ();
for (i = 0; en.hasMoreElements (), i) (
Vec3 v = (Vec3) en.nextElement ();
if (v.equals (a)) a_index = i;
if (v.equals (b)) b_index = i;
if (v.equals (c)) c_index = i;
}
if (a_index == -1) {
a_index = i;
outputVertices.addElement (a);
}
if (b_index == -1) {
b_index = i;
15

VRML 2.0 con Java CAPÍTULO 14

outputVertices.addElement (b);
}
if (c_index == -1) {
c_index = i;
outputVertices.addElement (c);
}
outputTriangles.addElement (nuevo triángulo (a_index, b_index, c_index));
}

La única cosa sutil sobre este código es que se comprueba si un vértice


idéntica a la dada uno ya está en la matriz de salida vértices. Si es así,
simplemente que reutiliza vértice. Esto no sólo guardar la memoria y
(potencialmente) acelerar el renderizado al exigir unos tranformations en el
pipeline de renderizado, también hace posible Gouraud-sombra el terreno
mediante el establecimiento de la normalPerVertex en el campo TriFractal nodo
a TRUE.

L-Systems

L-sistemas son vagamente relacionados con los fractales y también se basan


en el uso de la recursividad. Son muy adecuadas para la creación de formas
orgánicas como las plantas y los animales, pero también puede usarlos para
crear una variedad interesante de otras estructuras, como edificios.

L-sistemas se denomina formalmente "Lindenmayer sistemas," porque fueron


inventadas por un biólogo llamado Astrid Lindenmayer en 1968. Décadas más
tarde fueron adaptados por Alvy Ray Smith, con una manera de crear plantas
generados por ordenador, y su labor fue continuada por una serie de
investigadores, entre ellos Przemyslaw Prusinkiewicz.

L-sistemas utilizan una especie de gramática que permite estructuras muy


complejas que se crean a partir de simples especificaciones. Hay dos
componentes básicos para una L-sistema: un axioma y un conjunto de
producciones. Un axioma es una serie de caracteres que funciona como una
especie de código genético. Las producciones son una serie de reglas que
controlan la reproducción de las instrucciones en el axioma. Cada regla de
producción de mapas en un solo carácter en el axioma de una cadena de
caracteres, y cada "generación" de una L-sistema funciona mediante la
sustitución de cada carácter con su correspondiente producción. La cadena
resultante se convierte en el nuevo axioma para la próxima generación.
Aunque no hay recursión real en el sentido de las funciones de Java llamando a
sí mismos, hay todavía una expansión de la recursivo axioma.
16

VRML 2.0 con Java CAPÍTULO 14

Un ejemplo de los axiomas y Producciones

La forma más sencilla de explicar la forma en que el trabajo es la producción a


través de un ejemplo. Considere el siguiente axioma:

afgb - fwtm

y las siguientes producciones:

a = g-b
g = f-w
w = mmm

Nótese que escribir las normas de producción mediante un signo = entre el


personaje y se sustituye la cadena, se debe sustituir por. Después de una
generación, el axioma se han transformado en:

g-f des-wb - fmmmtm

En otras palabras, el se convirtió en un g-b, f es la izquierda sin cambios (ya


que no hay producción de f), la g se convirtió en FW, el b - f quedó sin cambios
(ya que ninguno de ellos tienen producciones), la w se convirtió en mmm, y el t
y m se pasa a través de la misma.

Después de otra generación, se convierte en el axioma

f-w-f-des mmmb - fmmmtm

Este proceso continúa, la generación por generación, y la cadena crece más y


más complejo.

Ampliación de la Axiom

Java es más inteligente sobre el manejo de memoria que muchas otras


lenguas, por lo que el código para ampliar el axioma es muy sencillo:

proceso estático de cadena (String axioma, String [] producciones) {


StringBuffer resultado = new StringBuffer ();
for (int i = 0; i <axiom.length (), i) {
char c = axiom.charAt (i);
S = cadena de producción [0x7f & (int) c];
if (s! = null)
result.append (s);
17

VRML 2.0 con Java CAPÍTULO 14

algo más
result.append (c);
}
return new String (resultado);
}

El carácter en cada posición es examinado y se utiliza para indexar en una


serie de producciones. Estamos emitidos en el carácter y la máscara de un int
tan sólo la parte inferior 7 bits son distinto de cero. Tenga en cuenta que esto
nos limita al ASCII, en vez de completa de Unicode, lo que no es un problema,
ya que todos los personajes de nuestro sistema de comando-L repertorio son
ASCII. También nos permite utilizar un simple cuadro de búsqueda en lugar de
una búsqueda y comparación de Unicode, lo que mejora el rendimiento. Esta es
una consideración importante cuando se utiliza Java.

Si se encuentra una producción para un determinado carácter, la producción se


añade al StringBuffer. Tenga en cuenta que el prefijo de la producción (la c =
parte) fue despojado fuera al mismo tiempo que las producciones de [] matriz
se cumplimentó Si no se encuentra la producción de un determinado
personaje, el personaje en sí se añade al StringBuffer.

Encendido de la Geometría en Axiom

Después de que el axioma se ha ampliado, el siguiente paso es convertirla en


la geometría real. Así como una cadena de ADN contiene una serie de sencillas
instrucciones para el montaje de una proteína, el axioma contiene un conjunto
de instrucciones sencillas para la creación de formas.

Cada personaje en el axioma es una instrucción, de forma similar a una


máquina-la enseñanza de idiomas tradicionales producidos por un compilador o
un bytecode producido por un compilador Java. La forma más sencilla de
visualizar cómo las instrucciones de trabajo es imaginar un ratón volador
inteligentes que pueden dibujar líneas y la creación de polígonos como moscas
alrededor.

Hay personajes que causan el ratón para avanzar, los caracteres que causan a
cambiar de dirección, y los personajes que comienzan y terminan polígono
caras. También hay personajes que "empujar" y "pop" el estado actual. Vamos
a examinar cada uno de estos caracteres comando en detalle.

El estado actual

En un momento dado, la L-sistema tiene un estado actual. Este contiene el


18

VRML 2.0 con Java CAPÍTULO 14

estado actual punto en el espacio, expresado como x, y y z valores. También


contiene la orientación de nuestro imaginario ratón, que es representada por
una matriz de rotación. Cuando el ratón se mueve hacia adelante, se mueve a
cierta distancia, esa distancia es la longitud y forma parte también de la
situación actual. Del mismo modo, la cantidad por la cual a su vez, cuando un
cambio de rumbo se produce se llama el ángulo. Y, por último, las líneas y
polígonos que se han señalado cada uno un color específico (como el tipo de
pluma de color en un estilo antiguo plotter), y que el color es también parte del
estado. Vamos a representar al Estado por un Estado de clase, como se
muestra en el Listado 14.4.

14.4 El Estado lista para la Clase LSystem Nodo

clase pública del Estado {


protegidas flotar x, y, z;
Matriz rotmat protegidas;
protegidas flotar ángulo;
protegidas flotar longitud;
protegidas int color;

flotar estáticos default_angle = 90 / / grados


flotar estáticos default_length = 100;
static int default_color = 1;

int [] polydata = new int [1000];


int polyindex = 0;
boolean polyactive = false;

public void setDefaultAngle (float a) (default_angle = a;)


public void setDefaultLength (float l) (default_length = l;)
public void setDefaultColor (int c) (default_color = c;)

pública del Estado () {


x = y = z = 0;
= ángulo default_angle;
= longitud default_length;
color = default_color;
rotmat = new Matriz ();
}

Hay algunas cosas a destacar sobre el Estado de clase. Además de la longitud,


el color y el ángulo, también hay valores por defecto para cada una de dichas
cantidades. Los valores por defecto son variables de clase (es decir, se
declaren estática y, por tanto, común a todas las instancias del Estado de
19

VRML 2.0 con Java CAPÍTULO 14

clase). Cuando un reset (el carácter *) se encuentra, la longitud actual, el color


y el ángulo se restablecen a los predeterminados. El constructor simplemente
restaura todo y asigna una nueva matriz (Matriz de la clase se describe más
adelante en este capítulo).

Observe también que hay alguna información en el Estado que resulta útil para
la creación de un polígono, lo que se explica más adelante.

El conjunto completo de caracteres de comando se muestra en la Tabla 14.1.


Cuadro 14.1 Caracteres Comando de la L-System

El tradicional sistema de la gramática-L tiene una característica que no vamos a usar: el


control del espesor de las líneas se está elaborando. Esto es útil para cosas como las ramas
de los árboles, sino que exigen que cada modelo del segmento en el camino de nuestro
ratón volador como un cilindro. Eso nos causa para producir una gran cantidad de
polígonos, y como usted descubrirá, L-sistemas ya están hambrientos de polígono.

A través de la intensificación de Axiom y emisor de Geometría

Procesamos el axioma de un carácter por vez. Aparte de un par de personajes que nos
enfrentamos a un nivel superior, la mayoría de los caracteres se transmiten al Estado actual
de objeto para la transformación. El Estado tiene un método llamado cmd () (para el
"comando"), que analiza el carácter y las actualizaciones de la situación actual en
consecuencia. En el proceso de actualización de la situación, el Estado objeto de necesidad
20

VRML 2.0 con Java CAPÍTULO 14

de generar vértices, segmentos de línea, y polígonos. Esto se hace usando un objeto emisor,
que esconde los detalles del formato de salida. Este emisor se pasa como parámetro a la
cmd () método.

El cmd () es simplemente un método de gran switch, con un caso de cada uno de los
caracteres en el cuadro 14.1 (excepto el Estado empujar / pop comandos, que se manejan en
otros lugares):

public void cmd (char c, emisor emisor) {


switch (c) {
...
}
}

Muchos de los casos simplemente actualizar la matriz de rotación sobre la base de la actual
ángulo:

case '+':
rotmat = rotmat.multiply (Matrix.yrot (ángulo));
break;
case '-':
rotmat = rotmat.multiply (Matrix.yrot (ángulo));
break;
case '&':
rotmat = rotmat.multiply (Matrix.xrot (ángulo));
break;
case '^':
rotmat = rotmat.multiply (Matrix.xrot (ángulo));
break;
case '<':
rotmat = rotmat.multiply (Matrix.zrot (ángulo));
break;
case '>':
rotmat = rotmat.multiply (Matrix.zrot (ángulo));
break;
caso '|':
rotmat = rotmat.multiply (Matrix.yrot (180));
break;
case '%':
rotmat = rotmat.multiply (Matrix.zrot (180));
break;

Otros simplemente la actualización de variables de estado:

case ' "': + + longitud; break;


case '\ ": - longitud; break;
caso ",": + + ángulo; break;
case ':': - ángulo; break;
21

VRML 2.0 con Java CAPÍTULO 14

case 'c': + + color; break;


case '*':
color = default_color;
= longitud default_length;
= ángulo default_angle;
break;

El dibujo de polígonos es manejado por el '(' y ')' caracteres:

case '(':
polyactive = true;
polyindex = 0;
polydata [polyindex + +] =
emitter.addVertex (x, y, z);
break;
case ')':
emitter.addPolygon (polydata, polyindex, color);
polyactive = false;
break;

El pabellón polyactive realiza un seguimiento de si estamos acumulando los vértices de un


polígono o no. El polyindex variable y lleva la cuenta de la cantidad de vértices que hemos
procesado, y la polydata [] realiza un seguimiento conjunto de los números de vértice para
el actual polígono. Cada vez emitter.addVertex () es llamado, devuelve el número de índice
de la emitida vértice.

Nuevos casos en el switch manejar el movimiento y la línea de dibujo:

case 'M':
case 'Z':
if (polyactive == false) {
int n = emitter.addVertex (x, y, z);
anticipo ((c == 'Z')? 2: 1);
emitter.addLine (n,
emitter.addVertex (x, y, z), color);
}
else {
anticipo ((c == 'Z')? 2: 1);
polydata [polyindex] =
emitter.addVertex (x, y, z);
}
break;
case 'f':
case 'z':
22

VRML 2.0 con Java CAPÍTULO 14

anticipo ((c == 'z')? 2: 1);


if (polyactive)
polydata [polyindex] =
emitter.addVertex (x, y, z);
break;
case 'g': avance (1); break;
caso "."
if (polyactive) {
polydata [polyindex 1] = polydata [polyindex];
polyindex;
}
break;
default: break;
}

La idea es que si estamos acumulando puntos en un polígono, cada nuevo vértice se emite y
se añadirán a la polydata [] matriz. Para aquellos comandos que dibujar líneas (F y Z), que
emiten las líneas si no estamos acumulando puntos para un polígono.

No todos los caracteres tienen que ser transformados, por ejemplo, algunos sólo pueden
utilizarse durante la expansión del axioma de las normas de producción. El valor por
defecto caso ignora cualquier cosa que no se reconoce.

El anticipo () rutina puede avanzar ni un paso completo (divisor == 1) o medio-paso


(divisor == 2). El paso completo es igual a la longitud, mientras que el paso medio es la
mitad de esa distancia. El movimiento es en la dirección z, como se extrae de la matriz de
rotación:

private void avanzar (int divisor) {


x = longitud rotmat.getElement * (0, 2) / divisor;
y = longitud rotmat.getElement * (1, 2) / divisor;
z = longitud * rotmat.getElement (2, 2) / divisor;
}

Tenga en cuenta que el resultado x, y, z y los desplazamientos se añaden a la ubicación


actual con el fin de actualizarlo.

El emisor y el Polígono Clases

El emisor de la clase se utiliza para registrar los vértices, líneas, polígonos y que luego se
utilizan para construir los objetos reales mismos. El emisor de la clase se muestra en el
Listado 14.5.

14.5 La lista del emisor de la clase

clase pública del emisor (


vértices protegidos vectores = new Vector ();
protegidas polígonos de vectores = new Vector ();
23

VRML 2.0 con Java CAPÍTULO 14

protegidas double_sided boolean = false;

pública del emisor (dsided booleana) (double_sided = dsided;)

público int addVertex (float x, float y, float z) (


vertices.addElement (nuevo Vec3 (x, y, z));
volver vertices.size () -1;
)

public void addPolygon (int puntos [], int npoints, int color) (
polygons.addElement (nuevo polígono (npoints, puntos, color));
)

public void addLine (int n1, int n2, int color) (


int [] pts = new int [2];
pts [0] = n1; pts [1] = n2;
polygons.addElement (nuevo polígono (2 pts, color));
)
)

Double_sided la bandera indica si la emisión polígonos debe ser visible desde ambos lados.
Dos vectores, vértices y polígonos, registrar los vértices y polígonos, respectivamente.
Líneas se representan simplemente como polígonos con sólo dos vértices. Ofrecemos
métodos para añadir vértices, polígonos, y las líneas.

El polígono del emisor de clase que utiliza es muy sencillo y se muestra en el Listado 14.6.
Simplemente utiliza un conjunto de índices de vértice y un valor de color.

14.6 La lista de la clase Polígono

clase pública Polígono {


protegidas int color;
int npoints protegidas;
protegidas int [] puntos;

público int getColor () (return color;)


público int getNumberOfPoints () (return npoints;)
público int getPoint (int n) (return puntos [n];)

Polígono público (npts int, int [] puntos, int col) {


color = col;
npoints = npts;
puntos = new int [npts];
for (int i = 0; i <npts; i)
puntos [i] = pts [i];
}
}
24

VRML 2.0 con Java CAPÍTULO 14

Aparte de los habituales métodos de acceso, es el constructor que hace todo el trabajo de
crear un nuevo polígono y el almacenamiento de los datos para ello.

Poner las piezas juntas

El código de más abajo tiene el axioma, los procesos mediante la producción, entonces los
pasos a través de él y ejecuta el carácter de cada uno. Es aquí que nosotros nos encargamos
de la '[' y ']' caracteres, mediante el mantenimiento de un Estado pila.

for (int i = 0; i <ngenerations; i)


= proceso axioma (axioma, producciones);

State_stack pila = new Pila ();


Estado current_state = new Estado ();
Emisor emisor = new emisor (true);

for (int i = 0; i <axiom.length (), i) {


char c = axiom.charAt (i);
switch (c) }
case '[':
state_stack.push (current_state);
current_state = new Estado ();
break;
case ']':
current_state = (Estado) state_stack.pop ();
break;
por defecto:
current_state.cmd (c, emisor);
break;
}
}

Una vez más, el código es muy sencillo. El axioma de una generación es procesada para
producir el axioma para la próxima. Un Estado y una pila de estado actual se crean, junto
con un emisor. Los personajes de los procesados plenamente axioma se examinan, y el '[' y
']' comandos son procesados por apilamiento y unstacking estados. Todos los demás
personajes se pasan a la situación actual.

Creación de Prototipo

Al igual que en el nodo TriFractal, el siguiente paso es crear un prototipo para un sistema
de L-objeto. Vamos a llamarlo LSystem, y tendrá el siguiente formulario:

LSystem {
SFInt32 numberOfGenerations 3
SFFloat initialAngle 1.5708 # 90 grados
SFString axioma ""
MFString producciones []
25

VRML 2.0 con Java CAPÍTULO 14

El numberOfGenerations, initialAngle, axioma y se como se ha descrito anteriormente. Las


producciones [] se forma cada una de las c = algo.

Por ejemplo, para crear una ciudad amurallada que podríamos hacer algo como
esto:

# VRML V2.0 utf8

EXTERNPROTO LSystem
[
SFInt32 numberOfGenerations campo
campo SFFloat initialAngle
campo SFString axioma
campo MFString producciones
SFBool campo ccw
SFBool materia sólida
campo SFFloat creaseAngle
SFBool normalPerVertex campo
SFBool materia de depuración
] "Lsystem.wrl"

Punto de vista de la posición (0 0 650)

Forma {
apariencia
Aspecto {
material
Material diffuseColor (1 1 0)
}
geometría
LSystem {
numberOfGenerations 2
initialAngle 90
axioma "-XS-XS-XS-XS"
producciones de [
"X = XS XS-XS-XS XS XSXS-X",
"S = (H ^ H ^ H ^ H ^ f)"
]
}
26

VRML 2.0 con Java CAPÍTULO 14

El objeto resultante se muestra en la Figura 14.7. Para implementar el nodo, se


inicia mediante la creación de un archivo VRML que contiene el prototipo, como
se muestra en el Listado de 14.7.

Figura 14.7 Una ciudad amurallada creado en VRML utilizando el nodo LSystem

14.7 La lista LSystem Prototipo

# VRML V2.0 utf8

PROTO LSystem
[
SFInt32 numberOfGenerations campo 3
campo SFFloat initialAngle 0.7854 # 45 grados
campo SFString axioma ""
campo MFString producciones []
SFBool campo ccw TRUE
SFBool materia sólida FALSE
campo SFFloat creaseAngle 0,5
campo SFBool normalPerVertex FALSE
SFBool materia de depuración FALSE
]
{
IFS IndexedFaceSet (DEF
coord coordenadas {}
coordIndex []
ccw ES ccw
TRUE convexo
creaseAngle ES creaseAngle
sólido es sólido
normalPerVertex ES normalPerVertex
}
Guión (DEF SC
url "LSystem.class"
SFInt32 numberOfGenerations campo se numberOfGenerations
campo SFFloat initialAngle ES initialAngle
campo SFString axioma es axioma
27

VRML 2.0 con Java CAPÍTULO 14

campo MFString producciones producciones ES


SFBool materia de depuración de depuración se
eventOut SFNode coord_changed
eventOut MFInt32 coordIndex_changed
}
VÍA SC.coord_changed A IFS.set_coord
VÍA SC.coordIndex_changed A IFS.set_coordIndex
}

Esto es muy similar al prototipo de TriFractal que estudió anteriormente.

El LSystem.java archivo (que se compila para crear el archivo LSystem.class


que se hace referencia en el nodo de secuencias de comandos) es simple y
hace uso del Estado, del emisor, y Polígono clases que ha descrito
anteriormente, así como el código de inicialización. La fuente completo se
muestra en el Listado 14.8.

14.8 La lista de archivos Lsystem.java

/ / Sistema de Lindenmayer nodo

/ / Escrito por Bernie Roehl, noviembre de 1996

lsystem paquete;

importación vrml .*;


importación vrml.field .*;
importación vrml.node .*;
importación java.util .*;
importación java.io. *;

clase pública se extiende LSystem Guión {

public void inicializar () {


int ngenerations =
((SFInt32) getField ( "numberOfGenerations")). GetValue ();
flotador ángulo = ((SFFloat) getField ( "initialAngle")). getValue ();
Axioma cadena = ((SFString) getField ( "axioma")). GetValue ();
MFString productionListField =
(MFString) getField ( "producciones");
String [] = productionList
new String [productionListField.getSize ()];
productionListField.getValue (productionList);
String [] producciones = new String [128];
28

VRML 2.0 con Java CAPÍTULO 14

/ / (Una para cada código ASCII)


boolean debug = ((SFBool) getField ( "debug")). getValue ();

for (int i = 0; i <productionList.length; i)


producciones [0xFF & (int) productionList [i]. charAt (0)] =
productionList [i]. subcadena (2);

for (int i = 0; i <ngenerations; i)


= proceso axioma (axioma, producciones);

if (debug)
System.out.println ( "Final axioma =" axioma);

State_stack pila = new Pila ();


Estado current_state = new Estado ();
Emisor emisor = new emisor (false);

for (int i = 0; i <axiom.length (), i) {


char c = axiom.charAt (i);
switch (c) {
case '[':
state_stack.push (current_state);
current_state = new Estado ();
break;
case ']':
current_state = (Estado) state_stack.pop ();
break;
por defecto:
current_state.cmd (c, emisor);
break;
}
}
try{
SFNode coord_changed = (SFNode) getEventOut ( "coord_changed");
coord_changed.setValue (getBrowser (). createVrmlFromString {
"El punto de coordenadas ([\ n"
emitter.getVertices ()
"]) \ N") [0]);
}
de capturas (InvalidVRMLSyntaxException ex) ()

MFInt32 coordIndex_changed =
(MFInt32) getEventOut ( "coordIndex_changed");
coordIndex_changed.setValue (emitter.getFaces ());
29

VRML 2.0 con Java CAPÍTULO 14

if (debug) {
System.out.println ( "Vértices: \ n" emitter.getVertices ());
int [] p = emitter.getFaces ();
for (int i = 0; i <p.length; i)
System.out.print {
"" P [i] ((p [i] == -1)? "\ N": ""));
}

Se mantiene un 128-elemento gama de producciones, uno para cada caracter


ASCII. La mayoría de las producciones será nula, pero la matriz no tiene mucho
espacio y no necesita ser buscado con el fin de proceso de cada personaje en
el axioma (ya que podemos simplemente índice en la matriz usando el valor
ASCII de el carácter ).

El nodo y la matriz de coordenadas de los puntos se generan gran parte como


lo fueron en TriFractal, excepto en el emisor de la clase es responsable de la
manipulación vértice singularidad y un -1 para añadir después de cada cara.

El proceso () método que tiene el axioma de expansión es muy sencillo:

proceso estático de cadena (String axioma, String [] producciones) {


StringBuffer resultado = new StringBuffer ();
for (int i = 0; i <axiom.length (); + + i) {
char c = axiom.charAt (i);
S = cadena de producción [0xFF & (int) c];
if (s == null)
result.append (c);
algo más
result.append (s);
}
return new String (resultado);
}

La Matriz de la clase

Lsystem el código introducido una nueva clase, llamada matriz, que se utiliza
para acumular y almacenar las rotaciones. Matrix es una utilidad de clase,
30

VRML 2.0 con Java CAPÍTULO 14

similar a la clase Vec3 que se introdujo de nuevo en el capítulo 13. El apoyo de


las matrices de esta clase son de 3 por 3, es decir, que sólo tienen la rotación
de componentes y ninguno de los traslacional queridos. Asimismo, la
ampliación no almacenar la información.

The Matrix clase proporciona métodos para la recuperación de elementos


individuales, así como métodos de clase que el regreso de las matrices de
rotación por un ángulo dado en torno a cada uno de los ejes (X, Y y Z). También
hay una matriz de multiplicación de rutina, lo que nos permite acumular los
resultados de las sucesivas rotaciones; para más detalles, consulta cualquier
libro de texto estándar de gráficos (tales como Computación Gráfica: Principios
y Práctica de Foley, van Dam, Feiner y Hughes, Addison-Wesley , 1990). El
código fuente Java para la Matriz de la clase se da en el Listado de 14.9.

14.9 La lista de la clase matriz

clase pública Matriz {


flotador protegidas [] [] valores;

flotador público getElement (int fila, int columna) {


valores de retorno [fila] [columna];
}

público Matriz () {
valores = new flotado [3] [3];
identidad ();
}

public void identidad () {


for (int i = 0; i <3; + + i)
for (int j = 0; j <3; + + j)
valores [i] [j] = (i == j)? 1: 0;
}

Matriz público multiplicar (Matriz mat){


Matriz resultado = new Matriz ();
for (int i = 0; i <3; + + i)
for (int j = 0; j <3; j + +){
result.values [i] [j] = 0;
for (int k = 0; k <3; k + +)
result.values [i] [j] + =
valores [i] [k] * mat.values [k] [j];
}
volver resultado;
31

VRML 2.0 con Java CAPÍTULO 14

public static Matriz xrot (ángulo de flotación) {


Matriz resultado = new Matriz ();
flotar un ángulo = * (float) Math.PI / 180;
result.values [1] [1] = result.values [2] [2] = (float) Math.cos (a);
result.values [1] [2] = - (result.values [2] [1] =
(float) Math.sin (a));
volver resultado;
}

public static Matriz yrot (ángulo de flotación) {


Matriz resultado = new Matriz ();
flotar un ángulo = * (float) Math.PI / 180;
result.values [0] [0] = result.values [2] [2] = (float) Math.cos (a);
result.values [0] [2] = - (result.values [2] [0] =
(float) Math.sin (a));
volver resultado;
}

public static Matriz zrot (ángulo de flotación) {


Matriz resultado = new Matriz ();
flotar un ángulo = * (float) Math.PI / 180;
result.values [0] [0] = result.values [1] [1] = (float) Math.cos (a);
result.values [1] [0] = - (result.values [0] [1] =
(float) Math.sin (a));
volver resultado;
}
}

Anda mungkin juga menyukai