--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007- VTT Technical Research Centre of Finland.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.proconf.g3d.base;\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.Queue;\r
+import java.util.Set;\r
+import java.util.concurrent.ConcurrentLinkedQueue;\r
+\r
+import org.simantics.db.AbstractQuery;\r
+import org.simantics.db.Graph;\r
+import org.simantics.db.GraphRequestAdapter;\r
+import org.simantics.db.GraphRequestStatus;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.Session;\r
+import org.simantics.db.queries.IGraphQuery;\r
+import org.simantics.db.queries.IQuery;\r
+import org.simantics.db.queries.IQueryListener;\r
+import org.simantics.db.utils.transaction.MergingTransactionRunner;\r
+import org.simantics.layer0.utils.EntityFactory;\r
+import org.simantics.layer0.utils.IEntity;\r
+import org.simantics.layer0.utils.Property;\r
+import org.simantics.proconf.g3d.Resources;\r
+import org.simantics.proconf.g3d.scenegraph.IGeometryNode;\r
+import org.simantics.proconf.g3d.scenegraph.IGraphicsNode;\r
+import org.simantics.proconf.g3d.scenegraph.RootGraphicsNode;\r
+import org.simantics.proconf.g3d.stubs.G3DNode;\r
+import org.simantics.utils.ui.ErrorLogger;\r
+import org.simantics.utils.datastructures.BijectionMap;\r
+\r
+import com.jme.scene.Node;\r
+\r
+\r
+/**\r
+ * Scene-graph adapter :\r
+ * 1. Adapts graph change events into changes in actual scene-graph nodes.\r
+ * 2. Handles instantiating and disposing of Scene-graph nodes.\r
+ * 3. \r
+ * \r
+ * @author Marko Luukkainen\r
+ *\r
+ */\r
+public abstract class ScenegraphAdapterImpl implements ScenegraphAdapter {\r
+ \r
+ protected static boolean DEBUG = false;\r
+ \r
+ private RootGraphicsNode root;\r
+ private HashMap<Resource,ScenegraphQuery> scenegraphQueries = new HashMap<Resource, ScenegraphQuery>();\r
+ private HashMap<Resource,NodePropertyQuery> propertyQueries = new HashMap<Resource, NodePropertyQuery>();\r
+ private HashMap<Resource,NodeTransformationQuery> transformationQueries = new HashMap<Resource, NodeTransformationQuery>();\r
+ private HashMap<Resource, IGraphicsNode> abstractGraphicsNodes = new HashMap<Resource, IGraphicsNode>();\r
+ protected Queue<IGeometryNode> geometryUpdates = new ConcurrentLinkedQueue<IGeometryNode>();\r
+ \r
+ private BijectionMap<Resource, String> nameMap = new BijectionMap<Resource, String>();\r
+ \r
+ protected JmeRenderingComponent component;\r
+ \r
+ protected boolean viewChanged = false;\r
+ \r
+ protected Session session;\r
+ \r
+ private MergingTransactionRunner transactionRunner;\r
+ \r
+ public ScenegraphAdapterImpl(Session session, JmeRenderingComponent component) {\r
+ this.component = component;\r
+ this.session = session;\r
+ transactionRunner = new MergingTransactionRunner(session,true);\r
+ }\r
+ \r
+ \r
+ @Override\r
+ public JmeRenderingComponent getRenderingComponent() {\r
+ return component;\r
+ }\r
+ \r
+ /* (non-Javadoc)\r
+ * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#setRootNode(fi.vtt.simantics.g3d.stubs.G3DNode)\r
+ */\r
+ public void setRootNode(G3DNode rootNode) {\r
+ addSubnodeListener(rootNode);\r
+ root = new RootGraphicsNode(component,rootNode.getResource());\r
+ abstractGraphicsNodes.put(rootNode.getResource(),root);\r
+ G3DTools.reloadCache(rootNode.getGraph(),root.getResource());\r
+ addRootPropertyListener(rootNode.getGraph());\r
+ }\r
+ \r
+ /* (non-Javadoc)\r
+ * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#needsUpdateGeometry()\r
+ */\r
+ public boolean needsUpdateGeometry() {\r
+ return geometryUpdates.size() > 0;\r
+ }\r
+ \r
+ /* (non-Javadoc)\r
+ * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#updateGeometry(fi.vtt.simantics.g3d.scenegraph.IGeometryNode)\r
+ */\r
+ public void updateGeometry(IGeometryNode node) {\r
+ geometryUpdates.add(node);\r
+ }\r
+ \r
+ /* (non-Javadoc)\r
+ * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#updateGeometry(fi.vtt.simantics.db.connection.Resource)\r
+ */\r
+ public void updateGeometry(Resource nodeResource) {\r
+ geometryUpdates.add((IGeometryNode)abstractGraphicsNodes.get(nodeResource));\r
+ }\r
+ \r
+ \r
+ /* (non-Javadoc)\r
+ * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#updateGeometry(fi.vtt.simantics.db.connection.Graph)\r
+ */\r
+ public synchronized void updateGeometry(Graph graph) {\r
+ if (geometryUpdates.size() > 0) {\r
+ for (IGeometryNode n : geometryUpdates) {\r
+ try {\r
+ n.updateGeometry(graph);\r
+ if (DEBUG) System.out.println("ScenegraphAdapterImpl: geometryUpdated " + n.getResource());\r
+ } catch (Exception e) {\r
+ ErrorLogger.defaultLogError("Failed to update geometry of node" + n.getResource(), e);\r
+ }\r
+ }\r
+ geometryUpdates.clear();\r
+ viewChanged = true;\r
+ //if (DEBUG) System.out.println("ScenegraphAdapterImpl: geometryUpdated");\r
+ }\r
+ }\r
+ \r
+ /* (non-Javadoc)\r
+ * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#getNodes()\r
+ */\r
+ public Collection<IGraphicsNode> getNodes() {\r
+ return abstractGraphicsNodes.values();\r
+ }\r
+ \r
+ /* (non-Javadoc)\r
+ * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#getNode(fi.vtt.simantics.db.connection.Resource)\r
+ */\r
+ public IGraphicsNode getNode(Resource resource) {\r
+ return abstractGraphicsNodes.get(resource);\r
+ }\r
+ \r
+ /* (non-Javadoc)\r
+ * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#getRootNode()\r
+ */\r
+ public IGraphicsNode getRootNode() {\r
+ return getNode(getRootResource());\r
+ }\r
+ \r
+ /* (non-Javadoc)\r
+ * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#getNode(fi.vtt.simantics.layer0.utils.IEntity)\r
+ */\r
+ public IGraphicsNode getNode(IEntity IEntity) {\r
+ return abstractGraphicsNodes.get(IEntity.getResource());\r
+ }\r
+ \r
+ /* (non-Javadoc)\r
+ * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#getNodeResource(java.lang.String)\r
+ */\r
+ public Resource getNodeResource(String uid) {\r
+ return nameMap.getLeft(uid);\r
+ }\r
+ \r
+ /* (non-Javadoc)\r
+ * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#getNodeUID(fi.vtt.simantics.db.connection.Resource)\r
+ */\r
+ public String getNodeUID(Resource nodeResource) {\r
+ String name = nameMap.getRight(nodeResource);\r
+ if (name == null) {\r
+ //name = UUID.randomUUID().toString();\r
+ name = Long.toString(nodeResource.getResourceId());\r
+ nameMap.map(nodeResource, name);\r
+ }\r
+ return name;\r
+ }\r
+ \r
+ /* (non-Javadoc)\r
+ * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#hasNode(fi.vtt.simantics.db.connection.Resource)\r
+ */\r
+ public boolean hasNode(Resource resource) {\r
+ return abstractGraphicsNodes.containsKey(resource);\r
+ }\r
+ \r
+ /* (non-Javadoc)\r
+ * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#getRootResource()\r
+ */\r
+ public Resource getRootResource() {\r
+ if (root == null)\r
+ return null;\r
+ return root.getResource();\r
+ }\r
+ \r
+ /* (non-Javadoc)\r
+ * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#getRoot()\r
+ */\r
+ public Node getRoot() {\r
+ return root.getGroup();\r
+ }\r
+ \r
+ /* (non-Javadoc)\r
+ * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#dispose()\r
+ */\r
+ public void dispose() {\r
+ Set<Resource> shapes = new HashSet<Resource>(abstractGraphicsNodes.keySet());\r
+ for (Resource r : shapes) {\r
+ removeNode(r);\r
+ }\r
+ }\r
+ \r
+ /* (non-Javadoc)\r
+ * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#isChanged()\r
+ */\r
+ public boolean isChanged() {\r
+ return viewChanged;\r
+ }\r
+ \r
+ /* (non-Javadoc)\r
+ * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#setChanged(boolean)\r
+ */\r
+ public void setChanged(boolean changed) {\r
+ viewChanged = changed;\r
+ }\r
+ \r
+ \r
+ /**\r
+ * Instantiates Listener that listens hierarchy changes in the node (subnode\r
+ * added and/or removed)\r
+ * \r
+ * @param node\r
+ * @return\r
+ */\r
+ protected abstract ScenegraphQuery newSubnodeListener(G3DNode node);\r
+ \r
+ protected void addSubnodeListener(G3DNode node) {\r
+ if (DEBUG) System.out.println("ScenegraphAdapter.addSubnodeListener( " + node.getResource() + " )");\r
+ ScenegraphQuery q = newSubnodeListener(node);\r
+ node.getGraph().performQuery(q);\r
+ scenegraphQueries.put(node.getResource(), q);\r
+ }\r
+ \r
+ /**\r
+ * Returns propertyQuery for a single scene-graph node.\r
+ * @param node\r
+ * @return\r
+ */\r
+ protected abstract NodePropertyQuery newPropertyListener(G3DNode node);\r
+ \r
+ /**\r
+ * Returns transformationQuery for a single scene-graph node\r
+ * @param root\r
+ * @return\r
+ */\r
+ protected abstract NodeTransformationQuery newTransformationListener(G3DNode node);\r
+ \r
+ /**\r
+ * Returns propertyQuery for the root node.\r
+ * May return null if root node has no interesting properties.\r
+ * \r
+ * Potentially root node could contain lighting settings, and so on...\r
+ * \r
+ * @param root\r
+ * @return\r
+ */\r
+ protected abstract NodePropertyQuery newRootPropertyListener(G3DNode root);\r
+ \r
+ protected void addPropertyListener(G3DNode node) {\r
+ if (DEBUG) System.out.println("ScenegraphAdapter.addPropertyListener( " + node.getResource() + " )");\r
+ NodePropertyQuery q = newPropertyListener(node);\r
+ node.getGraph().performQuery(q);\r
+ propertyQueries.put(node.getResource(),q);\r
+ }\r
+ \r
+ \r
+ protected void addTransformationListener(G3DNode node) {\r
+ if (DEBUG) System.out.println("ScenegraphAdapter.addPropertyListener( " + node.getResource() + " )");\r
+ NodeTransformationQuery q = newTransformationListener(node);\r
+ node.getGraph().performQuery(q);\r
+ transformationQueries.put(node.getResource(),q);\r
+ }\r
+ \r
+ protected void addRootPropertyListener(Graph g) {\r
+ G3DNode node = root.getG3DNode(g);\r
+ if (DEBUG) System.out.println("ScenegraphAdapter.addRootPropertyListener( " + node.getResource() + " )"); \r
+ NodePropertyQuery q = newRootPropertyListener(node);\r
+ if (q == null)\r
+ return;\r
+ node.getGraph().performQuery(q);\r
+ propertyQueries.put(node.getResource(),q);\r
+ }\r
+ \r
+ /**\r
+ * Instantiates a new scene-graph node\r
+ * \r
+ * @param parent the parent of the new node.\r
+ * @param node the new node.\r
+ * @return\r
+ */\r
+ protected abstract IGraphicsNode instantiateNode(IGraphicsNode parent, G3DNode node);\r
+ \r
+\r
+ /**\r
+ * Adds node into scene-graph\r
+ * @param parent the parent of the node\r
+ * @param r resource of the node\r
+ * @return created scene-graph node\r
+ */\r
+ protected IGraphicsNode addNode(IEntity parent, IEntity r) {\r
+ if (!r.isInstanceOf(Resources.g3dResource.G3DNode)) {\r
+ ErrorLogger.defaultLogError("Trying to add node into scenegraph that is not instance of G3DNode " + r,new Exception("ASSERT!"));\r
+ return null;\r
+ }\r
+ if (parent.equals(r)) {\r
+ if (DEBUG) System.out.println("ThreeDimensionalEditorBase.addNodeP(" + r.getResource().getResourceId() + ") adding node to itself?!");\r
+ ErrorLogger.defaultLogError("Adding scnegraphnode " + r.getResource().getResourceId() + " to itself!", new Exception("ASSERT!"));\r
+ return abstractGraphicsNodes.get(r);\r
+ }\r
+ \r
+ if (abstractGraphicsNodes.containsKey(r)) {\r
+ \r
+ IGraphicsNode inView = abstractGraphicsNodes.get(r);\r
+ if (inView.getParent() == null) {\r
+ //if graphicsNode has no parent it must be the root node\r
+ ErrorLogger.defaultLogError("Trying to add rootnode into scenegraph " + r, null);\r
+ return null;\r
+ }\r
+ if (parent.equals(inView.getParent().getResource())) {\r
+ if (DEBUG) System.out.println("ThreeDimensionalEditorBase.addNodeP(" + r.getResource().getResourceId() + ") already in view");\r
+ return inView;\r
+ } else {\r
+ if (DEBUG) System.out.println("ThreeDimensionalEditorBase.addNodeP(" + r.getResource().getResourceId() + ") already in view, but has different parent, current parent is ("+inView.getParent().getResource().getResourceId()+") and node is added to ("+parent+") -> removing from old parent and inserting to new");\r
+ removeNode(inView.getParent().getResource(),r.getResource());\r
+ }\r
+ }\r
+ \r
+ G3DNode node = new G3DNode(r);\r
+\r
+ IGraphicsNode mo;\r
+ IGraphicsNode parentNode = abstractGraphicsNodes.get(parent);\r
+ if (parentNode == null) {\r
+ if (DEBUG) System.out.println("No graphicsnode for (" + parent.getResource().getResourceId() + ")");\r
+ return null;\r
+ } else {\r
+ mo = instantiateNode(parentNode, node);\r
+ if (mo == null) {\r
+ ErrorLogger.defaultLogError("Could not instantiate scenegraph node for " + r.getResource().getResourceId(), null);\r
+ return null;\r
+ }\r
+ if (DEBUG) System.out.println("ThreeDimensionalEditorBase.addNodeP(" + r.getResource().getResourceId() + ") added to parent (" + parent.getResource().getResourceId() + ") " + mo.getClass());\r
+ }\r
+ addSubnodeListener(node);\r
+ addPropertyListener(node);\r
+ addTransformationListener(node);\r
+\r
+ abstractGraphicsNodes.put(r.getResource(), mo);\r
+\r
+ \r
+ \r
+ // FIXME : this is a hack to fix transformations of instantiated nodes\r
+// if (graph.getCurrentTransaction() != null) {\r
+// try {\r
+// G3DTools.propagateWorldTransformChange(parentNode\r
+// .getG3DNode());\r
+// graph.commitChanges(CommitMessage.CHANGE_MESSAGE);\r
+// // G3DNodeTools.transformationUpdate(graph, r.getId());\r
+// } catch (Exception e) {\r
+// ErrorLogger.defaultLogError(e);\r
+// }\r
+// }\r
+\r
+ \r
+ return mo;\r
+ }\r
+\r
+ /**\r
+ * This is used only when view is disposed!\r
+ * \r
+ * @param r\r
+ */\r
+ private void removeNode(Resource r) {\r
+ NodeTransformationQuery tq = transformationQueries.get(r);\r
+ //StructuralChangeMonitor monitor = monitors.getLeft(r);\r
+ if (tq == null) {\r
+ if (abstractGraphicsNodes.containsKey(r)) {\r
+ // root node has no monitor (no transformation to monitor)\r
+ //System.out.println("ThreeDimensionalEditorBase.removeNode(" + r + ") node has no monitor, but has node in scenegraph"); \r
+ abstractGraphicsNodes.remove(r);\r
+ if(scenegraphQueries.get(r) != null) {\r
+ scenegraphQueries.get(r).dispose();\r
+ scenegraphQueries.remove(r);\r
+ }\r
+ } else {\r
+ //System.out.println("ThreeDimensionalEditorBase.removeNode(" + r + ") not in view");\r
+ }\r
+ return;\r
+ }\r
+ // remove listeners\r
+ propertyQueries.remove(r).dispose(); \r
+ transformationQueries.remove(r).dispose();\r
+ scenegraphQueries.get(r);\r
+ scenegraphQueries.remove(r).dispose();\r
+ // remove children\r
+ IGraphicsNode node = abstractGraphicsNodes.get(r);\r
+ ArrayList<IGraphicsNode> children = new ArrayList<IGraphicsNode>(node.getChildren());\r
+ for (IGraphicsNode n : children) {\r
+ removeNode(n.getResource());\r
+ }\r
+ // remove the node\r
+ if (DEBUG) System.out.println("ThreeDimensionalEditorBase.removeNode(" + r + ") removed");\r
+ \r
+ node.dispose();\r
+ abstractGraphicsNodes.remove(r);\r
+\r
+ }\r
+ \r
+ /**\r
+ * Removes a scene-graph node.\r
+ * @param parent the parent of the node\r
+ * @param r the node.\r
+ */\r
+ protected void removeNode(Resource parent,Resource r) {\r
+ NodePropertyQuery q = propertyQueries.get(r);\r
+ if (q == null) {\r
+ assert(!abstractGraphicsNodes.containsKey(r));\r
+ if (DEBUG) System.out.println("ThreeDimensionalEditorBase.removeNodeP(" + r + ") not in view");\r
+ return;\r
+ }\r
+ \r
+ IGraphicsNode node = abstractGraphicsNodes.get(r);\r
+ Resource rParent = node.getParent().getResource();\r
+ if (!rParent.equals(parent)) {\r
+ // this event may happen, depending of the order of events in transaction\r
+ if (DEBUG) System.out.println("ThreeDimensionalEditorBase.removeNodeP(" + r + ") trying to remove from wrong parent current ("+rParent+") remove parentnode null("+parent+")"); \r
+ return;\r
+ }\r
+ // removing listeners\r
+ propertyQueries.remove(r).dispose();\r
+ transformationQueries.remove(r).dispose();\r
+ scenegraphQueries.remove(r).dispose();\r
+ // remove node's children\r
+ ArrayList<IGraphicsNode> children = new ArrayList<IGraphicsNode>(node.getChildren());\r
+ for (IGraphicsNode n : children) {\r
+ removeNode(r,n.getResource());\r
+ } \r
+ if (DEBUG) System.out.println("ThreeDimensionalEditorBase.removeNodeP(" + r + ") from ("+parent+")"); \r
+ // remove the node\r
+ \r
+ abstractGraphicsNodes.remove(r);\r
+ if (geometryUpdates.contains(node)) {\r
+ geometryUpdates.remove(node);\r
+ }\r
+ node.dispose();\r
+ }\r
+\r
+ \r
+ /**\r
+ * Query that tracks changes in resources.\r
+ * \r
+ * @author Marko Luukkainen\r
+ *\r
+ */\r
+ public abstract class NodeQuery extends AbstractQuery<Object>{\r
+ protected Resource nodeResource;\r
+ private boolean disposed = false;\r
+ \r
+ private IQueryListener listener = null;\r
+ \r
+ public NodeQuery(Resource r) {\r
+ this.nodeResource = r;\r
+ }\r
+ \r
+ protected abstract Object compute2(Graph graph);\r
+ \r
+ @Override\r
+ public Object performQuery(Graph graph) {\r
+ if (disposed) return null;\r
+ return compute2(graph);\r
+ }\r
+ \r
+ /**\r
+ * \r
+ * @param oldResult result of the query before the change.\r
+ * @param newResult result of the query after the change.\r
+ */\r
+ public abstract boolean updated(Graph graph, Object oldResult, Object newResult);\r
+ \r
+ @Override\r
+ public int getType() {\r
+ return IQuery.SCHEDULED_UPDATE;\r
+ }\r
+ \r
+ @Override\r
+ public void resultChangedRaw(final Object oldResult, final Object newResult) {\r
+ if (disposed)\r
+ throw new RuntimeException("Updating disposed query"); //return;\r
+ transactionRunner.run(new GraphRequestAdapter() {\r
+ @Override\r
+ public GraphRequestStatus perform(Graph g) throws Exception {\r
+ if (!disposed) {\r
+ if (oldResult == IQueryListener.NO_VALUE)\r
+ updated(g, null, newResult);\r
+ else\r
+ updated(g, oldResult, newResult);\r
+ } \r
+ return GraphRequestStatus.transactionComplete();\r
+ }\r
+ });\r
+\r
+ }\r
+ \r
+ \r
+ @Override\r
+ public boolean equalsQuery(IGraphQuery<Object> other) {\r
+ return nodeResource.equals(((NodeQuery)other).nodeResource);\r
+ }\r
+ \r
+ @Override\r
+ final public int hash() {\r
+ return nodeResource.hashCode();\r
+ }\r
+ \r
+ \r
+ /**\r
+ * Disposes the query\r
+ */\r
+ public void dispose() {\r
+ disposed = true;\r
+ if (DEBUG) System.out.println("NodeQuery " + nodeResource + " disposed()" + " " + this.getClass());\r
+ }\r
+ \r
+ @Override\r
+ public boolean isDisposed() {\r
+ return disposed;\r
+ }\r
+ \r
+ // without separate listener, this query would work only once\r
+ @Override\r
+ public IQueryListener getListener() {\r
+ if (listener == null) {\r
+ listener = new IQueryListener() {\r
+ @Override\r
+ public boolean isDisposed() {\r
+ return NodeQuery.this.disposed;\r
+ }\r
+ \r
+ @Override\r
+ public void resultChangedRaw(Object oldResult, Object newResult) {\r
+ NodeQuery.this.resultChangedRaw(oldResult, newResult);\r
+ }\r
+ };\r
+ }\r
+ return listener;\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * \r
+ * Query that tracks changes in scene-graph structure (parent/child relationships).\r
+ * \r
+ * @author Marko Luukkainen\r
+ *\r
+ */\r
+ public abstract class ScenegraphQuery extends NodeQuery {\r
+ \r
+ List<Resource> added = new ArrayList<Resource>();\r
+ List<Resource> removed = new ArrayList<Resource>();\r
+ \r
+ private boolean initialized;\r
+ \r
+ public ScenegraphQuery(Resource nodeResource) {\r
+ super(nodeResource);\r
+ initialized = false;\r
+ if(DEBUG)System.out.println("ScenegraphQuery created for " + nodeResource);\r
+ }\r
+ \r
+ @Override\r
+ public List<Resource> compute2(Graph g) {\r
+ IEntity node = EntityFactory.create(g,nodeResource);\r
+ Collection<IEntity> children = node.getRelatedObjects(Resources.g3dResource.HasChild);\r
+ List<Resource> list = new ArrayList<Resource>();\r
+ for (IEntity n: children)\r
+ list.add(n.getResource());\r
+ if (DEBUG) System.out.println("ScenegraphQuery " + nodeResource + " has " + list.size() + " children");\r
+ return list;\r
+ }\r
+ \r
+ @SuppressWarnings("unchecked")\r
+ @Override\r
+ public boolean updated(Graph graph, Object oldResult, Object newResult) {\r
+ List<Resource> oldChildren;\r
+ if (oldResult != null)\r
+ oldChildren = (List<Resource>)oldResult;\r
+ else\r
+ oldChildren = new ArrayList<Resource>();\r
+ List<Resource> newChildren = (List<Resource>)newResult;\r
+ \r
+ if (DEBUG) System.out.println("ScenegraphQuery " + nodeResource + " updated: had " + oldChildren.size() + " children, but now has " + newChildren.size() + " children");\r
+ added.clear();\r
+ removed.clear();\r
+ if (initialized) {\r
+ for (Resource r : oldChildren)\r
+ if (!newChildren.contains(r))\r
+ removed.add(r);\r
+ for (Resource r : newChildren)\r
+ if (!oldChildren.contains(r))\r
+ added.add(r);\r
+ for (Resource r : removed) {\r
+ if (DEBUG)\r
+ System.out.println("ScenegraphQuery " + nodeResource\r
+ + " removed " + r);\r
+ removeNode(nodeResource, r);\r
+ }\r
+ if (added.size() > 0) {\r
+ G3DNode parent = new G3DNode(graph, nodeResource);\r
+ /*\r
+ * try {\r
+ * \r
+ * G3DTools.propagateTransformChange(parent); } catch\r
+ * (Exception e) { ErrorLogger.defaultLogError(e); }\r
+ */\r
+ for (Resource r : added) {\r
+ IEntity e = EntityFactory.create(graph, r);\r
+ G3DTools.propagateLocalTransformChange(parent, e);\r
+ IGraphicsNode n = addNode(parent, e);\r
+ shapeAdded(graph, n);\r
+ }\r
+ }\r
+ return (added.size() > 0 || removed.size() > 0);\r
+ } else {\r
+ // when query is run for the first time, we can assume that transformations are correct.\r
+ initialized = true;\r
+ for (Resource r : newChildren)\r
+ added.add(r);\r
+ if (added.size() > 0) {\r
+ G3DNode parent = new G3DNode(graph, nodeResource);\r
+ for (Resource r : added) {\r
+ IEntity e = EntityFactory.create(graph, r);\r
+ IGraphicsNode n = addNode(parent, e);\r
+ shapeAdded(graph, n);\r
+ }\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+ \r
+ }\r
+ \r
+ \r
+ /**\r
+ * This method is run after a node is added to scene-graph.\r
+ * \r
+ * @param graph Graph of the current transaction.\r
+ * @param node the newly added scene-graph node\r
+ */\r
+ public abstract void shapeAdded(Graph graph,IGraphicsNode node);\r
+ \r
+// @Override\r
+// public void attach() {\r
+// scenegraphQueries.put(nodeResource, this);\r
+// }\r
+ \r
+ \r
+ }\r
+ \r
+ /**\r
+ * Tracks changes in scene-graph nodes' properties\r
+ * \r
+ * @author Marko Luukkainen\r
+ *\r
+ */\r
+ public abstract class NodePropertyQuery extends NodeQuery {\r
+ \r
+ private boolean initialized;\r
+ \r
+ public NodePropertyQuery(Resource nodeResource) {\r
+ super(nodeResource);\r
+ initialized = false;\r
+ if(DEBUG)System.out.println("NodePropertyQuery created for " + nodeResource);\r
+ }\r
+ \r
+ @Override\r
+ public List<Object> compute2(Graph g) {\r
+ IEntity t = EntityFactory.create(g,nodeResource);\r
+ \r
+ Collection<Property> properties = t.getRelatedProperties(Resources.g3dResource.HasNonTransformation);\r
+ List<Object> propertyValues = new ArrayList<Object>();\r
+ p(properties,propertyValues);\r
+\r
+ return propertyValues;\r
+ }\r
+ \r
+ private void p(Collection<Property> properties, List<Object> propertyValues) {\r
+ for (Property p : properties) {\r
+ Collection<Property> subProperties = p.getRelatedProperties(p.getGraph().getBuiltins().HasProperty);\r
+ if (subProperties.size() != 0) {\r
+ p(subProperties,propertyValues);\r
+ } \r
+ if (p.hasValue()){\r
+ propertyValues.add(p.getValue());\r
+ }\r
+ }\r
+ }\r
+ \r
+ @Override\r
+ public boolean updated(Graph graph, Object oldResult, Object newResult) {\r
+ if (initialized) {\r
+ if (DEBUG) System.out.println("NodePropertyQuery changed " + nodeResource + " " + abstractGraphicsNodes.size());\r
+ IGraphicsNode mo = abstractGraphicsNodes.get(nodeResource);\r
+ if (mo == null) {\r
+ if (DEBUG) System.out.println("NodePropertyQuery invalid change " + nodeResource + " " + abstractGraphicsNodes.size());\r
+ ErrorLogger.defaultLogError("Got update from resource " + nodeResource + " but its not part of the scenegraph", null);\r
+ dispose();\r
+ return false;\r
+ }\r
+ shapeUpdated(graph,mo);\r
+ return true;\r
+ } else {\r
+ initialized = true;\r
+ return false;\r
+ }\r
+ }\r
+\r
+ \r
+ /**\r
+ * This method is run when a scene-graph node is changed.\r
+ * \r
+ * @param shape the changed node\r
+ */\r
+ public abstract void shapeUpdated(Graph graph,IGraphicsNode shape);\r
+ \r
+// @Override\r
+// public void attach() {\r
+// propertyQueries.put(nodeResource, this);\r
+// }\r
+ \r
+ }\r
+ \r
+ public abstract class NodeTransformationQuery extends NodeQuery {\r
+ \r
+ private boolean initialized;\r
+ \r
+ public NodeTransformationQuery(Resource nodeResource) {\r
+ super(nodeResource);\r
+ initialized = false;\r
+ if(DEBUG)System.out.println("NodeTransformationQuery created for " + nodeResource);\r
+ }\r
+ \r
+ @Override\r
+ public List<Object> compute2(Graph g) {\r
+ IEntity t = EntityFactory.create(g,nodeResource);\r
+ \r
+ Collection<Property> properties = t.getRelatedProperties(Resources.g3dResource.HasTransformation);\r
+ \r
+ List<Object> propertyValues = new ArrayList<Object>();\r
+ p(properties,propertyValues);\r
+ return propertyValues;\r
+\r
+ }\r
+ \r
+ private void p(Collection<Property> properties, List<Object> propertyValues) {\r
+ for (Property p : properties) {\r
+ Collection<Property> subProperties = p.getRelatedProperties(p.getGraph().getBuiltins().HasProperty);\r
+ if (subProperties.size() != 0) {\r
+ p(subProperties,propertyValues);\r
+ } \r
+ if (p.hasValue()){\r
+ propertyValues.add(p.getValue());\r
+ }\r
+ }\r
+ }\r
+ \r
+ @Override\r
+ public boolean updated(Graph graph,Object oldResult, Object newResult) {\r
+ if (initialized) {\r
+ if (DEBUG) System.out.println("NodeTransformationQuery changed " + nodeResource + " " + abstractGraphicsNodes.size());\r
+\r
+ G3DTools.transformationUpdate(graph, nodeResource);\r
+\r
+ IGraphicsNode mo = abstractGraphicsNodes.get(nodeResource);\r
+ if (mo == null) {\r
+ if (DEBUG) System.out.println("NodeTransformationQuery invalid change " + nodeResource + " " + abstractGraphicsNodes.size());\r
+ ErrorLogger.defaultLogError("Got update from resource " + nodeResource + " but its not part of the scenegraph", null);\r
+ dispose();\r
+ return false;\r
+ }\r
+ shapeUpdated(graph,mo);\r
+ return true;\r
+ } else {\r
+ initialized = true;\r
+ return false;\r
+ }\r
+ }\r
+\r
+ \r
+ /**\r
+ * This method is run when a scene-graph node is changed.\r
+ * \r
+ * @param shape the changed node\r
+ */\r
+ public abstract void shapeUpdated(Graph graph,IGraphicsNode shape);\r
+ \r
+// @Override\r
+// public void attach() {\r
+// transformationQueries.put(nodeResource, this);\r
+// }\r
+ \r
+ }\r
+ \r
+\r
+}\r