Not all combinations of settings work, but limits of settings cannot be dynamic.
Flower power 🪷 (variation)
Flower power 🪷 (variation)
Flower power 🪷 (variation)
Log in to post a comment.
const points = 16; //min=3 max=40 step=1
const circles = 8; //min=1 max=10 step=1
const cutoff = 1; //min=0 max=1 step=1 (circle, fit)
const cutin = 2; //min=0 max=2 step=1 (none, circle, fit)
const cutinSkip = 0;//min=0 max=10 step=1
const swingMode = 2; //min=0 max=2 step=1 (gradient, half gradient, toggle)
const nearHop = .33; //min=.25 max=.5 step=.01
const opacity = .015; //min=0.001 max=.5 step=.001
const inc_1000 = 1; //min=1 max=100 step=1
const leafs = 0;// min=0 max=1 step=1 (Sinoid, Inverse sinoid)
const maxR = 95;
// You can find the Turtle API reference here: https://turtletoy.net/syntax
Canvas.setpenopacity(opacity);
// Global code will be evaluated once.
const turtle = new Turtle();
const getCirclePoint = (radius, pos, location=[0,0], rotation=0) => [location[0] + radius * Math.sin(pos * 2 * Math.PI + rotation), location[1] + radius * -Math.cos(pos * 2 * Math.PI + rotation)];
const distro = (t) => leafs == 0? -Math.cos(t * Math.PI) * .5 + .5: 1 - Math.acos((t-.5)*2) / Math.PI;
const radii = Array.from({length: circles}).map((v, i) => maxR - i * maxR / circles).reverse();
let polygonOfDoom = [];
let polygonOfDoomVectors = [];
let polygonOfDoomVectorsBuffer = [];
let lastCircle = 0;
let cutinBoundaries = [];
// The walk function will be called until it returns false.
function walk(i) {
const circle = i/points | 0;
const rotation = (swingMode == 2? ((circle % 2) / 2) /points: (swingMode == 1?circle / circles / points: 2 * circle / circles / points));
const r = radii[circle];//maxR;
if(circle != lastCircle) {
if(cutin == 2) {
polygonOfDoom = cutinBoundaries.flatMap((v, i, a) => [
v[0],
segment_intersect2(v[0], v[1], a[(i+1)%a.length][0], a[(i+1)%a.length][2])
]).map((pt, i, a) => [pt, a[(i+1)%a.length]]);
cutinBoundaries = [];
} else if(cutin == 1) {
polygonOfDoom = Array.from({length: (radii[circle - 1] * 2 * Math.PI) | 0})
.map((v, i, a) => getCirclePoint(radii[circle - 1], i / a.length))
.map((pt, i, a) => [pt, a[(i+1)%a.length]]);
}
polygonOfDoomVectorsBuffer.push(polygonOfDoom.map(v => [v[0], sub2(v[1], v[0])]));
if(lastCircle >= cutinSkip) {
polygonOfDoomVectors = polygonOfDoomVectorsBuffer.shift();
}
lastCircle = circle;
}
const start = (i%points) / points + rotation;
const origin = getCirclePoint(r, start);
const from = Math.max(1, Math.min(Math.ceil(points/2) - 1, Math.round(nearHop*points))) / points;
const extent = 1 - 2 * from;
const originFrom = getCirclePoint(r, start + from);
const originTo = getCirclePoint(r, start + from + extent);
const originFrom_From = getCirclePoint(r, start + from + from);
const originTo_To = getCirclePoint(r, start + from + extent + from + extent);
const intersection = segment_intersect2(originFrom, originFrom_From, originTo, originTo_To);
const limitLines = [[originFrom, intersection], [intersection, originTo]];
cutinBoundaries.push([origin, originFrom, originTo]);
for(let x = 0; x <= 1; x+=inc_1000/1000) {
const projection = getCirclePoint(r, start + from + extent * distro(x));
const to = cutoff == 0? projection: limitLines.map(ll => segment_intersect2(origin, projection, ...ll)).filter(i => i).pop()
const direction = sub2(to, origin);
const fromTo = [origin];
const intersections = polygonOfDoomVectors.map(v => intersect_info2(origin, direction, ...v)).filter(v => v !== false && 0 <= v[0] && v[0] <= 1 && 0 <= v[1] && v[1] <= 1).sort((a, b) => a[0] < b[0]? -1: 1);
intersections.forEach(v => fromTo.push(v[2]));
fromTo.push(to);
fromTo.forEach((v, i) => i%2 == 0? turtle.jump(v): turtle.goto(v));
}
return i < circles * points - 1;
}
function add2(a, b) { return [a[0]+b[0], a[1]+b[1]]; }
function sub2(a, b) { return [a[0]-b[0], a[1]-b[1]]; }
function mul2(a, b) { return [a[0]*b[0], a[1]*b[1]]; }
function scale2(a, s) { return mul2(a, [s,s]); }
function lenSq2(a) { return a[0]**2+a[1]**2; }
function len2(a) { return Math.sqrt(lenSq2(a)); }
function intersect_info2(as, ad, bs, bd) {
const d = [bs[0] - as[0], bs[1] - as[1]];
const det = bd[0] * ad[1] - bd[1] * ad[0];
if(det === 0) return false;
const res = [(d[1] * bd[0] - d[0] * bd[1]) / det, (d[1] * ad[0] - d[0] * ad[1]) / det];
return [...res, add2(as, scale2(ad, res[0]))];
}
function intersect_ray2(a, b, c, d) {
const i = intersect_info2(a, b, c, d);
return i === false? i: i[2];
}
function segment_intersect2(a,b,c,d, inclusive = true) {
const i = intersect_info2(a, sub2(b, a), c, sub2(d, c));
if(i === false) return false;
const t = inclusive? 0<=i[0]&&i[0]<=1&&0<=i[1]&&i[1]<=1: 0<i[0]&&i[0]<1&&0<i[1]&&i[1]<1;
return t?i[2]:false;
}