let vec = p5.Vector class ConstraintPoint { constructor(x, y, size) { this.size = size; this.pos = new vec(x, y); this.forward = new vec(0, 0); } follow(targetX, targetY, prevForward = null) { let target = new vec(targetX, targetY); let targetForward = vec.sub(target, this.pos); let targetAngle = targetForward.heading(); if (prevForward) { let a = targetAngle - prevForward; while (a > PI) { a -= TWO_PI; } while (a < -PI) { a += TWO_PI; } a = constrain(a, -0.3, 0.3); targetAngle = prevForward + a; } this.forward = vec.fromAngle(targetAngle); let dir = vec.mult(this.forward, -this.size); this.pos = vec.add(target, dir); } getRel(a, dist) { return vec.add(this.pos, vec.mult(vec.fromAngle(this.forward.heading() + a), dist)); } } let points = [] let sizes = [60, 55, 50, 40, 45, 47, 50, 47, 45, 42, 39, 35, 30, 25, 22, 17, 13] function setup() { createCanvas(400, 400); for (let i = 0;i < sizes.length;i++) { points.push(new ConstraintPoint(0, 0, 20)); } } function drawPoints(points, sizes) { beginShape(); for (let a = -HALF_PI;a < HALF_PI;a+=0.1) { let p = points[0].getRel(a, sizes[0]) vertex(p.x, p.y); } for (let i = 0;i < points.length;i++) { let p = points[i].getRel(HALF_PI, sizes[i]) vertex(p.x, p.y); } for (let a = HALF_PI;a < HALF_PI + PI;a+=0.1) { let p = points[points.length -1].getRel(a, sizes[points.length -1]) vertex(p.x, p.y); } for (let i = points.length-1;i >= 0;i--) { let p = points[i].getRel(-HALF_PI, sizes[i]) vertex(p.x, p.y); } endShape(); } function draw() { background(220); points[0].follow(mouseX, mouseY) for (let i = 1;i < sizes.length;i++) { let prev = points[i - 1]; points[i].follow(prev.pos.x, prev.pos.y, prev.forward.heading()); } fill(255) drawPoints(points, sizes); }