xxxxxxxxxx
255
const GRAVITY = 0.003;
const FRICTION = 0.9;
const MAX_FORCE = 0.001;
const STIFFNESS_POW = 3;
const STIFFNESS = 1;
const DIST_STIFFNESS = 10;
// const CENTER_FORCE = 0.1;
let joints = [];
let connections = [];
let dragging_joint_id;
let SIZE;
function setup() {
createCanvas(windowWidth, windowHeight);
SIZE = min(width, height);
const count = 30;
const r = 0.2;
for (let i=0; i<count; i++) {
const f = i / count;
joints.push({
id: i,
x: 0.5 + ncos(f) * r,
y: 0.5 + nsin(f) * r,
vel_x: 0,
vel_y: 0,
});
}
const count2 = count / 2;
for (let i1=0; i1<count; i1++) {
for (let j=0; j<count2; j++) {
const i2 = (i1 + 1 + j) % count
if (i1 === i2) continue;
connectJoints(joints[i1], joints[i2]);
}
}
}
function connectJoints(joint1, joint2) {
const dist = dist2d(joint1, joint2);
const stiffness = DIST_STIFFNESS * dist + STIFFNESS;
connections.push({
joint1,
joint2,
dist_target: dist,
stiffness,
});
}
function mousePressed() {
dragging_joint_id = joints
.map(j => ({id: j.id, mouse_dist: dist2d(j, mouse_p)}))
// .filter(j => j.mouse_dist < 0.1)
.toSorted((j1, j2) => j1.mouse_dist - j2.mouse_dist)
.at(0)
?.id
}
function keyPressed() {
if (key === "f")
fullscreen(!fullscreen());
}
function mouseReleased() {
dragging_joint_id = null;
}
let OFFSET;
let mouse_p;
function draw() {
const t = frameCount / 60
OFFSET = {
x: (width - SIZE) / 2,
y: (height - SIZE) / 2
}
mouse_p = {
x: (mouseX - OFFSET.x) / SIZE,
y: (mouseY - OFFSET.y) / SIZE,
}
let g_t = t / 20;
gravity_x = ncos(sinn(g_t) * 2);
gravity_y = nsin(cosn(g_t));
update();
drawScene();
}
function update() {
const dragging_joint = joints.find(j => j.id === dragging_joint_id);
if (dragging_joint) {
const vec = minus2d(mouse_p, dragging_joint);
// dragging_joint.vel_x += vec.x / 10;
// dragging_joint.vel_y += vec.y / 10;
dragging_joint.x = mouse_p.x;
dragging_joint.y = mouse_p.y;
dragging_joint.vel_x += vec.x;
dragging_joint.vel_y += vec.y;
}
connections.forEach(connection => {
const {joint1, joint2, dist_target, stiffness } = connection;
const vec = minus2d(joint1, joint2);
const dist = length2d(vec);
const dist_dist = dist - dist_target
const positive = dist_dist > 0;
let force = pow(abs(dist_dist), STIFFNESS_POW) * stiffness
force = min(force, MAX_FORCE);
if (!positive) force *= -1;
const norm = normalize2d(vec);
joint1.vel_x -= norm.x * force;
joint1.vel_y -= norm.y * force;
joint2.vel_x += norm.x * force;
joint2.vel_y += norm.y * force;
});
const middle = {
x: joints.reduce((tot, j) => tot + j.x, 0) / joints.length,
y: joints.reduce((tot, j) => tot + j.y, 0) / joints.length,
}
joints.forEach(joint => {
let { x, y, vel_x, vel_y } = joint;
vel_x += gravity_x * GRAVITY;
vel_y += gravity_y * GRAVITY;
// const center_vec = minus2d(joint, middle);
// const center_norm = normalize2d(center_vec);
// vel_x += center_vec.x * CENTER_FORCE;
// vel_y += center_vec.y * CENTER_FORCE;
vel_x *= FRICTION;
vel_y *= FRICTION;
x += vel_x;
y += vel_y;
const padding = 0.05;
if (x <= padding && vel_x < 0 || x > 1 - padding && vel_x > 0)
vel_x = 0;
if (y <= padding && vel_y < 0 || y > 1 - padding && vel_y > 0)
vel_y = 0;
x = constrain(x, padding, 1 - padding);
y = constrain(y, padding, 1 - padding);
joint.x = x;
joint.y = y;
joint.vel_x = vel_x;
joint.vel_y = vel_y;
});
}
let gravity_x, gravity_y;
function drawScene() {
background(0,0,0,255);
translate((width - SIZE) / 2, (height - SIZE) / 2);
strokeWeight(2);
connections.forEach(connection => {
const { joint1, joint2, stiffness } = connection;
const {x: x1, y: y1} = joint1;
const {x: x2, y: y2} = joint2;
stroke("white");
// line(x1 * SIZE, y1 * SIZE, x2 * SIZE, y2 * SIZE);
});
const count = joints.length;
for (let i=0; i<count; i++) {
const joint1 = joints[i];
const joint2 = joints[(i + 1) % count];
const {x: x1, y: y1} = joint1;
const {x: x2, y: y2} = joint2;
stroke("white");
line(x1 * SIZE, y1 * SIZE, x2 * SIZE, y2 * SIZE);
}
// joints.forEach(joint => {
// const {x, y } = joint;
// fill("red");
// noStroke();
// if (joint.id === dragging_joint_id) {
// // fill("green");
// stroke("white");
// }
// circle(x * SIZE, y * SIZE, 10);
// });
// circle(mouse_p.x * SIZE, mouse_p.y * SIZE, 10);
noFill();
circle(
(0.5 + gravity_x * 0.45) * SIZE,
(0.5 + gravity_y * 0.45) * SIZE,
20
);
}
const origin = {x: 0, y: 0};
function length2d(vec) {
return dist2d(origin, vec);
}
function normalize2d(vec) {
const len = length2d(vec);
if (len === 0) return {x: 1, y: 0};
return {
x: vec.x / len,
y: vec.y / len,
}
}
function dist2d(p1, p2) {
return sqrt(pow(p1.x - p2.x, 2) + pow(p1.y - p2.y, 2));
}
function minus2d(p1, p2) {
return {
x: p1.x - p2.x,
y: p1.y - p2.y,
}
}
function ncos(f) {
return cos(f * TAU);
}
function nsin(f) {
return sin(f * TAU);
}
function cosn(f) {
return ncos(f) / 2 + 0.5
}
function sinn(f) {
return nsin(f) / 2 + 0.5
}