Node[] nodes = graph.getNodes().toArray();
Edge[] edges = graph.getEdgesAndMetaEdges().toArray();
for (Node n : nodes) {
if (n.getNodeData().getLayoutData() == null || !(n.getNodeData().getLayoutData() instanceof ForceVectorNodeLayoutData)) {
n.getNodeData().setLayoutData(new ForceVectorNodeLayoutData());
}
ForceVectorNodeLayoutData layoutData = n.getNodeData().getLayoutData();
layoutData.dx = 0;
layoutData.dy = 0;
}
float maxDisplace = (float) (Math.sqrt(AREA_MULTIPLICATOR * area) / 10f); // Déplacement limite : on peut le calibrer...
float k = (float) Math.sqrt((AREA_MULTIPLICATOR * area) / (1f + nodes.length)); // La variable k, l'idée principale du layout.
for (Node N1 : nodes) {
for (Node N2 : nodes) { // On fait toutes les paires de noeuds
if (N1 != N2) {
float xDist = N1.getNodeData().x() - N2.getNodeData().x(); // distance en x entre les deux noeuds
float yDist = N1.getNodeData().y() - N2.getNodeData().y();
float dist = (float) Math.sqrt(xDist * xDist + yDist * yDist); // distance tout court
if (dist > 0) {
float repulsiveF = k * k / dist; // Force de répulsion
ForceVectorNodeLayoutData layoutData = N1.getNodeData().getLayoutData();
layoutData.dx += xDist / dist * repulsiveF; // on l'applique...
layoutData.dy += yDist / dist * repulsiveF;
}
}
}
}
for (Edge E : edges) {
// Idem, pour tous les noeuds on applique la force d'attraction
Node Nf = E.getSource();
Node Nt = E.getTarget();
float xDist = Nf.getNodeData().x() - Nt.getNodeData().x();
float yDist = Nf.getNodeData().y() - Nt.getNodeData().y();
float dist = (float) Math.sqrt(xDist * xDist + yDist * yDist);
float attractiveF = dist * dist / k;
if (dist > 0) {
ForceVectorNodeLayoutData sourceLayoutData = Nf.getNodeData().getLayoutData();
ForceVectorNodeLayoutData targetLayoutData = Nt.getNodeData().getLayoutData();
sourceLayoutData.dx -= xDist / dist * attractiveF;
sourceLayoutData.dy -= yDist / dist * attractiveF;
targetLayoutData.dx += xDist / dist * attractiveF;
targetLayoutData.dy += yDist / dist * attractiveF;
}
}
// gravity
for (Node n : nodes) {
NodeData nodeData = n.getNodeData();
ForceVectorNodeLayoutData layoutData = nodeData.getLayoutData();
float d = (float) Math.sqrt(nodeData.x() * nodeData.x() + nodeData.y() * nodeData.y());
float gf = 0.01f * k * (float) gravity * d;
layoutData.dx -= gf * nodeData.x() / d;
layoutData.dy -= gf * nodeData.y() / d;
}
// speed
for (Node n : nodes) {
ForceVectorNodeLayoutData layoutData = n.getNodeData().getLayoutData();
layoutData.dx *= speed / SPEED_DIVISOR;
layoutData.dy *= speed / SPEED_DIVISOR;
}
for (Node n : nodes) {
// Maintenant on applique le déplacement calculé sur les noeuds.
// nb : le déplacement à chaque passe "instantanné" correspond à la force : c'est une sorte d'accélération.
ForceVectorNodeLayoutData layoutData = n.getNodeData().getLayoutData();
float xDist = layoutData.dx;
float yDist = layoutData.dy;
float dist = (float) Math.sqrt(layoutData.dx * layoutData.dx + layoutData.dy * layoutData.dy);
if (dist > 0 && !n.getNodeData().isFixed()) {
float limitedDist = Math.min(maxDisplace * ((float) speed / SPEED_DIVISOR), dist);