package thaw.plugins.indexWebGrapher; import java.util.Vector; import java.util.Iterator; import java.awt.Graphics; import java.awt.Color; import thaw.gui.GUIHelper; public class Node implements Comparable { public final static int RADIUS = 4; private int id; private int indexId; private String indexName; private String indexKey; private Vector linkTo; private Vector linkedFrom; /** * @param indexId -1 if none */ public Node(int id, int indexId, String indexName, String indexKey, GraphPanel graphPanel) { this.id = id; this.indexId = indexId; this.indexName = indexName; this.indexKey = indexKey; linkTo = new Vector(); linkedFrom = new Vector(); graphPanel.registerNode(this); } public int getId() { return id; } public int getIndexId() { return indexId; } public String getIndexKey() { return indexKey; } public String getIndexName() { return indexName; } public void setLinkTo(Node node) { linkTo.add(node); node.setLinkedFrom(this); } private void setLinkedFrom(Node node) { linkedFrom.add(node); } public Vector getLinkList() { return linkTo; } public int getLinkCount() { return linkTo.size() + linkedFrom.size(); } /** * we want the nodes with the higher number of * links first => we consider them smaller */ public int compareTo(final Object o) { final Node node = (Node)o; if (node.getLinkCount() < getLinkCount()) return -1; if (node.getLinkCount() > getLinkCount()) return 1; return 0; } private boolean posSet = false; private double x = -1; private double y = -1; public boolean isPositionSet() { return posSet; } public void setPosition(double x, double y) { posSet = true; this.x = x; this.y = y; } public double getX() { return x; } public double getY() { return y; } private double velocityX = 0.0; private double velocityY = 0.0; public final static double TIMESTEP = 0.001; public final static int NMB_STEPS = 50000; public final static double FACTOR_ATTRACTION = 0.5; public final static double FACTOR_REPULSION = 1; public final static double REPULSE_LIMIT = 10000; public final static double FACTOR_DECELERATION = 1.1; public final static double FACTOR_INITIAL_DISTANCE = 5; public final static double MIN_KINETIC = 0.1; /* will stop if < */ /** * attracted by its peers/neightbours */ private double[] attraction(Node node) { double attrX = 0.0; double attrY = 0.0; attrX = (node.getX() - x)*FACTOR_ATTRACTION; attrY = (node.getY() - y)*FACTOR_ATTRACTION; return new double[] {attrX, attrY}; } /** * repulsed by all the node != peers / neightbours */ private double[] repulsion(Node node) { double repX = 0.0; double repY = 0.0; if (x != node.getX()) { repX = (1/(x-node.getX())*FACTOR_REPULSION); } if (y != node.getY()) { repY = (1/(y-node.getY())*FACTOR_REPULSION); } if (repX > REPULSE_LIMIT) repX = REPULSE_LIMIT; if (repY > REPULSE_LIMIT) repY = REPULSE_LIMIT; if (repX < -REPULSE_LIMIT) repX = -REPULSE_LIMIT; if (repY < -REPULSE_LIMIT) repY = -REPULSE_LIMIT; return new double[] {repX, repY}; } /** * see http://en.wikipedia.org/wiki/Force-based_algorithms * @return velocity */ public double computeVelocity(Vector nodeList) { double netForceX = 0.0; double netForceY = 0.0; /* repulsion */ for (Iterator it = nodeList.iterator(); it.hasNext();) { Node node = (Node)it.next(); if (node == this) continue; double[] repuls = repulsion(node); netForceX += repuls[0]; netForceY += repuls[1]; } /* attraction */ for (Iterator it = linkTo.iterator(); it.hasNext();) { Node node = (Node)it.next(); if (node == this) continue; double[] attr = attraction(node); netForceX += attr[0]; netForceY += attr[1]; } /* attraction */ for (Iterator it = linkedFrom.iterator(); it.hasNext();) { Node node = (Node)it.next(); if (node == this || linkTo.indexOf(node) >= 0) continue; double[] attr = attraction(node); netForceX += attr[0]; netForceY += attr[1]; } velocityX = velocityX/FACTOR_DECELERATION; velocityY = velocityY/FACTOR_DECELERATION; velocityX += netForceX; velocityY += netForceY; return Math.sqrt( Math.pow(velocityX,2) + Math.pow(velocityY, 2)); } /** * @return true if moved */ public boolean applyVelocity() { if (velocityX == 0 && velocityY == 0) return false; x += velocityX * TIMESTEP; y += velocityY * TIMESTEP; return true; } public int getNmbUnplacedNeightbours() { int unplaced = 0; for (Iterator it = linkTo.iterator(); it.hasNext(); ) { Node node = (Node)it.next(); if (!node.isPositionSet()) unplaced++; } return unplaced; } /** * Recursivity : Dirty, but easier :P */ public void setInitialNeightbourPositions() { int unplaced = 0; if ( (unplaced = getNmbUnplacedNeightbours()) == 0) return; double step = (2*Math.PI) / unplaced; double current = 0; for (Iterator it = linkTo.iterator(); it.hasNext();) { Node node = (Node)it.next(); if (!node.isPositionSet()) { int i = unplaced + node.getNmbUnplacedNeightbours(); double diffX = Math.cos(current) * (FACTOR_INITIAL_DISTANCE*(i+1)); double diffY = Math.sin(current) * (FACTOR_INITIAL_DISTANCE*(i+1)); node.setPosition(x + diffX, y + diffY); node.setInitialNeightbourPositions(); current += step; } } return; } public boolean linkTo(Node node) { return (linkTo.indexOf(node) >= 0); } private boolean selected = false; public void setSelected(boolean sel) { this.selected = sel; } public boolean isSelected() { return selected; } public double getXPixel(double zoom, int zeroX) { return (double)((int)(x*zoom) + zeroX); } public double getYPixel(double zoom, int zeroY) { return (double)((int)(y*zoom) + zeroY); } public void paintTaNodeFaceDeNoeud(Graphics g, double zoom, int zeroX, int zeroY) { int realX = (int)(x*zoom); int realY = (int)(y*zoom); if (selected) g.setColor(Color.ORANGE); else g.setColor(Color.GRAY); for (Iterator it = linkTo.iterator(); it.hasNext();) { Node target = (Node)it.next(); int targetX = (int)(target.getX()*zoom); int targetY = (int)(target.getY()*zoom); if (target.isSelected()) { g.setColor(Color.CYAN); } if ( (target.isSelected() || selected) && target.linkTo(this) ) { g.setColor(Color.PINK); } //g.drawLine(realX+zeroX, realY+zeroY, targetX+zeroX, targetY+zeroY); GUIHelper.paintArrow(g, targetX+zeroX, targetY+zeroY, realX+zeroX, realY+zeroY); if (target.isSelected()) g.setColor(Color.GRAY); } if (selected) g.setColor(Color.RED); else if (getLinkCount() == 0) g.setColor(Color.ORANGE); else g.setColor(Color.GREEN); g.fillOval( realX - RADIUS + zeroX, realY - RADIUS + zeroY, 2*RADIUS, 2*RADIUS); if (selected) { g.setColor(Color.BLACK); g.drawString(indexName, realX + zeroX, realY + zeroY - 10); } } public String toString() { return Double.toString(x) + " - " + Double.toString(y) + " ; "+ indexName; } }