Fractales y l-Systems
2
Contenido CAPÍTULO 1
• Fractales
• L-Systems
• La matriz de la clase
Fractales
3
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.
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.
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.
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.
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
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.
if (nivel <0) {
addTriangle (a, b, c);
retorno;
}
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.
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:
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
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);
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.
rand.setSeed (semillas
^ (Int) (-23465 * v.getX ())
^ (Int) (17365 * v.getY ())
^ (Int) (5364 * v.getZ ()));
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
MFInt32 fixFlags []
SFBool ccw TRUE
SFBool sólidos FALSE
SFFloat creaseAngle 0,5
SFBool normalPerVertex FALSE
)
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
]
}
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.
PROTO TriFractal
[
11
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
paquete fractales;
if (semilla! = 0)
rand.setSeed (semilla);
try {
SFNode coord_changed =
(SFNode) getEventOut ( "coord_changed");
coord_changed.setValue (getBrowser ().
createVrmlFromString (coordBuff.toString ()) [0]);
}
de capturas (InvalidVRMLSyntaxException ex) ()
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.
outputVertices.addElement (b);
}
if (c_index == -1) {
c_index = i;
outputVertices.addElement (c);
}
outputTriangles.addElement (nuevo triángulo (a_index, b_index, c_index));
}
L-Systems
afgb - fwtm
a = g-b
g = f-w
w = mmm
Ampliación de la Axiom
algo más
result.append (c);
}
return new String (resultado);
}
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
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.
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
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):
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;
case '(':
polyactive = true;
polyindex = 0;
polydata [polyindex + +] =
emitter.addVertex (x, y, z);
break;
case ')':
emitter.addPolygon (polydata, polyindex, color);
polyactive = false;
break;
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
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 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.
public void addPolygon (int puntos [], int npoints, int color) (
polygons.addElement (nuevo polígono (npoints, puntos, 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.
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.
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.
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
Por ejemplo, para crear una ciudad amurallada que podríamos hacer algo como
esto:
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"
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
Figura 14.7 Una ciudad amurallada creado en VRML utilizando el nodo LSystem
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
lsystem paquete;
if (debug)
System.out.println ( "Final axioma =" axioma);
MFInt32 coordIndex_changed =
(MFInt32) getEventOut ( "coordIndex_changed");
coordIndex_changed.setValue (emitter.getFaces ());
29
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": ""));
}
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
público Matriz () {
valores = new flotado [3] [3];
identidad ();
}