A chaotic "Orbit-Style" Circle
Log in to post a comment.
// You can find the Turtle API reference here: https://turtletoy.net/syntax Canvas.setpenopacity(0.3); const nRevs = 600; // How many revolutions should be plotted const mean_height = 60; const pointsPerRev = 100; // How many points should be plotted per full revolution const heightFreq = 4; // How often the height should be updated per revolution const heightScale = 12; const headingFreq = 1 const headingScale = 0.1 // Global code will be evaluated once. const turtle = new Turtle(); let height = mean_height; let position = { x: 0, y: 0, z: 1 } let heading = { x: 1, y: 0, z: 0 } function crossProduct(v1, v2) { return { x: v1.y * v2.z - v1.z * v2.y, y: v1.z * v2.x - v1.x * v2.z, z: v1.x * v2.y - v1.y * v2.x } } function normalize(vec) { const n = Math.sqrt(vec.x**2 + vec.y**2 + vec.z**2) return { x: vec.x/n, y: vec.y/n, z: vec.z/n } } function rotate(pos, vec, angle) { const n1 = vec.x; const n2 = vec.y; const n3 = vec.z; const cosa = Math.cos(angle); const sina = Math.sin(angle); const Axx = n1**2*(1-cosa) + cosa; const Axy = n1*n2*(1-cosa) - n3*sina; const Axz = n1*n3*(1-cosa) + n2*sina; const Ayx = n2*n1*(1-cosa) + n3*sina; const Ayy = n2**2*(1-cosa) + cosa; const Ayz = n2*n3*(1-cosa) - n1*sina; const Azx = n3*n1*(1-cosa) - n2*sina; const Azy = n3*n2*(1-cosa) + n1*sina; const Azz = n3**2*(1-cosa) + cosa; const px = pos.x; const py = pos.y; const pz = pos.z; pos.x = Axx*px + Axy*py + Axz*pz; pos.y = Ayx*px + Ayy*py + Ayz*pz; pos.z = Azx*px + Azy*py + Azz*pz; return pos; } function gotoPosition(pos, height) { turtle.goto(pos.x*height, pos.z*height); } const heightSlopeAt = []; for (let i = 0; i < nRevs*heightFreq+2; i++) { heightSlopeAt[i] = (Math.random()*2)-1; } const headingSlopeAt = []; for (let i = 0; i < nRevs*headingFreq+2; i++) { headingSlopeAt[i] = (Math.random()*2)-1; } function samplePerlin(x, slopeAt) { const lo = Math.floor(x); const hi = lo+1; const dist = x-lo; loSlope = slopeAt[lo]; hiSlope = slopeAt[hi]; loPos = loSlope * dist; hiPos = -hiSlope * (1-dist); const u = dist * dist * (3.0 - 2.0 * dist); // cubic curve return (loPos*(1-u)) + (hiPos*u); // interpolate } turtle.penup(); gotoPosition(position, height); turtle.pendown(); // The walk function will be called until it returns false. function walk(i) { let rotationVec = crossProduct(position, heading); // Advance point in heading direction position = normalize(rotate(position, rotationVec, 2*Math.PI/pointsPerRev)); heading = normalize(rotate(heading, rotationVec, 2*Math.PI/pointsPerRev)); const headingInc = samplePerlin(i/pointsPerRev*heightFreq, heightSlopeAt)*headingScale heading = rotate(heading, position, headingInc); height = mean_height + (samplePerlin(i/pointsPerRev*heightFreq, heightSlopeAt)*heightScale)**2; gotoPosition(position, height); return i < nRevs*pointsPerRev; }