xxxxxxxxxx
274
// force directed algorithm (with gravity)
// algo here https://youtu.be/WWm-g2nLHds?t=488
// customizable for behaviour
const MAX_ITERATIONS = 100;
const EPSILON_THRESHOLD = 0.0005;
var GRAVITY_ENABLED = false;
var GRAVITY_CONSTANT = 9.8; // or smth.
const SPRING_CONSTANT = 0.1;
const SPRING_LENGTH = 4;
const REPULSION_CONSTANT = SPRING_CONSTANT; //yes
const ATTRACTION_CONSTANT = 0.1;
var minx = -1;
var miny = -1;
var midx = 1;
var midy = 1;
var maxx = 1;
var maxy = 1;
// customizable for initial layout
const AMT = 50;
var amt;
// cosmetic parameters
const MARGIN = 25;
const BOXSIZE = 20;
var DEBUGGED = 50;
const vertices = [];
// from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log
function getBaseLog(x, y) {
return Math.log(y) / Math.log(x);
}
function setup() {
frameRate(10);
createCanvas(400, 400);
verticesCanvas = createGraphics(width, height);
verticesCanvas.rectMode(CENTER);
edgeCanvas = createGraphics(width, height);
// loop order shouldnt matter here.
var amt = Math.sqrt(AMT);
for (var x = 0; x < amt; x++) {
for (var y = 0; y < amt; y++) {
vertices.push(new Vertex(x, y));
if (vertices.length > 1) {
vertices[vertices.length - 2].connectTo(vertices[vertices.length - 1]);
}
}
}
vertices[0].connectTo(vertices[vertices.length - 1]);
for (var i = 0; i < 25; i++) {
var a = Math.floor(random() * vertices.length);
do {
var b = Math.floor(random() * vertices.length);
} while (b == a);
vertices[a].connectTo(vertices[b]);
}
}
class Vertex {
constructor(x, y) {
this.pos = createVector(x, y);
this.connected = [];
this.currentForces = [];
}
get x() {
return this.pos.x;
}
get y() {
return this.pos.y;
}
set x(value) {
this.pos.x = value;
}
set y(value) {
this.pos.y = value;
}
connectTo(other) {
this.connected.push(other);
}
draw() {
verticesCanvas.push();
// verticesCanvas.scale(.2, 0.2)
// verticesCanvas.translate(width/2, height/2);
// im just guessing here
// we need to translate to the middle of the canvas, but
// we need to translate the middle of the graph to the middle of hte canvas, or smth, right?
// scaling how? idk yet. divide width of canvas by the width of graph (+- a margin)
// its whatever. idk how to do these. hate it.
//verticesCanvas.translate ((width/2)-MARGIN, (height/2)-MARGIN,)
var x1 = map(this.x, minx, maxx, 100, 300);
var y1 = map(this.y,miny, maxy, 100, 300);
verticesCanvas.rect(x1, y2, BOXSIZE);
verticesCanvas.pop();
edgeCanvas.push();
// edgeCanvas.translate(width/2, height/2);
for (var c of this.connected) {
// edgeCanvas.translate ((width/2)-MARGIN, (height/2)-MARGIN))
var x1 = map(this.x, minx, maxx, 100, 300);
var y1 = map(this.y,miny, maxy, 100, 300);
var x2 = map(c.x, minx, maxx, 100, 300);
var y2 = map(c.y,miny, maxy, 100, 300);
if (DEBUGGED < 5 && DEBUGGED > 1){
print(x1,y1,x2,y2)
print(this.x,this.y,c.x,c.y)
print(minx,maxx, miny,maxy)
}
DEBUGGED-=1
edgeCanvas.line(
x1,y1,x2,y2
);
// print(MARGIN*this.x, MARGIN*this.y, MARGIN*c.x, MARGIN*c.y)
}
edgeCanvas.pop();
}
calcForces(Vertices) {
var mf = 0;
for (const v of Vertices) {
// magnets.
var repulsion_force = REPULSION_CONSTANT / this.pos.dist(v.pos);
var direction = p5.Vector.sub(v.pos, this.pos);
if (!isFinite(v.x)) {v.z = 1; console.count('not finite x ')}
if (!isFinite(v.y)) {v.z = 1; console.count('not finite y ')}
if (!isFinite(v.z)) {v.z = 1; console.count('not finite z?!')}
direction.mult(repulsion_force);
this.currentForces.push(direction);
mf = max(mf, repulsion_force);
}
for (const v of this.connected) {
// based on SPRING_CONSTANT
var attraction_force =
ATTRACTION_CONSTANT * Math.log(this.pos.dist(v.pos) / SPRING_LENGTH);
var direction = p5.Vector.sub(this.pos, v.pos);
direction.mult(-attraction_force);
this.currentForces.push(direction);
mf = max(mf, attraction_force);
}
if (GRAVITY_ENABLED) {
// GRAVITY_CONSTANT....
}
return mf;
}
applyForces(coolingFactor) {
if (this.currentForces != null) {
var totalForce = createVector(0, 0);
for (var f of this.currentForces) {
totalForce.add(f);
}
if (!isFinite(totalForce.z)) totalForce.z=0;
totalForce.mult(coolingFactor);
this.pos.add(f);
// doesnt really seem to be working
// this.pos.x = max(min(this.pos.x, width-MARGIN),MARGIN)
// this.pos.y = max(min(this.pos.y, height-MARGIN),MARGIN)
this.currentForces.splice(0, this.currentForces.length);
return;
}
print("erm, theres no forces.");
}
}
function createCoolingFactor(t) {
return -Math.log(0.7 * t) - 0.3;
}
var K = MAX_ITERATIONS;
var eps = EPSILON_THRESHOLD;
var t = 0;
var maxforce = 10000 * eps;
function draw() {
background(230);
verticesCanvas.clear();
edgeCanvas.clear();
if (t == 0) {
for (v of vertices) {
v.draw();
}
} else if (t < K && maxforce > eps) {
var mf = 0;
for (const v of vertices) {
f = v.calcForces(vertices);
mf = max(mf, f);
}
maxforce = mf;
// resetting them before each draw i think hehh
minx = -1;
miny = -1;
midx = 1;
midy = 1;
maxx = 1;
maxy = 1;
for (const v of vertices) {
v.applyForces(createCoolingFactor(t / MAX_ITERATIONS));
minx = min(v.x, minx);
miny = min(v.y, miny);
maxy = max(v.y, maxy);
maxx = max(v.x, maxx);
}
midx = (maxx + minx) / 2
midy = (maxy + miny) / 2
for (v of vertices) {
v.draw();
}
}
image(verticesCanvas, 0, 0);
image(edgeCanvas, 0, 0);
t++;
text(`iterations ${t}`, 5, 15);
if (t >= K) noLoop();
}
// took 1 hour to make this mess