const 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, 0.5)); } 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); // this.points[0].pos.x = targetX; // this.points[0].pos.y = 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; // this.points[numPts - 1].size = 1; // this.points[numPts - 1].follow(targetX, 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 numPoints = 0; const bodySizes = [60, 55, 50, 40, 45, 47, 50, 47, 45, 42, 39, 35, 30, 25, 22, 17, 13]; const legSizes = Array.from({ length: 20 }, () => 15); const bodyPoints = []; const leg1 = new Leg(); const leg2 = new Leg(); const leg3 = new Leg(); const leg4 = new Leg(); function setup() { createCanvas(800, 800); numPoints = bodySizes.length; for (let i = 0; i < numPoints; i++) { let pt = new ConstraintPoint(400, 400, 20); bodyPoints.push(pt); } } function drawPoints(points, sizes) { beginShape(); let numPoints = points.length; let head = points[0]; let tail = points[numPoints - 1]; for (let angle = -HALF_PI; angle <= HALF_PI; angle += PI / 10) { let p = head.getRel(angle, sizes[0]); vertex(p.x, p.y); } for (let i = 1; i < numPoints - 1; i++) { let p = points[i].getRel(HALF_PI, sizes[i]); vertex(p.x, p.y); } for (let angle = HALF_PI; angle <= HALF_PI + PI; angle += PI / 10) { let p = tail.getRel(angle, sizes[numPoints - 1]); vertex(p.x, p.y); } for (let i = numPoints - 2; i >= 1; i--) { let p = points[i].getRel(-HALF_PI, sizes[i]); vertex(p.x, p.y); } endShape(CLOSE); } function drawGills(basePoint, sign) { push(); let p = basePoint.getRel(sign * 1.5* PI, 35); translate(p.x, p.y); rotate(basePoint.forward.heading() + 0.7 * sign); strokeWeight(4); stroke(0); fill(243, 158, 189); for (let i = -1; i <= 1; i++) { push(); rotate(i * QUARTER_PI); ellipse(-10, 0, 80, 30); pop(); } noStroke(); for (let i = -1; i <= 1; i++) { push(); rotate(i * QUARTER_PI); ellipse(-10, 0, 80, 30); pop(); } fill(249, 182, 249); ellipse(16, 0, 40, 55); pop(); } let mouseInter = new vec(0, 0); function draw() { background(150); mouseInter.lerp(new vec(mouseX, mouseY), 0.1); // fisica bodyPoints[0].follow(mouseInter.x, mouseInter.y); for (let i = 1; i < numPoints; i++) { let prevPoint = bodyPoints[i - 1]; bodyPoints[i].follow(prevPoint.pos.x, prevPoint.pos.y, prevPoint.forward); } let legEndDist = 70; let baseJointL1 = bodyPoints[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 = bodyPoints[7]; 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); // dibujar fill(249, 182, 249); stroke(0); strokeWeight(2); drawPoints(leg1.getPoints(), legSizes); drawPoints(leg2.getPoints(), legSizes); drawPoints(leg3.getPoints(), legSizes); drawPoints(leg4.getPoints(), legSizes); drawPoints(bodyPoints, bodySizes); let p1 = bodyPoints[4].pos; let p2 = bodyPoints[6].pos; let p3 = bodyPoints[8].pos; let p4 = bodyPoints[10].pos; push(); // noStroke(); fill(243, 158, 189); bezier(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y); pop(); // line(p1.x, p1.y, p4.x, p4.y); drawGills(bodyPoints[2], 1); drawGills(bodyPoints[2], -1); let eye1 = bodyPoints[0].getRel(HALF_PI / 2, 45); let eye2 = bodyPoints[0].getRel(-HALF_PI / 2, 45); fill(0); circle(eye1.x, eye1.y, 12); circle(eye2.x, eye2.y, 12); }