jelly-example.js

total 0
used 0
limit 0
/* title: Flame categories: soft-body raw files: head stroke ../point_src/point-content.js pointlist point ../point_src/bisector.js ../point_src/functions/clamp.js dragging mouse ../point_src/random.js stage ../point_src/velocity.js */ var canvas; // const SPACING = 6; // const ITERATIONS = 8; // const MOUSE = SPACING * 2; // let GRAVITY = 0.01; // let SPEED = .8; const SPACING = 14; const ITERATIONS = 14; const MOUSE = SPACING * 5; let GRAVITY = 0.05; let SPEED = 1; // const canvas = document.querySelector('canvas'); // const ctx = canvas.getContext('2d'); // canvas.width = 800; // canvas.height = 600; const mouse = { x: 0, y: 0, px: 0, py: 0, points: [], }; class MainStage extends Stage { canvas = 'playspace' mounted() { // this.indicator = new Point({x: 300, y: 300}); // Start the draggable point somewhere // this.dragging.add(this.a, this.b) this.events.wake(); canvas = this.canvas const hue = 100; const squares = Array(2).fill(0).map((_, i) => { const size = 5 + i; return new Other( size, size, 5, hue + i * 20, ); }); this.allPoints = [].concat(...squares.map(({ points }) => points)); this.squares = squares } draw(ctx) { // this.clear(ctx); // update(ctx, this.allPoints, this.squares); } onmousedown(e) { this.onmousemove(e); mouse.down = true; for (const point of this.allPoints) { if (point.pos.distance(mouse) < MOUSE && !mouse.points.includes(point)) { mouse.points.push(point); point.mouseDiff = JellyVector.sub(point.pos, new JellyVector(mouse.x, mouse.y)); point.velocity.mul(0); point.force.mul(0); } } } onmouseup(){ mouse.points = []; mouse.down = false; } onmousemove(e){ e = e.touches ? e.touches[0] : e; // const rect = canvas.getBoundingClientRect(); const rect = this.dimensions mouse.px = mouse.x; mouse.py = mouse.y; mouse.x = e.clientX - rect.left; mouse.y = e.clientY - rect.top; }; } // const clamp = function (val, min, max) { // return Math.min(Math.max(val, min), max); // }; class JellyVector extends Vector { get length () { return Math.sqrt(this.x * this.x + this.y * this.y); } add(v) { const p = v instanceof JellyVector; this.x += p ? v.x : v; this.y += p ? v.y : v; return this; } sub(v) { const p = v instanceof JellyVector; this.x -= p ? v.x : v; this.y -= p ? v.y : v; return this; } mul(v) { const p = v instanceof JellyVector; this.x *= p ? v.x : v; this.y *= p ? v.y : v; return this; } scale(x) { this.x *= x; this.y *= x; return this; } normalize() { const len = this.length; if (len > 0) { this.x /= len; this.y /= len; } return this; } distance(v) { const x = this.x - v.x; const y = this.y - v.y; return Math.sqrt(x * x + y * y); } static add (v1, v2) { const v = v2 instanceof JellyVector; return new JellyVector( v1.x + (v ? v2.x : v2), v1.y + (v ? v2.y : v2) ); } static sub (v1, v2) { const v = v2 instanceof JellyVector; return new JellyVector( v1.x - (v ? v2.x : v2), v1.y - (v ? v2.y : v2) ); } static mul (v1, v2) { const v = v2 instanceof JellyVector; return new JellyVector( v1.x * (v ? v2.x : v2), v1.y * (v ? v2.y : v2) ); } static dot (v1, v2) { return v1.x * v2.x + v1.y * v2.y; } } const reactor = function (a, b, p) { const refA = JellyVector.add(a.toWorld(p), a.pos); const refB = JellyVector.add(b.toWorld(JellyVector.mul(p, -1)), b.pos); const diff = JellyVector.sub(refB, refA); const mid = JellyVector.add(refA, JellyVector.mul(diff, 0.5)); const t = clamp(b.p - a.p, -Math.PI, Math.PI); a.torque += t; b.torque -= t; const mfc = 0.04; const tfc = 0.02; const mf = JellyVector.mul(diff, mfc); const tf = JellyVector.mul(diff, tfc); const dm = JellyVector.sub(b.vat(mid), a.vat(mid)); mf.add(JellyVector.mul(dm, mfc)); tf.add(JellyVector.mul(dm, tfc)); a.addForce(mf, mid); b.addForce(JellyVector.mul(mf, -1), mid); a.addTorque(tf, mid); b.addTorque(JellyVector.mul(tf, -1), mid); }; const allContraints = []; class JellyPoint { constructor(pos, square) { this.pos = pos; this.velocity = new JellyVector(); this.force = new JellyVector(); this.p = 0; this.w = 0; this.torque = 0; this.square = square; } update() { this.velocity.add(JellyVector.mul(this.force, SPEED)); this.force = new JellyVector(0, GRAVITY / ITERATIONS); this.pos.add(JellyVector.mul(this.velocity, SPEED)); const qPI = Math.PI / 4; this.w += this.torque / ((SPACING / 2) ** 2 / 2); this.w = clamp(this.w * SPEED, -qPI, qPI); this.p += this.w; this.torque = 0; mouse.points.includes(this) && this.moveTo(mouse, this.mouseDiff); } toWorld(input) { return new JellyVector( -input.y * Math.sin(this.p) + input.x * Math.cos(this.p), input.y * Math.cos(this.p) + input.x * Math.sin(this.p) ); } vat(R) { const dr = JellyVector.sub(R, this.pos); const vdr = this.w * dr.length; dr.normalize(); return JellyVector.add( this.velocity, new JellyVector(vdr * -dr.y, vdr * dr.x) ); } addForce(F) { this.force.add(F); } addTorque(F, R) { const arm = JellyVector.sub(R, this.pos); const torque = F.y * arm.x - F.x * arm.y; this.torque += torque; } moveTo(v, offset) { const targetX = v.x + offset.x; const targetY = v.y + offset.y; const strength = 0.001; this.velocity.x += (targetX - this.pos.x) * strength * SPEED; this.velocity.y += (targetY - this.pos.y) * strength * SPEED; this.velocity.mul(0.99); } } class Square { constructor (width, height, spacing, hue) { this.width = width; this.height = height; this.spacing = spacing; this.hue = hue; const yOff = 500 const xOff = 600 const w = -0.1 //+ Math.random(); this.points = Array(width * height).fill(0).map((_, i) => { const x = i % width; const y = ~~(i / width); // const x = Math.cos(i) % Math.PI; // const y = Math.sin(i) % Math.PI; const p = new JellyPoint( new JellyVector( xOff + x * spacing, canvas.height - yOff + y * spacing, ), this, ); p.w = w; return p; }); this.points.forEach((point, i) => { // const x = Math.round(Math.sin(i/width) * 10); // const y = Math.round(Math.cos(2+i/width) * 10); const x = (i % width); const y = ~~(i / width); // const y = Math.sin(i/width) * 10; if (x > 0) { allContraints.push([ this.points[i - 1], point, new JellyVector(SPACING / 2, 0) ]); } if (y > 0) { allContraints.push([ this.points[i - width], point, new JellyVector(0, SPACING / 2) ]); } }); this.drawPoints = []; for (let i = 0; i < width; i++) { this.drawPoints.push(this.points[i].pos); } for (let i = 0; i < height; i++) { this.drawPoints.push(this.points[(width - 1) + width * i].pos); } for (let i = width - 1; i > -1; i--) { this.drawPoints.push(this.points[(height - 1) * width + i].pos); } for (let i = height - 1; i > -1; i--) { this.drawPoints.push(this.points[(width ) * i].pos); } } draw(ctx) { const { drawPoints, hue } = this; ctx.lineWidth = 2; ctx.fillStyle = `hsla(${hue}, 90%, 80%, 0.8)`; ctx.strokeStyle = `hsla(${hue}, 90%, 70%, 0.8)`; ctx.beginPath(); ctx.moveTo(drawPoints[0].x, drawPoints[0].y); drawPoints.forEach((p, i) =>{ i && ctx.lineTo(p.x, p.y); // ctx.stroke(); ctx.fill(); }); ctx.lineTo(drawPoints[0].x, drawPoints[0].y); ctx.stroke(); ctx.fill(); ctx.fillStyle = `hsla(${hue}, 10%, 50%, 1)`; drawPoints.forEach((p, i) =>{ ctx.beginPath(); ctx.arc(p.x, p.y, 2, 0, Math.PI * 2) // ctx.stroke(); ctx.fill(); }); } } class Other { constructor (width, height, spacing, hue) { this.width = width; this.height = height; this.spacing = spacing; this.hue = hue; const yOff = 500 const xOff = 600 const w = -0.1 //+ Math.random(); this.points = Array(width * height).fill(0).map((_, i) => { const x = i % width; const y = ~~(i / width); // const x = Math.cos(i) % Math.PI; // const y = Math.sin(i) % Math.PI; const p = new JellyPoint( new JellyVector( xOff + x * spacing, canvas.height - yOff + y * spacing, ), this, ); p.w = w; return p; }); this.points.forEach((point, i) => { // const x = Math.round(Math.sin(i/width) * 10); // const y = Math.round(Math.cos(2+i/width) * 10); const x = (i % width); const y = ~~(i / width); // const y = Math.sin(i/width) * 10; if (x > 0) { allContraints.push([ this.points[i - 1], point, new JellyVector(SPACING / 2, 0) ]); } if (y > 0) { allContraints.push([ this.points[i - width], point, new JellyVector(0, SPACING / 2) ]); } }); this.drawPoints = []; for (let i = 0; i < width; i++) { this.drawPoints.push(this.points[i].pos); } for (let i = 0; i < height; i++) { this.drawPoints.push(this.points[(width - 1) + width * i].pos); } for (let i = width - 1; i > -1; i--) { this.drawPoints.push(this.points[(height - 1) * width + i].pos); } for (let i = height - 1; i > -1; i--) { this.drawPoints.push(this.points[(width ) * i].pos); } } draw(ctx) { const { drawPoints, hue } = this; ctx.lineWidth = 2; ctx.fillStyle = `hsla(${hue}, 90%, 80%, 0.8)`; ctx.strokeStyle = `hsla(${hue}, 90%, 70%, 0.8)`; ctx.beginPath(); ctx.moveTo(drawPoints[0].x, drawPoints[0].y); drawPoints.forEach((p, i) =>{ i && ctx.lineTo(p.x, p.y); // ctx.stroke(); ctx.fill(); }); ctx.lineTo(drawPoints[0].x, drawPoints[0].y); ctx.stroke(); ctx.fill(); ctx.fillStyle = `hsla(${hue}, 10%, 50%, 1)`; drawPoints.forEach((p, i) =>{ ctx.beginPath(); ctx.arc(p.x, p.y, 2, 0, Math.PI * 2) // ctx.stroke(); ctx.fill(); }); } } const update = function (ctx, allPoints, squares) { const { width, height } = canvas; ctx.clearRect(0, 0, width, height); let i = ITERATIONS; while (i--) { allContraints.forEach((con, i) => { reactor(...con, i); }); allPoints.forEach((point, i) => { const { square } = point; const damping = 0.6; const spacing = (square ? square.spacing : SPACING) / 2; if (point.pos.x < spacing) { point.force.add(new JellyVector((spacing - point.pos.x) * 1, 0)); point.velocity.y *= damping; } else if (point.pos.x > canvas.width - spacing) { point.force.add(new JellyVector((point.pos.x - canvas.width + spacing) * -1, 0)); point.velocity.y *= damping; } if (point.pos.y < spacing) { point.force.add(new JellyVector(0, (spacing - point.pos.y) * 1)); point.velocity.x *= damping; } else if (point.pos.y > canvas.height - spacing) { point.force.add(new JellyVector(0, (point.pos.y - canvas.height + spacing) * -1)); point.velocity.x *= damping; } point.update(); }); } squares.forEach((s) => { s.draw(ctx); }); if (mouse.down) { ctx.fillStyle = 'rgba(0, 0, 100, 0.03)'; ctx.beginPath(); ctx.arc(mouse.x, mouse.y, MOUSE, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(mouse.x, mouse.y, SPACING, 0, Math.PI * 2); ctx.fill(); } mouse.px = mouse.x; mouse.py = mouse.y; // window.requestAnimationFrame(update); }; // onmousemove = ontouchmove = setMouse; // window.addEventListener('mousedown', onmousedown); // window.addEventListener('mouseup', onmouseup); // window.addEventListener('mousemove', onmousemove); stage = MainStage.go()
Run
Meta Data
title Flame
imports ()
files ('head', 'stroke', '../point_src/point-content.js', 'pointlist', 'point', '../point_src/bisector.js', '../point_src/functions/clamp.js', 'dragging', 'mouse', '../point_src/random.js', 'stage', '../point_src/velocity.js')
unused_keys ()
unknown_keys ('categories',)
categories ['soft-body', 'raw']
filepath_exists True
path jelly-example.js
filepath jelly-example.js
clean_files ('../point_src/core/head.js', '../point_src/setunset.js', '../point_src/stroke.js', '../point_src/compass.js', '../point_src/center.js', '../point_src/point-content.js', '../point_src/pointlistdraw.js', '../point_src/pointlistgradient.js', '../point_src/pointlistshape.js', '../point_src/pointlistgenerator.js', '../point_src/unpack.js', '../point_src/pointlist.js', '../point_src/pointlistpen.js', '../point_src/pointpen.js', '../point_src/pointdraw.js', '../point_src/relative-xy.js', '../point_src/pointcast.js', '../point_src/point.js', '../point_src/bisector.js', '../point_src/functions/clamp.js', '../point_src/distances.js', '../point_src/protractor.js', '../point_src/text/beta.js', '../point_src/dragging.js', '../point_src/events.js', '../point_src/automouse.js', '../point_src/random.js', '../point_src/stage-resize.js', '../point_src/functions/resolve.js', '../point_src/stage.js', '../point_src/velocity.js')
markdown {'html': '', 'content': 'title: Flame\ncategories: soft-body\n raw\nfiles:\n head\n stroke\n ../point_src/point-content.js\n pointlist\n point\n ../point_src/bisector.js\n ../point_src/functions/clamp.js\n dragging\n mouse\n ../point_src/random.js\n stage\n ../point_src/velocity.js'}