Data visualization: autoconfigurazione
Con classi sempre più complesse e utilizzandole anche per i collegamenti è possibile fare in modo che gli elementi (o nodi in questo caso) si muovano autonomamente fino a raggiungere una configurazione che, ad esempio, li mantenga a una determinata distanza.
const nodeSize = 10;
const nodeSpeed = 0.1;
const desiredDistance = 100;
const nodeCount = 10;
const linkCount = 30;
let nodes = [];
let links = [];
class Node {
constructor(x, y) {
this.x = x;
this.y = y;
this.vx = 0;
this.vy = 0;
}
updatePosition() {
let connectedNodes = this.getConnectedNodes();
if (connectedNodes.length > 0) {
let velVects = [];
// Vettori di spostamento
for (let i = 0; i < connectedNodes.length; i++) {
let connectedNode = connectedNodes[i];
let connectedVect = createVector(
connectedNode.x - this.x,
connectedNode.y - this.y
);
let distance = connectedVect.mag();
connectedVect.setMag(distance - desiredDistance);
velVects.push(connectedVect);
}
// Media degli spostamenti
let newVel = velVects[0];
for (let i = 1; i < velVects.length; i++) {
let vel = velVects[i];
newVel.x += vel.x;
newVel.y += vel.y;
}
newVel.x /= velVects.length;
newVel.y /= velVects.length;
this.vx = newVel.x * nodeSpeed;
this.vy = newVel.y * nodeSpeed;
}
// Spostamento
this.x += this.vx;
this.y += this.vy;
// Bordi dello schermo
if (this.x < 0 || this.x > width) {
this.vx *= -1;
}
if (this.y < 0 || this.y > height) {
this.vy *= -1;
}
}
display() {
noStroke();
fill(255);
circle(this.x, this.y, nodeSize);
}
getConnectedNodes() {
let connectedNodes = [];
for (let i = 0; i < links.length; i++) {
let link = links[i];
if (link.startNode === this || link.endNode === this) {
let connectedNode =
link.startNode === this ? link.endNode : link.startNode;
connectedNodes.push(connectedNode);
}
}
return connectedNodes;
}
}
class Link {
constructor(startNode, endNode) {
this.startNode = startNode;
this.endNode = endNode;
}
display() {
stroke(128);
line(
this.startNode.x, this.startNode.y,
this.endNode.x, this.endNode.y
);
}
}
function setup() {
createCanvas(windowWidth, windowHeight);
cursor(HAND);
// Creazione dei nodi
for (let i = 0; i < nodeCount; i++) {
let halfW = width / 2;
let halfH = height / 2;
let node = new Node(random(-halfW,halfW), random(-halfH,halfH));
nodes.push(node);
}
// Creazione dei link
for (let i = 0; i < linkCount; i++) {
let startNode = floor(random(nodeCount));
let endNode = floor(random(nodeCount));
let link = new Link(nodes[startNode], nodes[endNode]);
links.push(link);
}
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
}
function draw() {
background(0);
translate(width/2, height/2);
// Aggiornamento delle posizioni dei nodi
for (let i = 0; i < nodes.length; i++) {
let node = nodes[i];
node.updatePosition();
}
// Disegno dei link
for (let i = 0; i < links.length; i++) {
let link = links[i];
link.display();
}
// Disegno dei nodi
for (let i = 0; i < nodes.length; i++) {
let node = nodes[i];
node.display();
}
}
function mouseClicked() {
// Sposta nodi in relazione alla distanza
for (let i = 0; i < nodes.length; i++) {
let node = nodes[i];
let mouseDist = dist(mouseX-width/2, mouseY-height/2, node.x, node.y);
if (mouseDist < 200) {
let delta = 200 / 2; // più vicino -> spostamento maggiore
node.x = mouseX - width/2 + random(-delta, delta);
node.y = mouseY - height/2 + random(-delta, delta);
}
}
}