Package org.graphstream.ui.layout.springbox

Source Code of org.graphstream.ui.layout.springbox.BarnesHutLayout

/*
* Copyright 2006 - 2013
*     Stefan Balev     <stefan.balev@graphstream-project.org>
*     Julien Baudry    <julien.baudry@graphstream-project.org>
*     Antoine Dutot    <antoine.dutot@graphstream-project.org>
*     Yoann Pigné      <yoann.pigne@graphstream-project.org>
*     Guilhelm Savin   <guilhelm.savin@graphstream-project.org>
*
* This file is part of GraphStream <http://graphstream-project.org>.
*
* GraphStream is a library whose purpose is to handle static or dynamic
* graph, create them from scratch, file or any source and display them.
*
* This program is free software distributed under the terms of two licenses, the
* CeCILL-C license that fits European law, and the GNU Lesser General Public
* License. You can  use, modify and/ or redistribute the software under the terms
* of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following
* URL <http://www.cecill.info> or under the terms of the GNU LGPL as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL-C and LGPL licenses and that you accept their terms.
*/
package org.graphstream.ui.layout.springbox;

import org.graphstream.stream.Sink;
import org.graphstream.stream.SourceBase;
import org.graphstream.stream.sync.SinkTime;
import org.graphstream.ui.geom.Point3;
import org.graphstream.ui.graphicGraph.GraphPosLengthUtils;
import org.graphstream.ui.layout.Layout;
import org.miv.pherd.ParticleBox;
import org.miv.pherd.ParticleBoxListener;
import org.miv.pherd.ntree.Anchor;
import org.miv.pherd.ntree.CellSpace;
import org.miv.pherd.ntree.OctreeCellSpace;
import org.miv.pherd.ntree.QuadtreeCellSpace;

import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.util.HashMap;
import java.util.Locale;
import java.util.Random;
import java.util.logging.Logger;

/**
* Base implementation of a Barnes-Hut space decomposition and particle
* interaction algorithm to be used for force-based layout algorithms.
*
* <p>
* This base class creates the space decomposition method and manages the
* main loop of the simulation. The simulation is made of {@link NodeParticle}
* and {@link EdgeSpring} elements that are created and linked for you in
* response to graph events received via the {@link Sink} interface. However
* you have to provide an implementation of the abstract {@link NodeParticle} class
* (by overriding the abstract method {@link #newNodeParticle(String)}).
* </p>
*
* <p>
* As almost all the repulsion/attraction forces computation is done in the
* {@link NodeParticle} class, this is the most important one.
* </p>
*
* <p>
* This algorithm can be configured using several attributes put on the graph :
* <ul>
* <li>layout.force : a floating point number (default 0.5f), that allows to
* define the importance of movement of each node at each computation step. The
* larger the value the quicker nodes move to their position of lowest energy.
* However too high values can generate non stable layouts and oscillations.</li>
* <li>layout.quality : an integer between 0 and 4. With value 0 the layout is
* faster but it also can be farther from equilibrium. With value 4 the
* algorithm tries to be as close as possible from equilibrium (the n-tree and
* Barnes-Hut algorithms are disabled), but the computation can take a lot of
* time (the algorithm becomes O(n^2)). TODO change this into layout.barneshut
* or something similar.</li>
* </ul>
* You can also put the following attributes on nodes :
* <ul>
* <li>layout.weight : The force of repulsion of a node. The larger the value,
* the more the node repulses its neighbors.</li>
* </ul>
* And on edges :
* <ul>
* <li>layout.weight : the multiplier for the desired edge length. By default
* the algorithm tries to make each edge of length one. This is the position of
* lowest energy for a spring. This coefficient allows to modify this target
* spring length. Value larger than one will make the edge longer. Values
* between 0 and 1 will make the edge smaller.</li>
* <li>layout.stabilization-limit : the stabilization of a layout is a number
* between 0 and 1. 1 means fully stable, but this value is rare. Therefore one
* can consider the layout stable at a lower value. The default is 0.9. You can
* fix it with this attribute.</li>
* </ul>
* </p>
*/
public abstract class BarnesHutLayout extends SourceBase implements Layout, ParticleBoxListener {

    /**
     * class level logger
     */
    private static final Logger logger = Logger.getLogger(BarnesHutLayout.class.getName());

  /**
   * The nodes representation and the n-tree. The particle-box is an
   * implementation of a recursive space decomposition method that is used
   * here to break the O(n^2) complexity into a Barnes-Hut algorithm that is
   * closer to O(n log n).
   */
  protected ParticleBox nodes;

  /**
   * The set of edges.
   */
  protected HashMap<String, EdgeSpring> edges = new HashMap<String, EdgeSpring>();

  /**
   * Used to avoid stabilizing if an event occurred.
   */
  protected int lastElementCount = 0;

  /**
   * Random number generator.
   */
  protected Random random;

  /**
   * The lowest node position.
   */
  protected Point3 lo = new Point3(0, 0, 0);

  /**
   * The highest node position.
   */
  protected Point3 hi = new Point3(1, 1, 1);
 
  /**
   * The point in the middle of the layout.
   */
  protected Point3 center = new Point3(0.5, 0.5, 0.5);

  /**
   * Output stream for statistics if in debug mode.
   */
  protected PrintStream statsOut;

  /**
   * Energy, and the history of energies.
   */
  protected Energies energies = new Energies();

  /**
   * Global force strength. This is a factor in [0..1] that is used to scale
   * all computed displacements.
   */
  protected double force = 1f;

  /**
   * The view distance at which the cells of the n-tree are explored
   * exhaustively, after this the poles are used. This is a multiple of k.
   */
  protected double viewZone = 5f;

  /**
   * The Barnes/Hut theta threshold to know if we use a pole or not.
   */
  protected double theta = .7f;

  /**
   * The quality level.
   */
  protected double quality = 1;

  /**
   * Number of nodes per space-cell.
   */
  protected int nodesPerCell = 10;

  /**
   * The diagonal of the graph area at the current step.
   */
  protected double area = 1;

  /**
   * The stabilization limit of this algorithm.
   */
  protected double stabilizationLimit = 0.9;

  // Attributes -- Statistics

  /**
   * Current step.
   */
  protected int time;

  /**
   * The duration of the last step in milliseconds.
   */
  protected long lastStepTime;

  /**
   * The maximum length of a node displacement at the current step.
   */
  protected double maxMoveLength;

  /**
   * Average move length.
   */
  protected double avgLength;

  /**
   * Number of nodes that moved during last step.
   */
  protected int nodeMoveCount;

  // Attributes -- Settings

  /**
   * Compute the third coordinate ?.
   */
  protected boolean is3D = false;

  /**
   * The gravity factor. If set to 0 the gravity computation is disabled.
   */
  protected double gravity = 0;
 
  /**
   * Send node informations?.
   */
  protected boolean sendNodeInfos = false;

  /**
   * If true a file is created to output the statistics of the spring box
   * algorithm.
   */
  protected boolean outputStats = false;

  /**
   * If true a file is created for each node (!!!) and its movement statistics
   * are logged.
   */
  protected boolean outputNodeStats = false;

  /**
   * If greater than one, move events are sent only every N steps.
   */
  protected int sendMoveEventsEvery = 1;

  /**
   * Sink time.
   */
  protected SinkTime sinkTime;

  /**
   * New 2D Barnes-Hut simulation.
   */
  public BarnesHutLayout() {
    this(false);
  }

  /**
   * New Barnes-Hut simulation.
   *
   * @param is3D
   *            If true the simulation dimensions count is 3 else 2.
   */
  public BarnesHutLayout(boolean is3D) {
    this(is3D, new Random(System.currentTimeMillis()));
  }

  /**
   * New Barnes-Hut simulation.
   *
   * @param is3D
   *            If true the simulation dimensions count is 3 else 2.
   * @param randomNumberGenerator
   *            The random number generator to use.
   */
  public BarnesHutLayout(boolean is3D, Random randomNumberGenerator) {
    CellSpace space;

    this.is3D = is3D;
    this.random = randomNumberGenerator;

    if (is3D) {
      space = new OctreeCellSpace(new Anchor(-1, -1, -1), new Anchor(1,
          1, 1));
    } else {
      space = new QuadtreeCellSpace(new Anchor(-1, -1, -0.01f),
          new Anchor(1, 1, 0.01f));
    }

    this.nodes = new ParticleBox(nodesPerCell, space,
        new GraphCellData());

    nodes.addParticleBoxListener(this);
    setQuality(quality);

    sinkTime = new SinkTime();
    sourceTime.setSinkTime(sinkTime);
  }

  public Point3 getLowPoint() {
    org.miv.pherd.geom.Point3 p = nodes.getNTree().getLowestPoint();
    lo.set(p.x, p.y, p.z);
    return lo;
  }

  public Point3 getHiPoint() {
    org.miv.pherd.geom.Point3 p = nodes.getNTree().getHighestPoint();
    hi.set(p.x, p.y, p.z);
    return hi;
  }

  public double randomXInsideBounds() {
    org.miv.pherd.geom.Point3 c = ((GraphCellData)nodes.getNTree().getRootCell().getData()).center;
    return c.x + (random.nextDouble()*2-1);
    //org.miv.pherd.geom.Point3 lo = nodes.getNTree().getLowestPoint();
    //org.miv.pherd.geom.Point3 hi = nodes.getNTree().getHighestPoint();
    //return lo.x + ((hi.x - lo.x)*random.nextDouble());
  }

  public double randomYInsideBounds() {
    org.miv.pherd.geom.Point3 c = ((GraphCellData)nodes.getNTree().getRootCell().getData()).center;
    return c.y + (random.nextDouble()*2-1);
    //org.miv.pherd.geom.Point3 lo = nodes.getNTree().getLowestPoint();
    //org.miv.pherd.geom.Point3 hi = nodes.getNTree().getHighestPoint();
    //return lo.y + ((hi.y - lo.y)*random.nextDouble());
  }

  public double randomZInsideBounds() {
    org.miv.pherd.geom.Point3 c = ((GraphCellData)nodes.getNTree().getRootCell().getData()).center;
    return c.z + (random.nextDouble()*2-1);
    //org.miv.pherd.geom.Point3 lo = nodes.getNTree().getLowestPoint();
    //org.miv.pherd.geom.Point3 hi = nodes.getNTree().getHighestPoint();
    //return lo.z + ((hi.z - lo.z)*random.nextDouble());
  }
 
  public Point3 getCenterPoint() {
    return center;
  }
 
  /**
   * A gravity factor that attracts all nodes to the center of the layout to avoid flying components. If
   * set to zero, the gravity computation is disabled.
   * @return The gravity factor, usually between 0 and 1.
   */
  public double getGravityFactor() {
    return gravity;
  }
 
  /**
   * Set the gravity factor that attracts all nodes to the center of the layout to avoid flying components. If
   * set to zero, the gravity computation is disabled.
   * @param value The new gravity factor, usually between 0 and 1.
   */
  public void setGravityFactor(double value) {
    gravity = value;
  }

  /**
   * The spatial index as a n-tree.
   *
   * @return The n-tree.
   */
  public ParticleBox getSpatialIndex() {
    return nodes;
  }

  public long getLastStepTime() {
    return lastStepTime;
  }

  public abstract String getLayoutAlgorithmName();

  public int getNodeMovedCount() {
    return nodeMoveCount;
  }

  public double getStabilization() {
    if (lastElementCount == nodes.getParticleCount() + edges.size()) {
      if (time > energies.getBufferSize())
        return energies.getStabilization();
    }

    lastElementCount = nodes.getParticleCount() + edges.size();

    return 0;
  }

  public double getStabilizationLimit() {
    return stabilizationLimit;
  }

  public int getSteps() {
    return time;
  }

  public double getQuality() {
    return quality;
  }

  public boolean is3D() {
    return is3D;
  }

  public double getForce() {
    return force;
  }

  public Random getRandom() {
    return random;
  }

  public Energies getEnergies() {
    return energies;
  }

  /**
   * The Barnes-Hut theta value used to know if we use a pole or not.
   *
   * @return The theta value (between 0 and 1).
   */
  public double getBarnesHutTheta() {
    return theta;
  }

  public double getViewZone() {
    return viewZone;
  }

  public void setSendNodeInfos(boolean on) {
    sendNodeInfos = on;
  }

  /**
   * Change the barnes-hut theta parameter allowing to know if we use a pole
   * or not.
   *
   * @param theta
   *            The new value for theta (between 0 and 1).
   */
  public void setBarnesHutTheta(double theta) {
    if (theta > 0 && theta < 1) {
      this.theta = theta;
    }
  }

  public void setForce(double value) {
    this.force = value;
  }

  public void setStabilizationLimit(double value) {
    this.stabilizationLimit = value;
  }

  public void setQuality(double qualityLevel) {
    if (qualityLevel > 1)
      qualityLevel = 1;
    else if (qualityLevel < 0)
      qualityLevel = 0;
    quality = qualityLevel;
  }

  public void clear() {
    energies.clearEnergies();
    nodes.removeAllParticles();
    edges.clear();
    nodeMoveCount = 0;
    lastStepTime = 0;
  }

  public void compute() {
    long t1;

    computeArea();

    maxMoveLength = Double.MIN_VALUE;
    t1 = System.currentTimeMillis();
    nodeMoveCount = 0;
    avgLength = 0;

    // All the movement computation is done in this call.
    nodes.step();

    if (nodeMoveCount > 0)
      avgLength /= nodeMoveCount;

    // Ready for the next step.

    getLowPoint();
    getHiPoint();
    center.set(lo.x+(hi.x-lo.x)/2, lo.y+(hi.y-lo.y)/2, lo.z+(hi.z-lo.z)/2);
    //center.set(0, 0, 0);
    energies.storeEnergy();
    printStats();
    time++;
    lastStepTime = System.currentTimeMillis() - t1;
  }

  /**
   * Output some statistics on the layout process. This method is active only
   * if {@link #outputStats} is true.
   */
  protected void printStats() {
    if (outputStats) {
      if (statsOut == null) {
        try {
          statsOut = new PrintStream("springBox.dat");
          statsOut.printf("# stabilization nodeMoveCount energy energyDiff maxMoveLength avgLength area%n");
          statsOut.flush();
        } catch (FileNotFoundException e) {
          e.printStackTrace();
        }
      }

      if (statsOut != null) {
        double energyDiff = energies.getEnergy()
            - energies.getPreviousEnergyValue(30);

        statsOut.printf(Locale.US, "%f %d %f %f %f %f%n",
            getStabilization(), nodeMoveCount,
            energies.getEnergy(), energyDiff, maxMoveLength,
            avgLength, area);
        statsOut.flush();
      }
    }
  }

  protected void computeArea() {
    area = getHiPoint().distance(getLowPoint());
  }

  public void shake() {
    energies.clearEnergies();
  }

  protected NodeParticle addNode(String sourceId, String id) {
    NodeParticle np = newNodeParticle(id);
    nodes.addParticle(np);
    return np;
  }

  public void moveNode(String id, double x, double y, double z) {
    NodeParticle node = (NodeParticle) nodes.getParticle(id);

    if (node != null) {
      node.moveTo(x, y, z);
      energies.clearEnergies();
    }
  }

  public void freezeNode(String id, boolean on) {
    NodeParticle node = (NodeParticle) nodes.getParticle(id);

    if (node != null) {
      node.frozen = on;
    }
  }

  protected void setNodeWeight(String id, double weight) {
    NodeParticle node = (NodeParticle) nodes.getParticle(id);

    if (node != null)
      node.setWeight(weight);
  }

  protected void removeNode(String sourceId, String id) {
    NodeParticle node = (NodeParticle) nodes.removeParticle(id);

    if (node != null) {
      node.removeNeighborEdges();
    } else {
            logger.warning(String.format("layout %s: cannot remove non existing node %s%n", getLayoutAlgorithmName(), id));
    }
  }

  protected void addEdge(String sourceId, String id, String from, String to,
      boolean directed) {
    NodeParticle n0 = (NodeParticle) nodes.getParticle(from);
    NodeParticle n1 = (NodeParticle) nodes.getParticle(to);

    if (n0 != null && n1 != null) {
      EdgeSpring e = new EdgeSpring(id, n0, n1);
      EdgeSpring o = edges.put(id, e);

      if (o != null) {
        logger.warning(String.format("layout %s: edge '%s' already exists.", getLayoutAlgorithmName(), id));
      } else {
        n0.registerEdge(e);
        n1.registerEdge(e);
      }

      chooseNodePosition(n0, n1);
    } else {
      if (n0 == null)
                logger.warning(String.format("layout %s: node '%s' does not exist, cannot create edge %s.", getLayoutAlgorithmName(), from, id));
      if (n1 == null)
                logger.warning(String.format("layout %s: node '%s' does not exist, cannot create edge %s.", getLayoutAlgorithmName(), to, id));
    }
  }

  /**
   * Choose the best position for a node that was just connected by only one
   * edge to a cluster of nodes.
   *
   * @param n0
   *            source node of the edge.
   * @param n1
   *            target node of the edge.
   */
  protected abstract void chooseNodePosition(NodeParticle n0, NodeParticle n1);

  protected void addEdgeBreakPoint(String edgeId, int points) {
    logger.warning(String.format("layout %s: edge break points are not handled yet.", getLayoutAlgorithmName()));
  }

  protected void ignoreEdge(String edgeId, boolean on) {
    EdgeSpring edge = edges.get(edgeId);

    if (edge != null) {
      edge.ignored = on;
    }
  }

  protected void setEdgeWeight(String id, double weight) {
    EdgeSpring edge = edges.get(id);

    if (edge != null)
      edge.weight = weight;
  }

  protected void removeEdge(String sourceId, String id) {
    EdgeSpring e = edges.remove(id);

    if (e != null) {
      e.node0.unregisterEdge(e);
      e.node1.unregisterEdge(e);
    } else {
            logger.warning(String.format("layout %s: cannot remove non existing edge %s%n", getLayoutAlgorithmName(), id));
    }
  }

  // Particle box listener

  public void particleAdded(Object id, double x, double y, double z,
      Object mark) {
  }

  public void particleAdded(Object id, double x, double y, double z) {
  }

  public void particleMarked(Object id, Object mark) {
  }

  public void particleMoved(Object id, double x, double y, double z) {
    if ((time % sendMoveEventsEvery) == 0) {
      Object xyz[] = new Object[3];
      xyz[0] = x;
      xyz[1] = y;
      xyz[2] = z;

      sendNodeAttributeChanged(sourceId, (String) id, "xyz", xyz, xyz);
    }
  }

  public void particleRemoved(Object id) {
  }

  public void stepFinished(int time) {
  }

  public void particleAttributeChanged(Object id, String attribute,
      Object newValue, boolean removed) {
  }

  // SourceBase interface

  public void edgeAdded(String graphId, long time, String edgeId,
      String fromNodeId, String toNodeId, boolean directed) {
    if (sinkTime.isNewEvent(graphId, time)) {
      addEdge(graphId, edgeId, fromNodeId, toNodeId, directed);
      sendEdgeAdded(graphId, time, edgeId, fromNodeId, toNodeId, directed);
    }
  }

  public void nodeAdded(String graphId, long time, String nodeId) {
    if (sinkTime.isNewEvent(graphId, time)) {
      NodeParticle np = addNode(graphId, nodeId);
      sendNodeAdded(graphId, time, nodeId);
    }
  }

  public void edgeRemoved(String graphId, long time, String edgeId) {
    if (sinkTime.isNewEvent(graphId, time)) {
      removeEdge(graphId, edgeId);
      sendEdgeRemoved(graphId, time, edgeId);
    }
  }

  public void nodeRemoved(String graphId, long time, String nodeId) {
    if (sinkTime.isNewEvent(graphId, time)) {
      removeNode(graphId, nodeId);
      sendNodeRemoved(graphId, time, nodeId);
    }
  }

  public void graphCleared(String graphId, long time) {
    if (sinkTime.isNewEvent(graphId, time)) {
      clear();
      sendGraphCleared(graphId, time);
    }
  }

  public void stepBegins(String graphId, long time, double step) {
    if (sinkTime.isNewEvent(graphId, time)) {
      sendStepBegins(graphId, time, step);
    }
  }

  public void graphAttributeAdded(String graphId, long time,
      String attribute, Object value) {
    if (sinkTime.isNewEvent(graphId, time)) {
      graphAttributeChanged_(graphId, attribute, null, value);
      sendGraphAttributeAdded(graphId, time, attribute, value);
    }
  }

  public void graphAttributeChanged(String graphId, long time,
      String attribute, Object oldValue, Object newValue) {
    if (sinkTime.isNewEvent(graphId, time)) {
      graphAttributeChanged_(graphId, attribute, oldValue, newValue);
      sendGraphAttributeChanged(graphId, time, attribute, oldValue,
          newValue);
    }
  }

  protected void graphAttributeChanged_(String graphId, String attribute,
      Object oldValue, Object newValue) {
    if (attribute.equals("layout.force")) {
      if (newValue instanceof Number)
        setForce(((Number) newValue).doubleValue());
      energies.clearEnergies();
    } else if (attribute.equals("layout.quality")) {
      if (newValue instanceof Number) {
        int q = ((Number) newValue).intValue();

        q = q > 4 ? 4 : q;
        q = q < 0 ? 0 : q;

        setQuality(q);
                logger.fine(String.format("layout.%s.quality: %d.", getLayoutAlgorithmName(), q));
      }

      energies.clearEnergies();
    } else if(attribute.equals("layout.gravity")) {
      if(newValue instanceof Number) {
        double value = ((Number)newValue).doubleValue();
        setGravityFactor(value);
                logger.fine(String.format("layout.%s.gravity: %f.", getLayoutAlgorithmName(), value));
      }
    } else if (attribute.equals("layout.exact-zone")) {
      if (newValue instanceof Number) {
        double factor = ((Number) newValue).doubleValue();

        factor = factor > 1 ? 1 : factor;
        factor = factor < 0 ? 0 : factor;

        viewZone = factor;
                logger.fine(String.format("layout.%s.exact-zone: %f of [0..1]%n", getLayoutAlgorithmName(), viewZone));

        energies.clearEnergies();
      }
    } else if (attribute.equals("layout.output-stats")) {
      if (newValue == null)
        outputStats = false;
      else
        outputStats = true;

            logger.fine(String.format("layout.%s.output-stats: %b%n", getLayoutAlgorithmName(), outputStats));
    } else if (attribute.equals("layout.stabilization-limit")) {
      if (newValue instanceof Number) {
        stabilizationLimit = ((Number) newValue).doubleValue();
        if (stabilizationLimit > 1)
          stabilizationLimit = 1;
        else if (stabilizationLimit < 0)
          stabilizationLimit = 0;

        energies.clearEnergies();
      }
    }
  }

  public void graphAttributeRemoved(String graphId, long time,
      String attribute) {
    if (sinkTime.isNewEvent(graphId, time)) {
      sendGraphAttributeRemoved(graphId, time, attribute);
    }
  }

  public void nodeAttributeAdded(String graphId, long time, String nodeId,
      String attribute, Object value) {
    if (sinkTime.isNewEvent(graphId, time)) {
      nodeAttributeChanged_(graphId, nodeId, attribute, null, value);
      sendNodeAttributeAdded(graphId, time, nodeId, attribute, value);
    }
  }

  public void nodeAttributeChanged(String graphId, long time, String nodeId,
      String attribute, Object oldValue, Object newValue) {
    if (sinkTime.isNewEvent(graphId, time)) {
      nodeAttributeChanged_(graphId, nodeId, attribute, oldValue,
          newValue);
      sendNodeAttributeChanged(graphId, time, nodeId, attribute,
          oldValue, newValue);
    }
  }

  protected void nodeAttributeChanged_(String graphId, String nodeId,
      String attribute, Object oldValue, Object newValue) {
    if (attribute.equals("layout.weight")) {
      if (newValue instanceof Number)
        setNodeWeight(nodeId, ((Number) newValue).doubleValue());
      else if (newValue == null)
        setNodeWeight(nodeId, 1);

      energies.clearEnergies();
    } else if (attribute.equals("layout.frozen")) {
      freezeNode(nodeId, (newValue != null));
    } else if (attribute.equals("xyz") || attribute.equals("xy")) {
      double xyz[] = new double[3];
      GraphPosLengthUtils.positionFromObject(newValue, xyz);
      moveNode(nodeId, xyz[0], xyz[1], xyz[2]);
    } else if (attribute.equals("x") && newValue instanceof Number) {
      NodeParticle node = (NodeParticle) nodes.getParticle(nodeId);
      if (node != null) {
        moveNode(nodeId, ((Number) newValue).doubleValue(),
            node.getPosition().y, node.getPosition().z);
      }
    } else if (attribute.equals("y") && newValue instanceof Number) {
      NodeParticle node = (NodeParticle) nodes.getParticle(nodeId);
      if (node != null) {
        moveNode(nodeId, node.getPosition().x,
            ((Number) newValue).doubleValue(), node.getPosition().z);
      }
    }
  }

  public void nodeAttributeRemoved(String graphId, long time, String nodeId,
      String attribute) {
    if (sinkTime.isNewEvent(graphId, time)) {
      nodeAttributeChanged_(graphId, nodeId, attribute, null, null);
      sendNodeAttributeRemoved(graphId, time, nodeId, attribute);
    }
  }

  public void edgeAttributeAdded(String graphId, long time, String edgeId,
      String attribute, Object value) {
    if (sinkTime.isNewEvent(graphId, time)) {
      edgeAttributeChanged_(graphId, edgeId, attribute, null, value);
      sendEdgeAttributeAdded(graphId, time, edgeId, attribute, value);
    }
  }

  public void edgeAttributeChanged(String graphId, long time, String edgeId,
      String attribute, Object oldValue, Object newValue) {
    if (sinkTime.isNewEvent(graphId, time)) {
      edgeAttributeChanged_(graphId, edgeId, attribute, oldValue,
          newValue);
      sendEdgeAttributeChanged(graphId, time, edgeId, attribute,
          oldValue, newValue);
    }
  }

  protected void edgeAttributeChanged_(String graphId, String edgeId,
      String attribute, Object oldValue, Object newValue) {
    if (attribute.equals("layout.weight")) {
      if (newValue instanceof Number)
        setEdgeWeight(edgeId, ((Number) newValue).doubleValue());
      else if (newValue == null)
        setEdgeWeight(edgeId, 1);

      energies.clearEnergies();
    } else if (attribute.equals("layout.ignored")) {
      if (newValue instanceof Boolean)
        ignoreEdge(edgeId, (Boolean) newValue);
      energies.clearEnergies();
    }
  }

  public void edgeAttributeRemoved(String graphId, long time, String edgeId,
      String attribute) {
    if (sinkTime.isNewEvent(graphId, time)) {
      edgeAttributeChanged_(graphId, edgeId, attribute, null, null);
      sendEdgeAttributeRemoved(attribute, time, edgeId, attribute);
    }
  }

  /**
   * Factory method to create node particles.
   *
   * @param id
   *            The identifier of the new node/particle.
   * @return The new node/particle.
   */
  public abstract NodeParticle newNodeParticle(String id);
}
TOP

Related Classes of org.graphstream.ui.layout.springbox.BarnesHutLayout

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.