let vec = p5.Vector class Leg { constructor() { this.base = new vec(0, 0); this.end = new vec(0, 0); this.interPos = new vec(0, 0); this.ikJoint = new IKJoint(8, 8); this.repositionDist = 50; this.moveSpeed = 0.4; } moveTo(startX, startY, endX, endY) { this.base = new vec(startX, startY); let target = new vec(endX, endY); if (vec.dist(target, this.end) > this.repositionDist) { this.end = new vec(endX, endY); } this.interPos.lerp(this.end, this.moveSpeed); this.ikJoint.moveTo(startX, startY, this.interPos.x, this.interPos.y); } getPoints() { return this.ikJoint.points; } } class IKJoint { constructor(numSegments, pointDist) { this.origin = new vec(0, 0); this.points = []; for (let i = 0; i < numSegments + 1; i++) { this.points.push(new ConstraintPoint(0, 0, pointDist, 1.3)); } this.pointDist = pointDist; this.iterations = 20; } moveStart(targetX, targetY) { let numPts = this.points.length; this.points[0].size = 1; this.points[0].follow(targetX, targetY); for (let i = 1; i < numPts; i++) { let prevPoint = this.points[i - 1]; this.points[i].follow(prevPoint.pos.x, prevPoint.pos.y, prevPoint.forward); } } moveEnd(targetX, targetY) { let numPts = this.points.length; this.points[numPts - 1].pos.x = targetX; this.points[numPts - 1].pos.y = targetY; for (let i = numPts - 2; i >= 0; i--) { let prevPoint = this.points[i + 1]; this.points[i].follow(prevPoint.pos.x, prevPoint.pos.y, null); } } moveTo(origX, origY, targetX, targetY) { for (let i = 0; i < this.iterations; i++) { this.moveEnd(targetX, targetY); this.moveStart(origX, origY); } } } class ConstraintPoint { constructor(x, y, size, angleConstraint = 0.2) { this.size = size; this.pos = new vec(x, y); this.forward = new vec(0, 0); this.angleConstraint = angleConstraint; } getRel(angleOffset, dist) { let dir = vec.rotate(this.forward, angleOffset); dir.setMag(dist); return vec.add(this.pos, dir); } follow(targetX, targetY, parentForward = null) { let target = new vec(targetX, targetY); let targetForward = vec.sub(target, this.pos); let targetAngle = targetForward.heading(); if (parentForward) { let parentAngle = parentForward.heading(); let diff = targetAngle - parentAngle; while (diff < -PI) diff += TWO_PI; while (diff > PI) diff -= TWO_PI; let maxAngle = this.angleConstraint; diff = constrain(diff, -maxAngle, maxAngle); targetAngle = parentAngle + diff; } this.forward = p5.Vector.fromAngle(targetAngle); let dir = p5.Vector.mult(this.forward, -this.size); this.pos = vec.add(target, dir); } } let baseColor; let gillColor; 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)); } baseColor = color(249, 182, 249) gillColor = color(243, 158, 189) } 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(); } let leg1 = new Leg() let leg2 = new Leg() let leg3 = new Leg() let leg4 = new Leg() let legSizes = [15,15,15,15,15,15,15,15,15,15] 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); } let legEndDist = 70; let baseJointL1 = points[2]; let baseL1 = baseJointL1.getRel(-HALF_PI, 30); let endL1 = baseJointL1.getRel(-HALF_PI, legEndDist); let baseR1 = baseJointL1.getRel(HALF_PI, 30); let endR1 = baseJointL1.getRel(HALF_PI, legEndDist); let baseJointL2 = points[6]; let baseL2 = baseJointL2.getRel(-HALF_PI, 30); let endL2 = baseJointL2.getRel(-HALF_PI, legEndDist); let baseR2 = baseJointL2.getRel(HALF_PI, 30); let endR2 = baseJointL2.getRel(HALF_PI, legEndDist); leg1.moveTo(baseL1.x, baseL1.y, endL1.x, endL1.y); leg2.moveTo(baseR1.x, baseR1.y, endR1.x, endR1.y); leg3.moveTo(baseL2.x, baseL2.y, endL2.x, endL2.y); leg4.moveTo(baseR2.x, baseR2.y, endR2.x, endR2.y); fill(baseColor); drawPoints(leg1.getPoints(), legSizes); drawPoints(leg2.getPoints(), legSizes); drawPoints(leg3.getPoints(), legSizes); drawPoints(leg4.getPoints(), legSizes); drawPoints(points, sizes); let eye1 = points[0].getRel(HALF_PI / 2, 45) let eye2 = points[0].getRel(-HALF_PI / 2, 45) fill(0); circle(eye1.x, eye1.y, 15); circle(eye2.x, eye2.y, 15); }