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