Skip to content

Commit 06532f8

Browse files
authored
Merge pull request #736 from nicolandu/main
fractal_circle-Nicolas_Landucci
2 parents 60b01c6 + 87ee80f commit 06532f8

File tree

4 files changed

+188
-0
lines changed

4 files changed

+188
-0
lines changed
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
/*
2+
@title: Fractal Circle
3+
@author: Nicolas Landucci
4+
@snapshot: 0.png
5+
*/
6+
7+
// Fractal Circle by Nicolas Landucci is GPLv3-licensed.
8+
// NOTE TO SELF: Arrays are passed by reference. To "duplicate" points,
9+
// use Array.slice() .
10+
11+
// mm
12+
const width = 125;
13+
const height = 125;
14+
15+
// interesting effect (toggle)
16+
const showSkeleton = true;
17+
18+
// determines next branch length
19+
function lenMapping(x) {
20+
return x * .75;
21+
}
22+
23+
// length of trunk
24+
const initialLen =14;
25+
26+
// determines branch length threshold
27+
const minLen = 1;
28+
const angle = 20;
29+
30+
// base value
31+
const randomFactor = 0.05;
32+
// how much randomFactor increases at each sample on a branch
33+
const randomFactorProgression = 0.05;
34+
35+
const circleRadius = 55;
36+
const circlePointCount = 3000;
37+
const circleRandomFactor = 2;
38+
const drawCircle = true;
39+
40+
const signaturePointPositionRandomFactor = 0.05;
41+
const signaturePositionRandomFactor = 0.5;
42+
const signatureRotationRandomFactor = 2;
43+
const jitterSignature = true;
44+
const jitterInterval = 0.002;
45+
const jitterRandomFactor = 0.5;
46+
47+
// we only use the Turtle to measure lines, not to draw them, unless showSkeleton is active
48+
// in any case, pen down is OK
49+
const t = new bt.Turtle()
50+
.left(90)
51+
.down();
52+
53+
setDocDimensions(width, height);
54+
55+
// store final lines here
56+
const finalLines = [];
57+
58+
const allLines = [];
59+
60+
function rec(x) {
61+
if (x < minLen) return;
62+
const line = [];
63+
const initialPos = t.pos;
64+
const initialAngle = t.angle;
65+
line.push(t.pos);
66+
for (let i = 0; i < 3; i++) {
67+
t.forward(x / 4);
68+
t.left(90);
69+
const step = bt.randInRange(
70+
-randomFactor * x - randomFactorProgression * i * x,
71+
randomFactor * x + randomFactorProgression * i * x
72+
);
73+
t.forward(step);
74+
line.push(t.pos);
75+
t.forward(-step);
76+
t.right(90);
77+
}
78+
t.forward(x / 4);
79+
line.push(t.pos);
80+
81+
allLines.push(line);
82+
t.left(angle);
83+
rec(lenMapping(x));
84+
t.right(2 * angle);
85+
rec(lenMapping(x));
86+
t.jump(initialPos);
87+
t.setAngle(initialAngle);
88+
}
89+
90+
for (let i = 0; i < 6; i++) {
91+
rec(initialLen);
92+
t.right(60);
93+
}
94+
for (let line in allLines) {
95+
const tmp = [bt.catmullRom(allLines[line])];
96+
bt.join(finalLines, tmp);
97+
}
98+
99+
if (showSkeleton) bt.join(finalLines, t.path);
100+
101+
if (drawCircle) {
102+
const circlePoints = [];
103+
for (let i=0; i<circlePointCount; i++) {
104+
const val = bt.randInRange(-circleRandomFactor,circleRandomFactor);
105+
circlePoints.push([Math.cos(i*2*Math.PI/circlePointCount)*(circleRadius+val),
106+
Math.sin(i*2*Math.PI/circlePointCount)*(circleRadius+val)]);
107+
}
108+
// close the circle
109+
circlePoints.push(circlePoints[0].slice());
110+
bt.join(finalLines, [circlePoints]);
111+
}
112+
113+
bt.translate(
114+
finalLines,
115+
[width / 2, height / 2],
116+
bt.bounds(finalLines).cc
117+
);
118+
119+
const signaturePoints = [
120+
[-2.07, 1.26],
121+
[0, 2],
122+
[1.33, 3.4],
123+
[1.22, 4.65],
124+
[0.18, 5.5],
125+
[-0.93, 4.29],
126+
[-1.64, -1.8],
127+
[-0.68, 0.79],
128+
[0.54, 1.06],
129+
[1.67, -2],
130+
[3.49, -1.29],
131+
[4.84, 2.55],
132+
[3.41, 6.11],
133+
[-1.22, 7.39],
134+
[-4.14, 4.86],
135+
[-4.5, 1.76],
136+
[-3.44, -1.43],
137+
];
138+
139+
for (let i = 0; i < signaturePoints.length; i++) {
140+
signaturePoints[i][0] += bt.randInRange(
141+
-signaturePointPositionRandomFactor,
142+
signaturePointPositionRandomFactor);
143+
signaturePoints[i][1] += bt.randInRange(
144+
-signaturePointPositionRandomFactor,
145+
signaturePointPositionRandomFactor);
146+
}
147+
148+
const signatureBase = bt.catmullRom(signaturePoints);
149+
let signature = [];
150+
151+
if (jitterSignature) {
152+
for (let i = 0; i <= 1; i+=jitterInterval) {
153+
const val = bt.randInRange(-jitterRandomFactor,jitterRandomFactor);
154+
const point = bt.getPoint([signatureBase], i);
155+
const normal = bt.getNormal([signatureBase], i);
156+
signature.push([
157+
point[0]+normal[0]*val,
158+
point[1]+normal[1]*val
159+
]);
160+
}
161+
162+
} else {
163+
signature = signatureBase;
164+
}
165+
166+
signature = [signature];
167+
168+
bt.rotate(signature, bt.randInRange(
169+
-signatureRotationRandomFactor, signatureRotationRandomFactor
170+
), bt.bounds(signature).cc
171+
);
172+
bt.translate(
173+
signature,
174+
[
175+
width -15 + bt.randInRange(
176+
-signaturePositionRandomFactor, signaturePositionRandomFactor),
177+
15 + bt.randInRange(
178+
-signaturePositionRandomFactor, signaturePositionRandomFactor)
179+
],
180+
bt.bounds(signature).cc
181+
);
182+
bt.join(finalLines, signature);
183+
184+
185+
186+
187+
// draw it
188+
drawLines(finalLines);
299 KB
Loading
297 KB
Loading
263 KB
Loading

0 commit comments

Comments
 (0)