Similar a Generador de árboles recursivos , pero un poco más orgánico. Esta implementación utiliza curvas Bezier para delinear el tronco, las ramas y las ramitas. Cuando las ramitas se dividen demasiado finamente, se agrega un grupo de hojas. La función JSL path(...) se usa para dibujar el tronco Bezier y las hojas, aunque las hojas son solo un octágono. Cuando se rellena un trazado y se cruza sobre sí mismo, realiza un recuento de pares e impares para determinar si se aplica el color de relleno. Con las ramitas esto no es realmente deseable, pero con las hojas tiene un efecto agradable.
Lots of leaf octagons with odd/even overlapMuchos octógonos de hojas con superposición impar/par
Internamente, el algoritmo crea un árbol de forma aleatoria, siguiendo la columna central de cada tronco/rama/ramita y registrando los puntos Bézier para el lado izquierdo en el camino hacia las hojas y los puntos Bézier para el lado derecho en el camino de regreso a las hojas. trompa. El punto que está en el centro de un segmento es un punto dibujado; los dos puntos de control en cada lado son donde el segmento se encuentra con otros segmentos.
Spine, bark, control points for BezierColumna vertebral, corteza, puntos de control para Bezier
Cambiar el tamaño mínimo de la extremidad controla la profundidad de la recursividad; algunos de estos apagaron las hojas para mayor claridad.
Just a trunk. The end cap is rounded with a Bezier too.Solo un baúl. La tapa del extremo también está redondeada con un Bezier.
This branch point chose a three-way split.Este punto de bifurcación eligió una división de tres vías.
Add the octagon leaves. They don't overlap because the size was adjusted.Agregue las hojas octagonales. No se superponen porque se ajustó el tamaño.
One more level.Un nivel más.
The overlapping leaves show the odd/even effect of crossing paths.Las hojas superpuestas muestran el efecto par/impar del cruce de caminos.
Tiny leaves on a big tree.Hojas diminutas en un árbol grande.
Tiny, still more branchingDiminuto, aún más ramificado
Big leaves, lots of branches.Hojas grandes, muchas ramas.
// Recursive routine builds a random tree using Bezier curves in path(...) command
TreePath = Function( {x0, y0, size0, angle},// parameters for recursion
{x0L, x0R, y0L, y0R, x1, y1, size1, x1L, x1R, y1L, y1R, nbranches, leftangle, rightangle, ibranch, nbranches, angles, length, biggest, biggestv,
middle, middlev, cx0, cy0, cx1, cy1, cxc, cyc, xcleaf, ycleaf, radleaf, xleaf, yleaf, diameter, leafangle}, // local variables
// x0,y0 is the center of this trunk segment, starting point. angle is the direction, and size0 the radius.
leftangle = angle + Pi() / 2; // find two points at right angles to the direction of growth
rightangle = angle - Pi() / 2; // by adding +/- 90 degrees ... this is all radians, so PI/2
x0L = x0 + size0 * Cos( leftangle ); // size0 is the radius
y0L = y0 + size0 * Sin( leftangle );
x0R = x0 + size0 * Cos( rightangle );
y0R = y0 + size0 * Sin( rightangle );
length = size0 ^ 1 * Abs( Random Normal( 30, 2 ) );// scaling constants...play with these
size1 = .95 * size0;// .95 is a limb shrinkage constant. Not sure this is enough to notice the taper.
x1 = x0 + length * Cos( angle ); // x1,y1 is the other endpoint of this trunk segment
y1 = y0 + length * Sin( angle );
x1L = x1 + size1 * Cos( leftangle ); // and similar right angle calculation
y1L = y1 + size1 * Sin( leftangle ); // to find the bark at the other end
x1R = x1 + size1 * Cos( rightangle );
y1R = y1 + size1 * Sin( rightangle );
// the center of the trunk segment is ON the drawn curve. the endpoints become
// control points that pull the curve toward the joints, partway, leaving smooth bends.
Insert Into( global:pathTrunk, Eval List( {Eval List( {x0L, y0L, 0 /*control*/} )} ) );
Insert Into( global:pathTrunk, Eval List( {Eval List( {(x0L + x1L) / 2, (y0L + y1L) / 2, 3/*bezier*/} )} ) );
Insert Into( global:pathTrunk, Eval List( {Eval List( {x1L, y1L, 0 /*control*/} )} ) );
If( size0 > .5, // the recursion stops when the branch becomes twigs; leaves are added then <GRIN>
nbranches = Random Integer( 2, 3 ); // experiment with number of branches here
angles = J( nbranches, 1, Abs( Random Normal( 4, 1 ) ) );
// I decided the trees generally looked better if the bigger branch was centered...
biggest = Loc Max( angles );
biggestv = angles[biggest];
middle = Ceiling( N Rows( angles ) / 2 );
middlev = angles[middle];
angles[middle] = biggestv;
angles[biggest] = middlev;
angles = angles / Sum( angles ) * Pi();//normalize, radians
// repurpose leftangle and rightangle, using previous leftangle as a starting point:
// there is a semicircle at the end of this branch, pi() radians (180 deg), going through
// x1L,y1L and x1R,y1R which form a diameter. The first branch leaves from x1L,y1L and
// the rightangle on the semicircle (computed inside the loop.) The angle is half way,
// and the x0y0 is on the chord, halfway. When this routine is recursively called,
// it will recompute the two points on the chord from the angle and size, which is half the chord length.
For( ibranch = 1, ibranch <= nbranches, ibranch += 1,
// working on a branch...figure out its base
rightangle = leftangle - angles[ibranch];
// x1,y1 is the semicircle center. get the chord endpoints at the left and right angles and radius size.
cx0 = x1 + size1 * Cos( leftangle );
cy0 = y1 + size1 * Sin( leftangle );
cx1 = x1 + size1 * Cos( rightangle );
cy1 = y1 + size1 * Sin( rightangle );
// and the midpoint of the chord
cxc = (cx0 + cx1) / 2;
cyc = (cy0 + cy1) / 2;
// and the diameter of the branch (length of chord)
diameter = Sqrt( (cx0 - cx1) ^ 2 + (cy0 - cy1) ^ 2 );
// here it is! do the child branch (which will do its child...)
Recurse( cxc, cyc, diameter / 2, (leftangle + rightangle) / 2 );
// set up for next branch...
leftangle = rightangle;
);
, //else cap this branch and add the leaves
// the cap is a rounded line across the end instead of more branching
Insert Into( global:pathTrunk, Eval List( {Eval List( {x1L, y1L, 0 /*control*/} )} ) );
Insert Into( global:pathTrunk, Eval List( {Eval List( {(x1L + x1R) / 2, (y1L + y1R) / 2, 3/*bezier*/} )} ) );
Insert Into( global:pathTrunk, Eval List( {Eval List( {x1R, y1R, 0 /*control*/} )} ) );
// add leaves to the twigs...circle diameter twig length, centered on twig...
// the single path with all these circles makes an interesting pattern when the circles overlap
// and cancel each other out in an even/odd way. The trunk does that too, and there seems to be no way to prevent it.
xcleaf = (x1L + x1R) / 2; // center a sphere of leaves over the cap segment
ycleaf = (y1L + y1R) / 2;
radleaf = 100 * size1; // constant to play with - radius of leaf ball around cap's size
For( leafangle = 0, leafangle < 2 * Pi(), leafangle += Pi() / 4,
xleaf = xcleaf + radleaf * Cos( leafangle );
yleaf = ycleaf + radleaf * Sin( leafangle );
// move to the new leaf ball, then draw the ball
Insert Into( global:pathLeaves, Eval List( {Eval List( {xleaf, yleaf, If( leafangle == 0, 1/*move*/, 2/*draw*/ )} )} ) );
);
);
// ditto, x0R,y0R to x1R,y1R is the trailing (right side) edge. But reversed.
Insert Into( global:pathTrunk, Eval List( {Eval List( {x1R, y1R, 0 /*control*/} )} ) );
Insert Into( global:pathTrunk, Eval List( {Eval List( {(x0R + x1R) / 2, (y0R + y1R) / 2, 3/*bezier*/} )} ) );
Insert Into( global:pathTrunk, Eval List( {Eval List( {x0R, y0R, 0 /*control*/} )} ) );
);
// these two globals are filled in by the recursive routine
global:pathTrunk = {};
global:pathLeaves = {};
size = 11; // of the trunk, radius really
treepath( 0, 0, size, Pi() / 2 ); // do the recursion
// leading move, control...this adds the left-side ground
Insert Into( global:pathTrunk, {{-size, 0, 0}}, 1 );//push control
Insert Into( global:pathTrunk, {{-size - 5, 0, 1}}, 1 );//push move
// trailing control, draw...this adds the right-side ground
Insert Into( global:pathTrunk, {{size, 0, 0}} );//push control
Insert Into( global:pathTrunk, {{size + 5, 0, 3}} );//push move
// convert the lists into matrices. It was easier to add to the lists during recursion
// but path() needs the matrix
matTrunk = Matrix( global:pathTrunk );
matLeaves = Matrix( global:pathLeaves );
New Window( "Example",
Graph Box(
X Scale( -600, 600 ),
Y Scale( -100, 1100 ),
framesize( 950, 950 ),
<<BACKGROUNDCOLOR></BACKGROUNDCOLOR></GRIN>Es un poco difícil averiguar qué espera la función path() para una curva Bezier, pero tiene sentido cuando se hace. El primer elemento de la lista debe ser un movimiento (1) y el último elemento debe ser un punto Bézier (3).Aparecen dos puntos de control (0) entre cada par de puntos Bezier y entre el movimiento inicial y Bezier. La línea dibujada pasa por los puntos de movimiento (1) y Bézier (3). Los puntos de control (0) determinan la forma de la curva; la línea no los atraviesa, sino que va hacia ellos. El código anterior hace que el par de puntos de control sean ambos el mismo punto (azul, muy arriba). Todavía debe haber dos.
Ver esta respuesta para que el código experimente con el comportamiento.
It is a graph.es un grafo
Bézier es la ortografía correcta. La pronunciación es más difícil para mí. https://en.wikipedia.org/wiki/B%C3%A9zier_curve