Ähnlich zu Rekursiver Baumgenerator , aber etwas organischer. Diese Implementierung verwendet Bezier-Kurven, um den Rumpf, die Gliedmaßen und die Zweige zu umreißen. Wenn die Zweige zu fein geteilt sind, wird ein Büschel Blätter hinzugefügt. Die JSL-Funktion path(...) wird zum Zeichnen des Bezier-Stamms und der Blätter verwendet, obwohl die Blätter nur ein Achteck sind. Wenn ein Pfad gefüllt ist und sich selbst kreuzt, wird eine gerade/ungerade Zählung durchgeführt, um festzustellen, ob die Füllfarbe angewendet wird. Bei den Zweigen ist das nicht unbedingt erwünscht, bei den Blättern macht es aber einen schönen Effekt.
Lots of leaf octagons with odd/even overlapLots of leaf octagons with odd/even overlapLots of leaf octagons with odd/even overlapLots of leaf octagons with odd/even overlapViele Blatt-Achtecke mit ungerader/gerader Überlappung
Intern erstellt der Algorithmus einen zufällig geformten Baum, indem er der zentralen Wirbelsäule jedes Stamms/Astes/Zweigs folgt und die Bezier-Punkte für die linke Seite auf dem Weg zu den Blättern und die Bezier-Punkte für die rechte Seite auf dem Weg zurück zu den Blättern aufzeichnet Stamm. Der Punkt, der in der Mitte eines Segments liegt, ist ein gezeichneter Punkt; Die beiden Kontrollpunkte auf jeder Seite sind die Stellen, an denen das Segment auf andere Segmente trifft.
Spine, bark, control points for BezierSpine, bark, control points for BezierSpine, bark, control points for BezierSpine, bark, control points for BezierWirbelsäule, Rinde, Kontrollpunkte für Bezier
Durch Ändern der Mindestgröße der Gliedmaßen wird gesteuert, wie tief die Rekursion geht. Bei einigen davon waren der Übersichtlichkeit halber die Blätter abgeschnitten.
Just a trunk. The end cap is rounded with a Bezier too.Just a trunk. The end cap is rounded with a Bezier too.Just a trunk. The end cap is rounded with a Bezier too.Just a trunk. The end cap is rounded with a Bezier too.Nur ein Koffer. Auch die Endkappe ist mit einem Bezier abgerundet.
This branch point chose a three-way split.This branch point chose a three-way split.This branch point chose a three-way split.This branch point chose a three-way split.Dieser Verzweigungspunkt wählte eine Drei-Wege-Aufteilung.
Add the octagon leaves. They don't overlap because the size was adjusted.Add the octagon leaves. They don't overlap because the size was adjusted.Add the octagon leaves. They don't overlap because the size was adjusted.Add the octagon leaves. They don't overlap because the size was adjusted.Fügen Sie die achteckigen Blätter hinzu. Sie überlappen sich nicht, da die Größe angepasst wurde.
One more level.One more level.One more level.One more level.Noch eine Ebene.
The overlapping leaves show the odd/even effect of crossing paths.The overlapping leaves show the odd/even effect of crossing paths.The overlapping leaves show the odd/even effect of crossing paths.The overlapping leaves show the odd/even effect of crossing paths.Die überlappenden Blätter zeigen den ungeraden/geraden Effekt sich kreuzender Wege.
Tiny leaves on a big tree.Tiny leaves on a big tree.Tiny leaves on a big tree.Tiny leaves on a big tree.Winzige Blätter an einem großen Baum.
Tiny, still more branchingTiny, still more branchingTiny, still more branchingTiny, still more branchingWinzig, noch verzweigter
Big leaves, lots of branches.Big leaves, lots of branches.Big leaves, lots of branches.Big leaves, lots of branches.Große Blätter, viele Zweige.
// 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 ist etwas schwierig herauszufinden, was die Funktion path() für eine Bezier-Kurve erwartet, aber wenn es fertig ist, ergibt es Sinn. Das erste Element in der Liste sollte ein Zug (1) und das letzte Element ein Bezier-Punkt (3) sein.Zwischen jedem Paar von Bezier-Punkten und zwischen der ersten Bewegung und Bezier erscheinen zwei Kontrollpunkte (0). Die gezeichnete Linie verläuft durch die Punkte Move (1) und Bezier (3). Die Kontrollpunkte (0) bestimmen die Form der Kurve; die Linie geht nicht durch sie hindurch, sondern auf sie zu. Der obige Code macht beide Kontrollpunkte zum gleichen Punkt (blau, ganz oben). Es müssen noch zwei sein.
Sehen diese Antwort damit Code mit Verhalten experimentieren kann.
It is a graph.It is a graph.It is a graph.It is a graph.Es ist eine Grafik.
Bézier ist die korrekte Schreibweise. Die Aussprache fällt mir schwerer. https://en.wikipedia.org/wiki/B%C3%A9zier_curve