Package ca.nengo.ui.lib.world.elastic

Source Code of ca.nengo.ui.lib.world.elastic.ElasticGround$UpdateGraphResult

package ca.nengo.ui.lib.world.elastic;

import java.awt.geom.Point2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;

import ca.nengo.ui.lib.util.Util;
import ca.nengo.ui.lib.world.ObjectSet;
import ca.nengo.ui.lib.world.WorldObject;
import ca.nengo.ui.lib.world.piccolo.WorldGroundImpl;
import ca.nengo.ui.lib.world.piccolo.primitives.PXEdge;
import edu.uci.ics.jung.graph.impl.AbstractSparseEdge;
import edu.uci.ics.jung.graph.impl.DirectedSparseEdge;
import edu.uci.ics.jung.graph.impl.SparseGraph;
import edu.uci.ics.jung.graph.impl.UndirectedSparseEdge;
import edu.uci.ics.jung.utils.UserData;
import edu.uci.ics.jung.visualization.Layout;
import edu.umd.cs.piccolo.PNode;
import edu.umd.cs.piccolo.util.PBounds;

public class ElasticGround extends WorldGroundImpl {

  public static final String ELASTIC_LENGTH_KEY = "elasticLength";

  private boolean childrenUpdatedFlag = false;
  private ObjectSet<ElasticObject> elasticChildren = new ObjectSet<ElasticObject>();

  private ElasticLayoutRunner elasticLayoutThread;

  private Hashtable<PXEdge, AbstractSparseEdge> myEdgeMap = new Hashtable<PXEdge, AbstractSparseEdge>();

  private SparseGraph myGraph;

  private Hashtable<ElasticObject, ElasticVertex> myVertexMap = new Hashtable<ElasticObject, ElasticVertex>();

  public ElasticGround() {
    super();
    getPiccolo().addPropertyChangeListener(new PropertyChangeListener() {
      public void propertyChange(PropertyChangeEvent evt) {
        if (evt.getPropertyName().compareTo(PNode.PROPERTY_CHILDREN) == 0) {
          childrenUpdatedFlag = true;
        }
      }

    });
  }

  public void modifyEdgeDistances(ElasticObject obj, double delta) {
    for (PXEdge edge : getEdges()) {
      if (edge instanceof ElasticEdge) {
        ElasticEdge elasticEdge = (ElasticEdge) edge;

        if (obj == elasticEdge.getStartNode() || obj == elasticEdge.getEndNode()) {

          double distance = elasticEdge.getLength() + delta;

          if (distance < 100) {
            distance = 100;
          }

          elasticEdge.setLength(distance);

        }

      }

    }

  }

  @Override
  protected void prepareForDestroy() {
    setElasticEnabled(false);
    super.prepareForDestroy();
  }

  @Override
  public void childAdded(WorldObject wo) {
    super.childAdded(wo);
    if (wo instanceof ElasticObject) {
      elasticChildren.add((ElasticObject) wo);
    }
  }

  @Override
  public void childRemoved(WorldObject wo) {
    super.childRemoved(wo);
    if (wo instanceof ElasticObject) {
      if (!elasticChildren.remove((ElasticObject) wo)) {
        Util.Assert(false);
      }
    }
  }

  public Iterable<ElasticObject> getElasticChildren() {
    return elasticChildren;
  }

  public Point2D getElasticPosition(ElasticObject node) {
    if (elasticLayoutThread != null) {
      ElasticVertex vertex = myVertexMap.get(node);
      if (vertex != null) {
        if (!elasticLayoutThread.isLocked(vertex)) {
          return elasticLayoutThread.getLocation(myVertexMap.get(node));
        }
      }
    }
    return node.getOffsetReal();
  }

  /**
   * @return The current graph representation of the Ground.
   */
  public SparseGraph getGraph() {
    return myGraph;
  }

  @Override
  public ElasticWorld getWorld() {
    return (ElasticWorld) super.getWorld();
  }

  public boolean isElasticMode() {
    if (elasticLayoutThread != null) {
      return true;
    } else {
      return false;
    }
  }

  public boolean isPositionLocked(ElasticObject node) {
    if (elasticLayoutThread != null) {
      ElasticVertex vertex = myVertexMap.get(node);

      if (vertex != null) {
        return elasticLayoutThread.isLocked(vertex);
      }
    }
    return false;
  }

  public void setElasticEnabled(boolean enabled) {
    if (elasticLayoutThread != null) {
      elasticLayoutThread.stopLayout();
      elasticLayoutThread = null;
    }
    if (enabled) {
      myVertexMap.clear();
      myEdgeMap.clear();
      myGraph = null;
      elasticLayoutThread = new ElasticLayoutRunner(this);
      elasticLayoutThread.start();
    }

  }

  public void setElasticPosition(ElasticObject node, double x, double y) {
    boolean doRealMove = true;
    if (x == 0 || y == 0)
      return;

    if (elasticLayoutThread != null) {
      ElasticVertex vertex = myVertexMap.get(node);
      if (vertex != null) {

        elasticLayoutThread.forceMove(vertex, x, y);
        if (!elasticLayoutThread.isLocked(vertex)) {
          doRealMove = false;
        }
      }
    }
    if (doRealMove) {
      node.setOffsetReal(x, y);
    }
  }

  /**
   * Locks the position of an elastic node so it isn't affected by the layout
   * runner
   *
   * @param node
   * @param lockEnabled
   */
  public void setPositionLocked(ElasticObject node, boolean lockEnabled) {
    if (elasticLayoutThread != null) {
      ElasticVertex vertex = myVertexMap.get(node);

      if (vertex == null) {
        // Try to update the layout and get the vertex again
        // Node might have been updated recently and not added to the
        // graph
        elasticLayoutThread.updateLayout();
        vertex = myVertexMap.get(node);
      }

      if (vertex != null) {
        if (lockEnabled) {
          elasticLayoutThread.lockVertex(vertex);
        } else {
          elasticLayoutThread.unlockVertex(vertex);
        }
      } else {
        // Util.Assert(false, "Vertex not found");
      }
    }

  }

  public void updateChildrenFromLayout(Layout layout, boolean animateNodes, boolean zoomToLayout) {
    /**
     * Layout nodes
     */
    boolean foundNode = false;

    double startX = Double.POSITIVE_INFINITY;
    double startY = Double.POSITIVE_INFINITY;
    double endX = Double.NEGATIVE_INFINITY;
    double endY = Double.NEGATIVE_INFINITY;

    for (ElasticObject elasticObj : getElasticChildren()) {

      ElasticVertex vertex = myVertexMap.get(elasticObj);
      if (vertex != null) {

        Point2D coord = layout.getLocation(vertex);

        if (coord != null) {

          foundNode = true;
          double x = coord.getX();
          double y = coord.getY();

          if (elasticObj.isAnimating()) {
            // If the object is being animated in another process,
            // then we force update it's position in the layout
            x = elasticObj.getOffsetReal().getX();
            y = elasticObj.getOffsetReal().getY();
            if (elasticLayoutThread != null) {
              elasticLayoutThread.forceMove(vertex, x, y);
            }
          } else {
            x = coord.getX();
            y = coord.getY();
            if (animateNodes) {
              elasticObj.animateToPositionScaleRotation(x, y, 1, 0, 1000);
            } else {
              elasticObj.setOffsetReal(x, y);
            }
          }

          if (x < startX) {
            startX = x;
          }
          if (x + elasticObj.getWidth() > endX) {
            endX = x + elasticObj.getWidth();
          }

          if (y < startY) {
            startY = y;
          }
          if (y + elasticObj.getHeight() > endY) {
            endY = y + elasticObj.getHeight();
          }
        }
      }
    }

    if (zoomToLayout && foundNode) {
      PBounds fullBounds = new PBounds(startX, startY, endX - startX, endY - startY);
      getWorld().zoomToBounds(fullBounds);
    }

  }

  /**
   * This method must be executed from the swing dispatcher thread because it
   * must be synchronized with the Graphical children elements.
   */
  public UpdateGraphResult updateGraph() {

    boolean graphChanged = false;
    if (myGraph == null) {
      graphChanged = true;
      childrenUpdatedFlag = true;
      myGraph = new SparseGraph();
    }
    Collection<ElasticVertex> addedVertexes = Collections.emptyList();
    if (childrenUpdatedFlag) {
      addedVertexes = new LinkedList<ElasticVertex>();
      childrenUpdatedFlag = false;

      /**
       * Add vertices
       */
      for (ElasticObject obj : getElasticChildren()) {

        if (!myVertexMap.containsKey(obj)) {
          ElasticVertex vertex = new ElasticVertex(obj);
          myGraph.addVertex(vertex);
          myVertexMap.put(obj, vertex);
          addedVertexes.add(vertex);

          graphChanged = true;
        }
      }

      /**
       * Remove vertices
       */
      List<ElasticObject> elasticObjToRemove = new ArrayList<ElasticObject>();
      for (ElasticObject elasticObj : myVertexMap.keySet()) {
        if (elasticObj.getParent() != this) {
          elasticObjToRemove.add(elasticObj);
        }
      }

      for (ElasticObject elasticObj : elasticObjToRemove) {
        myGraph.removeVertex(myVertexMap.get(elasticObj));
        myVertexMap.remove(elasticObj);
        graphChanged = true;
      }
    }

    /*
     * TODO: Only update edges when they are changed. Have to figure out a
     * way to know when edge start and end nodes are changed. This is tricky
     * because the start and end nodes we're interested in are the Elastic
     * Objects above the ground, which the dosen't know about
     */

    /**
     * Add edges
     */

    for (PXEdge uiEdge : getEdges()) {

      if (uiEdge instanceof ElasticEdge) {
        ElasticEdge elasticEdge = (ElasticEdge) uiEdge;
        WorldObject startNode = uiEdge.getStartNode();
        WorldObject endNode = uiEdge.getEndNode();

        // Find the Elastic Objects which are ancestors of the start and
        // end
        // nodes
        while (startNode.getParent() != this && startNode != null) {
          startNode = startNode.getParent();
          if (startNode == null) {
            break;
          }
        }

        while (endNode.getParent() != this && endNode != null) {
          endNode = endNode.getParent();
          if (endNode == null) {
            break;
          }
        }

        if (startNode == null || endNode == null) {
          Util.Assert(false, "Edge nodes do not exist on this ground");
        } else if (!(startNode instanceof ElasticObject || endNode instanceof ElasticGround)) {
          /*
           * The parent nodes are not elastic, we can ignore them
           */
        } else if (startNode.getParent() == this && endNode.getParent() == this) {
          ElasticVertex startVertex = myVertexMap.get(startNode);
          ElasticVertex endVertex = myVertexMap.get(endNode);

          if (!(startVertex != null && endVertex != null)) {
            Util.Assert(false, "Could not find vertice");
          }

          AbstractSparseEdge jungEdge = myEdgeMap.get(uiEdge);

          boolean createJungEdge = false;
          if (jungEdge != null) {
            // find if an existing edge has changed
            boolean edgeChanged = false;

            if (uiEdge instanceof ElasticEdge) {
              Double length = (Double) jungEdge.getUserDatum(ELASTIC_LENGTH_KEY);
              if (length != ((ElasticEdge) uiEdge).getLength()) {
                edgeChanged = true;
              }
            } else if (jungEdge.getEndpoints().getFirst() != startVertex
                || jungEdge.getEndpoints().getSecond() != endVertex) {
              edgeChanged = true;
            }

            if (edgeChanged) {
              myEdgeMap.remove(uiEdge);
              myGraph.removeEdge(jungEdge);
              graphChanged = true;

              // try to add the new changed one
              createJungEdge = true;

            }

          } else {
            createJungEdge = true;
          }

          if (createJungEdge) {
            // avoid recursive edges
            if (startVertex != endVertex) {
              if (uiEdge.isDirected()) {
                jungEdge = new DirectedSparseEdge(startVertex, endVertex);
              } else {
                jungEdge = new UndirectedSparseEdge(startVertex, endVertex);
              }

              jungEdge.addUserDatum(ELASTIC_LENGTH_KEY, new Double(elasticEdge
                  .getLength()), UserData.SHARED);

              myEdgeMap.put(uiEdge, jungEdge);

              myGraph.addEdge(jungEdge);

              graphChanged = true;
            }
          }

        } else {
          Util.Assert(false, "Could not find Elastic Nodes of edge");
        }
      }
    }
    /*
     * Remove edges
     */
    List<PXEdge> edgesToRemove = new ArrayList<PXEdge>();
    for (PXEdge uiEdge : myEdgeMap.keySet()) {
      if (!containsEdge(uiEdge)) {
        edgesToRemove.add(uiEdge);

      }
    }
    for (PXEdge uiEdge : edgesToRemove) {
      AbstractSparseEdge jungEdge = myEdgeMap.get(uiEdge);
      graphChanged = true;
      // have to check if the edge is still there because it might have
      // been removed when the vertice was removed
      if (myGraph.getEdges().contains(jungEdge)) {
        myGraph.removeEdge(jungEdge);
      }
      myEdgeMap.remove(uiEdge);
    }

    // Return whether the graph changed
    return new UpdateGraphResult(graphChanged, addedVertexes);

  }

  public static class UpdateGraphResult {
    private Collection<ElasticVertex> addedVertices;
    private boolean graphUpdated;

    public UpdateGraphResult(boolean graphUpdated, Collection<ElasticVertex> addedVertices) {
      super();
      this.graphUpdated = graphUpdated;
      this.addedVertices = addedVertices;
    }

    public Collection<ElasticVertex> getAddedVertices() {
      return addedVertices;
    }

    public boolean isGraphUpdated() {
      return graphUpdated;
    }

  }

}
TOP

Related Classes of ca.nengo.ui.lib.world.elastic.ElasticGround$UpdateGraphResult

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.