/**
   * Positions the nodes on the layout according to the results of numerous iterations of the
   * Kamada-Kawai spring-embedding algorithm. Essentially, the network is modeled as a collection of
   * nodes connected by springs with resting lengths proportional to the length of the shortest path
   * distance between each node pair. Nodes are normally positioned in a circle, and then each node
   * in sequence is repositioned until the "energy" of all of its springs are minimized to a
   * parameter value epsilon. The location of the local minima for each node is estimated with
   * iterations of a Newtown-Raphson steepest descent method. Repositioning ceases when all nodes
   * have energy below epsilon. In this implementation, epsilon is initialized at a high value, and
   * than decreased as in simulated annealing. the layout SHOULD stop when a low value (epsilon < 1)
   * is reached or when energies of nodes can now longer be decreased.
   *
   * <p>Note: In the current implementation the layout may not always converge! however, the
   * maxPasses parameter can be set lower to interrupt cycling layouts. Also has not been tested/
   * implemented on weighted graphs. The Kamada-Kawai algorithm was not intended to run on
   * disconnected graphs (graphs with multiple components. The kludgy solution implemented here is
   * to run the algorithm independently on each of the components (of size > 1). This is somewhat
   * unsatisfactory as the components will often overlap.
   *
   * <p>The KK algorithm is relatively slow, especially on the first round. However, it often
   * discovers layouts of regularly structured graphs which are "better" and more repeatable than
   * the Fruchmen-Reingold technique. Implementation of the numerics of the Newton-Raphson method
   * follows Shawn Lorae Stutzman, Auburn University, 12/12/96 <A
   * href="http://mathcs.mta.ca/research/rosebrugh/gdct/javasource.htm">
   * http://mathcs.mta.ca/research/rosebrugh/gdct/javasource.htm</A>
   *
   * <p>Kamada, Tomihisa and Satoru Kawai (1989) "An Algorithm for Drawing Undirected Graphs" <CITE>
   * Information Processing Letters</CITE> 31:7-15
   */
  public void updateLayout() {
    // check that layout should be drawn
    if (update) {
      isEventThread = SwingUtilities.isEventDispatchThread();
      stop = false;
      if (circleLayout) {
        // give nodes circular initial coord to begin with
        circleLayout();
      }

      if (firstLayout) {
        firstLayout = false;
        circleLayout();
      }

      // runs kk algorithm on each component individualy
      ArrayList components = NetUtilities.getComponents(nodeList);
      Iterator compIter = components.iterator();

      while (compIter.hasNext() && !stop) {
        ArrayList comp = (ArrayList) compIter.next();
        if (comp.size() > 1) runKamadaOn(comp);
      }

      // rescale node positions to fit in window
      if (rescaleLayout) rescalePositions(nodeList);
    }
  }
  // RENORM COORDS TO 0-1 range before running ?
  private void runKamadaOn(ArrayList componentNodes) {
    int nNodes = componentNodes.size();
    // sets up the matrix of path distances
    DenseDoubleMatrix2D distMatrix = NetUtilities.getAllShortPathMatrix(componentNodes);
    // sets up kmatrix of forces
    DenseDoubleMatrix2D kMatrix = calcKMatrix(distMatrix, springConst);
    // calc desired distance between nodes
    double optDist = Math.min(width, height) / Math.max(getDiam(distMatrix), 1);
    // sets up lMatrix of distance between nodes pairs
    DenseDoubleMatrix2D lMatrix = calcLMatrix(distMatrix, optDist);
    // arrays for quick acess to node coords
    double[] xPos = new double[nNodes];
    double[] yPos = new double[nNodes];

    int numEdges = 0;

    for (int i = 0; i < nNodes; i++) {
      DrawableNonGridNode workNode = (DrawableNonGridNode) componentNodes.get(i);
      xPos[i] = workNode.getX();
      yPos[i] = workNode.getY();
      numEdges += ((ArrayList) workNode.getOutEdges()).size();
    }

    // calc value to start minimization from (should be based on previous?)
    // epsilon = (nNodes * numEdges)/2;
    // figure out the initial stat to compare to at the end
    double initialEnergy = getEnergy(lMatrix, kMatrix, xPos, yPos);
    double epsilon = initialEnergy / nNodes;
    // figure out which node to start moving first
    double deltaM;
    int maxDeltaMIndex = 0;
    double maxDeltaM = getDeltaM(0, lMatrix, kMatrix, xPos, yPos);
    for (int i = 1; i < nNodes; i++) {
      deltaM = getDeltaM(i, lMatrix, kMatrix, xPos, yPos);
      if (deltaM > maxDeltaM) {
        maxDeltaM = deltaM;
        maxDeltaMIndex = i;
      }
    }

    int passes = 0;
    int subPasses = 0;
    // epsilon minimizing loop
    while ((epsilon > minEpsilon) && !stop) {
      double previousMaxDeltaM = maxDeltaM + 1;
      // KAMADA-KAWAI LOOP: while the deltaM of the node with
      // the largest deltaM  > epsilon..
      while ((maxDeltaM > epsilon) && ((previousMaxDeltaM - maxDeltaM) > 0.1) && !stop) {

        double[] deltas;
        double moveNodeDeltaM = maxDeltaM;
        // double previousDeltaM = moveNodeDeltaM + 1;

        // KK INNER LOOP while the node with the largest energy > epsilon...
        while ((moveNodeDeltaM > epsilon) && !stop) {

          // get the deltas which will move node towards the local minima
          deltas = getDeltas(maxDeltaMIndex, lMatrix, kMatrix, xPos, yPos);
          // set coords of node to old coords + changes
          xPos[maxDeltaMIndex] += deltas[0];
          yPos[maxDeltaMIndex] += deltas[1];
          // previousDeltaM = moveNodeDeltaM;
          // recalculate the deltaM of the node w/ new vals
          moveNodeDeltaM = getDeltaM(maxDeltaMIndex, lMatrix, kMatrix, xPos, yPos);
          subPasses++;
          if (subPasses > maxPasses) stop = true;
        }
        // previousDeltaM = maxDeltaM;
        // recalculate deltaMs and find node with max
        maxDeltaMIndex = 0;
        maxDeltaM = getDeltaM(0, lMatrix, kMatrix, xPos, yPos);
        for (int i = 1; i < nNodes; i++) {
          deltaM = getDeltaM(i, lMatrix, kMatrix, xPos, yPos);
          if (deltaM > maxDeltaM) {
            maxDeltaM = deltaM;
            maxDeltaMIndex = i;
          }
        }

        // if set to update display, update on every nth pass
        if (updates > 0) {
          if ((passes % updates) == 0) {
            for (int i = 0; i < nNodes; i++) {
              DrawableNonGridNode node = (DrawableNonGridNode) componentNodes.get(i);
              node.setX(xPos[i]);
              node.setY(yPos[i]);
            }
            updateDisplay();
          }
        }

        passes++;
      }

      epsilon -= epsilon / 4;
    }

    if (animate) animateTransition(8, componentNodes, xPos, yPos);
    else {
      // set positions of nodes to coord array vals
      for (int i = 0; i < nNodes; i++) {
        DrawableNonGridNode node = (DrawableNonGridNode) componentNodes.get(i);
        node.setX(xPos[i]);
        node.setY(yPos[i]);
      }
    }
  }