A circle of points connected by springs. They are constrained to a slowly growing circle. When they get too far from their neighbors, they split. Each point leaves a line behind as it moves.
Log in to post a comment.
// Forked from "Mushroom Gills 3" by mathrabbit
// https://turtletoy.net/turtle/c8430a132b
// Forked from "Mushroom Gills 1" by trashrabbit
// https://turtletoy.net/turtle/540dbd4001
const RAD_INITIAL = 15;
const RAD_FINAL = 95;
const NUM_SEEDS = 6; // min=3, max=100, step=1
const NUM_STEPS = 46; // min=1, max=100, step=1
const SEED_JITTER = 0.0; // min=0, max=1, step=0.05
const LERP_FRACTION = 0.00; // min=0, max=1.0, step=0.01
const SPLIT_DIST = 21; // min=0, max=40, step=1
let count = 0;
Canvas.setpenopacity(1);
const turtle = new Turtle();5
// turtle.pendown();
let rand = (min, max) =>
Math.random() * (max - min) + min;
let randInt = (min, max) =>
Math.floor(rand(min, max));
let remap = (x, oldmin, oldmax, newmin, newmax) => {
let t = (x - oldmin) / (oldmax - oldmin);
return t * (newmax - newmin) + newmin;
}
let Point = (x, y) => {
let self = {x: x, y: y};
self.len = () =>
Math.sqrt((self.x * self.x) + (self.y * self.y));
self.mul = (f) => {
self.x *= f;
self.y *= f;
}
self.setlen = (newlen) => {
self.mul(newlen/self.len());
}
self.saveold = () => {
self.xold = self.x;
self.yold = self.y;
}
self.lerpto = (p2, t) => {
self.x = (self.x * (1-t)) + p2.x * t;
self.y = (self.y * (1-t)) + p2.y * t;
}
self.clone = () => {
let p2 = Point(self.x, self.y);
p2.xold = self.xold;
p2.yold = self.yold;
return p2;
}
self.jitter = (r) => {
self.x += rand(-r, r);
self.y += rand(-r, r);
}
return self;
};
let randPoint = () =>
Point(rand(-1, 1), rand(-1, 1));
let dist = (p1, p2) => {
let dx = p1.x - p2.x;
let dy = p1.y - p2.y;
return Math.sqrt(dx*dx + dy*dy);
}
let distold = (p1, p2) => {
let dx = p1.xold - p2.xold;
let dy = p1.yold - p2.yold;
return Math.sqrt(dx*dx + dy*dy);
}
let points = [];
// The walk function will be called until it returns false.
function walk(ii) {
if (ii === NUM_STEPS ) { return false; }
let rad0 = remap(ii, 0, NUM_STEPS, RAD_INITIAL, RAD_FINAL);
let rad1 = remap(ii+1, 0, NUM_STEPS, RAD_INITIAL, RAD_FINAL);
// set out initial points
if (ii === 0) {
for (let pp = 0; pp < NUM_SEEDS; pp++) {
let theta = remap(pp + Math.random() * SEED_JITTER, 0, NUM_SEEDS, 0, Math.PI * 2);
let p = Point(Math.sin(theta), Math.cos(theta));
p.setlen(rad0);
points.push(p);
}
}
// save existing positions
for (let p of points) {
p.saveold();
}
// smooth with neighbors and normalize to new radius
for (let pp = 0; pp < points.length ; pp++) {
let p0 = points[(pp + 0) % points.length];
let p1 = points[(pp + 1) % points.length];
let p2 = points[(pp + 2) % points.length];
let midpoint = Point((p0.xold + p2.xold)/2, (p0.yold + p2.yold)/2);
p1.lerpto(midpoint, LERP_FRACTION);
p1.setlen(rad1);
}
// split when dist between neighbors is large enough
let points2 = [...points];
for (let pp = points2.length-1; pp >= 0; pp--) {
let p0 = points2[(pp + 0) % points2.length];
let p1 = points2[(pp + 1) % points2.length];
let p2 = points2[(pp + 2) % points2.length];
if (dist(p0, p2) > SPLIT_DIST) {
let p1b = p1.clone();
p1b.lerpto(p0, rand(0.1, 0.7));
p1b.setlen(rad1);
// p1b.saveold();
points.splice(pp + 1, 0, p1b);
}
}
// draw
for (let p of points ) {
turtle.penup();
if(count > 0)turtle.pendown();
turtle.goto(p.xold, p.yold);
turtle.goto(p.x, p.y);
count ++;
}
return true;
}