xxxxxxxxxx
391
const COUNT = 100;
const MIN_RADIUS = 10;
const MAX_RADIUS = 45;
let delaunay;
let mouseDragging = false;
let pointDragging = null;
let dt, pdt;
let colors = [];
let points = [];
let grid = [];
let gridSize = MAX_RADIUS*2;
let gridW, gridH;
let trisAroundPoint = [];
let triangles = [];
let constraints = [];
let stop = false;
let forcefield = false;
const MAX_FRAME = 800;
let lastTime = 0;
let fps = 0;
function setup() {
createCanvas(600, 600);
prevDeltaTime = deltaTime;
gridW = ceil(width / gridSize);
gridH = ceil(height / gridSize);
grid = new Array(gridW * gridH);
for (let i = 0; i < grid.length; i++) {
grid[i] = [];
}
// randomSeed(0);
colorMode(HSL);
for (let i = 0; i < 12; i++) {
colors.push(color(i / 12 * 360, random(80, 100), random(55, 80)));
}
colorMode(RGB);
// let totalArea = width * height;
let totalArea = (width+MAX_RADIUS/2) * (height+MAX_RADIUS/2);
// let ratio = 0.83;
let ratio = 0.78;
let area = 0;
while (area < totalArea * ratio) {
// for (let i = 0; i < COUNT; i++) {
let x = random(width);
let y = random(height);
let r = random(MIN_RADIUS, MAX_RADIUS);
area += PI * r*r;
let p = new Point(x, y, r);
points.push(p);
}
let r = sqrt((area - totalArea * ratio) / PI);
area -= points[points.length-1].r*points[points.length-1].r*PI;
points[points.length-1].r = r;
area += r*r*PI;
print("Particle count: " + points.length);
delaunay = Delaunator.from(points, (p) => p.pos.x, (p) => p.pos.y);
trisAroundPoint = getTrianglesAroundPoint(points, delaunay);
triangles = getTriangles(points, delaunay);
getPointsAroundPoint(points, triangles, trisAroundPoint);
forEachTriangleEdge(points, delaunay, (e, p, q) => {
let c = new Constraint(p, q);
constraints.push(c);
});
}
function draw() {
background(220);
if (frameCount > MAX_FRAME) {
if (!forcefield) {
forcefield = true;
}
if (!stop) {
print("Stopped")
stop = true;
}
}
if (millis() - lastTime > 1000) {
lastTime += 1000;
// print("FPS: " + fps)
fps = 0;
}
fps++;
if (frameCount < 2) {
dt = 1/60;
pdt = 1;
} else {
dt = deltaTime / 1000;
}
// if (frameCount < 60) {
// delaunay = Delaunator.from(points, (p) => p.pos.x, (p) => p.pos.y);
// trisAroundPoint = getTrianglesAroundPoint(points, delaunay);
// triangles = getTriangles(points, delaunay);
// getPointsAroundPoint(points, triangles, trisAroundPoint);
// constraints = [];
// forEachTriangleEdge(points, delaunay, (e, p, q) => {
// let c = new Constraint(p, q);
// constraints.push(c);
// });
// }
// for (let tri of triangles) {
// let p0 = points[tri[0]];
// let p1 = points[tri[1]];
// let p2 = points[tri[2]];
// beginShape();
// vertex(p0.pos.x, p0.pos.y);
// vertex(p1.pos.x, p1.pos.y);
// vertex(p2.pos.x, p2.pos.y);
// endShape(CLOSE);
// }
if (pointDragging && mouseDragging) {
pointDragging.ppos.x = pmouseX;
pointDragging.ppos.y = pmouseY;
pointDragging.pos.x = mouseX;
pointDragging.pos.y = mouseY;
} else if (mouseDragging) {
pointDragging = getPointAt(mouseX, mouseY);
} else {
pointDragging = null;
}
for (let i = 0; i < grid.length; i++) {
grid[i] = [];
}
for (let i = 0; i < points.length; i++) {
let p = points[i];
let x = mod(ceil(p.pos.x / gridSize), gridW);
let y = mod(ceil(p.pos.y / gridSize), gridH);
grid[x + y * gridW].push(p);
}
stroke(0);
for (let i = 0; i < points.length; i++) {
points[i].update();
points[i].draw();
}
// forEachTriangleEdge(points, delaunay, (e, p, q) => {
// line(p.pos.x, p.pos.y, q.pos.x, q.pos.y);
// });
if (pointDragging) {
noStroke();
fill(255, 120);
circle(pointDragging.pos.x,
pointDragging.pos.y, pointDragging.r*2);
}
// for (let i = 0; i < constraints.length; i++) {
// for (let j = 0; j < 1; j++)
// constraints[i].update();
// }
pdt = dt;
}
function keyTyped() {
if (keyCode === ENTER) {
print("Positions:");
print(points.map(x => [x.pos.x, x.pos.y].toString()).join(",\n"));
// print(JSON.stringify(points.map(x => [x.pos.x, x.pos.y])));
print("Radii:");
print(points.map(x => x.r).join("\n"));
// print(JSON.stringify(points.map(x => x.r)));
}
}
function mouseDragged() {
if (isMouseOutside())
return;
// if (mouseButton == LEFT)
mouseDragging = true;
}
function mouseReleased() {
mouseDragging = false;
}
function mousePressed() {
if (mouseButton != LEFT)
return;
if (isMouseOutside())
return;
delaunay = Delaunator.from(points, (p) => p.pos.x, (p) => p.pos.y);
trisAroundPoint = getTrianglesAroundPoint(points, delaunay);
triangles = getTriangles(points, delaunay);
getPointsAroundPoint(points, triangles, trisAroundPoint);
let point = getPointAt(mouseX, mouseY);
for (let i = 0; i < points.length; i++) {
points[i].depth = -1;
}
if (point) {
point.depth = 0;
let stack = [point];
while (stack.length > 0) {
let p = stack.pop();
let d = p.depth;
let temp = [];
for (let ni of p.neighbors) {
let n = points[ni];
let collides = p.pos.dist(n.pos) < p.r + n.r + 2;
if (n.depth == -1 && collides) {
n.depth = d + 1;
temp.push(n);
}
}
stack = temp.concat(stack);
}
}
}
function isMouseOutside() {
return mouseX < 0 || mouseX >= width ||
mouseY < 0 || mouseY >= height;
}
function mod(x, y) {
return x - y * floor(x/y);
}
function getPoints(px, py) {
let ix = mod(ceil(px / gridSize), gridW);
let iy = mod(ceil(py / gridSize), gridH);
let result = [];
for (let ox = -1; ox <= 1; ox++) {
for (let oy = -1; oy <= 1; oy++) {
let x = mod(ix + ox, gridW);
let y = mod(iy + oy, gridH);
// if (x < 0 || x >= gridW || y < 0 || y >= gridH)
// continue;
let cell = grid[x + y * gridW];
result = result.concat(cell);
}
}
return result;
}
function getPointAt(x, y) {
let gridPoints = getPoints(x, y);
for (let i = 0; i < gridPoints.length; i++) {
let p = gridPoints[i];
let dx = abs(p.pos.x - x);
let dy = abs(p.pos.y - y);
if (dx > width/2)
dx = width - dx;
if (dy > height/2)
dy = height - dy;
let d = (dx*dx + dy*dy);
if (d < p.r*p.r) {
return p;
}
}
return null;
}
function edgesOfTriangle(t) { return [3 * t, 3 * t + 1, 3 * t + 2]; }
function triangleOfEdge(e) { return Math.floor(e / 3); }
function nextHalfedge(e) { return (e % 3 === 2) ? e - 2 : e + 1; }
function prevHalfedge(e) { return (e % 3 === 0) ? e + 2 : e - 1; }
function forEachTriangleEdge(points, delaunay, callback) {
for (let e = 0; e < delaunay.triangles.length; e++) {
if (e > delaunay.halfedges[e]) {
const p = points[delaunay.triangles[e]];
const q = points[delaunay.triangles[nextHalfedge(e)]];
callback(e, p, q);
}
}
}
function getTriangles(points, delaunay, callback) {
let triangles = [];
for (let t = 0; t < delaunay.triangles.length; t += 3) {
let i0 = delaunay.triangles[t];
let i1 = delaunay.triangles[t + 1];
let i2 = delaunay.triangles[t + 2];
let tri = [i0, i1, i2];
triangles.push(tri);
let p0 = points[i0];
let p1 = points[i1];
let p2 = points[i2];
if (callback) {
callback(tri);
}
}
return triangles;
}
function getTrianglesAroundPoint(points, delaunay) {
let trianglesAroundPoint = [];
for (let i = 0; i < points.length; i++) {
trianglesAroundPoint.push([]);
}
for (let i = 0; i < delaunay.triangles.length; i++) {
let pointId = delaunay.triangles[i];
trianglesAroundPoint[pointId].push(floor(i / 3));
}
return trianglesAroundPoint;
}
function getPointsAroundPoint(points, triangles, trianglesAroundPoint) {
for (let i = 0; i < points.length; i++) {
let p = new Set();
for (let j = 0; j < trianglesAroundPoint[i].length; j++) {
let tri = triangles[trianglesAroundPoint[i][j]];
for (let k = 0; k < 3; k++) {
if (tri[k] == i)
continue;
p.add(tri[k]);
}
}
points[i].neighbors = p;
}
}
function smoothstep(e0, e1, x) {
if (x < e0)
return 0;
if (x >= e1)
return 1;
x = (x - e0) / (e1 - e0);
return x * x * (3 - 2 * x);
}