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