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; }