quadratic curve maths adapted from: wa.zozuar.org/code.php?c=9os2
LOVE MATRIX by nutsu, a study for drawing curl curve.
Log in to post a comment.
// You can find the Turtle API reference here: https://turtletoy.net/syntax Canvas.setpenopacity(0.85); // Global code will be evaluated once. const turtle = new Turtle(); turtle.penup(); //////////////////////////////////////////////////////////////////////////// turtle.quadraticCurveTo = (cx, cy, x1, y1, steps = 20) => { const s = 1 / steps; const p = turtle.pos(); const x0 = p[0]; const y0 = p[1]; for (let t = 0; t < 1; t += s) { turtle.goto( (1 - t) * (1 - t) * x0 + 2 * (1 - t) * t * cx + t * t * x1, (1 - t) * (1 - t) * y0 + 2 * (1 - t) * t * cy + t * t * y1 ); } turtle.goto(x1, y1); }; //////////////////////////////////////////////////////////////////////////// // quadratic curve maths adapted from: http://wa.zozuar.org/code.php?c=9os2 // LOVE MATRIX by nutsu, a study for drawing curl curve. class Mat2 { constructor() { this.a = 1.0; this.b = 0.0; this.c = 0.0; this.d = 1.0; this.tx = 0.0; this.ty = 0.0; } static create() { return new Mat2(); } multiply(b) { const aa = this.a, ab = this.b, ac = this.c, ad = this.d; this.a = aa * b.a + ac * b.b; this.b = ab * b.a + ad * b.b; this.c = aa * b.c + ac * b.d; this.d = ab * b.c + ad * b.d; this.tx += aa * b.tx + ac * b.ty; this.ty += ab * b.tx + ad * b.ty; return this; } rotate(rad) { const aa = this.a, ab = this.b, ac = this.c, ad = this.d; const s = Math.sin(rad); const c = Math.cos(rad); this.a = aa * c + ac * s; this.b = ab * c + ad * s; this.c = aa * -s + ac * c; this.d = ab * -s + ad * c; return this; } translate(x, y) { this.tx += this.a * x + this.c * y; this.ty += this.b * x + this.d * y; return this; } scale(x, y) { this.a *= x; this.b *= x; this.c *= y; this.d *= y; return this; } } class WormObject { constructor(x, y, vx, vy, len) { this.vmt = Mat2.create().translate(x, y).rotate(Math.atan2(vy, vx)); const angle = (Math.random() * Math.PI / 2) - (Math.PI / 4); this.tmt = Mat2.create().scale(0.92, 0.92).translate(len, 0).rotate(angle); this.c1x = this.p1x = -0.5 * this.vmt.c + this.vmt.tx; this.c1y = this.p1y = -0.5 * this.vmt.d + this.vmt.ty; this.c2x = this.p2x = 0.5 * this.vmt.c + this.vmt.tx; this.c2y = this.p2y = 0.5 * this.vmt.d + this.vmt.ty; this.r = angle; this.w = len * 0.4; } draw() { if (Math.random() > 0.9) { this.tmt.rotate(-this.r * 2); this.r *= -1; } this.vmt.multiply(this.tmt); const cc1x = -this.w * this.vmt.c + this.vmt.tx; const cc1y = -this.w * this.vmt.d + this.vmt.ty; const pp1x = (this.c1x + cc1x) / 2; const pp1y = (this.c1y + cc1y) / 2; const cc2x = this.w * this.vmt.c + this.vmt.tx; const cc2y = this.w * this.vmt.d + this.vmt.ty; const pp2x = (this.c2x + cc2x) / 2; const pp2y = (this.c2y + cc2y) / 2; turtle.goto(this.p1x, this.p1y); turtle.down(); turtle.quadraticCurveTo(this.c1x, this.c1y, pp1x, pp1y); turtle.goto(pp2x, pp2y); turtle.quadraticCurveTo(this.c2x, this.c2y, this.p2x, this.p2y); turtle.up(); this.c1x = cc1x; this.c1y = cc1y; this.p1x = pp1x; this.p1y = pp1y; this.c2x = cc2x; this.c2y = cc2y; this.p2x = pp2x; this.p2y = pp2y; } } // The walk function will be called until it returns false. let px = 0, py = -20; function walk(i) { const t = i / 20; const x = 4 * (16 * Math.pow(Math.sin(t), 3)); const y = -4 * (13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t)); const vx = x - px; const vy = y - py; px = x; py = y; let len = 1.3 * Math.sqrt(vx * vx + vy * vy); if (Math.random() > 0.95) len *= 2; const o = new WormObject(x, y, -vx, -vy, len); for (let j = 0; j < 50; j++) { const x = o.vmt.a * o.vmt.a + o.vmt.b * o.vmt.b; if (x * o.w < 0.02) break; o.draw(); } return i < 120; }