/** * 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; }
/** * Adds the given edge to those to be displayed in the viewer. Note that the edge must connect * nodes that have already been added to the viewer. This version will use the locations of the * two nodes to calculate their distance of separation. * * @param edge Edge to add to the display. * @return True if edge was added successfully. False if edge contains nodes that have not been * added to the viewer. */ public boolean addEdge(E edge) { Particle p1 = nodes.get(edge.getNode1()); if (p1 == null) { System.err.println("Warning: Node1 not found when creating edge."); return false; } Particle p2 = nodes.get(edge.getNode2()); if (p2 == null) { System.err.println("Warning: Node2 not found when creating edge."); return false; } // Only add edge if it does not already exist in the collection if (!edges.containsKey(edge)) { float x1 = p1.position().x(); float y1 = p1.position().y(); float x2 = p2.position().x(); float y2 = p2.position().y(); // Strength, damping, reset length edges.put( edge, physics.makeSpring( p1, p2, EDGE_STRENGTH, DAMPING, (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)))); } return true; }
/** * Creates a attractive or repulsive force between the two given nodes. If the two nodes already * have a force between them, it will be replaced by this one. * * @param node1 First of the two nodes to have a force between them. * @param node2 Second of the two nodes to have a force between them. * @param force Force to create between the two nodes. If positive, the nodes will attract each * other, if negative they will repulse. The larger the magnitude the stronger the force. * @param minDistance Minimum distance within which no force is applied. * @return True if the viewer contains the two nodes and a force between them has been created. */ public boolean addForce(N node1, N node2, float force, float minDistance) { 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 force if it exists between these two nodes. for (int i = 0; i < physics.getNumAttractions(); i++) { Attraction a = physics.getAttraction(i); if (((a.getOneEnd() == p1) && (a.getTheOtherEnd() == p2)) || ((a.getOneEnd() == p2) && (a.getTheOtherEnd() == p1))) { physics.removeAttraction(a); break; } } // Add the new force. physics.makeAttraction(p1, p2, force, minDistance); return false; }
/** Centres the particle view on the currently visible nodes. */ private void updateCentroid() { float xMax = Float.NEGATIVE_INFINITY, xMin = Float.POSITIVE_INFINITY, yMin = Float.POSITIVE_INFINITY, yMax = Float.NEGATIVE_INFINITY; for (int i = 0; i < physics.getNumParticles(); ++i) { Particle p = physics.getParticle(i); xMax = Math.max(xMax, p.position().x()); xMin = Math.min(xMin, p.position().x()); yMin = Math.min(yMin, p.position().y()); yMax = Math.max(yMax, p.position().y()); } float xRange = xMax - xMin; float yRange = yMax - yMin; if ((xRange == 0) && (yRange == 0)) { xRange = Math.max(1, xMax); yRange = Math.max(1, yMax); } float zScale = (float) Math.min(height / (yRange * 1.2), width / (xRange * 1.2)); centroid.setTarget(xMin + 0.5f * xRange, yMin + 0.5f * yRange, zScale); }
/** * 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; }
/** * Adds the given edge to those to be displayed in the viewer. Note that the edge must connect * nodes that have already been added to the viewer. This version will fix the distance of * separation between nodes to the given value * * @param edge Edge to add to the display. * @return True if edge was added successfully. False if edge contains nodes that have not been * added to the viewer. */ public boolean addEdge(E edge, float distance) { Particle p1 = nodes.get(edge.getNode1()); if (p1 == null) { System.err.println("Warning: Node1 not found when creating edge."); return false; } Particle p2 = nodes.get(edge.getNode2()); if (p2 == null) { System.err.println("Warning: Node2 not found when creating edge."); return false; } // Only add edge if it does not already exist in the collection if (!edges.containsKey(edge)) { // Strength, damping, reset length edges.put(edge, physics.makeSpring(p1, p2, EDGE_STRENGTH, DAMPING, distance)); } return true; }
/** * 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); } } } } } }
/** * Adds a node to those to be displayed in the viewer. * * @param node Node to add to the viewer. */ public void addNode(N node) { Particle p = physics.makeParticle(1, node.getLocation().x, node.getLocation().y, 0); nodes.put(node, p); }
/** * Sets the drag on all particles in the system. By default drag is set to 0.75 which is enough to * allow particles to move smoothly. * * @param drag Drag effect (larger numbers slow down movement). */ public void setDrag(float drag) { physics.setDrag(drag); }
/** * Updates the positions of nodes and edges in the viewer. This method does not normally need to * be called as update happens every time draw() is called. Calling this method can be useful if * you wish to speed up the movement of nodes and edges by updating their position more than once * every draw cycle. */ public void updateParticles() { physics.tick(0.3f); // Advance time in the physics engine. }