비슷하다 재귀 트리 생성기 , 하지만 좀 더 유기적입니다. 이 구현에서는 베지어 곡선을 사용하여 몸통, 팔다리 및 잔가지의 윤곽을 그립니다. 나뭇가지를 너무 잘게 쪼개면 잎 덩어리가 추가됩니다. JSL path(...) 함수는 베지어 줄기와 잎을 그리는 데 사용됩니다. 그러나 잎은 단지 팔각형입니다. 패스가 채워지고 패스 자체를 가로지르면 짝수-홀수 계산을 수행하여 채우기 색상이 적용되는지 확인합니다. 나뭇가지에는 그다지 바람직하지 않지만 잎에는 좋은 효과를 줍니다.
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 overlap홀수/짝수가 겹치는 나뭇잎 팔각형이 많음
내부적으로 알고리즘은 각 줄기/다리/나뭇가지의 중앙 척추를 따라 임의의 모양의 나무를 생성하고 잎으로 가는 길에 왼쪽에 대한 베지어 점을 기록하고 잎으로 돌아가는 길에 오른쪽에 베지어 점을 기록합니다. 트렁크. 세그먼트 중심에 있는 점이 그려진 점입니다. 양쪽에 있는 두 개의 제어점은 세그먼트가 다른 세그먼트와 만나는 지점입니다.
Spine, bark, control points for BezierSpine, bark, control points for BezierSpine, bark, control points for BezierSpine, bark, control points for Bezier척추, 나무껍질, 베지어 제어점
최소 사지 크기를 변경하면 재귀의 깊이가 제어됩니다. 이들 중 일부는 명확성을 위해 나뭇잎을 껐습니다.
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.그냥 트렁크. 엔드 캡도 베지어로 둥글게 처리되어 있습니다.
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.이 분기점은 3방향 분할을 선택했습니다.
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.팔각형 잎을 추가합니다. 크기가 조정되었기 때문에 겹치지 않습니다.
One more level.One more level.One more level.One more level.한 단계 더.
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.겹치는 나뭇잎은 교차 경로의 홀수/짝수 효과를 보여줍니다.
Tiny leaves on a big tree.Tiny leaves on a big tree.Tiny leaves on a big tree.Tiny leaves on a big tree.큰 나무에 작은 잎이 있습니다.
Tiny, still more branchingTiny, still more branchingTiny, still more branchingTiny, still more branching작지만 더 많은 분기
Big leaves, lots of branches.Big leaves, lots of branches.Big leaves, lots of branches.Big leaves, lots of branches.큰 잎, 많은 가지.
// 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>베지어 곡선에 대해 path() 함수가 기대하는 것이 무엇인지 파악하는 것은 약간 어렵지만 완료되면 의미가 있습니다. 목록의 첫 번째 항목은 이동(1)이어야 하고 마지막 항목은 베지어 점(3)이어야 합니다.각 베지어 점 쌍 사이와 초기 이동과 베지어 사이에 두 개의 제어점(0)이 나타납니다. 그려진 선은 이동(1) 및 베지어(3) 점을 통과합니다. 제어점(0)은 곡선의 모양을 결정합니다. 선은 그들을 통과하지 않고 그들을 향해 나아갑니다. 위의 코드는 제어점 쌍을 모두 동일한 지점(파란색, 위쪽)으로 만듭니다. 아직 두 개가 있어야 합니다.
보다 이 답변 코드에서 동작을 실험합니다.
It is a graph.It is a graph.It is a graph.It is a graph.그래프입니다.
Bézier가 올바른 철자법입니다. 나에겐 발음이 더 어렵다. https://en.wikipedia.org/wiki/B%C3%A9zier_curve