--- /dev/null
+package org.simantics.g3d.jme.common;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.HashMap;\r
+import java.util.HashSet;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Set;\r
+import java.util.Stack;\r
+\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Session;\r
+import org.simantics.db.WriteGraph;\r
+import org.simantics.db.common.request.ReadRequest;\r
+import org.simantics.db.common.request.WriteRequest;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.g3d.ontology.G3D;\r
+import org.simantics.g3d.scenegraph.IG3DNode;\r
+import org.simantics.g3d.scenegraph.RenderListener;\r
+import org.simantics.g3d.scenegraph.base.INode;\r
+import org.simantics.g3d.scenegraph.base.NodeListener;\r
+import org.simantics.g3d.scenegraph.base.ParentNode;\r
+import org.simantics.objmap.graph.IMapping;\r
+import org.simantics.objmap.graph.IMappingListener;\r
+import org.simantics.utils.datastructures.MapList;\r
+import org.simantics.utils.datastructures.MapSet;\r
+import org.simantics.utils.datastructures.Pair;\r
+\r
+import com.jme3.app.Application;\r
+import com.jme3.scene.Spatial;\r
+\r
+public abstract class AbstractJMENodeMap<E extends IG3DNode> implements JMENodeMap, IMappingListener, NodeListener, RenderListener {\r
+\r
+ protected Session session;\r
+ protected IMapping mapping;\r
+ protected Application app;\r
+// protected InteractiveVtkPanel panel;\r
+ \r
+ protected MapList<E, Spatial> nodeToActor = new MapList<E, Spatial>();\r
+ protected Map<Spatial,E> actorToNode = new HashMap<Spatial, E>();\r
+\r
+ protected ParentNode<E> rootNode;\r
+ \r
+ public AbstractJMENodeMap(Session session, IMapping mapping, Application app, ParentNode<E> rootNode) {\r
+ this.session = session;\r
+ this.mapping = mapping;\r
+ this.rootNode = rootNode;\r
+ this.app = app;\r
+// this.panel = panel;\r
+// panel.addListener(this);\r
+ mapping.addMappingListener(this);\r
+ rootNode.addListener(this);\r
+ }\r
+ \r
+ protected abstract void addActor(E node);\r
+ protected abstract void removeActor(E node);\r
+ protected abstract void updateActor(E node,Set<String> ids);\r
+ \r
+ \r
+ public void repaint() {\r
+ \r
+ }\r
+ \r
+ public void populate() {\r
+ for (E node : rootNode.getNodes()) {\r
+ receiveAdd(node, node.getParentRel(),true);\r
+ }\r
+ repaint();\r
+ }\r
+ \r
+ @Override\r
+ public IG3DNode getNode(Spatial prop) {\r
+ return actorToNode.get(prop);\r
+ }\r
+ \r
+ @SuppressWarnings("unchecked")\r
+ @Override\r
+ public Collection<Spatial> getRenderObjects(IG3DNode node) {\r
+ return nodeToActor.getValues((E)node);\r
+ }\r
+ \r
+ @SuppressWarnings("unchecked")\r
+ @Override\r
+ public ParentNode<IG3DNode> getRootNode() {\r
+ return (ParentNode<IG3DNode>)rootNode;\r
+ }\r
+ \r
+ @Override\r
+ public void commit() {\r
+ session.asyncRequest(new WriteRequest() {\r
+ \r
+ @Override\r
+ public void perform(WriteGraph graph) throws DatabaseException {\r
+ synchronized(syncMutex) {\r
+ graphUpdates = true;\r
+ mapping.updateDomain(graph);\r
+ graphUpdates = false;\r
+ }\r
+ }\r
+ });\r
+ }\r
+ \r
+ @Override\r
+ public boolean isChangeTracking() {\r
+ return changeTracking;\r
+ }\r
+ \r
+ @Override\r
+ public void setChangeTracking(boolean enabled) {\r
+ changeTracking = enabled;\r
+ }\r
+ \r
+ private boolean changeTracking = true;\r
+ \r
+ private Object syncMutex = new Object(); \r
+ \r
+\r
+ private List<Pair<E,String>> added = new ArrayList<Pair<E,String>>();\r
+ private List<Pair<E,String>> removed = new ArrayList<Pair<E,String>>();\r
+ //private List<Pair<E,String>> updated = new ArrayList<Pair<E,String>>();\r
+ private MapSet<E, String> updated = new MapSet<E, String>();//new MapSet.Hash<E, String>();\r
+\r
+ \r
+ @SuppressWarnings("unchecked")\r
+ @Override\r
+ public void updateRenderObjectsFor(IG3DNode node) {\r
+ List<Spatial> toDelete = new ArrayList<Spatial>();\r
+ for (Spatial prop : nodeToActor.getValues((E)node)) {\r
+// if (prop.GetVTKId() != 0) {\r
+// panel.GetRenderer().RemoveActor(prop);\r
+// //prop.Delete();\r
+// toDelete.add(prop);\r
+// }\r
+ prop.removeFromParent();\r
+ actorToNode.remove(prop);\r
+ }\r
+ nodeToActor.remove((E)node);\r
+ Collection<Spatial> coll = getActors((E)node);\r
+ if (coll == null)\r
+ return;\r
+ for (Spatial prop : coll) {\r
+ nodeToActor.add((E)node,prop);\r
+ actorToNode.put(prop, (E)node);\r
+ toDelete.remove(prop);\r
+ }\r
+ for (Spatial p : toDelete) {\r
+ //p.Delete();\r
+ }\r
+ }\r
+ \r
+ protected abstract Collection<Spatial> getActors(E node);\r
+ \r
+ @SuppressWarnings("unchecked")\r
+ private void receiveAdd(E node, String id, boolean db) {\r
+ synchronized (syncMutex) {\r
+ for (Pair<E, String> n : added) {\r
+ if (n.first.equals(node))\r
+ return;\r
+ }\r
+ if (changeTracking) {\r
+ mapping.rangeModified(node.getParent());\r
+ }\r
+ added.add(new Pair<E, String>(node, id)); \r
+ }\r
+ repaint();\r
+ }\r
+ \r
+ @SuppressWarnings("unchecked")\r
+ private void receiveRemove(E node, String id, boolean db) {\r
+ synchronized (syncMutex) {\r
+ for (Pair<E, String> n : removed) {\r
+ if (n.first.equals(node))\r
+ return;\r
+ }\r
+ if (changeTracking && !db)\r
+ mapping.rangeModified(node.getParent());\r
+ removed.add(new Pair<E, String>(node, id));\r
+ }\r
+ repaint();\r
+ }\r
+ \r
+ @SuppressWarnings("unchecked")\r
+ private void receiveUpdate(E node, String id, boolean db) {\r
+ synchronized (syncMutex) {\r
+// for (Pair<E, String> n : updated) {\r
+// if (n.first.equals(node))\r
+// return;\r
+// }\r
+ if (changeTracking && !db)\r
+ mapping.rangeModified(node);\r
+ //updated.add(new Pair<E, String>(node, id));\r
+ updated.add(node, id);\r
+ }\r
+ repaint();\r
+ }\r
+ \r
+ private boolean graphUpdates = false;\r
+ private Set<Object> graphModified = new HashSet<Object>();\r
+ \r
+ @Override\r
+ public void domainModified() {\r
+ if (graphUpdates)\r
+ return;\r
+ //System.out.println("domainModified");\r
+ session.asyncRequest(new ReadRequest() {\r
+ \r
+ @SuppressWarnings("unchecked")\r
+ @Override\r
+ public void run(ReadGraph graph) throws DatabaseException {\r
+ graphUpdates = true;\r
+ for (Object domainObject : mapping.getDomainModified()) {\r
+ Object rangeObject = mapping.get(domainObject);\r
+ if (rangeObject != null)\r
+ graphModified.add(rangeObject);\r
+ }\r
+ mapping.updateRange(graph);\r
+ graphModified.clear();\r
+ graphUpdates = false;\r
+ if (mapping.isRangeModified())\r
+ commit();\r
+ }\r
+ });\r
+ \r
+ }\r
+ \r
+ @Override\r
+ public void rangeModified() {\r
+ //System.out.println("rangeModified");\r
+\r
+ }\r
+ \r
+ @Override\r
+ public void postRender() {\r
+ \r
+ }\r
+ \r
+ List<Pair<E, String>> rem = new ArrayList<Pair<E,String>>();\r
+ List<Pair<E, String>> add = new ArrayList<Pair<E,String>>();\r
+ MapSet<E, String> mod = new MapSet<E, String>();//new MapSet.Hash<E, String>();\r
+ Set<E> propagation = new HashSet<E>();\r
+ Stack<E> stack = new Stack<E>();\r
+ \r
+ @SuppressWarnings("unchecked")\r
+ @Override\r
+ public synchronized void preRender() {\r
+ rem.clear();\r
+ add.clear();\r
+ mod.clear();\r
+ propagation.clear();\r
+ \r
+ synchronized (syncMutex) {\r
+ rem.addAll(removed);\r
+ add.addAll(added);\r
+ //mod.addAll(updated);\r
+ for (E e : updated.getKeys()) {\r
+ for (String s : updated.getValues(e))\r
+ mod.add(e, s);\r
+ }\r
+ \r
+ removed.clear();\r
+ added.clear();\r
+ updated.clear();\r
+ }\r
+ \r
+ for (Pair<E, String> n : rem) {\r
+ stopListening(n.first);\r
+ removeActor(n.first);\r
+ \r
+ }\r
+ \r
+ for (Pair<E, String> n : add) {\r
+ addActor(n.first);\r
+ listen(n.first);\r
+ }\r
+ \r
+ for (E e : mod.getKeys()) {\r
+ Set<String> ids = mod.getValues(e);\r
+ if (ids.contains(G3D.URIs.hasPosition) || ids.contains(G3D.URIs.hasOrientation)) {\r
+ if (!propagation.contains(e))\r
+ propagation.add(e);\r
+ }\r
+ }\r
+ \r
+ if (propagation.size() > 0) {\r
+ stack.clear();\r
+ stack.addAll(propagation);\r
+ propagation.clear();\r
+ while (!stack.isEmpty()) {\r
+ E node = stack.pop();\r
+ if (propagation.contains(node))\r
+ continue;\r
+ propagation.add(node);\r
+ for (NodeListener l : node.getListeners()) {\r
+ if (l == this) {\r
+ //changeTracking = false;\r
+ //l.propertyChanged(node, G3D.URIs.hasPosition);\r
+ //changeTracking = true;\r
+ } else {\r
+ l.propertyChanged(node, G3D.URIs.hasPosition);\r
+ }\r
+ }\r
+ if (node instanceof ParentNode) {\r
+ stack.addAll(((ParentNode<E>)node).getNodes());\r
+ }\r
+ }\r
+ }\r
+ \r
+ for (E e : mod.getKeys()) {\r
+ Set<String> ids = mod.getValues(e);\r
+ updateActor(e,ids);\r
+ }\r
+ \r
+ for (Pair<E, String> n : rem) {\r
+ for (NodeListener l : nodeListeners)\r
+ l.nodeRemoved(null, n.first, n.second);\r
+ }\r
+ for (Pair<E, String> n : add) {\r
+ for (NodeListener l : nodeListeners)\r
+ l.nodeAdded(n.first.getParent(), n.first, n.second);\r
+ }\r
+// for (Pair<E, String> n : mod) {\r
+// for (NodeListener l : nodeListeners)\r
+// l.propertyChanged(n.first, n.second);\r
+// }\r
+ for (E e : mod.getKeys()) {\r
+ for (NodeListener l : nodeListeners)\r
+ for (String s : mod.getValues(e))\r
+ l.propertyChanged(e, s);\r
+ }\r
+ }\r
+ \r
+ @SuppressWarnings("unchecked")\r
+ private void listen(INode node) {\r
+ node.addListener(this);\r
+ if (node instanceof ParentNode<?>) {\r
+ ParentNode<INode> parentNode = (ParentNode<INode>)node;\r
+ for (INode n : parentNode.getNodes())\r
+ listen(n);\r
+ }\r
+ }\r
+ \r
+ private void stopListening(INode node) {\r
+ node.removeListener(this);\r
+ if (node instanceof ParentNode<?>) {\r
+ @SuppressWarnings("unchecked")\r
+ ParentNode<INode> parentNode = (ParentNode<INode>)node;\r
+ for (INode n : parentNode.getNodes())\r
+ stopListening(n);\r
+ }\r
+ }\r
+ \r
+ @SuppressWarnings("unchecked")\r
+ @Override\r
+ public void propertyChanged(INode node, String id) {\r
+ //receiveUpdate((E)node, id, graphUpdates);\r
+ receiveUpdate((E)node, id, graphModified.contains(node));\r
+ \r
+ }\r
+ \r
+ @SuppressWarnings("unchecked")\r
+ @Override\r
+ public <T extends INode> void nodeAdded(ParentNode<T> node, INode child,\r
+ String rel) {\r
+ //receiveAdd((E)child, rel ,graphUpdates);\r
+ receiveAdd((E)child, rel ,graphModified.contains(node));\r
+ \r
+ }\r
+ \r
+ @SuppressWarnings("unchecked")\r
+ @Override\r
+ public <T extends INode> void nodeRemoved(ParentNode<T> node, INode child,\r
+ String rel) {\r
+ //receiveRemove((E)child, rel, graphUpdates);\r
+ receiveRemove((E)child, rel, graphModified.contains(node));\r
+ }\r
+ \r
+ @Override\r
+ public void delete() {\r
+ changeTracking = false;\r
+ //panel.removeListener(this);\r
+ mapping.removeMappingListener(this);\r
+\r
+ List<E> nodes = new ArrayList<E>(nodeToActor.getKeySize());\r
+ nodes.addAll(nodeToActor.getKeys());\r
+ for (E node : nodes) {\r
+ node.removeListener(this);\r
+ removeActor(node);\r
+ node.cleanup();\r
+ }\r
+ for (Spatial prop : actorToNode.keySet()) {\r
+ prop.removeFromParent();\r
+ //if (prop.GetVTKId() != 0) \r
+ // prop.Delete();\r
+ }\r
+ actorToNode.clear();\r
+ nodeToActor.clear();\r
+ \r
+ }\r
+ \r
+ \r
+ private List<NodeListener> nodeListeners = new ArrayList<NodeListener>();\r
+ @Override\r
+ public void addListener(NodeListener listener) {\r
+ nodeListeners.add(listener);\r
+ \r
+ }\r
+ \r
+ @Override\r
+ public void removeListener(NodeListener listener) {\r
+ nodeListeners.remove(listener);\r
+ \r
+ }\r
+\r
+}\r