let ctx = canvas.getContext('2d');
let pos = PosGrid(20,20,250,250, 20);
let vel = pos.map( () => [0,0]);
let force = pos.map( () => [0,0]);
let mass = pos.map( () => .1 + 100*Math.random());
let fixed = pos.map( () => false);
let [el_node, el_len] = ElementMesh(30);
let el_width = el_len.map(()=> 2*Math.random());
let g = 10;
let spring = 2000;
let max_strain = .2;
let mouse_x, mouse_y;
canvas.onmousemove = (e) => {
mouse_x = e.offsetX;
mouse_y = e.offsetY;
}
(function loop() {
SnipNear(mouse_x, mouse_y);
for (let i = 0; i < 20; i++) {
RemoveOverstrainedElements();
UpdateForce();
UpdateVelocity(.05);
UpdatePosition(.05);
}
DrawElements();
DrawNodes();
requestAnimationFrame(loop);
})();
/* - - - Functions - - - */
function DrawNodes() {
for (let node in pos) {
let x = pos[node][0];
let y = pos[node][1];
let s = Math.sqrt(mass[node]);
if (fixed[node]) {
ctx.fillStyle = '#cc0000';
ctx.fillRect(x-5, y-5, 10, 10);
} else {
ctx.fillStyle = '#001100';
ctx.fillRect(x-s/2,y-s/2,s,s);
}
}
}
function DrawElements(){
ctx.clearRect(0,0,canvas.width, canvas.height);
ctx.strokeStyle = '#000077';
for (let el in el_node) {
let [a,b] = el_node[el];
ctx.beginPath();
ctx.lineWidth = el_width[el];
ctx.moveTo(pos[a][0], pos[a][1]);
ctx.lineTo(pos[b][0], pos[b][1]);
ctx.stroke();
}
}
function ElementMesh(max_len) {
let el_node = [], el_len = [];
for (let a in pos) {
for (let b in pos) {
let dx = pos[b][0] - pos[a][0];
let dy = pos[b][1] - pos[a][1];
let L = Math.sqrt(dx*dx +dy*dy);
if (L > 0 && L < max_len) {
if (!el_node.includes([b,a])) {
el_node.push([a,b]);
el_len.push(L);
}
}
}
}
return [el_node, el_len];
}
function PosGrid(x,y,w,h, step){
let pos = [];
for (let X = x; X < x + w; X += step) {
for (let Y = y; Y < y + h; Y += step) {
pos.push( [X,Y]);
}
}
return pos;
}
function RemoveOverstrainedElements() {
for (let el in el_node) {
let a = el_node[el][0];
let b = el_node[el][1];
let dx = pos[b][0] - pos[a][0];
let dy = pos[b][1] - pos[a][1];
let L = Math.sqrt(dx*dx + dy*dy);
let L0 = el_len[el];
let strain = Math.abs((L - L0)/L0);
if (strain > max_strain) {
el_node.splice(el,1);
el_len.splice(el,1);
el_width.splice(el,1);
}
}
}
function SnipNear(x,y, radius=20) {
for (let el in el_node) {
let a = el_node[el][0];
let b = el_node[el][1];
let dx = x - (pos[b][0] + pos[a][0])/2;
let dy = y - (pos[b][1] + pos[a][1])/2;
let L = Math.sqrt(dx*dx + dy*dy);
if (L < radius) {
el_node.splice(el,1);
el_len.splice(el,1);
el_width.splice(el,1);
}
}
}
function UpdateForce() {
for (let node in force) {
force[node][0] = 0;
force[node][1] = g;
}
for (let el in el_node) {
let a = el_node[el][0];
let b = el_node[el][1];
let dx = pos[b][0] - pos[a][0];
let dy = pos[b][1] - pos[a][1];
let L = Math.sqrt(dx*dx + dy*dy);
let L0 = el_len[el];
let strain = (L - L0)/L0;
let fx = (dx/L)*strain*spring;
let fy = (dy/L)*strain*spring;
force[a][0] += fx;
force[a][1] += fy;
force[b][0] -= fx;
force[b][1] -= fy;
}
}
function UpdatePosition(dt) {
for (let node in pos) {
pos[node][0] += vel[node][0]*dt;
pos[node][1] += vel[node][1]*dt;
if (pos[node][1] > canvas.height) {
pos[node][1] = canvas.height;
vel[node][1] *= -.95;
}
}
}
function UpdateVelocity(dt) {
for (let node in vel) {
if (!fixed[node]){
let fx = force[node][0];
let fy = force[node][1];
let m = mass[node];
vel[node][0] *= .999;
vel[node][1] *= .999;
vel[node][0] += fx/m*dt;
vel[node][1] += fy/m*dt;
}
}
}
Comments