/** * Updates the particle view. This should be called on each draw cycle in order to update the * positions of all nodes and edges in the viewer. If you need to update the positions of * particles without drawing it (e.g. to speed up movement, call updateParticles() instead. */ public void draw() { parent.pushStyle(); parent.pushMatrix(); zoomer.transform(); updateCentroid(); centroid.tick(); parent.translate(width / 2, height / 2); parent.scale(centroid.getZ()); parent.translate(-centroid.getX(), -centroid.getY()); if (!isPaused) { updateParticles(); } // Ensure that any selected element is positioned at the mouse location. if (selectedNode != null) { Particle p = nodes.get(selectedNode); p.makeFixed(); float mX = (zoomer.getMouseCoord().x - (width / 2)) / centroid.getZ() + centroid.getX(); float mY = (zoomer.getMouseCoord().y - (height / 2)) / centroid.getZ() + centroid.getY(); p.position().set(mX, mY, 0); } // Draw edges if we have positive stroke weight. if (parent.g.strokeWeight > 0) { parent.stroke(0, 180); parent.noFill(); for (Map.Entry<E, Spring> row : edges.entrySet()) { E edge = row.getKey(); Spring spring = row.getValue(); Vector3D p1 = spring.getOneEnd().position(); Vector3D p2 = spring.getTheOtherEnd().position(); edge.draw(parent, p1.x(), p1.y(), p2.x(), p2.y()); } } // Draw nodes. parent.noStroke(); parent.fill(120, 50, 50, 180); for (Map.Entry<N, Particle> row : nodes.entrySet()) { N node = row.getKey(); Vector3D p = row.getValue().position(); node.draw(parent, p.x(), p.y()); } parent.popMatrix(); parent.popStyle(); }
/** * Creates a spring between the two given nodes with the given strength. If the two nodes not * directly connected by an edge already have a spring between them, it will be replaced by this * one. * * @param node1 First of the two nodes to have a spring between them. * @param node2 Second of the two nodes to have a spring between them. * @param length The length of this spring (natural rest distance at which the two nodes would * sit). * @param strength The strength of this new spring. * @return True if the viewer contains the two nodes and a spring between them has been created. */ public boolean addSpring(N node1, N node2, float length, float strength) { Particle p1 = nodes.get(node1); if (p1 == null) { return false; } Particle p2 = nodes.get(node2); if (p2 == null) { return false; } // We may have to remove existing spring if it exists between these two nodes. for (int i = 0; i < physics.getNumSprings(); i++) { Spring spring = physics.getSpring(i); if ((((spring.getOneEnd() == p1) && (spring.getTheOtherEnd() == p2)) || ((spring.getOneEnd() == p2) && (spring.getTheOtherEnd() == p1))) && (spring.strength() != EDGE_STRENGTH)) { physics.removeSpring(spring); break; } } // Add the new force. physics.makeSpring(p1, p2, strength, DAMPING, length); return false; }
/** * Attempts to space out non-connected nodes from one another. This is achieved by adding a strong * repulsive force between non-connected nodes. Note that this produces n-squared forces so can be * slow for large networks where many nodes are not connected to each other. */ public void spaceNodes() { ArrayList<Particle> pList = new ArrayList<Particle>(nodes.values()); for (int i = 0; i < pList.size(); i++) { for (int j = 0; j < pList.size(); j++) { if (i > j) { Particle p1 = pList.get(i); Particle p2 = pList.get(j); // See if we have a connection between nodes for (Spring spring : edges.values()) { if (((spring.getOneEnd() == p1) && (spring.getTheOtherEnd() == p2)) || ((spring.getOneEnd() == p2) && (spring.getTheOtherEnd() == p1))) { // Do nothing as we already have an edge connecting these two particles } else { // Add a small repulsive force physics.makeAttraction(p1, p2, -1000, 0.1f); } } } } } }
/** * Tethers the given node to its location with the given strength. * * @param node The node to be tethered. * @param strength Strength of the tether. * @return True if the viewer contains the given node and it was tethered successfully. */ public boolean tether(N node, float strength) { Particle p1 = nodes.get(node); if (p1 == null) { return false; } // Grab the tethering stake if it has already been created, otherwise create a new one. Particle stake = stakes.get(node); if (stake == null) { stake = physics.makeParticle(1, node.getLocation().x, node.getLocation().y, 0); stake.makeFixed(); stakes.put(node, stake); } // Grab the tether if it has already been created, otherwise create a new one. Spring tether = tethers.get(stake); if (tether == null) { tether = physics.makeSpring(stake, p1, strength, DAMPING, Float.MIN_VALUE); tethers.put(stake, tether); } else { tether.setStrength(strength); } return true; }