From: Marko Luukkainen Date: Tue, 18 Apr 2017 10:46:51 +0000 (+0300) Subject: Adding exact INode type as generic parameter to NodeMap interface X-Git-Tag: v1.43.0~278 X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F17%2F417%2F1;p=simantics%2F3d.git Adding exact INode type as generic parameter to NodeMap interface fixes #7143 Change-Id: Ieaca3070bf9c27bfbbb2252f7e7e1a99c3aee846 --- diff --git a/org.simantics.g3d.csg/src/org/simantics/g3d/csg/scenegraph2/CSGrootNode.java b/org.simantics.g3d.csg/src/org/simantics/g3d/csg/scenegraph2/CSGrootNode.java index 58db64e7..d88b42ed 100644 --- a/org.simantics.g3d.csg/src/org/simantics/g3d/csg/scenegraph2/CSGrootNode.java +++ b/org.simantics.g3d.csg/src/org/simantics/g3d/csg/scenegraph2/CSGrootNode.java @@ -1,168 +1,168 @@ -/******************************************************************************* - * Copyright (c) 2012, 2013 Association for Decentralized Information Management in - * Industry THTH ry. - * 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.g3d.csg.scenegraph2; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; -import java.util.Stack; - -import javax.vecmath.Quat4d; -import javax.vecmath.Vector3d; - -import org.simantics.g3d.csg.editor.CSGNodeMap; -import org.simantics.g3d.csg.ontology.CSG; -import org.simantics.g3d.math.MathTools; -import org.simantics.g3d.scenegraph.IG3DNode; -import org.simantics.g3d.scenegraph.NodeMap; -import org.simantics.g3d.scenegraph.NodeMapProvider; -import org.simantics.g3d.scenegraph.base.NodeException; -import org.simantics.g3d.scenegraph.base.ParentNode; -import org.simantics.objmap.graph.annotations.GraphType; -import org.simantics.objmap.graph.annotations.RelatedElementsAdd; -import org.simantics.objmap.graph.annotations.RelatedElementsGet; -import org.simantics.objmap.graph.annotations.RelatedElementsRem; - -import vtk.vtkProp; - - -@GraphType(CSG.URIs.Model) -public class CSGrootNode extends ParentNode implements IG3DNode, NodeMapProvider { - - - private CSGNodeMap nodeMap; - - public void setNodeMap(CSGNodeMap nodeMap) { - this.nodeMap = nodeMap; - } - - @Override - public NodeMap getNodeMap() { - return nodeMap; - } - - @Override - public ParentNode getParent() { - return null; - } - - @Override - public ParentNode getRootNode() { - return this; - } - - @RelatedElementsAdd(CSG.URIs.hasChildShape) - public void addChild(ICSGnode node) { - addNode("child",node); - } - - @RelatedElementsGet(CSG.URIs.hasChildShape) - public Collection getChild() { - return getNodes("child"); - } - - @RelatedElementsRem(CSG.URIs.hasChildShape) - public void remChild(ICSGnode node) { - removeNode("child", node); - } - - public javax.vecmath.Quat4d getOrientation() { - return MathTools.getIdentityQuat(); - }; - - @Override - public Vector3d getPosition() { - return new Vector3d(); - } - - @Override - public Quat4d getWorldOrientation() { - return MathTools.getIdentityQuat(); - } - - @Override - public Vector3d getWorldPosition() { - return new Vector3d(); - } - - @Override - public Quat4d getWorldOrientation(Quat4d localOrientation) { - return localOrientation; - } - - @Override - public Vector3d getWorldPosition(Vector3d localPosition) { - return localPosition; - } - - @Override - public Quat4d getLocalOrientation(Quat4d worldOrientation) { - return worldOrientation; - } - - @Override - public Vector3d getLocalPosition(Vector3d worldPosition) { - return worldPosition; - } - - @Override - public void setPosition(Vector3d position) { - throw new NodeException("Cannot set root node position"); - } - - @Override - public void setOrientation(Quat4d orientation) { - throw new NodeException("Cannot set root node orientation"); - } - - @Override - public void setWorldOrientation(Quat4d orientation) { - throw new NodeException("Cannot set root node orientation"); - } - - @Override - public void setWorldPosition(Vector3d position) { - throw new NodeException("Cannot set root node orientation"); - } - - public String getUniqueName(String prefix) { - Set names = new HashSet(); - Stack nodes = new Stack(); - nodes.addAll(getChild()); - while (!nodes.isEmpty()) { - ICSGnode n = nodes.pop(); - names.add(((ICSGnode)n).getName()); - if (n instanceof CSGparentNode) { - nodes.addAll(((CSGparentNode)n).getChild()); - nodes.addAll(((CSGparentNode)n).getPrimaryChild()); - nodes.addAll(((CSGparentNode)n).getSecondaryChild()); - } - } - int i = 1; - while (true) { - String genName = prefix + "_" + i; - if (!names.contains(genName)) - return genName; - i++; - } - } - - - @Override - public Object getAdapter(Class adapter) { - if (NodeMap.class == adapter) - return nodeMap; - return null; - } - - -} +/******************************************************************************* + * Copyright (c) 2012, 2013 Association for Decentralized Information Management in + * Industry THTH ry. + * 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.g3d.csg.scenegraph2; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.Stack; + +import javax.vecmath.Quat4d; +import javax.vecmath.Vector3d; + +import org.simantics.g3d.csg.editor.CSGNodeMap; +import org.simantics.g3d.csg.ontology.CSG; +import org.simantics.g3d.math.MathTools; +import org.simantics.g3d.scenegraph.IG3DNode; +import org.simantics.g3d.scenegraph.NodeMap; +import org.simantics.g3d.scenegraph.NodeMapProvider; +import org.simantics.g3d.scenegraph.base.NodeException; +import org.simantics.g3d.scenegraph.base.ParentNode; +import org.simantics.objmap.graph.annotations.GraphType; +import org.simantics.objmap.graph.annotations.RelatedElementsAdd; +import org.simantics.objmap.graph.annotations.RelatedElementsGet; +import org.simantics.objmap.graph.annotations.RelatedElementsRem; + +import vtk.vtkProp; + + +@GraphType(CSG.URIs.Model) +public class CSGrootNode extends ParentNode implements IG3DNode, NodeMapProvider { + + + private CSGNodeMap nodeMap; + + public void setNodeMap(CSGNodeMap nodeMap) { + this.nodeMap = nodeMap; + } + + @Override + public NodeMap getNodeMap() { + return nodeMap; + } + + @Override + public ParentNode getParent() { + return null; + } + + @Override + public ParentNode getRootNode() { + return this; + } + + @RelatedElementsAdd(CSG.URIs.hasChildShape) + public void addChild(ICSGnode node) { + addNode("child",node); + } + + @RelatedElementsGet(CSG.URIs.hasChildShape) + public Collection getChild() { + return getNodes("child"); + } + + @RelatedElementsRem(CSG.URIs.hasChildShape) + public void remChild(ICSGnode node) { + removeNode("child", node); + } + + public javax.vecmath.Quat4d getOrientation() { + return MathTools.getIdentityQuat(); + }; + + @Override + public Vector3d getPosition() { + return new Vector3d(); + } + + @Override + public Quat4d getWorldOrientation() { + return MathTools.getIdentityQuat(); + } + + @Override + public Vector3d getWorldPosition() { + return new Vector3d(); + } + + @Override + public Quat4d getWorldOrientation(Quat4d localOrientation) { + return localOrientation; + } + + @Override + public Vector3d getWorldPosition(Vector3d localPosition) { + return localPosition; + } + + @Override + public Quat4d getLocalOrientation(Quat4d worldOrientation) { + return worldOrientation; + } + + @Override + public Vector3d getLocalPosition(Vector3d worldPosition) { + return worldPosition; + } + + @Override + public void setPosition(Vector3d position) { + throw new NodeException("Cannot set root node position"); + } + + @Override + public void setOrientation(Quat4d orientation) { + throw new NodeException("Cannot set root node orientation"); + } + + @Override + public void setWorldOrientation(Quat4d orientation) { + throw new NodeException("Cannot set root node orientation"); + } + + @Override + public void setWorldPosition(Vector3d position) { + throw new NodeException("Cannot set root node orientation"); + } + + public String getUniqueName(String prefix) { + Set names = new HashSet(); + Stack nodes = new Stack(); + nodes.addAll(getChild()); + while (!nodes.isEmpty()) { + ICSGnode n = nodes.pop(); + names.add(((ICSGnode)n).getName()); + if (n instanceof CSGparentNode) { + nodes.addAll(((CSGparentNode)n).getChild()); + nodes.addAll(((CSGparentNode)n).getPrimaryChild()); + nodes.addAll(((CSGparentNode)n).getSecondaryChild()); + } + } + int i = 1; + while (true) { + String genName = prefix + "_" + i; + if (!names.contains(genName)) + return genName; + i++; + } + } + + + @Override + public Object getAdapter(Class adapter) { + if (NodeMap.class == adapter) + return nodeMap; + return null; + } + + +} diff --git a/org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/common/AbstractVTKNodeMap.java b/org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/common/AbstractVTKNodeMap.java index d9c4b5b1..8b04a775 100644 --- a/org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/common/AbstractVTKNodeMap.java +++ b/org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/common/AbstractVTKNodeMap.java @@ -1,501 +1,502 @@ -/******************************************************************************* - * Copyright (c) 2012, 2013 Association for Decentralized Information Management in - * Industry THTH ry. - * 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.g3d.vtk.common; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Stack; - -import org.simantics.db.ReadGraph; -import org.simantics.db.Resource; -import org.simantics.db.Session; -import org.simantics.db.WriteGraph; -import org.simantics.db.common.request.ReadRequest; -import org.simantics.db.common.request.WriteRequest; -import org.simantics.db.exception.DatabaseException; -import org.simantics.g3d.ontology.G3D; -import org.simantics.g3d.scenegraph.IG3DNode; -import org.simantics.g3d.scenegraph.RenderListener; -import org.simantics.g3d.scenegraph.base.INode; -import org.simantics.g3d.scenegraph.base.NodeListener; -import org.simantics.g3d.scenegraph.base.ParentNode; -import org.simantics.objmap.graph.IMapping; -import org.simantics.objmap.graph.IMappingListener; -import org.simantics.utils.datastructures.Callback; -import org.simantics.utils.datastructures.MapList; -import org.simantics.utils.datastructures.MapSet; -import org.simantics.utils.datastructures.Pair; -import org.simantics.utils.ui.ExceptionUtils; - -import vtk.vtkProp; - -public abstract class AbstractVTKNodeMap implements VTKNodeMap, IMappingListener, RenderListener, NodeListener{ - - private static final boolean DEBUG = false; - - protected Session session; - protected IMapping mapping; - protected InteractiveVtkPanel panel; - - protected MapList nodeToActor = new MapList(); - protected Map actorToNode = new HashMap(); - - protected ParentNode rootNode; - - public AbstractVTKNodeMap(Session session, IMapping mapping, InteractiveVtkPanel panel, ParentNode rootNode) { - this.session = session; - this.mapping = mapping; - this.panel = panel; - this.rootNode = rootNode; - panel.addListener(this); - mapping.addMappingListener(this); - rootNode.addListener(this); - } - - - protected abstract void addActor(E node); - protected abstract void removeActor(E node); - protected abstract void updateActor(E node,Set ids); - - public void repaint() { - panel.repaint(); - } - - public void populate() { - for (E node : rootNode.getNodes()) { - receiveAdd(node, node.getParentRel(),true); - } - repaint(); - } - - @Override - public INode getNode(vtkProp prop) { - return actorToNode.get(prop); - } - - @SuppressWarnings("unchecked") - @Override - public Collection getRenderObjects(INode node) { - return nodeToActor.getValues((E)node); - } - - @SuppressWarnings("unchecked") - @Override - public ParentNode getRootNode() { - return (ParentNode)rootNode; - } - - - - @Override - public boolean isChangeTracking() { - return changeTracking; - } - - @Override - public void setChangeTracking(boolean enabled) { - changeTracking = enabled; - } - - private boolean changeTracking = true; - - protected Object syncMutex = new Object(); - - - private List> added = new ArrayList>(); - private List> removed = new ArrayList>(); - //private List> updated = new ArrayList>(); - private MapSet updated = new MapSet.Hash(); - - private boolean rangeModified = false; - - @SuppressWarnings("unchecked") - @Override - public void updateRenderObjectsFor(INode node) { - List toDelete = new ArrayList(); - for (vtkProp prop : nodeToActor.getValues((E)node)) { - if (prop.GetVTKId() != 0) { - panel.GetRenderer().RemoveActor(prop); - //prop.Delete(); - toDelete.add(prop); - } - actorToNode.remove(prop); - } - nodeToActor.remove((E)node); - Collection coll = getActors((E)node); - if (coll == null) - return; - for (vtkProp prop : coll) { - nodeToActor.add((E)node,prop); - actorToNode.put(prop, (E)node); - toDelete.remove(prop); - } - for (vtkProp p : toDelete) - p.Delete(); - } - - protected abstract Collection getActors(E node); - - @SuppressWarnings("unchecked") - private void receiveAdd(E node, String id, boolean db) { - if (DEBUG) System.out.println("receiveAdd " + node + " " + id + " " + db); - synchronized (syncMutex) { - for (Pair n : added) { - if (n.first.equals(node)) - return; - } - if (changeTracking) { - mapping.rangeModified((E)node.getParent()); - } - added.add(new Pair(node, id)); - rangeModified = true; - } - panel.repaint(); - } - - @SuppressWarnings("unchecked") - private void receiveRemove(E node, String id, boolean db) { - if (DEBUG) System.out.println("receiveRemove " + node + " " + id + " " + db); - synchronized (syncMutex) { - for (Pair n : removed) { - if (n.first.equals(node)) - return; - } - if (changeTracking && !db) - mapping.rangeModified((E)node.getParent()); - removed.add(new Pair(node, id)); - rangeModified = true; - } - panel.repaint(); - } - - @SuppressWarnings("unchecked") - private void receiveUpdate(E node, String id, boolean db) { - if (DEBUG) System.out.println("receiveUpdate " + node + " " + id + " " + db); - synchronized (syncMutex) { -// for (Pair n : updated) { -// if (n.first.equals(node)) -// return; -// } - if (changeTracking && !db) - mapping.rangeModified(node); - //updated.add(new Pair(node, id)); - updated.add(node, id); - rangeModified = true; - } - panel.repaint(); - } - - private boolean graphUpdates = false; - private Set graphModified = new HashSet(); - - private boolean requestCommit = false; - - @Override - public void commit() { - requestCommit = true; - } - - protected void doCommit() { - session.asyncRequest(new WriteRequest() { - - @Override - public void perform(WriteGraph graph) throws DatabaseException { - commit(graph); - } - - }, new Callback() { - - @Override - public void run(DatabaseException parameter) { - if (parameter != null) - ExceptionUtils.logAndShowError("Cannot commit editor changes", parameter); - } - }); - } - - protected void commit(WriteGraph graph) throws DatabaseException { - synchronized(syncMutex) { - if (DEBUG) System.out.println("Commit"); - graphUpdates = true; - mapping.updateDomain(graph); - graphUpdates = false; - } - } - - @Override - public void domainModified() { - if (graphUpdates) - return; - if (DEBUG)System.out.println("domainModified"); - session.asyncRequest(new ReadRequest() { - - @SuppressWarnings("unchecked") - @Override - public void run(ReadGraph graph) throws DatabaseException { - update(graph); - } - }); - - } - - protected void update(ReadGraph graph) throws DatabaseException { - synchronized (syncMutex) { - graphUpdates = true; - for (Object domainObject : mapping.getDomainModified()) { - E rangeObject = mapping.get(domainObject); - if (rangeObject != null) - graphModified.add(rangeObject); - } - mapping.updateRange(graph); - graphModified.clear(); - graphUpdates = false; - } - - if (mapping.isRangeModified()) - commit(); - } - - @Override - public void rangeModified() { - //System.out.println("rangeModified"); - - } - - @Override - public void postRender() { - // Commit changes if - // 1. Commit has been requested - // 2. There are no pending changes that should be processed in preRender() - if (requestCommit && !rangeModified) { // FIXME : not thread safe. - requestCommit = false; - doCommit(); - } - } - - List> rem = new ArrayList>(); - List> add = new ArrayList>(); - MapSet mod = new MapSet.Hash(); - Set propagation = new HashSet(); - Stack stack = new Stack(); - - - @Override - public synchronized void preRender() { - updateCycle(); - } - - @SuppressWarnings("unchecked") - protected void updateCycle() { - rem.clear(); - add.clear(); - mod.clear(); - propagation.clear(); - - synchronized (syncMutex) { - rem.addAll(removed); - add.addAll(added); - for (E e : updated.getKeys()) { - for (String s : updated.getValues(e)) { - mod.add(e, s); - } - } - - removed.clear(); - added.clear(); - updated.clear(); - } - - for (Pair n : rem) { - stopListening(n.first); - removeActor(n.first); - - } - - for (Pair n : add) { - addActor(n.first); - listen(n.first); - } - - for (E e : mod.getKeys()) { - Set ids = mod.getValues(e); - if (ids.contains(G3D.URIs.hasPosition) || ids.contains(G3D.URIs.hasOrientation)) { - if (!propagation.contains(e)) - propagation.add(e); - } - } - - if (propagation.size() > 0) { - stack.clear(); - stack.addAll(propagation); - propagation.clear(); - while (!stack.isEmpty()) { - E node = stack.pop(); - if (propagation.contains(node)) - continue; - propagation.add(node); - for (NodeListener l : node.getListeners()) { - if (l == this) { - //changeTracking = false; - //l.propertyChanged(node, G3D.URIs.hasPosition); - //changeTracking = true; - } else { - l.propertyChanged(node, G3D.URIs.hasWorldPosition); - } - } - if (node instanceof ParentNode) { - stack.addAll(((ParentNode)node).getNodes()); - } - } - } - -// synchronized (syncMutex) { -// rem.addAll(removed); -// add.addAll(added); -// //mod.addAll(updated); -// for (E e : updated.getKeys()) { -// for (String s : updated.getValues(e)) -// mod.add(e, s); -// } -// -// removed.clear(); -// added.clear(); -// updated.clear(); -// } - - for (E e : mod.getKeys()) { - Set ids = mod.getValues(e); - updateActor(e,ids); - } - - - for (Pair n : rem) { - for (NodeListener l : nodeListeners) - l.nodeRemoved(null, n.first, n.second); - } - for (Pair n : add) { - for (NodeListener l : nodeListeners) - l.nodeAdded(n.first.getParent(), n.first, n.second); - } -// for (Pair n : mod) { -// for (NodeListener l : nodeListeners) -// l.propertyChanged(n.first, n.second); -// } - for (E e : mod.getKeys()) { - for (NodeListener l : nodeListeners) - for (String s : mod.getValues(e)) - l.propertyChanged(e, s); - } - synchronized (syncMutex) { - if (added.isEmpty() && removed.isEmpty() && updated.getKeys().size() == 0) - rangeModified = false; - } - } - - @SuppressWarnings("unchecked") - private void listen(INode node) { - node.addListener(this); - if (node instanceof ParentNode) { - ParentNode parentNode = (ParentNode)node; - for (INode n : parentNode.getNodes()) - listen(n); - } - } - - private void stopListening(INode node) { - node.removeListener(this); - if (node instanceof ParentNode) { - @SuppressWarnings("unchecked") - ParentNode parentNode = (ParentNode)node; - for (INode n : parentNode.getNodes()) - stopListening(n); - } - } - - @SuppressWarnings("unchecked") - @Override - public void propertyChanged(INode node, String id) { - //receiveUpdate((E)node, id, graphUpdates); - receiveUpdate((E)node, id, graphModified.contains(node)); - - } - - @SuppressWarnings("unchecked") - @Override - public void nodeAdded(ParentNode node, INode child, - String rel) { - if (DEBUG) System.out.println("Node added " + child + " parent " + node); - //receiveAdd((E)child, rel ,graphUpdates); - receiveAdd((E)child, rel ,graphModified.contains(node)); - - } - - @SuppressWarnings("unchecked") - @Override - public void nodeRemoved(ParentNode node, INode child, - String rel) { - if (DEBUG) System.out.println("Node removed " + child + " parent " + node); - //receiveRemove((E)child, rel, graphUpdates); - receiveRemove((E)child, rel, graphModified.contains(node)); - - //FIXME : sometimes removed structural models cause ObjMap to add their children again. - // removing the listener here prevents corruption of visual model, but better fix is needed. - stopListening(child); - } - - @Override - public void delete() { - changeTracking = false; - panel.removeListener(this); - mapping.removeMappingListener(this); - - List nodes = new ArrayList(nodeToActor.getKeySize()); - nodes.addAll(nodeToActor.getKeys()); - for (E node : nodes) { - node.removeListener(this); - removeActor(node); - node.cleanup(); - } - for (vtkProp prop : actorToNode.keySet()) { - if (prop.GetVTKId() != 0) - prop.Delete(); - } - actorToNode.clear(); - nodeToActor.clear(); - - } - - - private List nodeListeners = new ArrayList(); - @Override - public void addListener(NodeListener listener) { - nodeListeners.add(listener); - - } - - @Override - public void removeListener(NodeListener listener) { - nodeListeners.remove(listener); - - } - - public IMapping getMapping() { - return mapping; - } - - -} +/******************************************************************************* + * Copyright (c) 2012, 2013 Association for Decentralized Information Management in + * Industry THTH ry. + * 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.g3d.vtk.common; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; + +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.Session; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.request.ReadRequest; +import org.simantics.db.common.request.WriteRequest; +import org.simantics.db.exception.DatabaseException; +import org.simantics.g3d.ontology.G3D; +import org.simantics.g3d.scenegraph.G3DNode; +import org.simantics.g3d.scenegraph.IG3DNode; +import org.simantics.g3d.scenegraph.RenderListener; +import org.simantics.g3d.scenegraph.base.INode; +import org.simantics.g3d.scenegraph.base.NodeListener; +import org.simantics.g3d.scenegraph.base.ParentNode; +import org.simantics.objmap.graph.IMapping; +import org.simantics.objmap.graph.IMappingListener; +import org.simantics.utils.datastructures.Callback; +import org.simantics.utils.datastructures.MapList; +import org.simantics.utils.datastructures.MapSet; +import org.simantics.utils.datastructures.Pair; +import org.simantics.utils.ui.ExceptionUtils; + +import vtk.vtkProp; + +public abstract class AbstractVTKNodeMap implements VTKNodeMap, IMappingListener, RenderListener, NodeListener{ + + private static final boolean DEBUG = false; + + protected Session session; + protected IMapping mapping; + protected InteractiveVtkPanel panel; + + protected MapList nodeToActor = new MapList(); + protected Map actorToNode = new HashMap(); + + protected ParentNode rootNode; + + public AbstractVTKNodeMap(Session session, IMapping mapping, InteractiveVtkPanel panel, ParentNode rootNode) { + this.session = session; + this.mapping = mapping; + this.panel = panel; + this.rootNode = rootNode; + panel.addListener(this); + mapping.addMappingListener(this); + rootNode.addListener(this); + } + + + protected abstract void addActor(E node); + protected abstract void removeActor(E node); + protected abstract void updateActor(E node,Set ids); + + public void repaint() { + panel.repaint(); + } + + public void populate() { + for (E node : rootNode.getNodes()) { + receiveAdd(node, node.getParentRel(),true); + } + repaint(); + } + + @Override + public E getNode(vtkProp prop) { + return actorToNode.get(prop); + } + + @SuppressWarnings("unchecked") + @Override + public Collection getRenderObjects(IG3DNode node) { + return nodeToActor.getValues((E)node); + } + + @SuppressWarnings("unchecked") + @Override + public ParentNode getRootNode() { + return (ParentNode)rootNode; + } + + + + @Override + public boolean isChangeTracking() { + return changeTracking; + } + + @Override + public void setChangeTracking(boolean enabled) { + changeTracking = enabled; + } + + private boolean changeTracking = true; + + protected Object syncMutex = new Object(); + + + private List> added = new ArrayList>(); + private List> removed = new ArrayList>(); + //private List> updated = new ArrayList>(); + private MapSet updated = new MapSet.Hash(); + + private boolean rangeModified = false; + + @SuppressWarnings("unchecked") + @Override + public void updateRenderObjectsFor(IG3DNode node) { + List toDelete = new ArrayList(); + for (vtkProp prop : nodeToActor.getValues((E)node)) { + if (prop.GetVTKId() != 0) { + panel.GetRenderer().RemoveActor(prop); + //prop.Delete(); + toDelete.add(prop); + } + actorToNode.remove(prop); + } + nodeToActor.remove((E)node); + Collection coll = getActors((E)node); + if (coll == null) + return; + for (vtkProp prop : coll) { + nodeToActor.add((E)node,prop); + actorToNode.put(prop, (E)node); + toDelete.remove(prop); + } + for (vtkProp p : toDelete) + p.Delete(); + } + + protected abstract Collection getActors(E node); + + @SuppressWarnings("unchecked") + private void receiveAdd(E node, String id, boolean db) { + if (DEBUG) System.out.println("receiveAdd " + node + " " + id + " " + db); + synchronized (syncMutex) { + for (Pair n : added) { + if (n.first.equals(node)) + return; + } + if (changeTracking) { + mapping.rangeModified((E)node.getParent()); + } + added.add(new Pair(node, id)); + rangeModified = true; + } + panel.repaint(); + } + + @SuppressWarnings("unchecked") + private void receiveRemove(E node, String id, boolean db) { + if (DEBUG) System.out.println("receiveRemove " + node + " " + id + " " + db); + synchronized (syncMutex) { + for (Pair n : removed) { + if (n.first.equals(node)) + return; + } + if (changeTracking && !db) + mapping.rangeModified((E)node.getParent()); + removed.add(new Pair(node, id)); + rangeModified = true; + } + panel.repaint(); + } + + @SuppressWarnings("unchecked") + private void receiveUpdate(E node, String id, boolean db) { + if (DEBUG) System.out.println("receiveUpdate " + node + " " + id + " " + db); + synchronized (syncMutex) { +// for (Pair n : updated) { +// if (n.first.equals(node)) +// return; +// } + if (changeTracking && !db) + mapping.rangeModified(node); + //updated.add(new Pair(node, id)); + updated.add(node, id); + rangeModified = true; + } + panel.repaint(); + } + + private boolean graphUpdates = false; + private Set graphModified = new HashSet(); + + private boolean requestCommit = false; + + @Override + public void commit() { + requestCommit = true; + } + + protected void doCommit() { + session.asyncRequest(new WriteRequest() { + + @Override + public void perform(WriteGraph graph) throws DatabaseException { + commit(graph); + } + + }, new Callback() { + + @Override + public void run(DatabaseException parameter) { + if (parameter != null) + ExceptionUtils.logAndShowError("Cannot commit editor changes", parameter); + } + }); + } + + protected void commit(WriteGraph graph) throws DatabaseException { + synchronized(syncMutex) { + if (DEBUG) System.out.println("Commit"); + graphUpdates = true; + mapping.updateDomain(graph); + graphUpdates = false; + } + } + + @Override + public void domainModified() { + if (graphUpdates) + return; + if (DEBUG)System.out.println("domainModified"); + session.asyncRequest(new ReadRequest() { + + @SuppressWarnings("unchecked") + @Override + public void run(ReadGraph graph) throws DatabaseException { + update(graph); + } + }); + + } + + protected void update(ReadGraph graph) throws DatabaseException { + synchronized (syncMutex) { + graphUpdates = true; + for (Object domainObject : mapping.getDomainModified()) { + E rangeObject = mapping.get(domainObject); + if (rangeObject != null) + graphModified.add(rangeObject); + } + mapping.updateRange(graph); + graphModified.clear(); + graphUpdates = false; + } + + if (mapping.isRangeModified()) + commit(); + } + + @Override + public void rangeModified() { + //System.out.println("rangeModified"); + + } + + @Override + public void postRender() { + // Commit changes if + // 1. Commit has been requested + // 2. There are no pending changes that should be processed in preRender() + if (requestCommit && !rangeModified) { // FIXME : not thread safe. + requestCommit = false; + doCommit(); + } + } + + List> rem = new ArrayList>(); + List> add = new ArrayList>(); + MapSet mod = new MapSet.Hash(); + Set propagation = new HashSet(); + Stack stack = new Stack(); + + + @Override + public synchronized void preRender() { + updateCycle(); + } + + @SuppressWarnings("unchecked") + protected void updateCycle() { + rem.clear(); + add.clear(); + mod.clear(); + propagation.clear(); + + synchronized (syncMutex) { + rem.addAll(removed); + add.addAll(added); + for (E e : updated.getKeys()) { + for (String s : updated.getValues(e)) { + mod.add(e, s); + } + } + + removed.clear(); + added.clear(); + updated.clear(); + } + + for (Pair n : rem) { + stopListening(n.first); + removeActor(n.first); + + } + + for (Pair n : add) { + addActor(n.first); + listen(n.first); + } + + for (E e : mod.getKeys()) { + Set ids = mod.getValues(e); + if (ids.contains(G3D.URIs.hasPosition) || ids.contains(G3D.URIs.hasOrientation)) { + if (!propagation.contains(e)) + propagation.add(e); + } + } + + if (propagation.size() > 0) { + stack.clear(); + stack.addAll(propagation); + propagation.clear(); + while (!stack.isEmpty()) { + E node = stack.pop(); + if (propagation.contains(node)) + continue; + propagation.add(node); + for (NodeListener l : node.getListeners()) { + if (l == this) { + //changeTracking = false; + //l.propertyChanged(node, G3D.URIs.hasPosition); + //changeTracking = true; + } else { + l.propertyChanged(node, G3D.URIs.hasWorldPosition); + } + } + if (node instanceof ParentNode) { + stack.addAll(((ParentNode)node).getNodes()); + } + } + } + +// synchronized (syncMutex) { +// rem.addAll(removed); +// add.addAll(added); +// //mod.addAll(updated); +// for (E e : updated.getKeys()) { +// for (String s : updated.getValues(e)) +// mod.add(e, s); +// } +// +// removed.clear(); +// added.clear(); +// updated.clear(); +// } + + for (E e : mod.getKeys()) { + Set ids = mod.getValues(e); + updateActor(e,ids); + } + + + for (Pair n : rem) { + for (NodeListener l : nodeListeners) + l.nodeRemoved(null, n.first, n.second); + } + for (Pair n : add) { + for (NodeListener l : nodeListeners) + l.nodeAdded(n.first.getParent(), n.first, n.second); + } +// for (Pair n : mod) { +// for (NodeListener l : nodeListeners) +// l.propertyChanged(n.first, n.second); +// } + for (E e : mod.getKeys()) { + for (NodeListener l : nodeListeners) + for (String s : mod.getValues(e)) + l.propertyChanged(e, s); + } + synchronized (syncMutex) { + if (added.isEmpty() && removed.isEmpty() && updated.getKeys().size() == 0) + rangeModified = false; + } + } + + @SuppressWarnings("unchecked") + private void listen(INode node) { + node.addListener(this); + if (node instanceof ParentNode) { + ParentNode parentNode = (ParentNode)node; + for (INode n : parentNode.getNodes()) + listen(n); + } + } + + private void stopListening(INode node) { + node.removeListener(this); + if (node instanceof ParentNode) { + @SuppressWarnings("unchecked") + ParentNode parentNode = (ParentNode)node; + for (INode n : parentNode.getNodes()) + stopListening(n); + } + } + + @SuppressWarnings("unchecked") + @Override + public void propertyChanged(INode node, String id) { + //receiveUpdate((E)node, id, graphUpdates); + receiveUpdate((E)node, id, graphModified.contains(node)); + + } + + @SuppressWarnings("unchecked") + @Override + public void nodeAdded(ParentNode node, INode child, + String rel) { + if (DEBUG) System.out.println("Node added " + child + " parent " + node); + //receiveAdd((E)child, rel ,graphUpdates); + receiveAdd((E)child, rel ,graphModified.contains(node)); + + } + + @SuppressWarnings("unchecked") + @Override + public void nodeRemoved(ParentNode node, INode child, + String rel) { + if (DEBUG) System.out.println("Node removed " + child + " parent " + node); + //receiveRemove((E)child, rel, graphUpdates); + receiveRemove((E)child, rel, graphModified.contains(node)); + + //FIXME : sometimes removed structural models cause ObjMap to add their children again. + // removing the listener here prevents corruption of visual model, but better fix is needed. + stopListening(child); + } + + @Override + public void delete() { + changeTracking = false; + panel.removeListener(this); + mapping.removeMappingListener(this); + + List nodes = new ArrayList(nodeToActor.getKeySize()); + nodes.addAll(nodeToActor.getKeys()); + for (E node : nodes) { + node.removeListener(this); + removeActor(node); + node.cleanup(); + } + for (vtkProp prop : actorToNode.keySet()) { + if (prop.GetVTKId() != 0) + prop.Delete(); + } + actorToNode.clear(); + nodeToActor.clear(); + + } + + + private List nodeListeners = new ArrayList(); + @Override + public void addListener(NodeListener listener) { + nodeListeners.add(listener); + + } + + @Override + public void removeListener(NodeListener listener) { + nodeListeners.remove(listener); + + } + + public IMapping getMapping() { + return mapping; + } + + +} diff --git a/org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/common/SelectionHighlighter.java b/org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/common/SelectionHighlighter.java index 2d5f8fbe..95ba7aee 100644 --- a/org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/common/SelectionHighlighter.java +++ b/org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/common/SelectionHighlighter.java @@ -1,199 +1,199 @@ -/******************************************************************************* - * Copyright (c) 2012, 2013 Association for Decentralized Information Management in - * Industry THTH ry. - * 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.g3d.vtk.common; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.SelectionChangedEvent; -import org.simantics.g3d.scenegraph.IG3DNode; -import org.simantics.g3d.scenegraph.NodeHighlighter; -import org.simantics.g3d.scenegraph.NodeHighlighter.HighlightEventType; -import org.simantics.g3d.scenegraph.NodeHighlighter.HighlightObjectType; -import org.simantics.g3d.scenegraph.base.INode; -import org.simantics.g3d.tools.AdaptationUtils; -import org.simantics.utils.threads.AWTThread; -import org.simantics.utils.threads.ThreadUtils; - -import vtk.vtkActor; -import vtk.vtkAlgorithm; -import vtk.vtkAlgorithmOutput; -import vtk.vtkFeatureEdges; -import vtk.vtkMapper; -import vtk.vtkPanel; -import vtk.vtkProp; -import vtk.vtkProperty; - -public class SelectionHighlighter implements ISelectionChangedListener{ - - - - vtkPanel panel; - VTKNodeMap nodeMap; - - List selectedNodes = new ArrayList(); - List selectedActors = new ArrayList(); - - HighlightObjectType type = HighlightObjectType.Node; - - public SelectionHighlighter(vtkPanel panel, VTKNodeMap nodeMap) { - this.panel = panel; - this.nodeMap = nodeMap; - } - - @Override - public void selectionChanged(SelectionChangedEvent event) { - final ISelection s = event.getSelection(); - - if (Thread.currentThread().equals(AWTThread.getThreadAccess().getThread())) - highlight(s); - else { - ThreadUtils.asyncExec(AWTThread.getThreadAccess(), new Runnable() { - @Override - public void run() { - highlight(s); - //System.out.println(this.getClass().getName() + " highlight "); - panel.Render(); - //panel.repaint(); - } - }); - } - - } - - protected void hilight(IG3DNode node, HighlightEventType type) { - if (node instanceof NodeHighlighter) { - ((NodeHighlighter)node).highlight(type); - return; - } - if (type == HighlightEventType.Selection) { - setSelectedColor(node); - } else if (type == HighlightEventType.ClearSelection) { - setDefaultColor(node); - } - } - - protected void hilight(vtkActor actor, HighlightEventType type) { - if (type == HighlightEventType.Selection) { - setColor(actor,false,new double[]{1,0,0}); - setColor(actor,true,new double[]{1,0,1}); - } else if (type == HighlightEventType.ClearSelection) { - setColor(actor,false,new double[]{1,1,0}); - setColor(actor,true,new double[]{0,0,0}); - } - } - - protected void highlight(ISelection s) { - highlight(s, HighlightEventType.Selection, HighlightEventType.ClearSelection); - } - - protected void highlight(ISelection s, HighlightEventType apply, HighlightEventType clear) { - - boolean changed = false; - if (type == HighlightObjectType.Node) { - Collection currentSelectedNodes = AdaptationUtils.adaptToCollection(s,IG3DNode.class);//getSelectedNodes(currentSelectedActors); - if (currentSelectedNodes.size() == 0) { - Collection currentSelectedActors = AdaptationUtils.adaptToCollection(s, vtkProp.class); - currentSelectedNodes = getSelectedNodes(currentSelectedActors); - } - for (IG3DNode node : selectedNodes) { - if (!currentSelectedNodes.contains(node)) { - hilight(node, clear); - changed = true; - } - } - for (IG3DNode node : currentSelectedNodes) { - if (!selectedNodes.contains(node)) { - hilight(node, apply); - changed = true; - } - } - selectedNodes.clear(); - selectedNodes.addAll(currentSelectedNodes); - //selectedNodes = currentSelectedNodes; - - } else { - - Collection currentSelectedActors = AdaptationUtils.adaptToCollection(s, vtkActor.class); - - for (vtkActor act : selectedActors) { - if (!currentSelectedActors.contains(act)) { - hilight(act,clear); - changed = true; - } - } - for (vtkActor act : currentSelectedActors) { - if (!selectedActors.contains(act)) { - hilight(act,apply); - changed = true; - } - } - selectedActors.clear(); - selectedActors.addAll(currentSelectedActors); - } - if (changed) { - panel.repaint(); - } - } - - protected List getSelectedNodes(Collection selectedActors) { - List currentSelectedNodes = new ArrayList(); - - for (vtkProp a : selectedActors) { - INode node = nodeMap.getNode((vtkProp)a); - if (node == null || !(node instanceof IG3DNode)) - continue; - if (!currentSelectedNodes.contains(node)) - currentSelectedNodes.add((IG3DNode)node); - } - return currentSelectedNodes; - } - - protected void setDefaultColor(IG3DNode node) { - double color[] = new double[]{1,1,0}; - setColor(node, false, color); - } - - protected void setSelectedColor(IG3DNode node) { - double color[] = new double[]{1,0,0}; - setColor(node, false, color); - } - - - protected void setColor(IG3DNode node, boolean edge, double color[]) { - for (vtkProp prop : nodeMap.getRenderObjects(node)) { - if (prop instanceof vtkActor) { - vtkActor act = (vtkActor)prop; - setColor(act, edge, color); - } - } - } - - protected void setColor(vtkActor act, boolean edge, double color[]) { - - vtkMapper mapper = act.GetMapper(); - vtkAlgorithmOutput out = mapper.GetInputConnection(0, 0); - vtkAlgorithm producer = out.GetProducer(); - boolean isEdge = (producer instanceof vtkFeatureEdges); - producer.Delete(); - if (isEdge == edge) { - vtkProperty property = act.GetProperty(); - property.SetColor(color); - property.Delete(); - } - out.Delete(); - mapper.Delete(); - } -} +/******************************************************************************* + * Copyright (c) 2012, 2013 Association for Decentralized Information Management in + * Industry THTH ry. + * 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.g3d.vtk.common; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.simantics.g3d.scenegraph.IG3DNode; +import org.simantics.g3d.scenegraph.NodeHighlighter; +import org.simantics.g3d.scenegraph.NodeHighlighter.HighlightEventType; +import org.simantics.g3d.scenegraph.NodeHighlighter.HighlightObjectType; +import org.simantics.g3d.scenegraph.base.INode; +import org.simantics.g3d.tools.AdaptationUtils; +import org.simantics.utils.threads.AWTThread; +import org.simantics.utils.threads.ThreadUtils; + +import vtk.vtkActor; +import vtk.vtkAlgorithm; +import vtk.vtkAlgorithmOutput; +import vtk.vtkFeatureEdges; +import vtk.vtkMapper; +import vtk.vtkPanel; +import vtk.vtkProp; +import vtk.vtkProperty; + +public class SelectionHighlighter implements ISelectionChangedListener{ + + + + vtkPanel panel; + VTKNodeMap nodeMap; + + List selectedNodes = new ArrayList(); + List selectedActors = new ArrayList(); + + HighlightObjectType type = HighlightObjectType.Node; + + public SelectionHighlighter(vtkPanel panel, VTKNodeMap nodeMap) { + this.panel = panel; + this.nodeMap = nodeMap; + } + + @Override + public void selectionChanged(SelectionChangedEvent event) { + final ISelection s = event.getSelection(); + + if (Thread.currentThread().equals(AWTThread.getThreadAccess().getThread())) + highlight(s); + else { + ThreadUtils.asyncExec(AWTThread.getThreadAccess(), new Runnable() { + @Override + public void run() { + highlight(s); + //System.out.println(this.getClass().getName() + " highlight "); + panel.Render(); + //panel.repaint(); + } + }); + } + + } + + protected void hilight(E node, HighlightEventType type) { + if (node instanceof NodeHighlighter) { + ((NodeHighlighter)node).highlight(type); + return; + } + if (type == HighlightEventType.Selection) { + setSelectedColor(node); + } else if (type == HighlightEventType.ClearSelection) { + setDefaultColor(node); + } + } + + protected void hilight(vtkActor actor, HighlightEventType type) { + if (type == HighlightEventType.Selection) { + setColor(actor,false,new double[]{1,0,0}); + setColor(actor,true,new double[]{1,0,1}); + } else if (type == HighlightEventType.ClearSelection) { + setColor(actor,false,new double[]{1,1,0}); + setColor(actor,true,new double[]{0,0,0}); + } + } + + protected void highlight(ISelection s) { + highlight(s, HighlightEventType.Selection, HighlightEventType.ClearSelection); + } + + protected void highlight(ISelection s, HighlightEventType apply, HighlightEventType clear) { + + boolean changed = false; + if (type == HighlightObjectType.Node) { + Collection currentSelectedNodes = AdaptationUtils.adaptToCollection(s,IG3DNode.class);//getSelectedNodes(currentSelectedActors); + if (currentSelectedNodes.size() == 0) { + Collection currentSelectedActors = AdaptationUtils.adaptToCollection(s, vtkProp.class); + currentSelectedNodes = getSelectedNodes(currentSelectedActors); + } + for (IG3DNode node : selectedNodes) { + if (!currentSelectedNodes.contains(node)) { + hilight((E)node, clear); + changed = true; + } + } + for (IG3DNode node : currentSelectedNodes) { + if (!selectedNodes.contains(node)) { + hilight((E)node, apply); + changed = true; + } + } + selectedNodes.clear(); + selectedNodes.addAll(currentSelectedNodes); + //selectedNodes = currentSelectedNodes; + + } else { + + Collection currentSelectedActors = AdaptationUtils.adaptToCollection(s, vtkActor.class); + + for (vtkActor act : selectedActors) { + if (!currentSelectedActors.contains(act)) { + hilight(act,clear); + changed = true; + } + } + for (vtkActor act : currentSelectedActors) { + if (!selectedActors.contains(act)) { + hilight(act,apply); + changed = true; + } + } + selectedActors.clear(); + selectedActors.addAll(currentSelectedActors); + } + if (changed) { + panel.repaint(); + } + } + + protected List getSelectedNodes(Collection selectedActors) { + List currentSelectedNodes = new ArrayList(); + + for (vtkProp a : selectedActors) { + INode node = nodeMap.getNode((vtkProp)a); + if (node == null || !(node instanceof IG3DNode)) + continue; + if (!currentSelectedNodes.contains(node)) + currentSelectedNodes.add((IG3DNode)node); + } + return currentSelectedNodes; + } + + protected void setDefaultColor(E node) { + double color[] = new double[]{1,1,0}; + setColor(node, false, color); + } + + protected void setSelectedColor(E node) { + double color[] = new double[]{1,0,0}; + setColor(node, false, color); + } + + + protected void setColor(E node, boolean edge, double color[]) { + for (vtkProp prop : nodeMap.getRenderObjects(node)) { + if (prop instanceof vtkActor) { + vtkActor act = (vtkActor)prop; + setColor(act, edge, color); + } + } + } + + protected void setColor(vtkActor act, boolean edge, double color[]) { + + vtkMapper mapper = act.GetMapper(); + vtkAlgorithmOutput out = mapper.GetInputConnection(0, 0); + vtkAlgorithm producer = out.GetProducer(); + boolean isEdge = (producer instanceof vtkFeatureEdges); + producer.Delete(); + if (isEdge == edge) { + vtkProperty property = act.GetProperty(); + property.SetColor(color); + property.Delete(); + } + out.Delete(); + mapper.Delete(); + } +} diff --git a/org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/common/VTKNodeMap.java b/org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/common/VTKNodeMap.java index 81ed0ba1..e72ea808 100644 --- a/org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/common/VTKNodeMap.java +++ b/org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/common/VTKNodeMap.java @@ -1,22 +1,24 @@ -/******************************************************************************* - * Copyright (c) 2012, 2013 Association for Decentralized Information Management in - * Industry THTH ry. - * 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.g3d.vtk.common; - -import org.simantics.g3d.scenegraph.NodeMap; - -import vtk.vtkProp; - -public interface VTKNodeMap extends NodeMap{ - - - -} +/******************************************************************************* + * Copyright (c) 2012, 2013 Association for Decentralized Information Management in + * Industry THTH ry. + * 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.g3d.vtk.common; + +import org.simantics.g3d.scenegraph.IG3DNode; +import org.simantics.g3d.scenegraph.NodeMap; +import org.simantics.g3d.scenegraph.base.INode; + +import vtk.vtkProp; + +public interface VTKNodeMap extends NodeMap{ + + + +} diff --git a/org.simantics.g3d/src/org/simantics/g3d/math/MathTools.java b/org.simantics.g3d/src/org/simantics/g3d/math/MathTools.java index 92d954f4..73d5e606 100644 --- a/org.simantics.g3d/src/org/simantics/g3d/math/MathTools.java +++ b/org.simantics.g3d/src/org/simantics/g3d/math/MathTools.java @@ -1,860 +1,920 @@ -/******************************************************************************* - * Copyright (c) 2012, 2013 Association for Decentralized Information Management in - * Industry THTH ry. - * 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.g3d.math; - -import javax.vecmath.AxisAngle4d; -import javax.vecmath.Matrix3d; -import javax.vecmath.Matrix4d; -import javax.vecmath.Quat4d; -import javax.vecmath.Tuple3d; -import javax.vecmath.Tuple4d; -import javax.vecmath.Vector2f; -import javax.vecmath.Vector3d; - -import org.simantics.g3d.math.EulerTools.Order; - - -/** - * Some useful geometry related math functions. Beware, methods may modify their input parameters! - * - * @author Marko Luukkainen - * - */ -public class MathTools { - - public static double NEAR_ZERO = 0.0000001; - public static double NEAR_HALF = 0.4999999; - - public static final Vector3d Z_AXIS = new Vector3d(0.0,0.0,1.0); - public static final Vector3d Y_AXIS = new Vector3d(0.0,1.0,0.0); - public static final Vector3d X_AXIS = new Vector3d(1.0,0.0,0.0); - public static final Vector3d ORIGIN = new Vector3d(0.0,0.0,0.0); - - final static double EPS = 1.0e-12; - - - public static boolean equals(double d1, double d2) { - return Math.abs(d1-d2) < EPS; - } - - public static boolean equals(Tuple3d p1, Tuple3d p2) { - return distanceSquared(p1, p2) < NEAR_ZERO; - } - - public static boolean equals(Tuple4d p1, Tuple4d p2) { - return distanceSquared(p1, p2) < NEAR_ZERO; - } - - public static double distance(Tuple3d p1, Tuple3d p2) { - double dx, dy, dz; - - dx = p2.x - p1.x; - dy = p2.y - p1.y; - dz = p2.z - p1.z; - return Math.sqrt(dx*dx+dy*dy+dz*dz); - } - - public static double distance(Tuple4d p1, Tuple4d p2) { - double dx, dy, dz, dw; - - dx = p2.x - p1.x; - dy = p2.y - p1.y; - dz = p2.z - p1.z; - dw = p2.w - p1.w; - return Math.sqrt(dx*dx+dy*dy+dz*dz+dw*dw); - } - - public static double distanceSquared(Tuple3d p1, Tuple3d p2) { - double dx, dy, dz; - - dx = p2.x - p1.x; - dy = p2.y - p1.y; - dz = p2.z - p1.z; - return dx*dx+dy*dy+dz*dz; - } - - public static double distanceSquared(Tuple4d p1, Tuple4d p2) { - double dx, dy, dz, dw; - - dx = p2.x - p1.x; - dy = p2.y - p1.y; - dz = p2.z - p1.z; - dw = p2.w - p1.w; - return dx*dx+dy*dy+dz*dz+dw*dw; - } - - public static boolean isValid(Tuple3d t) { - return !(Double.isInfinite(t.x) || Double.isNaN(t.x) || - Double.isInfinite(t.y) || Double.isNaN(t.y) || - Double.isInfinite(t.z) || Double.isNaN(t.z)); - } - - public static Vector3d closestPointOnEdge(Vector3d point, Vector3d edgePoint1, Vector3d edgePoint2) { - point.sub(edgePoint1); - Vector3d v = new Vector3d(edgePoint2); - v.sub(edgePoint1); - double t = v.dot(point); - t /= v.lengthSquared(); - if (t <= 0.0f) - return edgePoint1; - if (t >= 1.0f) - return edgePoint2; - v.scale(t); - v.add(edgePoint1); - return v; - } - - public static Vector3d closestPointOnStraight(Tuple3d point, Tuple3d straightPoint, Vector3d straightDir) { - Vector3d v = new Vector3d(point); - v.sub(straightPoint); - double t = straightDir.dot(v); - t /= straightDir.lengthSquared(); - v.set(straightDir); - v.scale(t); - v.add(straightPoint); - return v; - } - - public static Vector3d closestPointOnStraight(Tuple3d point, Tuple3d straightPoint, Vector3d straightDir, double u[]) { - Vector3d v = new Vector3d(point); - v.sub(straightPoint); - u[0] = straightDir.dot(v); - u[0] /= straightDir.lengthSquared(); - v.set(straightDir); - v.scale(u[0]); - v.add(straightPoint); - return v; - } - - public static double distanceFromPlane(Vector3d point, Vector3d planeNormal, Tuple3d planePoint) { - point.sub(planePoint); - - return planeNormal.dot(point); - } - - public static double distanceFromPlane(Vector3d point, Vector3d planeNormal, float d) { - return (planeNormal.dot(point) + d); - } - - public static boolean intersectStraightPlane(Tuple3d linePoint, Vector3d lineDir, Tuple3d planePoint, Vector3d planeNormal, Tuple3d intersectPoint) { - intersectPoint.set(planePoint); - intersectPoint.sub(linePoint); - double u = planeNormal.dot(new Vector3d(intersectPoint)); - double v = planeNormal.dot(lineDir); - if (Math.abs(v) < NEAR_ZERO) - return false; - u /= v; - intersectPoint.set(lineDir); - intersectPoint.scale(u); - intersectPoint.add(linePoint); - return true; - } - - public static boolean intersectStraightPlane(Tuple3d linePoint, Vector3d lineDir, Tuple3d planePoint, Vector3d planeNormal, Vector3d intersectPoint, double[] u) { - intersectPoint.set(planePoint); - intersectPoint.sub(linePoint); - u[0] = planeNormal.dot(intersectPoint); - double v = planeNormal.dot(lineDir); - if (Math.abs(v) < NEAR_ZERO) - return false; - u[0] /= v; - intersectPoint.set(lineDir); - intersectPoint.scale(u[0]); - intersectPoint.add(linePoint); - return true; - } - - public static boolean intersectLineLine(Tuple3d l1_start,Tuple3d l1_end,Tuple3d l2_start,Tuple3d l2_end,Tuple3d l1_pos, Tuple3d l2_pos) { - Vector3d p13 = new Vector3d(); - Vector3d p43 = new Vector3d(); - Vector3d p21 = new Vector3d(); - double d1343,d4321,d1321,d4343,d2121; - double numer,denom; - p13.sub(l1_start, l2_start); - p43.sub(l2_end,l2_start); - if (Math.abs(p43.x) < NEAR_ZERO && Math.abs(p43.y) < NEAR_ZERO && Math.abs(p43.z) < NEAR_ZERO) - return false; - p21.sub(l1_end,l1_start); - if (Math.abs(p21.x) < NEAR_ZERO && Math.abs(p21.y) < NEAR_ZERO && Math.abs(p21.z) < NEAR_ZERO) - return false; - - d1343 = p13.dot(p43); - d4321 = p43.dot(p21); - d1321 = p13.dot(p21); - d4343 = p43.lengthSquared(); - d2121 = p21.lengthSquared(); - - denom = d2121 * d4343 - d4321 * d4321; - if (Math.abs(denom) < NEAR_ZERO) - return false; - numer = d1343 * d4321 - d1321 * d4343; - - double mua = numer / denom; - double mub = (d1343 + d4321 * mua) / d4343; - - l1_pos.x = l1_start.x + mua * p21.x; - l1_pos.y = l1_start.y + mua * p21.y; - l1_pos.z = l1_start.z + mua * p21.z; - l2_pos.x = l2_start.x + mub * p43.x; - l2_pos.y = l2_start.y + mub * p43.y; - l2_pos.z = l2_start.z + mub * p43.z; - - return true; - } - - public static boolean intersectStraightStraight(Tuple3d p1,Vector3d p21,Tuple3d p3,Vector3d p43,Tuple3d pa,Tuple3d pb) { - Vector3d p13 = new Vector3d(); - - double d1343,d4321,d1321,d4343,d2121; - double numer,denom; - - p13.sub(p1, p3); - if (Math.abs(p43.x) < NEAR_ZERO && Math.abs(p43.y) < NEAR_ZERO && Math.abs(p43.z) < NEAR_ZERO) - return false; - if (Math.abs(p21.x) < NEAR_ZERO && Math.abs(p21.y) < NEAR_ZERO && Math.abs(p21.z) < NEAR_ZERO) - return false; - - d1343 = p13.dot(p43); - d4321 = p43.dot(p21); - d1321 = p13.dot(p21); - d4343 = p43.lengthSquared(); - d2121 = p21.lengthSquared(); - - denom = d2121 * d4343 - d4321 * d4321; - if (Math.abs(denom) < NEAR_ZERO) - return false; - numer = d1343 * d4321 - d1321 * d4343; - - double mua = numer / denom; - double mub = (d1343 + d4321 * mua) / d4343; - - pa.x = p1.x + mua * p21.x; - pa.y = p1.y + mua * p21.y; - pa.z = p1.z + mua * p21.z; - pb.x = p3.x + mub * p43.x; - pb.y = p3.y + mub * p43.y; - pb.z = p3.z + mub * p43.z; - - return true; - } - - /** - * Calculate the line segment PaPb that is the shortest route between - * two lines P1P2 and P3P4. Calculate also the values of mua and mub where - * Pa = P1 + mua (P2 - P1) - * Pb = P3 + mub (P4 - P3) - * @param p1 - * @param p21 - * @param p3 - * @param p43 - * @param pa - * @param pb - * @param mu - * @return - */ - public static boolean intersectStraightStraight(Tuple3d p1,Vector3d p21,Tuple3d p3,Vector3d p43,Tuple3d pa,Tuple3d pb, double mu[]) { - Vector3d p13 = new Vector3d(); - - double d1343,d4321,d1321,d4343,d2121; - double numer,denom; - double EPS = 0.001; - p13.sub(p1, p3); - if (Math.abs(p43.x) < EPS && Math.abs(p43.y) < EPS && Math.abs(p43.z) < EPS) - return false; - if (Math.abs(p21.x) < EPS && Math.abs(p21.y) < EPS && Math.abs(p21.z) < EPS) - return false; - - d1343 = p13.dot(p43); - d4321 = p43.dot(p21); - d1321 = p13.dot(p21); - d4343 = p43.lengthSquared(); - d2121 = p21.lengthSquared(); - - denom = d2121 * d4343 - d4321 * d4321; - if (Math.abs(denom) < EPS) - return false; - numer = d1343 * d4321 - d1321 * d4343; - - mu[0] = numer / denom; - mu[1] = (d1343 + d4321 * mu[0]) / d4343; - - pa.x = p1.x + mu[0] * p21.x; - pa.y = p1.y + mu[0] * p21.y; - pa.z = p1.z + mu[0] * p21.z; - pb.x = p3.x + mu[1] * p43.x; - pb.y = p3.y + mu[1] * p43.y; - pb.z = p3.z + mu[1] * p43.z; - - return true; - } - - - - public static void rotate(Quat4d q, Tuple3d in, Tuple3d out) { - // p' = q * p * q' - double tw = - q.x*in.x - q.y*in.y - q.z*in.z; - double tx = q.w*in.x + q.y*in.z - q.z*in.y; - double ty = q.w*in.y - q.x*in.z + q.z*in.x; - double tz = q.w*in.z + q.x*in.y - q.y*in.x ; - - //temp * q' -> x = -x, y = -y z = -z - //out.w = tw*q.w + tx*q.x + ty*q.y + tz*q.z; - out.x = -tw*q.x + tx*q.w - ty*q.z + tz*q.y; - out.y = -tw*q.y + tx*q.z + ty*q.w - tz*q.x; - out.z = -tw*q.z - tx*q.y + ty*q.x + tz*q.w; - } - - public static void getMatrix(Quat4d quat, Matrix3d m) { - m.m00 = 1.0f - 2.0 * (quat.y * quat.y + quat.z * quat.z); - m.m01 = 2.0 * (quat.x * quat.y + quat.w * quat.z); - m.m02 = 2.0 * (quat.x * quat.z - quat.w * quat.y); - m.m10 = 2.0 * (quat.x * quat.y - quat.w * quat.z); - m.m11 = 1.0 - 2.0f * (quat.x * quat.x + quat.z * quat.z); - m.m12 = 2.0 * (quat.y * quat.z + quat.w * quat.x); - m.m20 = 2.0 * (quat.x * quat.z + quat.w * quat.y); - m.m21 = 2.0 * (quat.y * quat.z - quat.w * quat.x); - m.m22 = 1.0 - 2.0f * (quat.x * quat.x + quat.y * quat.y); - - } - - - private static double q[] = new double[3]; - private static int nxt[] = { 1, 2, 0 }; - /** - * Converts Matrix to Quaternion - * - * Note: non-thread safe. - * - * @param mat - * @param quat - */ - public static void getQuat(Matrix3d mat, Quat4d quat) { - double tr = mat.m00 + mat.m11 + mat.m22; - if (tr > 0.0) { - double s = Math.sqrt(tr + 1.0); - quat.w = 0.5 * s; - s = 0.5 / s; - quat.x = (mat.m21 - mat.m12) * s; - quat.y = (mat.m02 - mat.m20) * s; - quat.z = (mat.m10 - mat.m01) * s; - } else { - int i = 0, j, k; - if (mat.m11 > mat.m00) - i = 1; - if (mat.m22 > mat.getElement(i, i)) - i = 2; - - - j = nxt[i]; - k = nxt[j]; - - double s = Math.sqrt((mat.getElement(i, i) - (mat.getElement(j, j) + mat.getElement(k, k))) + 1.0); - - q[i] = s * 0.5; - - if (Math.abs(s) > 0.001) - s = 0.5 / s; - - quat.w = (mat.getElement(k, j) - mat.getElement(j, k)) * s; - q[j] = (mat.getElement(j, i) + mat.getElement(i, j)) * s; - q[k] = (mat.getElement(k, i) + mat.getElement(i, k)) * s; - - quat.x = q[0]; - quat.y = q[1]; - quat.z = q[2]; - } - } - - public static Quat4d getQuat(Matrix3d mat) { - Quat4d q = new Quat4d(); - getQuat(mat, q); - return q; - } - - public static AxisAngle4d getFromPseudoEuler(Vector3d euler) { - AxisAngle4d aa = new AxisAngle4d(); - aa.angle = euler.length(); - Vector3d normal = new Vector3d(euler); - if (aa.angle > NEAR_ZERO) { - normal.normalize(); - aa.x = normal.x; - aa.y = normal.y; - aa.z = normal.z; - } else { - aa.x = 1.0; - aa.y = 0.0; - aa.z = 0.0; - } - - return aa; - } - - public static Vector3d getPseudoEuler(AxisAngle4d aa) { - Vector3d euler = new Vector3d(aa.x,aa.y,aa.z); - euler.scale(aa.angle); - return euler; - } - - - public static void getQuat(Vector3d euler, Quat4d quat) { - Quat4d q = EulerTools.getQuatFromEuler(Order.YXZ, euler.y,euler.x,euler.z); - quat.set(q); - // http://en.wikipedia.org/wiki/Rotation_formalisms_in_three_dimensions#Conversion_formulae_between_formalisms - // Using the x-convention, the 3-1-3 Euler angles phi, theta and psi (around the Z, X and again the Z-axis) -// quat.x = -Math.cos((euler.x - euler.z)*0.5)*Math.sin(euler.y*0.5); -// quat.y = -Math.sin((euler.x - euler.z)*0.5)*Math.sin(euler.y*0.5); -// quat.z = -Math.sin((euler.x + euler.z)*0.5)*Math.cos(euler.y*0.5); -// quat.w = Math.sin((euler.x + euler.z)*0.5)*Math.cos(euler.y*0.5); - - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToQuaternion/index.htm - // Y, Z, X order -// double c1 = Math.cos(euler.y*0.5); -// double s1 = Math.sin(euler.y*0.5); -// double c2 = Math.cos(euler.z*0.5); -// double s2 = Math.sin(euler.z*0.5); -// double c3 = Math.cos(euler.x*0.5); -// double s3 = Math.sin(euler.x*0.5); -// double c1c2 = c1*c2; -// double s1s2 = s1*s2; -// quat.w =c1c2*c3 - s1s2*s3; -// quat.x =c1c2*s3 + s1s2*c3; -// quat.y =s1*c2*c3 + c1*s2*s3; -// quat.z =c1*s2*c3 - s1*c2*s3; - -// Quat4d q2 = EulerTools.getQuatFromEuler(Order.YZX, euler.y,euler.z,euler.x); -// System.out.println("Q " + quat + " Q2 " + q2); -// double c1 = Math.cos(euler.y); -// double s1 = Math.sin(euler.y); -// double c2 = Math.cos(euler.z); -// double s2 = Math.sin(euler.z); -// double c3 = Math.cos(euler.x); -// double s3 = Math.sin(euler.x); -// quat.w = Math.sqrt(1.0 + c1 * c2 + c1*c3 - s1 * s2 * s3 + c2*c3) / 2.0; -// double w4 = (4.0 * quat.w); -// quat.x = (c2 * s3 + c1 * s3 + s1 * s2 * c3) / w4 ; -// quat.y = (s1 * c2 + s1 * c3 + c1 * s2 * s3) / w4 ; -// quat.z = (-s1 * s3 + c1 * s2 * c3 +s2) / w4 ; - } - - - - - public static void getEuler(Quat4d quat,Vector3d euler) { - Vector3d e = EulerTools.getEulerFromQuat(Order.YXZ, quat); - euler.x = e.y; - euler.y = e.x; - euler.z = e.z; - - // http://en.wikipedia.org/wiki/Rotation_formalisms_in_three_dimensions#Conversion_formulae_between_formalisms -// euler.x = Math.atan2(quat.x * quat.z + quat.y* quat.w, quat.y*quat.z - quat.x * quat.w); -// euler.y = Math.acos(-square(quat.x) - square(quat.y) + square(quat.z) + square(quat.w)); -// euler.z = -Math.atan2(quat.x * quat.z - quat.y* quat.w, quat.y*quat.z + quat.x * quat.w); - - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/index.htm - // Y, Z, X order -// double test = quat.x * quat.y + quat.z * quat.w; -// if (test > NEAR_HALF) { -// euler.y = 2.0 * Math.atan2(quat.x,quat.w); -// euler.z = Math.PI * 0.5; -// euler.x = 0.0; -// } else if (test < -NEAR_HALF) { -// euler.y = -2.0 * Math.atan2(quat.x,quat.w); -// euler.z = -Math.PI * 0.5; -// euler.x = 0.0; -// } else { -// double sqx = square(quat.x); -// double sqy = square(quat.y); -// double sqz = square(quat.z); -// euler.y = Math.atan2(2.0*(quat.y*quat.w-quat.x*quat.z), 1.0 - 2.0*(sqy-sqz)); -// euler.z = Math.asin(2.0*test); -// euler.x = Math.atan2(2.0*(quat.x*quat.w-quat.y*quat.z), 1.0 - 2.0*(sqx-sqz)); -// System.out.println(euler + " " + EulerTools.getEulerFromQuat(Order.YXZ, quat) + " " + quat); -// } -// double sqw = quat.w*quat.w; -// double sqx = quat.x*quat.x; -// double sqy = quat.y*quat.y; -// double sqz = quat.z*quat.z; -// double unit = sqx + sqy + sqz + sqw; // if normalised is one, otherwise is correction factor -// double test = quat.x*quat.y + quat.z*quat.w; -// if (test > 0.499*unit) { // singularity at north pole -// euler.y = 2 * Math.atan2(quat.x,quat.w); -// euler.z = Math.PI/2; -// euler.x = 0; -// return; -// } -// if (test < -0.499*unit) { // singularity at south pole -// euler.y = -2 * Math.atan2(quat.x,quat.w); -// euler.z = -Math.PI/2; -// euler.x = 0; -// return; -// } -// euler.y = Math.atan2(2*quat.y*quat.w-2*quat.x*quat.z , sqx - sqy - sqz + sqw); -// euler.z = Math.asin(2*test/unit); -// euler.x = Math.atan2(2*quat.x*quat.w-2*quat.y*quat.z , -sqx + sqy - sqz + sqw); - } - - public static Quat4d getQuat(Vector3d euler) { - Quat4d q = new Quat4d(); - getQuat(euler,q); - return q; - } - - - public static Vector3d getEuler(Quat4d quat) { - Vector3d v = new Vector3d(); - getEuler(quat, v); - return v; - } - - public static Quat4d getQuat(AxisAngle4d aa) { - Quat4d q = new Quat4d(); - getQuat(aa, q); - return q; - } - - public static AxisAngle4d getAxisAngle(Quat4d q) { - AxisAngle4d aa = new AxisAngle4d(); - aa.set(q); - return aa; - } - - public static Quat4d getIdentityQuat() { - return new Quat4d(0, 0, 0, 1); - } - - public static void getQuat(AxisAngle4d aa, Quat4d q) { - double mag,amag; - // Quat = cos(theta/2) + sin(theta/2)(roation_axis) - - amag = Math.sqrt( aa.x*aa.x + aa.y*aa.y + aa.z*aa.z); - if( amag < NEAR_ZERO ) { - q.w = 1.0; - q.x = 0.0; - q.y = 0.0; - q.z = 0.0; - } else { - amag = 1.0/amag; - double a2 = aa.angle * 0.5; - mag = Math.sin(a2); - q.w = Math.cos(a2); - q.x = aa.x*amag*mag; - q.y = aa.y*amag*mag; - q.z = aa.z*amag*mag; - } - } - - - /* - * Cohen-Sutherland - */ - - private static final int IN = 0; - private static final int LEFT = 1; - private static final int RIGHT = 2; - private static final int BOTTOM = 4; - private static final int TOP = 8; - - - private static int bitcode(Vector2f p1, Vector2f min, Vector2f max) { - int code = IN; - if (p1.x < min.x) - code |= LEFT; - else if (p1.x > max.x) - code |= RIGHT; - if (p1.y < min.y) - code |= BOTTOM; - else if (p1.y > max.y) - code |= TOP; - return code; - } - - public static boolean clipLineRectangle(Vector2f p1,Vector2f p2, Vector2f min, Vector2f max, Vector2f r1, Vector2f r2) { - while (true) { - int o1 = bitcode(p1, min, max); - int o2 = bitcode(p2, min, max); - int and = o1 & o2; - int or = o1 | o2; - if (and != IN) { - return false; - } - if (or == IN) { - r1.set(p1); - r2.set(p2); - return true; - } - if (o1 == IN) { - Vector2f t = p1; - p1 = p2; - p2 = t; - int t2 = o1; - o1 = o2; - o2 = t2; - } - if ((o1 & TOP) != IN) { - float t = (max.y - p1.y) / (p2.y - p1.y); - p1.x += t * (p2.x - p1.x); - p1.y = max.y; - } else if ((o1 & BOTTOM) != IN) { - float t = (min.y - p1.y) / (p2.y - p1.y); - p1.x += t * (p2.x - p1.x); - p1.y = min.y; - } else if ((o1 & LEFT) != IN) { - float t = (min.x - p1.x) / (p2.x - p1.x); - p1.y += t * (p2.y - p1.y); - p1.x = min.x; - } else if ((o1 & RIGHT) != IN) { - float t = (max.x - p1.x) / (p2.x - p1.x); - p1.y += t * (p2.y - p1.y); - p1.x = max.x; - } else { - throw new RuntimeException("Error in clipping code"); - } - } - - } - - public static double square(double d) { - return d * d; - } - - - public static void multiplyOrientation(AxisAngle4d aa, AxisAngle4d rot) { - Quat4d q1 = new Quat4d(); - getQuat(aa, q1); - Quat4d q2 = new Quat4d(); - getQuat(rot, q2); - q2.mul(q1); - rot.set(q2); - } - - public static double radToDeg(double rad) { - return (rad / Math.PI) * 180.0; - } - - public static double degToRad(double deg) { - return (deg / 180.0) * Math.PI; - } - - public static double clamp(double min, double max,double v) { - if (v < min) - return min; - if (v > max) - return max; - return v; - } - - public static AxisAngle4d createRotation(Vector3d original, Vector3d rotated) { - AxisAngle4d result = new AxisAngle4d(); - if (createRotation(original, rotated, result)) - return result; - return null; - } - - - public static void setIdentity(Quat4d q) { - q.w = 1.0; - q.x = 0.0; - q.y = 0.0; - q.z = 0.0; - } - - public static void setIdentity(AxisAngle4d aa) { - aa.angle = 0.0; - aa.x = 0.0; - aa.y = 1.0; - aa.z = 0.0; - } - - public static void set(Matrix3d mat, double m00, double m01, double m02, - double m10, double m11, double m12, double m20, double m21, - double m22) { - mat.m00 = m00; - mat.m01 = m01; - mat.m02 = m02; - - mat.m10 = m10; - mat.m11 = m11; - mat.m12 = m12; - - mat.m20 = m20; - mat.m21 = m21; - mat.m22 = m22; - } - - public static void set(Matrix4d mat, double[] v) { - mat.m00 = v[0]; - mat.m01 = v[1]; - mat.m02 = v[2]; - mat.m03 = v[3]; - - mat.m10 = v[4]; - mat.m11 = v[5]; - mat.m12 = v[6]; - mat.m13 = v[7]; - - mat.m20 = v[8]; - mat.m21 = v[9]; - mat.m22 = v[10]; - mat.m23 = v[11]; - - mat.m30 = v[12]; - mat.m31 = v[13]; - mat.m32 = v[14]; - mat.m33 = v[15]; - - } - - public static boolean createRotation(Vector3d original, Vector3d rotated, AxisAngle4d result) { - - if (rotated.lengthSquared() > 0.01) - rotated.normalize(); - else - return false; - double d = original.dot(rotated); - if (d > 0.9999) { - // original and rotated are parallel, pointing at the same direction - result.angle = 0.0; - result.x = 0.0; - result.y = 1.0; - result.z = 0.0; - } else if (d < -0.9999) { - // original and rotated are parallel, pointing at the opposite direction - Vector3d a = Z_AXIS; - if (Math.abs(a.dot(original)) > 0.8 ) - a = Y_AXIS; - result.set(a, Math.PI); - } else { - double angle = original.angle(rotated); - Vector3d axis = new Vector3d(); - axis.cross(original, rotated); - result.set(axis,angle); - } - return true; - } - - public static boolean createRotation(Vector3d original, Vector3d rotated, Quat4d result) { - - if (rotated.lengthSquared() > 0.01) - rotated.normalize(); - else - return false; - double d = original.dot(rotated); - if (d > 0.9999) { - // original and rotated are parallel, pointing at the same direction - result.w = 1.0; - result.x = 0.0; - result.y = 0.0; - result.z = 0.0; - } else if (d < -0.9999) { - // original and rotated are parallel, pointing at the opposite direction - Vector3d a = Z_AXIS; - if (Math.abs(a.dot(original)) > 0.8 ) - a = Y_AXIS; - getQuat(a, Math.PI, result); - - } else { - double angle = original.angle(rotated); - Vector3d axis = new Vector3d(); - axis.cross(original, rotated); - getQuat(axis, angle, result); - } - return true; - } - - public static void getQuat(Vector3d axis, double angle, Quat4d q) - { - double mag,amag; - // Quat = cos(theta/2) + sin(theta/2)(roation_axis) - - amag = Math.sqrt( axis.x*axis.x + axis.y*axis.y + axis.z*axis.z); - if( amag < EPS ) { - q.w = 1.0; - q.x = 0.0; - q.y = 0.0; - q.z = 0.0; - } else { - amag = 1.0/amag; - double a2 = angle*0.5; - mag = Math.sin(a2); - q.w = Math.cos(a2); - q.x = axis.x*amag*mag; - q.y = axis.y*amag*mag; - q.z = axis.z*amag*mag; - } - - } - - /** - * Linear interpolation of quaternions. Result IS set to q1. - * @param q1 - * @param q2 - * @param alpha - */ - public static void lip(Quat4d q1, Quat4d q2, double alpha) { - double s1 = 1.0 - alpha; - double s2 = alpha; - q1.scale(s1); - mad(q1,q2,s2); - q1.normalize(); - } - - public static double dot(Quat4d q1, Quat4d q2) { - return q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w; - } - - public static void mad(Tuple3d q1, Tuple3d q2, double s2) { - q1.x += q2.x * s2; - q1.y += q2.y * s2; - q1.z += q2.z * s2; - } - - public static void mad(Quat4d q1, Quat4d q2, double s2) { - q1.x += q2.x * s2; - q1.y += q2.y * s2; - q1.z += q2.z * s2; - q1.w += q2.w * s2; - } - - /** - * Slerp - * - * Sets results to q1. Modifies q2. - * - * @param q1 - * @param q2 - * @param alpha - */ - public static void sip(Quat4d q1, Quat4d q2, double alpha) { - double cosom = dot(q1,q2); - if (cosom < 0.0) { - cosom = -cosom; - q2.negate(); - } - - if (cosom > 0.9999) { - q2.sub(q1); - q2.scale(alpha); - q1.add(q2); - q1.normalize(); - return; - } - double theta_0 = Math.acos(cosom); - double theta = theta_0 * alpha; - Quat4d t = new Quat4d(q1); - t.scale(-cosom); - t.add(q2); - t.normalize(); - t.scale(Math.sin(theta)); - q1.scale(Math.cos(theta)); - q1.add(t); - } -} +/******************************************************************************* + * Copyright (c) 2012, 2013 Association for Decentralized Information Management in + * Industry THTH ry. + * 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.g3d.math; + +import javax.vecmath.AxisAngle4d; +import javax.vecmath.Matrix3d; +import javax.vecmath.Matrix4d; +import javax.vecmath.Quat4d; +import javax.vecmath.Tuple2d; +import javax.vecmath.Tuple3d; +import javax.vecmath.Tuple4d; +import javax.vecmath.Vector2d; +import javax.vecmath.Vector2f; +import javax.vecmath.Vector3d; + +import org.simantics.g3d.math.EulerTools.Order; + + +/** + * Some useful geometry related math functions. Beware, methods may modify their input parameters! + * + * @author Marko Luukkainen + * + */ +public class MathTools { + + public static double NEAR_ZERO = 0.0000001; + public static double NEAR_HALF = 0.4999999; + + public static final Vector3d Z_AXIS = new Vector3d(0.0,0.0,1.0); + public static final Vector3d Y_AXIS = new Vector3d(0.0,1.0,0.0); + public static final Vector3d X_AXIS = new Vector3d(1.0,0.0,0.0); + public static final Vector3d ORIGIN = new Vector3d(0.0,0.0,0.0); + + final static double EPS = 1.0e-12; + + + public static boolean equals(double d1, double d2) { + return Math.abs(d1-d2) < EPS; + } + + public static boolean equals(Tuple3d p1, Tuple3d p2) { + return distanceSquared(p1, p2) < NEAR_ZERO; + } + + public static boolean equals(Tuple4d p1, Tuple4d p2) { + return distanceSquared(p1, p2) < NEAR_ZERO; + } + + public static double distance(Tuple3d p1, Tuple3d p2) { + double dx, dy, dz; + + dx = p2.x - p1.x; + dy = p2.y - p1.y; + dz = p2.z - p1.z; + return Math.sqrt(dx*dx+dy*dy+dz*dz); + } + + public static double distance(Tuple4d p1, Tuple4d p2) { + double dx, dy, dz, dw; + + dx = p2.x - p1.x; + dy = p2.y - p1.y; + dz = p2.z - p1.z; + dw = p2.w - p1.w; + return Math.sqrt(dx*dx+dy*dy+dz*dz+dw*dw); + } + + public static double distanceSquared(Tuple3d p1, Tuple3d p2) { + double dx, dy, dz; + + dx = p2.x - p1.x; + dy = p2.y - p1.y; + dz = p2.z - p1.z; + return dx*dx+dy*dy+dz*dz; + } + + public static double distanceSquared(Tuple4d p1, Tuple4d p2) { + double dx, dy, dz, dw; + + dx = p2.x - p1.x; + dy = p2.y - p1.y; + dz = p2.z - p1.z; + dw = p2.w - p1.w; + return dx*dx+dy*dy+dz*dz+dw*dw; + } + + public static boolean isValid(Tuple3d t) { + return !(Double.isInfinite(t.x) || Double.isNaN(t.x) || + Double.isInfinite(t.y) || Double.isNaN(t.y) || + Double.isInfinite(t.z) || Double.isNaN(t.z)); + } + + public static Vector3d closestPointOnEdge(Vector3d point, Vector3d edgePoint1, Vector3d edgePoint2) { + point.sub(edgePoint1); + Vector3d v = new Vector3d(edgePoint2); + v.sub(edgePoint1); + double t = v.dot(point); + t /= v.lengthSquared(); + if (t <= 0.0f) + return edgePoint1; + if (t >= 1.0f) + return edgePoint2; + v.scale(t); + v.add(edgePoint1); + return v; + } + + public static Vector3d closestPointOnStraight(Tuple3d point, Tuple3d straightPoint, Vector3d straightDir) { + Vector3d v = new Vector3d(point); + v.sub(straightPoint); + double t = straightDir.dot(v); + t /= straightDir.lengthSquared(); + v.set(straightDir); + v.scale(t); + v.add(straightPoint); + return v; + } + + public static Vector3d closestPointOnStraight(Tuple3d point, Tuple3d straightPoint, Vector3d straightDir, double u[]) { + Vector3d v = new Vector3d(point); + v.sub(straightPoint); + u[0] = straightDir.dot(v); + u[0] /= straightDir.lengthSquared(); + v.set(straightDir); + v.scale(u[0]); + v.add(straightPoint); + return v; + } + + public static double distanceFromPlane(Vector3d point, Vector3d planeNormal, Tuple3d planePoint) { + point.sub(planePoint); + + return planeNormal.dot(point); + } + + public static double distanceFromPlane(Vector3d point, Vector3d planeNormal, float d) { + return (planeNormal.dot(point) + d); + } + + public static boolean intersectStraightPlane(Tuple3d linePoint, Vector3d lineDir, Tuple3d planePoint, Vector3d planeNormal, Tuple3d intersectPoint) { + intersectPoint.set(planePoint); + intersectPoint.sub(linePoint); + double u = planeNormal.dot(new Vector3d(intersectPoint)); + double v = planeNormal.dot(lineDir); + if (Math.abs(v) < NEAR_ZERO) + return false; + u /= v; + intersectPoint.set(lineDir); + intersectPoint.scale(u); + intersectPoint.add(linePoint); + return true; + } + + public static boolean intersectStraightPlane(Tuple3d linePoint, Vector3d lineDir, Tuple3d planePoint, Vector3d planeNormal, Vector3d intersectPoint, double[] u) { + intersectPoint.set(planePoint); + intersectPoint.sub(linePoint); + u[0] = planeNormal.dot(intersectPoint); + double v = planeNormal.dot(lineDir); + if (Math.abs(v) < NEAR_ZERO) + return false; + u[0] /= v; + intersectPoint.set(lineDir); + intersectPoint.scale(u[0]); + intersectPoint.add(linePoint); + return true; + } + + public static boolean intersectLineLine(Tuple3d l1_start,Tuple3d l1_end,Tuple3d l2_start,Tuple3d l2_end,Tuple3d l1_pos, Tuple3d l2_pos) { + Vector3d p13 = new Vector3d(); + Vector3d p43 = new Vector3d(); + Vector3d p21 = new Vector3d(); + double d1343,d4321,d1321,d4343,d2121; + double numer,denom; + p13.sub(l1_start, l2_start); + p43.sub(l2_end,l2_start); + if (Math.abs(p43.x) < NEAR_ZERO && Math.abs(p43.y) < NEAR_ZERO && Math.abs(p43.z) < NEAR_ZERO) + return false; + p21.sub(l1_end,l1_start); + if (Math.abs(p21.x) < NEAR_ZERO && Math.abs(p21.y) < NEAR_ZERO && Math.abs(p21.z) < NEAR_ZERO) + return false; + + d1343 = p13.dot(p43); + d4321 = p43.dot(p21); + d1321 = p13.dot(p21); + d4343 = p43.lengthSquared(); + d2121 = p21.lengthSquared(); + + denom = d2121 * d4343 - d4321 * d4321; + if (Math.abs(denom) < NEAR_ZERO) + return false; + numer = d1343 * d4321 - d1321 * d4343; + + double mua = numer / denom; + double mub = (d1343 + d4321 * mua) / d4343; + + l1_pos.x = l1_start.x + mua * p21.x; + l1_pos.y = l1_start.y + mua * p21.y; + l1_pos.z = l1_start.z + mua * p21.z; + l2_pos.x = l2_start.x + mub * p43.x; + l2_pos.y = l2_start.y + mub * p43.y; + l2_pos.z = l2_start.z + mub * p43.z; + + return true; + } + + public static boolean intersectStraightStraight(Tuple3d p1,Vector3d p21,Tuple3d p3,Vector3d p43,Tuple3d pa,Tuple3d pb) { + Vector3d p13 = new Vector3d(); + + double d1343,d4321,d1321,d4343,d2121; + double numer,denom; + + p13.sub(p1, p3); + if (Math.abs(p43.x) < NEAR_ZERO && Math.abs(p43.y) < NEAR_ZERO && Math.abs(p43.z) < NEAR_ZERO) + return false; + if (Math.abs(p21.x) < NEAR_ZERO && Math.abs(p21.y) < NEAR_ZERO && Math.abs(p21.z) < NEAR_ZERO) + return false; + + d1343 = p13.dot(p43); + d4321 = p43.dot(p21); + d1321 = p13.dot(p21); + d4343 = p43.lengthSquared(); + d2121 = p21.lengthSquared(); + + denom = d2121 * d4343 - d4321 * d4321; + if (Math.abs(denom) < NEAR_ZERO) + return false; + numer = d1343 * d4321 - d1321 * d4343; + + double mua = numer / denom; + double mub = (d1343 + d4321 * mua) / d4343; + + pa.x = p1.x + mua * p21.x; + pa.y = p1.y + mua * p21.y; + pa.z = p1.z + mua * p21.z; + pb.x = p3.x + mub * p43.x; + pb.y = p3.y + mub * p43.y; + pb.z = p3.z + mub * p43.z; + + return true; + } + + /** + * Calculate the line segment PaPb that is the shortest route between + * two lines P1P2 and P3P4. Calculate also the values of mua and mub where + * Pa = P1 + mua (P2 - P1) + * Pb = P3 + mub (P4 - P3) + * @param p1 + * @param p21 + * @param p3 + * @param p43 + * @param pa + * @param pb + * @param mu + * @return + */ + public static boolean intersectStraightStraight(Tuple3d p1,Vector3d p21,Tuple3d p3,Vector3d p43,Tuple3d pa,Tuple3d pb, double mu[]) { + Vector3d p13 = new Vector3d(); + + double d1343,d4321,d1321,d4343,d2121; + double numer,denom; + double EPS = 0.001; + p13.sub(p1, p3); + if (Math.abs(p43.x) < EPS && Math.abs(p43.y) < EPS && Math.abs(p43.z) < EPS) + return false; + if (Math.abs(p21.x) < EPS && Math.abs(p21.y) < EPS && Math.abs(p21.z) < EPS) + return false; + + d1343 = p13.dot(p43); + d4321 = p43.dot(p21); + d1321 = p13.dot(p21); + d4343 = p43.lengthSquared(); + d2121 = p21.lengthSquared(); + + denom = d2121 * d4343 - d4321 * d4321; + if (Math.abs(denom) < EPS) + return false; + numer = d1343 * d4321 - d1321 * d4343; + + mu[0] = numer / denom; + mu[1] = (d1343 + d4321 * mu[0]) / d4343; + + pa.x = p1.x + mu[0] * p21.x; + pa.y = p1.y + mu[0] * p21.y; + pa.z = p1.z + mu[0] * p21.z; + pb.x = p3.x + mu[1] * p43.x; + pb.y = p3.y + mu[1] * p43.y; + pb.z = p3.z + mu[1] * p43.z; + + return true; + } + + + + public static void rotate(Quat4d q, Tuple3d in, Tuple3d out) { + // p' = q * p * q' + double tw = - q.x*in.x - q.y*in.y - q.z*in.z; + double tx = q.w*in.x + q.y*in.z - q.z*in.y; + double ty = q.w*in.y - q.x*in.z + q.z*in.x; + double tz = q.w*in.z + q.x*in.y - q.y*in.x ; + + //temp * q' -> x = -x, y = -y z = -z + //out.w = tw*q.w + tx*q.x + ty*q.y + tz*q.z; + out.x = -tw*q.x + tx*q.w - ty*q.z + tz*q.y; + out.y = -tw*q.y + tx*q.z + ty*q.w - tz*q.x; + out.z = -tw*q.z - tx*q.y + ty*q.x + tz*q.w; + } + + public static void getMatrix(Quat4d quat, Matrix3d m) { + m.m00 = 1.0f - 2.0 * (quat.y * quat.y + quat.z * quat.z); + m.m01 = 2.0 * (quat.x * quat.y + quat.w * quat.z); + m.m02 = 2.0 * (quat.x * quat.z - quat.w * quat.y); + m.m10 = 2.0 * (quat.x * quat.y - quat.w * quat.z); + m.m11 = 1.0 - 2.0f * (quat.x * quat.x + quat.z * quat.z); + m.m12 = 2.0 * (quat.y * quat.z + quat.w * quat.x); + m.m20 = 2.0 * (quat.x * quat.z + quat.w * quat.y); + m.m21 = 2.0 * (quat.y * quat.z - quat.w * quat.x); + m.m22 = 1.0 - 2.0f * (quat.x * quat.x + quat.y * quat.y); + + } + + + private static double q[] = new double[3]; + private static int nxt[] = { 1, 2, 0 }; + /** + * Converts Matrix to Quaternion + * + * Note: non-thread safe. + * + * @param mat + * @param quat + */ + public static void getQuat(Matrix3d mat, Quat4d quat) { + double tr = mat.m00 + mat.m11 + mat.m22; + if (tr > 0.0) { + double s = Math.sqrt(tr + 1.0); + quat.w = 0.5 * s; + s = 0.5 / s; + quat.x = (mat.m21 - mat.m12) * s; + quat.y = (mat.m02 - mat.m20) * s; + quat.z = (mat.m10 - mat.m01) * s; + } else { + int i = 0, j, k; + if (mat.m11 > mat.m00) + i = 1; + if (mat.m22 > mat.getElement(i, i)) + i = 2; + + + j = nxt[i]; + k = nxt[j]; + + double s = Math.sqrt((mat.getElement(i, i) - (mat.getElement(j, j) + mat.getElement(k, k))) + 1.0); + + q[i] = s * 0.5; + + if (Math.abs(s) > 0.001) + s = 0.5 / s; + + quat.w = (mat.getElement(k, j) - mat.getElement(j, k)) * s; + q[j] = (mat.getElement(j, i) + mat.getElement(i, j)) * s; + q[k] = (mat.getElement(k, i) + mat.getElement(i, k)) * s; + + quat.x = q[0]; + quat.y = q[1]; + quat.z = q[2]; + } + } + + public static Quat4d getQuat(Matrix3d mat) { + Quat4d q = new Quat4d(); + getQuat(mat, q); + return q; + } + + public static AxisAngle4d getFromPseudoEuler(Vector3d euler) { + AxisAngle4d aa = new AxisAngle4d(); + aa.angle = euler.length(); + Vector3d normal = new Vector3d(euler); + if (aa.angle > NEAR_ZERO) { + normal.normalize(); + aa.x = normal.x; + aa.y = normal.y; + aa.z = normal.z; + } else { + aa.x = 1.0; + aa.y = 0.0; + aa.z = 0.0; + } + + return aa; + } + + public static Vector3d getPseudoEuler(AxisAngle4d aa) { + Vector3d euler = new Vector3d(aa.x,aa.y,aa.z); + euler.scale(aa.angle); + return euler; + } + + + public static void getQuat(Vector3d euler, Quat4d quat) { + Quat4d q = EulerTools.getQuatFromEuler(Order.YXZ, euler.y,euler.x,euler.z); + quat.set(q); + // http://en.wikipedia.org/wiki/Rotation_formalisms_in_three_dimensions#Conversion_formulae_between_formalisms + // Using the x-convention, the 3-1-3 Euler angles phi, theta and psi (around the Z, X and again the Z-axis) +// quat.x = -Math.cos((euler.x - euler.z)*0.5)*Math.sin(euler.y*0.5); +// quat.y = -Math.sin((euler.x - euler.z)*0.5)*Math.sin(euler.y*0.5); +// quat.z = -Math.sin((euler.x + euler.z)*0.5)*Math.cos(euler.y*0.5); +// quat.w = Math.sin((euler.x + euler.z)*0.5)*Math.cos(euler.y*0.5); + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToQuaternion/index.htm + // Y, Z, X order +// double c1 = Math.cos(euler.y*0.5); +// double s1 = Math.sin(euler.y*0.5); +// double c2 = Math.cos(euler.z*0.5); +// double s2 = Math.sin(euler.z*0.5); +// double c3 = Math.cos(euler.x*0.5); +// double s3 = Math.sin(euler.x*0.5); +// double c1c2 = c1*c2; +// double s1s2 = s1*s2; +// quat.w =c1c2*c3 - s1s2*s3; +// quat.x =c1c2*s3 + s1s2*c3; +// quat.y =s1*c2*c3 + c1*s2*s3; +// quat.z =c1*s2*c3 - s1*c2*s3; + +// Quat4d q2 = EulerTools.getQuatFromEuler(Order.YZX, euler.y,euler.z,euler.x); +// System.out.println("Q " + quat + " Q2 " + q2); +// double c1 = Math.cos(euler.y); +// double s1 = Math.sin(euler.y); +// double c2 = Math.cos(euler.z); +// double s2 = Math.sin(euler.z); +// double c3 = Math.cos(euler.x); +// double s3 = Math.sin(euler.x); +// quat.w = Math.sqrt(1.0 + c1 * c2 + c1*c3 - s1 * s2 * s3 + c2*c3) / 2.0; +// double w4 = (4.0 * quat.w); +// quat.x = (c2 * s3 + c1 * s3 + s1 * s2 * c3) / w4 ; +// quat.y = (s1 * c2 + s1 * c3 + c1 * s2 * s3) / w4 ; +// quat.z = (-s1 * s3 + c1 * s2 * c3 +s2) / w4 ; + } + + + + + public static void getEuler(Quat4d quat,Vector3d euler) { + Vector3d e = EulerTools.getEulerFromQuat(Order.YXZ, quat); + euler.x = e.y; + euler.y = e.x; + euler.z = e.z; + + // http://en.wikipedia.org/wiki/Rotation_formalisms_in_three_dimensions#Conversion_formulae_between_formalisms +// euler.x = Math.atan2(quat.x * quat.z + quat.y* quat.w, quat.y*quat.z - quat.x * quat.w); +// euler.y = Math.acos(-square(quat.x) - square(quat.y) + square(quat.z) + square(quat.w)); +// euler.z = -Math.atan2(quat.x * quat.z - quat.y* quat.w, quat.y*quat.z + quat.x * quat.w); + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/index.htm + // Y, Z, X order +// double test = quat.x * quat.y + quat.z * quat.w; +// if (test > NEAR_HALF) { +// euler.y = 2.0 * Math.atan2(quat.x,quat.w); +// euler.z = Math.PI * 0.5; +// euler.x = 0.0; +// } else if (test < -NEAR_HALF) { +// euler.y = -2.0 * Math.atan2(quat.x,quat.w); +// euler.z = -Math.PI * 0.5; +// euler.x = 0.0; +// } else { +// double sqx = square(quat.x); +// double sqy = square(quat.y); +// double sqz = square(quat.z); +// euler.y = Math.atan2(2.0*(quat.y*quat.w-quat.x*quat.z), 1.0 - 2.0*(sqy-sqz)); +// euler.z = Math.asin(2.0*test); +// euler.x = Math.atan2(2.0*(quat.x*quat.w-quat.y*quat.z), 1.0 - 2.0*(sqx-sqz)); +// System.out.println(euler + " " + EulerTools.getEulerFromQuat(Order.YXZ, quat) + " " + quat); +// } +// double sqw = quat.w*quat.w; +// double sqx = quat.x*quat.x; +// double sqy = quat.y*quat.y; +// double sqz = quat.z*quat.z; +// double unit = sqx + sqy + sqz + sqw; // if normalised is one, otherwise is correction factor +// double test = quat.x*quat.y + quat.z*quat.w; +// if (test > 0.499*unit) { // singularity at north pole +// euler.y = 2 * Math.atan2(quat.x,quat.w); +// euler.z = Math.PI/2; +// euler.x = 0; +// return; +// } +// if (test < -0.499*unit) { // singularity at south pole +// euler.y = -2 * Math.atan2(quat.x,quat.w); +// euler.z = -Math.PI/2; +// euler.x = 0; +// return; +// } +// euler.y = Math.atan2(2*quat.y*quat.w-2*quat.x*quat.z , sqx - sqy - sqz + sqw); +// euler.z = Math.asin(2*test/unit); +// euler.x = Math.atan2(2*quat.x*quat.w-2*quat.y*quat.z , -sqx + sqy - sqz + sqw); + } + + public static Quat4d getQuat(Vector3d euler) { + Quat4d q = new Quat4d(); + getQuat(euler,q); + return q; + } + + + public static Vector3d getEuler(Quat4d quat) { + Vector3d v = new Vector3d(); + getEuler(quat, v); + return v; + } + + public static Quat4d getQuat(AxisAngle4d aa) { + Quat4d q = new Quat4d(); + getQuat(aa, q); + return q; + } + + public static AxisAngle4d getAxisAngle(Quat4d q) { + AxisAngle4d aa = new AxisAngle4d(); + double mag = q.x * q.x + q.y * q.y + q.z * q.z; + + if (mag > EPS) { + mag = Math.sqrt(mag); + aa.angle = 2.0 * Math.atan2(mag, q.w); + mag = 1.0 / mag; + aa.x = q.x * mag; + aa.y = q.y * mag; + aa.z = q.z * mag; + + } else { + aa.x = 0.0; + aa.y = 1.0; + aa.z = 0.0; + aa.angle = 0.0; + } + // aa.set(q); + return aa; + } + + public static Quat4d getIdentityQuat() { + return new Quat4d(0, 0, 0, 1); + } + + public static void getQuat(AxisAngle4d aa, Quat4d q) { + double mag,amag; + // Quat = cos(theta/2) + sin(theta/2)(roation_axis) + + amag = Math.sqrt( aa.x*aa.x + aa.y*aa.y + aa.z*aa.z); + if( amag < NEAR_ZERO ) { + q.w = 1.0; + q.x = 0.0; + q.y = 0.0; + q.z = 0.0; + } else { + amag = 1.0/amag; + double a2 = aa.angle * 0.5; + mag = Math.sin(a2); + q.w = Math.cos(a2); + q.x = aa.x*amag*mag; + q.y = aa.y*amag*mag; + q.z = aa.z*amag*mag; + } + } + + + /* + * Cohen-Sutherland + */ + + private static final int IN = 0; + private static final int LEFT = 1; + private static final int RIGHT = 2; + private static final int BOTTOM = 4; + private static final int TOP = 8; + + + private static int bitcode(Vector2f p1, Vector2f min, Vector2f max) { + int code = IN; + if (p1.x < min.x) + code |= LEFT; + else if (p1.x > max.x) + code |= RIGHT; + if (p1.y < min.y) + code |= BOTTOM; + else if (p1.y > max.y) + code |= TOP; + return code; + } + + public static boolean clipLineRectangle(Vector2f p1,Vector2f p2, Vector2f min, Vector2f max, Vector2f r1, Vector2f r2) { + while (true) { + int o1 = bitcode(p1, min, max); + int o2 = bitcode(p2, min, max); + int and = o1 & o2; + int or = o1 | o2; + if (and != IN) { + return false; + } + if (or == IN) { + r1.set(p1); + r2.set(p2); + return true; + } + if (o1 == IN) { + Vector2f t = p1; + p1 = p2; + p2 = t; + int t2 = o1; + o1 = o2; + o2 = t2; + } + if ((o1 & TOP) != IN) { + float t = (max.y - p1.y) / (p2.y - p1.y); + p1.x += t * (p2.x - p1.x); + p1.y = max.y; + } else if ((o1 & BOTTOM) != IN) { + float t = (min.y - p1.y) / (p2.y - p1.y); + p1.x += t * (p2.x - p1.x); + p1.y = min.y; + } else if ((o1 & LEFT) != IN) { + float t = (min.x - p1.x) / (p2.x - p1.x); + p1.y += t * (p2.y - p1.y); + p1.x = min.x; + } else if ((o1 & RIGHT) != IN) { + float t = (max.x - p1.x) / (p2.x - p1.x); + p1.y += t * (p2.y - p1.y); + p1.x = max.x; + } else { + throw new RuntimeException("Error in clipping code"); + } + } + + } + + public static double square(double d) { + return d * d; + } + + + public static void multiplyOrientation(AxisAngle4d aa, AxisAngle4d rot) { + Quat4d q1 = new Quat4d(); + getQuat(aa, q1); + Quat4d q2 = new Quat4d(); + getQuat(rot, q2); + q2.mul(q1); + rot.set(q2); + } + + public static double radToDeg(double rad) { + return (rad / Math.PI) * 180.0; + } + + public static double degToRad(double deg) { + return (deg / 180.0) * Math.PI; + } + + public static double clamp(double min, double max,double v) { + if (v < min) + return min; + if (v > max) + return max; + return v; + } + + public static AxisAngle4d createRotation(Vector3d original, Vector3d rotated) { + AxisAngle4d result = new AxisAngle4d(); + if (createRotation(original, rotated, result)) + return result; + return null; + } + + + public static void setIdentity(Quat4d q) { + q.w = 1.0; + q.x = 0.0; + q.y = 0.0; + q.z = 0.0; + } + + public static void setIdentity(AxisAngle4d aa) { + aa.angle = 0.0; + aa.x = 0.0; + aa.y = 1.0; + aa.z = 0.0; + } + + public static void set(Matrix3d mat, double m00, double m01, double m02, + double m10, double m11, double m12, double m20, double m21, + double m22) { + mat.m00 = m00; + mat.m01 = m01; + mat.m02 = m02; + + mat.m10 = m10; + mat.m11 = m11; + mat.m12 = m12; + + mat.m20 = m20; + mat.m21 = m21; + mat.m22 = m22; + } + + public static void set(Matrix4d mat, double[] v) { + mat.m00 = v[0]; + mat.m01 = v[1]; + mat.m02 = v[2]; + mat.m03 = v[3]; + + mat.m10 = v[4]; + mat.m11 = v[5]; + mat.m12 = v[6]; + mat.m13 = v[7]; + + mat.m20 = v[8]; + mat.m21 = v[9]; + mat.m22 = v[10]; + mat.m23 = v[11]; + + mat.m30 = v[12]; + mat.m31 = v[13]; + mat.m32 = v[14]; + mat.m33 = v[15]; + + } + + public static boolean createRotation(Vector3d original, Vector3d rotated, AxisAngle4d result) { + + if (rotated.lengthSquared() > 0.01) + rotated.normalize(); + else + return false; + double d = original.dot(rotated); + if (d > 0.9999) { + // original and rotated are parallel, pointing at the same direction + result.angle = 0.0; + result.x = 0.0; + result.y = 1.0; + result.z = 0.0; + } else if (d < -0.9999) { + // original and rotated are parallel, pointing at the opposite direction + Vector3d a = Z_AXIS; + if (Math.abs(a.dot(original)) > 0.8 ) + a = Y_AXIS; + result.set(a, Math.PI); + } else { + double angle = original.angle(rotated); + Vector3d axis = new Vector3d(); + axis.cross(original, rotated); + result.set(axis,angle); + } + return true; + } + + public static boolean createRotation(Vector3d original, Vector3d rotated, Quat4d result) { + + if (rotated.lengthSquared() > 0.01) + rotated.normalize(); + else + return false; + double d = original.dot(rotated); + if (d > 0.9999) { + // original and rotated are parallel, pointing at the same direction + result.w = 1.0; + result.x = 0.0; + result.y = 0.0; + result.z = 0.0; + } else if (d < -0.9999) { + // original and rotated are parallel, pointing at the opposite direction + Vector3d a = Z_AXIS; + if (Math.abs(a.dot(original)) > 0.8 ) + a = Y_AXIS; + getQuat(a, Math.PI, result); + + } else { + double angle = original.angle(rotated); + Vector3d axis = new Vector3d(); + axis.cross(original, rotated); + getQuat(axis, angle, result); + } + return true; + } + + public static void getQuat(Vector3d axis, double angle, Quat4d q) + { + double mag,amag; + // Quat = cos(theta/2) + sin(theta/2)(roation_axis) + + amag = Math.sqrt( axis.x*axis.x + axis.y*axis.y + axis.z*axis.z); + if( amag < EPS ) { + q.w = 1.0; + q.x = 0.0; + q.y = 0.0; + q.z = 0.0; + } else { + amag = 1.0/amag; + double a2 = angle*0.5; + mag = Math.sin(a2); + q.w = Math.cos(a2); + q.x = axis.x*amag*mag; + q.y = axis.y*amag*mag; + q.z = axis.z*amag*mag; + } + + } + + /** + * Linear interpolation of quaternions. Result IS set to q1. + * @param q1 + * @param q2 + * @param alpha + */ + public static void lip(Quat4d q1, Quat4d q2, double alpha) { + double s1 = 1.0 - alpha; + double s2 = alpha; + q1.scale(s1); + mad(q1,q2,s2); + q1.normalize(); + } + + public static double dot(Quat4d q1, Quat4d q2) { + return q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w; + } + + public static void mad(Tuple3d q1, Tuple3d q2, double s2) { + q1.x += q2.x * s2; + q1.y += q2.y * s2; + q1.z += q2.z * s2; + } + + public static void mad(Quat4d q1, Quat4d q2, double s2) { + q1.x += q2.x * s2; + q1.y += q2.y * s2; + q1.z += q2.z * s2; + q1.w += q2.w * s2; + } + + /** + * Slerp + * + * Sets results to q1. Modifies q2. + * + * @param q1 + * @param q2 + * @param alpha + */ + public static void sip(Quat4d q1, Quat4d q2, double alpha) { + double cosom = dot(q1,q2); + if (cosom < 0.0) { + cosom = -cosom; + q2.negate(); + } + + if (cosom > 0.9999) { + q2.sub(q1); + q2.scale(alpha); + q1.add(q2); + q1.normalize(); + return; + } + double theta_0 = Math.acos(cosom); + double theta = theta_0 * alpha; + Quat4d t = new Quat4d(q1); + t.scale(-cosom); + t.add(q2); + t.normalize(); + t.scale(Math.sin(theta)); + q1.scale(Math.cos(theta)); + q1.add(t); + } + + + public static void rotate(double angle, Tuple2d v1, Tuple2d v2) { + // TODO : verify implementation + double sin = Math.sin(angle); + if (sin == 1.0) { + v2.x = v1.y; + v2.y = -v1.x; + } else if (sin == -1.0) { + v2.x = -v1.y; + v2.y = v1.x; + } else { + double cos = Math.cos(angle); + if (cos == -1.0) { + v2.x = -v1.x; + v2.y = -v1.y; + } else if (cos != 1.0) { + v2.x= v1.x * cos + v1.y * -sin; + v2.y= v1.x* sin + v1.y *cos; + } + } + } + + public static Tuple3d getPosRot(double m3x2[]) { + Vector3d t = new Vector3d(); + t.x = m3x2[4]; + t.y = m3x2[5]; + + + Vector2d v2 = new Vector2d(1,0); + Vector2d v = new Vector2d(); + // use rotation of (1,0) to calculate the rotation component + v.x = m3x2[0]; + v.y = m3x2[2]; + double a1 = v2.angle(v); + if (v.y < 0) { + t.z = a1; + } else { + t.z = Math.PI*2.0 - a1; + } + return t; + } +} diff --git a/org.simantics.g3d/src/org/simantics/g3d/property/AnnotatedPropertyTabContributorFactory.java b/org.simantics.g3d/src/org/simantics/g3d/property/AnnotatedPropertyTabContributorFactory.java index b922e77d..21e773c1 100644 --- a/org.simantics.g3d/src/org/simantics/g3d/property/AnnotatedPropertyTabContributorFactory.java +++ b/org.simantics.g3d/src/org/simantics/g3d/property/AnnotatedPropertyTabContributorFactory.java @@ -1,1307 +1,1307 @@ -/******************************************************************************* - * Copyright (c) 2012, 2013 Association for Decentralized Information Management in - * Industry THTH ry. - * 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.g3d.property; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Consumer; - -import org.eclipse.jface.layout.GridDataFactory; -import org.eclipse.jface.viewers.AbstractTableViewer; -import org.eclipse.jface.viewers.CellEditor; -import org.eclipse.jface.viewers.CellEditor.LayoutData; -import org.eclipse.jface.viewers.CellLabelProvider; -import org.eclipse.jface.viewers.CellNavigationStrategy; -import org.eclipse.jface.viewers.ColumnViewer; -import org.eclipse.jface.viewers.ColumnViewerEditor; -import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent; -import org.eclipse.jface.viewers.ColumnViewerEditorActivationListener; -import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy; -import org.eclipse.jface.viewers.ColumnViewerEditorDeactivationEvent; -import org.eclipse.jface.viewers.EditingSupport; -import org.eclipse.jface.viewers.FocusCellOwnerDrawHighlighter; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.ISelectionProvider; -import org.eclipse.jface.viewers.IStructuredContentProvider; -import org.eclipse.jface.viewers.SelectionChangedEvent; -import org.eclipse.jface.viewers.StructuredSelection; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.TableViewerColumn; -import org.eclipse.jface.viewers.TableViewerFocusCellManager; -import org.eclipse.jface.viewers.TextCellEditor; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.jface.viewers.ViewerCell; -import org.eclipse.jface.viewers.ViewerColumn; -import org.eclipse.jface.viewers.ViewerRow; -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.TableEditor; -import org.eclipse.swt.events.TraverseEvent; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Event; -import org.eclipse.swt.widgets.Item; -import org.eclipse.swt.widgets.TableColumn; -import org.eclipse.swt.widgets.TableItem; -import org.eclipse.ui.IWorkbenchSite; -import org.simantics.db.management.ISessionContext; -import org.simantics.g3d.property.annotations.CompoundGetPropertyValue; -import org.simantics.g3d.property.annotations.CompoundSetPropertyValue; -import org.simantics.g3d.property.annotations.GetPropertyValue; -import org.simantics.g3d.property.annotations.PropertyTabBlacklist; -import org.simantics.g3d.property.annotations.SetPropertyValue; -import org.simantics.g3d.scenegraph.IG3DNode; -import org.simantics.g3d.scenegraph.NodeMap; -import org.simantics.g3d.scenegraph.NodeMapProvider; -import org.simantics.g3d.scenegraph.base.INode; -import org.simantics.g3d.scenegraph.base.NodeListener; -import org.simantics.g3d.scenegraph.base.ParentNode; -import org.simantics.g3d.scenegraph.structural.IStructuralNode; -import org.simantics.g3d.tools.AdaptationUtils; -import org.simantics.selectionview.IPropertyTab; -import org.simantics.selectionview.IPropertyTab2; -import org.simantics.utils.datastructures.Callback; -import org.simantics.utils.datastructures.MapList; - -public class AnnotatedPropertyTabContributorFactory implements PropertyTabContributorFactory { - - private static final boolean DEBUG = false; - - @SuppressWarnings("unchecked") - @Override - public List getContributors(Object input) { - Map items = new LinkedHashMap(); - List blacklist = new ArrayList(); - try { - collectItems(input.getClass(), items); - collectBlacklist(input.getClass(), blacklist); - } catch (InstantiationException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IllegalAccessException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - if (items.size() == 0) - return Collections.EMPTY_LIST; - - MapList tabMap = new MapList(); - List tabs = new ArrayList(); - for (String id : items.keySet()) { - IPropertyItem item = items.get(id); - tabMap.add(item.getTabId(), item); - if (!tabs.contains(item.getTabId())) { - tabs.add(item.getTabId()); - //System.out.println(item.tabId + " " + item.name + " " + item.id); - } - } - for (String s : blacklist) { - tabs.remove(s); - } - - List contributors = new ArrayList(tabs.size()); - for (String tabId : tabs) { - contributors.add(new AnnotatedPropertyTabContributor(tabId, tabMap.getValues(tabId))); - } - - return contributors; - } - - - private static void collectItems(Class clazz, Map items) throws InstantiationException, IllegalAccessException { - Class superclass = clazz.getSuperclass(); - if(superclass != null) - collectItems(superclass, items); - - for (Method m : clazz.getDeclaredMethods()) { - m.setAccessible(true); - for (Annotation annotation : m.getAnnotations()) { - if (annotation.annotationType().equals(GetPropertyValue.class)) { - GetPropertyValue get = (GetPropertyValue)annotation; - PropertyItem item = (PropertyItem)items.get(get.value()); - if (item == null) { - item = new PropertyItem(get.value()); - items.put(item.id, item); - } - - item.getter = m; - item.manipulatorClass = get.manipulator().newInstance().get(m,null); - - item.tabId = get.tabId(); - - item.name = get.name(); - - - } else if (annotation.annotationType().equals(SetPropertyValue.class)) { - SetPropertyValue set = (SetPropertyValue)annotation; - PropertyItem item = (PropertyItem)items.get(set.value()); - if (item == null) { - item = new PropertyItem(set.value()); - items.put(item.id, item); - } - - item.setter = m; - } else if (annotation.annotationType().equals(CompoundGetPropertyValue.class)) { - CompoundGetPropertyValue get = (CompoundGetPropertyValue)annotation; - CompoundPropertyItem item = (CompoundPropertyItem)items.get(get.value()); - if (item == null) { - item = new CompoundPropertyItem(get.value()); - items.put(item.id, item); - } - - item.getter = m; - item.manipulatorFactory = get.manipulator().newInstance(); - - item.tabId = get.tabId(); - - item.name = get.name(); - } else if (annotation.annotationType().equals(CompoundSetPropertyValue.class)) { - CompoundSetPropertyValue set = (CompoundSetPropertyValue)annotation; - CompoundPropertyItem item = (CompoundPropertyItem)items.get(set.value()); - if (item == null) { - item = new CompoundPropertyItem(set.value()); - items.put(item.id, item); - } - - item.setter = m; - } - } - } - - - } - - private static void collectBlacklist(Class clazz, List blacklist) throws InstantiationException, IllegalAccessException { - Class superclass = clazz.getSuperclass(); - if(superclass != null) - collectBlacklist(superclass, blacklist); - - PropertyTabBlacklist ann = clazz.getAnnotation(PropertyTabBlacklist.class); - if (ann == null) - return; - String s = ann.value(); - if (s == null) - return; - if (s.length() == 0) - return; - for (String item : s.split(";")) { - blacklist.add(item); - } - - - } - - private static Map createManipulators(CompoundPropertyItem item, Object obj) { - try { - - Map map = (Map)item.getter.invoke(obj); - Map result = new HashMap(); - for (String key : map.keySet()) { - MethodWithMapValueProvider provider = new MethodWithMapValueProvider(item.getter, item.setter, key); - Class clazz = item.manipulatorFactory.get(null,map.get(key)); - PropertyManipulator manipulator = clazz.getConstructor(ValueProvider.class,Object.class).newInstance(provider,obj); - PropertyItem i = new PropertyItem(item.id+"."+key); - i.getter = item.getter; - i.setter = item.setter; - i.name = key; - i.tabId = item.tabId; - result.put(i,manipulator); - } - return result; - } catch (Exception e) { - e.printStackTrace(); - return Collections.EMPTY_MAP; - } - - } - - private static PropertyManipulator createManipulator(PropertyItem item, Object obj) { - try { - MethodValueProvider provider = new MethodValueProvider(item.getter, item.setter); - PropertyManipulator manipulator = item.manipulatorClass.getConstructor(ValueProvider.class,Object.class).newInstance(provider,obj); - return manipulator; - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - private static interface IPropertyItem { - public String getTabId(); - } - - private static class PropertyItem implements IPropertyItem{ - private String id; - private String name; - private String tabId; - private Method getter; - private Method setter; - private Class manipulatorClass; - - - public PropertyItem(String id) { - if (id == null) - throw new NullPointerException(); - this.id = id; - } - - @Override - public int hashCode() { - return id.hashCode(); - } - - @Override - public String getTabId() { - return tabId; - } - } - - private static class CompoundPropertyItem implements IPropertyItem{ - private String id; - private String name; - private String tabId; - private Method getter; - private Method setter; - private PropertyManipulatorFactory manipulatorFactory; - - - public CompoundPropertyItem(String id) { - if (id == null) - throw new NullPointerException(); - this.id = id; - } - - @Override - public int hashCode() { - return id.hashCode(); - } - - @Override - public String getTabId() { - return tabId; - } - } - - private static class AnnotatedPropertyTabContributor implements PropertyTabContributor { - private String id; - List items; - - public AnnotatedPropertyTabContributor(String id, List items) { - if (id == null) - throw new NullPointerException(); - this.id = id; - this.items = items; - } - - @Override - public IPropertyTab create(Composite parent, IWorkbenchSite site, - ISessionContext context, Object input) { - AnnotatedPropertyTab tab = new AnnotatedPropertyTab(id, items); - tab.createControl(parent, context); - return tab; - } - - @Override - public String getId() { - return id; - } - - } - - private static class AnnotatedPropertyTab implements IPropertyTab2, NodeListener { - //private String id; - List contibutedItems; - List resolvedItems = new ArrayList(); - private Map manipulators = new HashMap(); - - private TableViewer viewer; - - private IG3DNode node; - private NodeMap nodeMap; - - private List valueColumns = new ArrayList(); - - public AnnotatedPropertyTab(String id, List items) { - //this.id = id; - this.contibutedItems = items; - - - } - - @Override - public void createControl(Composite parent, ISessionContext context) { - //parent.setLayout(new FillLayout()); - viewer = new TableViewer(parent, SWT.FULL_SELECTION | SWT.SINGLE); - - GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(viewer.getTable()); - - //viewer.setLabelProvider(new AnnotatedTableLabelProvider(object)) - - viewer.setContentProvider(new PropertyItemContentsProvider()); - - TableViewerColumn name = new TableViewerColumn(viewer, SWT.LEFT); - //TableViewerColumn value = new TableViewerColumn(viewer, SWT.LEFT); - name.setLabelProvider(new PropertyItemNameProvider()); - //value.setLabelProvider(new PropertyValueLabelProvider(null)); - name.getColumn().setText("Property"); - //value.getColumn().setText("Value"); - name.getColumn().setWidth(200); - //value.getColumn().setWidth(200); - name.getViewer().addSelectionChangedListener(new ISelectionChangedListener() { - - @Override - public void selectionChanged(SelectionChangedEvent event) { - PropertyItem item = AdaptationUtils.adaptToSingle(event.getSelection(),PropertyItem.class); - if (item != null) { - PropertyManipulator manipulator = manipulators.get(item);//createManipulator(item, null); - for (int i = 0; i < valueColumns.size(); i++) { - TableViewerColumn c = valueColumns.get(i); - if (i < manipulator.getValueCount()) { - c.getColumn().setText(manipulator.getDescription(i)); - } else { - c.getColumn().setText(""); - } - } - } - - } - }); - - int valueCount = 0; - for (IPropertyItem item : contibutedItems) { - if (item instanceof PropertyItem) { - PropertyManipulator manipulator = createManipulator((PropertyItem)item, null); - if (manipulator == null) - continue; - if (valueCount < manipulator.getValueCount()) - valueCount = manipulator.getValueCount(); - } else if (item instanceof CompoundPropertyItem) { - if (valueCount < 1) - valueCount = 1; - } - } - for (int i = 0; i < valueCount; i++) { - TableViewerColumn value = new TableViewerColumn(viewer, SWT.LEFT); - //value.getColumn().setText("Value " + (i+1)); - value.getColumn().setText(""); - value.getColumn().setWidth(200); - valueColumns.add(value); - //value.setEditingSupport(new ) - } - viewer.getTable().setHeaderVisible(true); - viewer.getTable().setLinesVisible(true); - viewer.addSelectionChangedListener(new ISelectionChangedListener() { - - @Override - public void selectionChanged(SelectionChangedEvent event) { - PropertyItem item = AdaptationUtils.adaptToSingle(event.getSelection(), PropertyItem.class); - selectedItem = item; - if (!manipulators.get(selectedItem).getEditMode()) - manipulators.get(selectedItem).setEditMode(true); - - - for (IPropertyItem i : delayedUpdate) { - if (!i.equals(selectedItem)) { - manipulators.get(i).setEditMode(false); - viewer.update(i,null); - } - } - if (delayedUpdate.contains(selectedItem)) { - delayedUpdate.clear(); - delayedUpdate.add(selectedItem); - } else { - delayedUpdate.clear(); - } - } - }); - - CellNavigationStrategy nStrategy = new CellNavigationStrategy() { - private ViewerCell internalFindSelectedCell( - ColumnViewer viewer, ViewerCell currentSelectedCell, - Event event) { - switch (event.keyCode) { - case SWT.ARROW_UP: - if (currentSelectedCell != null) { - return getNeighbor(currentSelectedCell, - ViewerCell.ABOVE, false); - } - break; - case SWT.ARROW_DOWN: - if (currentSelectedCell != null) { - return getNeighbor(currentSelectedCell, - ViewerCell.BELOW, false); - } - break; - case SWT.ARROW_LEFT: - if (currentSelectedCell != null) { - return getNeighbor(currentSelectedCell, - ViewerCell.LEFT, true); - } - break; - case SWT.ARROW_RIGHT: - if (currentSelectedCell != null) { - return getNeighbor(currentSelectedCell, - ViewerCell.RIGHT, true); - } - break; - } - return null; - } - - public ViewerCell findSelectedCell(ColumnViewer viewer, - ViewerCell currentSelectedCell, Event event) { - ViewerCell cell = internalFindSelectedCell(viewer, - currentSelectedCell, event); - if (cell != null) { - TableColumn t = AnnotatedPropertyTab.this.viewer.getTable().getColumn( - cell.getColumnIndex()); - AnnotatedPropertyTab.this.viewer.getTable().showColumn(t); - } - return cell; - } - }; - - TableViewerFocusCellManager focusCellManager = new TableViewerFocusCellManager( - viewer, new FocusCellOwnerDrawHighlighter(viewer)); - try { - Field f = focusCellManager.getClass().getSuperclass() - .getDeclaredField("navigationStrategy"); - f.setAccessible(true); - f.set(focusCellManager, nStrategy); - } catch (SecurityException e) { - e.printStackTrace(); - } catch (NoSuchFieldException e) { - e.printStackTrace(); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - ColumnViewerEditorActivationStrategy actSupport = new ColumnViewerEditorActivationStrategy( - viewer) { - protected boolean isEditorActivationEvent( - ColumnViewerEditorActivationEvent event) { - return event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL - || event.eventType == ColumnViewerEditorActivationEvent.MOUSE_DOUBLE_CLICK_SELECTION - || (event.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED && event.keyCode == SWT.CR) - || event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC; - } - }; - TableViewerEditor.create(viewer, focusCellManager, actSupport, - ColumnViewerEditor.TABBING_HORIZONTAL - | ColumnViewerEditor.TABBING_MOVE_TO_ROW_NEIGHBOR - | ColumnViewerEditor.TABBING_VERTICAL - | ColumnViewerEditor.KEYBOARD_ACTIVATION); - viewer.getColumnViewerEditor().addEditorActivationListener( - new ColumnViewerEditorActivationListener() { - public void afterEditorActivated( - ColumnViewerEditorActivationEvent event) { - } - - public void afterEditorDeactivated( - ColumnViewerEditorDeactivationEvent event) { - } - - public void beforeEditorActivated( - ColumnViewerEditorActivationEvent event) { - ViewerCell cell = (ViewerCell) event.getSource(); - viewer.getTable().showColumn( - viewer.getTable().getColumn(cell.getColumnIndex())); - } - - public void beforeEditorDeactivated( - ColumnViewerEditorDeactivationEvent event) { - } - }); - } - - - - - private IPropertyItem selectedItem = null; - private Set delayedUpdate = new HashSet(); - - - @Override - public void setInput(ISessionContext context, ISelection selection, - boolean force) { - Collection nodes = AdaptationUtils.adaptToCollection(selection, IG3DNode.class); - if (nodes.size() != 1) { - if (node != null) { - node.removeListener(this); - node = null; - } - return; - } - IG3DNode n = nodes.iterator().next(); - if (node != null) { - if (!node.equals(n)) { - node.removeListener(this); - setInput(n); - } - } else { - setInput(n); - } - } - - - - private void setInput(IG3DNode node) { - this.node = node; - this.node.addListener(this); - // resolve nodemap - IG3DNode n = node; - while (true) { - if (n == null) { - nodeMap = null; - break; - } - if (n instanceof NodeMapProvider) { - nodeMap = ((NodeMapProvider) n).getNodeMap(); - if (nodeMap != null) - break; - } - n = (IG3DNode)n.getParent(); - } - boolean readOnly = (node instanceof IStructuralNode && ((IStructuralNode)node).isPartOfInstantiatedModel() && !((IStructuralNode)node).isInstantiatedModelRoot()); - // create label providers - PropertyValueLabelProvider2 p = new PropertyValueLabelProvider2(this); - int index = 0; - for (TableViewerColumn c : valueColumns) { - c.setLabelProvider(p); - if (!readOnly) { - PropertyEditingSupport support = new PropertyEditingSupport(this, viewer, index++, nodeMap); - c.setEditingSupport(support); - } - } - resolvedItems.clear(); - manipulators.clear(); - for (IPropertyItem item : contibutedItems) { - if (item instanceof PropertyItem) { - resolvedItems.add((PropertyItem)item); - manipulators.put((PropertyItem)item, createManipulator((PropertyItem)item, node)); - } - else { - CompoundPropertyItem compound = (CompoundPropertyItem)item; - Map manipulators = createManipulators(compound, node); - for (PropertyItem i : manipulators.keySet()) { - resolvedItems.add(i); - this.manipulators.put(i, manipulators.get(i)); - } - } - } - - viewer.getTable().setEnabled(!readOnly); - viewer.setInput(resolvedItems); - } - - @Override - public void requestFocus() { - viewer.getTable().forceFocus(); - } - - @Override - public void dispose() { - if (node != null) { - node.removeListener(this); - node = null; - } - - } - - @Override - public Control getControl() { - return viewer.getTable(); - } - - @Override - public ISelectionProvider getSelectionProvider() { - return null; - } - - @Override - public boolean isDisposed() { - return viewer.getTable().isDisposed(); - } - - @Override - public void nodeAdded(ParentNode node, - INode child, String rel) { - - } - - @Override - public void nodeRemoved(ParentNode node, - INode child, String rel) { - - } - - @Override - public void propertyChanged(INode node, final String id) { -// for (final PropertyItem item : items) { -// if (item.id.equals(id)) { -// Display.getDefault().asyncExec(new Runnable() { -// -// @Override -// public void run() { -// viewer.update(item, null); -// -// } -// }); -// } -// } - if (Thread.currentThread() == Display.getDefault().getThread()) { - if (DEBUG)System.out.println("Viewer refresh " + id); - for (PropertyItem item : resolvedItems) - if (!item.equals(selectedItem)) - viewer.refresh(item); - if (selectedItem != null) - delayedUpdate.add(selectedItem); - } else if (!editing){ - // running delayed refresh when a cell editor is active would cancel cell editing. - Display.getDefault().asyncExec(new Runnable() { - @Override - public void run() { - if (viewer.getTable().isDisposed()) { - if (AnnotatedPropertyTab.this.node != null) - AnnotatedPropertyTab.this.node.removeListener(AnnotatedPropertyTab.this); - return; - } - if (DEBUG) System.out.println("Viewer threaded refresh " + id); - for (PropertyItem item : resolvedItems) - if (!item.equals(selectedItem)) - viewer.refresh(item); - if (selectedItem != null) - delayedUpdate.add(selectedItem); - - } - }); - } else { - for (PropertyItem item : resolvedItems) { - delayedUpdate.add(item); - } - } - - } - - - - @Override - public void updatePartName(Consumer updateCallback) { - if (node != null) - updateCallback.accept(node.toString()); - - } - - public PropertyManipulator getManipulator(PropertyItem item) { - return manipulators.get(item); - } - - private boolean editing = false; - - public void setEditing(boolean editing) { - this.editing = editing; - } - } - - - - private static class PropertyEditingSupport extends EditingSupport { - AnnotatedPropertyTab tab; - int index; - NodeMap nodeMap; - TableViewer viewer; - CellEditor editor; - - public PropertyEditingSupport(AnnotatedPropertyTab tab, TableViewer viewer, int index, NodeMap nodeMap) { - super(viewer); - this.tab = tab; - this.index = index; - this.viewer = viewer; - this.nodeMap = nodeMap; - } - - @Override - protected boolean canEdit(Object element) { - PropertyItem item = (PropertyItem)element; - if (tab.getManipulator(item).getValueCount() <= index) - return false; - return (item.setter != null); - } - - @Override - protected CellEditor getCellEditor(Object element) { - - if (tab.getManipulator((PropertyItem)element).getValueCount() <= index) - return null; - if (editor == null) - editor = new TextCellEditor(viewer.getTable(),SWT.NONE) { - @Override - public void activate() { - tab.setEditing(true); - } - - @Override - public void deactivate() { - super.deactivate(); - tab.setEditing(false); - } - }; - if (DEBUG)System.err.println("CELL EDITOR: " + element); - return editor; - } - - @Override - protected Object getValue(Object element) { - PropertyItem item = (PropertyItem)element; - PropertyManipulator manipulator = tab.getManipulator(item);//createManipulator(item, obj); - if (manipulator.getValueCount() <= index) - return null; - Object value = manipulator.getValue(index); - return value; - } - - @Override - protected void setValue(Object element, Object value) { - - PropertyItem item = (PropertyItem)element; - PropertyManipulator manipulator = tab.getManipulator(item);//createManipulator(item, obj); - if (manipulator.getValueCount() <= index) - throw new IllegalAccessError("Editing value in index " + index + " is not allowed."); - if (DEBUG)System.err.println("CELL SET VALUE: " + element + " " + value); - manipulator.setValue((String)value,index); - viewer.refresh(item); - nodeMap.commit(); - - } - - - - } - - private static class PropertyItemNameProvider extends CellLabelProvider { - - - @Override - public void update(ViewerCell cell) { - PropertyItem item = (PropertyItem)cell.getElement(); - - if (item.name.length() > 0) - cell.setText(item.name); - else - cell.setText(item.id); - - - } - } - - private static class PropertyValueLabelProvider2 extends CellLabelProvider { - AnnotatedPropertyTab tab; - //private Object object; - - public PropertyValueLabelProvider2(AnnotatedPropertyTab tab) { - this.tab = tab; - } - - @Override - public void update(ViewerCell cell) { - PropertyItem item = (PropertyItem)cell.getElement(); - int index = cell.getColumnIndex() -1; - PropertyManipulator manipulator = tab.getManipulator(item);//createManipulator(item, object); - if (manipulator.getValueCount() <= index) - return; - cell.setText(manipulator.getValue(index)); - } - } - - private static class PropertyItemContentsProvider implements IStructuredContentProvider { - @SuppressWarnings("unchecked") - @Override - public Object[] getElements(Object inputElement) { - List items = (List)inputElement; - return items.toArray(); - } - - @Override - public void dispose() { - - } - - @Override - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - - } - } - - - - private static ViewerCell getNeighbor(ViewerCell currentCell, - int directionMask, boolean sameLevel) { - ViewerRow row; - if ((directionMask & ViewerCell.ABOVE) == ViewerCell.ABOVE) { - row = currentCell.getViewerRow().getNeighbor(ViewerRow.ABOVE, - sameLevel); - } else if ((directionMask & ViewerCell.BELOW) == ViewerCell.BELOW) { - row = currentCell.getViewerRow().getNeighbor(ViewerRow.BELOW, - sameLevel); - } else { - row = currentCell.getViewerRow(); - } - if (row != null) { - int columnIndex; - columnIndex = getVisualIndex(row, currentCell.getColumnIndex()); - int modifier = 0; - if ((directionMask & ViewerCell.LEFT) == ViewerCell.LEFT) { - modifier = -1; - } else if ((directionMask & ViewerCell.RIGHT) == ViewerCell.RIGHT) { - modifier = 1; - } - columnIndex += modifier; - if (columnIndex >= 0 && columnIndex < row.getColumnCount()) { - ViewerCell cell = getCellAtVisualIndex(row, columnIndex); - if (cell != null) { - while (cell != null - && columnIndex < row.getColumnCount() - 1 - && columnIndex > 0) { - if (isVisible(cell)) { - break; - } - columnIndex += modifier; - cell = getCellAtVisualIndex(row, columnIndex); - if (cell == null) { - break; - } - } - } - return cell; - } - } - return null; - } - - - - public static class TableViewerEditor extends ColumnViewerEditor { - /** - * This viewer's table editor. - */ - private TableEditor tableEditor; - private TableViewerFocusCellManager focusCellManager; - private int feature; - - /** - * @param viewer - * the viewer the editor is attached to - * @param focusCellManager - * the cell focus manager if one used or null - * @param editorActivationStrategy - * the strategy used to decide about the editor activation - * @param feature - * the feature mask - */ - TableViewerEditor(TableViewer viewer, - TableViewerFocusCellManager focusCellManager, - ColumnViewerEditorActivationStrategy editorActivationStrategy, - int feature) { - super(viewer, editorActivationStrategy, feature); - this.feature = feature; - tableEditor = new TableEditor(viewer.getTable()); - this.focusCellManager = focusCellManager; - } - - /** - * Create a customized editor with focusable cells - * - * @param viewer - * the viewer the editor is created for - * @param focusCellManager - * the cell focus manager if one needed else - * null - * @param editorActivationStrategy - * activation strategy to control if an editor activated - * @param feature - * bit mask controlling the editor - *
    - *
  • {@link ColumnViewerEditor#DEFAULT}
  • - *
  • {@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}
  • - *
  • {@link ColumnViewerEditor#TABBING_HORIZONTAL}
  • - *
  • - * {@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}
  • - *
  • {@link ColumnViewerEditor#TABBING_VERTICAL}
  • - *
- * @see #create(TableViewer, ColumnViewerEditorActivationStrategy, int) - */ - public static void create(TableViewer viewer, - TableViewerFocusCellManager focusCellManager, - ColumnViewerEditorActivationStrategy editorActivationStrategy, - int feature) { - TableViewerEditor editor = new TableViewerEditor(viewer, - focusCellManager, editorActivationStrategy, feature); - viewer.setColumnViewerEditor(editor); - if (focusCellManager != null) { - try { - Method m = focusCellManager.getClass().getSuperclass() - .getDeclaredMethod("init", null); - m.setAccessible(true); - m.invoke(focusCellManager, null); - } catch (SecurityException e) { - e.printStackTrace(); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } - // focusCellManager.init(); - } - } - - /** - * Create a customized editor whose activation process is customized - * - * @param viewer - * the viewer the editor is created for - * @param editorActivationStrategy - * activation strategy to control if an editor activated - * @param feature - * bit mask controlling the editor - *
    - *
  • {@link ColumnViewerEditor#DEFAULT}
  • - *
  • {@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}
  • - *
  • {@link ColumnViewerEditor#TABBING_HORIZONTAL}
  • - *
  • - * {@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}
  • - *
  • {@link ColumnViewerEditor#TABBING_VERTICAL}
  • - *
- */ - public static void create(TableViewer viewer, - ColumnViewerEditorActivationStrategy editorActivationStrategy, - int feature) { - create(viewer, null, editorActivationStrategy, feature); - } - - protected void setEditor(Control w, Item item, int columnNumber) { - tableEditor.setEditor(w, (TableItem) item, columnNumber); - } - - protected void setLayoutData(LayoutData layoutData) { - tableEditor.grabHorizontal = layoutData.grabHorizontal; - tableEditor.horizontalAlignment = layoutData.horizontalAlignment; - tableEditor.minimumWidth = layoutData.minimumWidth; - } - - public ViewerCell getFocusCell() { - if (focusCellManager != null) { - return focusCellManager.getFocusCell(); - } - return super.getFocusCell(); - } - - protected void updateFocusCell(ViewerCell focusCell, - ColumnViewerEditorActivationEvent event) { - // Update the focus cell when we activated the editor with these 2 - // events - if (event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC - || event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL) { - if (focusCellManager != null) { - try { - if (DEBUG)System.err.println("FOCUS CELL: " + focusCell); - - Method m = AbstractTableViewer.class.getDeclaredMethod( - "getSelectionFromWidget", null); - m.setAccessible(true); - List l = (List) m.invoke(getViewer(), null); - if (focusCellManager != null) { - m = focusCellManager - .getClass() - .getSuperclass() - .getDeclaredMethod("setFocusCell", - new Class[] { ViewerCell.class }); - m.setAccessible(true); - m.invoke(focusCellManager, - new Object[] { focusCell }); - } - if (!l.contains(focusCell.getElement())) { - getViewer().setSelection( - new StructuredSelection(focusCell - .getElement())); - } - } catch (SecurityException e) { - e.printStackTrace(); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } - } - } - } - - protected void processTraverseEvent(int columnIndex, ViewerRow row, - TraverseEvent event) { - ViewerCell cell2edit = null; - if (event.detail == SWT.TRAVERSE_TAB_PREVIOUS) { - event.doit = false; - if ((event.stateMask & SWT.CTRL) == SWT.CTRL - && (feature & TABBING_VERTICAL) == TABBING_VERTICAL) { - cell2edit = searchCellAboveBelow(row, getViewer(), - columnIndex, true); - } else if ((feature & TABBING_HORIZONTAL) == TABBING_HORIZONTAL) { - cell2edit = searchPreviousCell(row, - row.getCell(columnIndex), row.getCell(columnIndex), - getViewer()); - } - } else if (event.detail == SWT.TRAVERSE_TAB_NEXT) { - event.doit = false; - if ((event.stateMask & SWT.CTRL) == SWT.CTRL - && (feature & TABBING_VERTICAL) == TABBING_VERTICAL) { - cell2edit = searchCellAboveBelow(row, getViewer(), - columnIndex, false); - } else if ((feature & TABBING_HORIZONTAL) == TABBING_HORIZONTAL) { - cell2edit = searchNextCell(row, row.getCell(columnIndex), - row.getCell(columnIndex), getViewer()); - } - } - if (DEBUG) System.err.println("NEXT CELL: " + cell2edit); - if (cell2edit != null) { - getViewer().getControl().setRedraw(false); - ColumnViewerEditorActivationEvent acEvent = new ColumnViewerEditorActivationEvent( - cell2edit, event); - try { - Method m = ColumnViewer.class - .getDeclaredMethod( - "triggerEditorActivationEvent", - new Class[] { ColumnViewerEditorActivationEvent.class }); - m.setAccessible(true); - m.invoke(getViewer(), new Object[] { acEvent }); - } catch (SecurityException e) { - e.printStackTrace(); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } - getViewer().getControl().setRedraw(true); - } - } - - private ViewerCell searchCellAboveBelow(ViewerRow row, - ColumnViewer viewer, int columnIndex, boolean above) { - ViewerCell rv = null; - ViewerRow newRow = null; - if (above) { - newRow = row.getNeighbor(ViewerRow.ABOVE, false); - } else { - newRow = row.getNeighbor(ViewerRow.BELOW, false); - } - try { - if (newRow != null) { - Method m = ColumnViewer.class.getDeclaredMethod( - "getViewerColumn", new Class[] { int.class }); - m.setAccessible(true); - ViewerColumn column = (ViewerColumn) m.invoke(viewer, - new Object[] { new Integer(columnIndex) }); - m = ViewerColumn.class.getDeclaredMethod( - "getEditingSupport", null); - m.setAccessible(true); - EditingSupport es = (EditingSupport) m.invoke(column, null); - if (column != null && es != null) { - m = EditingSupport.class.getDeclaredMethod("canEdit", - new Class[] { Object.class }); - m.setAccessible(true); - Boolean b = (Boolean) m.invoke(es, - new Object[] { newRow.getItem().getData() }); - if (b.booleanValue()) { - rv = newRow.getCell(columnIndex); - } - } else { - rv = searchCellAboveBelow(newRow, viewer, columnIndex, - above); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - return rv; - } - - private ViewerCell searchPreviousCell(ViewerRow row, - ViewerCell currentCell, ViewerCell originalCell, - ColumnViewer viewer) { - ViewerCell rv = null; - ViewerCell previousCell; - if (currentCell != null) { - previousCell = getNeighbor(currentCell, ViewerCell.LEFT, true); - } else { - if (row.getColumnCount() != 0) { - previousCell = row.getCell(getCreationIndex(row, - row.getColumnCount() - 1)); - } else { - previousCell = row.getCell(0); - } - } - // No endless loop - if (originalCell.equals(previousCell)) { - return null; - } - if (previousCell != null) { - if (isCellEditable(viewer, previousCell)) { - rv = previousCell; - } else { - rv = searchPreviousCell(row, previousCell, originalCell, - viewer); - } - } else { - if ((feature & TABBING_CYCLE_IN_ROW) == TABBING_CYCLE_IN_ROW) { - rv = searchPreviousCell(row, null, originalCell, viewer); - } else if ((feature & TABBING_MOVE_TO_ROW_NEIGHBOR) == TABBING_MOVE_TO_ROW_NEIGHBOR) { - ViewerRow rowAbove = row - .getNeighbor(ViewerRow.ABOVE, false); - if (rowAbove != null) { - rv = searchPreviousCell(rowAbove, null, originalCell, - viewer); - } - } - } - return rv; - } - - private ViewerCell searchNextCell(ViewerRow row, - ViewerCell currentCell, ViewerCell originalCell, - ColumnViewer viewer) { - ViewerCell rv = null; - ViewerCell nextCell; - if (currentCell != null) { - nextCell = getNeighbor(currentCell, ViewerCell.RIGHT, true); - } else { - nextCell = row.getCell(getCreationIndex(row, 0)); - } - // No endless loop - if (originalCell.equals(nextCell)) { - return null; - } - if (nextCell != null) { - if (isCellEditable(viewer, nextCell)) { - rv = nextCell; - } else { - rv = searchNextCell(row, nextCell, originalCell, viewer); - } - } else { - if ((feature & TABBING_CYCLE_IN_ROW) == TABBING_CYCLE_IN_ROW) { - rv = searchNextCell(row, null, originalCell, viewer); - } else if ((feature & TABBING_MOVE_TO_ROW_NEIGHBOR) == TABBING_MOVE_TO_ROW_NEIGHBOR) { - ViewerRow rowBelow = row - .getNeighbor(ViewerRow.BELOW, false); - if (rowBelow != null) { - rv = searchNextCell(rowBelow, null, originalCell, - viewer); - } - } - } - return rv; - } - - private boolean isCellEditable(ColumnViewer viewer, ViewerCell cell) { - try { - Method m = ColumnViewer.class.getDeclaredMethod( - "getViewerColumn", new Class[] { int.class }); - m.setAccessible(true); - ViewerColumn column = (ViewerColumn) m.invoke(viewer, - new Object[] { new Integer(cell.getColumnIndex()) }); - m = ViewerColumn.class.getDeclaredMethod("getEditingSupport", - null); - m.setAccessible(true); - EditingSupport es = (EditingSupport) m.invoke(column, null); - if (column != null && es != null) { - m = EditingSupport.class.getDeclaredMethod("canEdit", - new Class[] { Object.class }); - m.setAccessible(true); - // return true; - Boolean b = (Boolean) m.invoke(es, - new Object[] { cell.getElement() }); - return b.booleanValue(); - } - } catch (Exception e) { - e.printStackTrace(); - } - return false; - } - } - - // Reimplementation of ViewerCell-Methods - private static int getVisualIndex(ViewerRow row, int creationIndex) { - TableItem item = (TableItem) row.getItem(); - int[] order = item.getParent().getColumnOrder(); - for (int i = 0; i < order.length; i++) { - if (order[i] == creationIndex) { - return i; - } - } - return creationIndex; - } - - private static int getCreationIndex(ViewerRow row, int visualIndex) { - TableItem item = (TableItem) row.getItem(); - if (item != null && !item.isDisposed() /* - * && hasColumns() && - * isValidOrderIndex - * (visualIndex) - */) { - return item.getParent().getColumnOrder()[visualIndex]; - } - return visualIndex; - } - - private static ViewerCell getCellAtVisualIndex(ViewerRow row, - int visualIndex) { - return getCell(row, getCreationIndex(row, visualIndex)); - } - - private static boolean isVisible(ViewerCell cell) { - return getWidth(cell) > 0; - } - - private static int getWidth(ViewerCell cell) { - TableItem item = (TableItem) cell.getViewerRow().getItem(); - return item.getParent().getColumn(cell.getColumnIndex()).getWidth(); - } - - private static ViewerCell getCell(ViewerRow row, int index) { - return row.getCell(index); - } - - -} +/******************************************************************************* + * Copyright (c) 2012, 2013 Association for Decentralized Information Management in + * Industry THTH ry. + * 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.g3d.property; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; + +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.viewers.AbstractTableViewer; +import org.eclipse.jface.viewers.CellEditor; +import org.eclipse.jface.viewers.CellEditor.LayoutData; +import org.eclipse.jface.viewers.CellLabelProvider; +import org.eclipse.jface.viewers.CellNavigationStrategy; +import org.eclipse.jface.viewers.ColumnViewer; +import org.eclipse.jface.viewers.ColumnViewerEditor; +import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent; +import org.eclipse.jface.viewers.ColumnViewerEditorActivationListener; +import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy; +import org.eclipse.jface.viewers.ColumnViewerEditorDeactivationEvent; +import org.eclipse.jface.viewers.EditingSupport; +import org.eclipse.jface.viewers.FocusCellOwnerDrawHighlighter; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.jface.viewers.TableViewerFocusCellManager; +import org.eclipse.jface.viewers.TextCellEditor; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerCell; +import org.eclipse.jface.viewers.ViewerColumn; +import org.eclipse.jface.viewers.ViewerRow; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.TableEditor; +import org.eclipse.swt.events.TraverseEvent; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Item; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.swt.widgets.TableItem; +import org.eclipse.ui.IWorkbenchSite; +import org.simantics.db.management.ISessionContext; +import org.simantics.g3d.property.annotations.CompoundGetPropertyValue; +import org.simantics.g3d.property.annotations.CompoundSetPropertyValue; +import org.simantics.g3d.property.annotations.GetPropertyValue; +import org.simantics.g3d.property.annotations.PropertyTabBlacklist; +import org.simantics.g3d.property.annotations.SetPropertyValue; +import org.simantics.g3d.scenegraph.IG3DNode; +import org.simantics.g3d.scenegraph.NodeMap; +import org.simantics.g3d.scenegraph.NodeMapProvider; +import org.simantics.g3d.scenegraph.base.INode; +import org.simantics.g3d.scenegraph.base.NodeListener; +import org.simantics.g3d.scenegraph.base.ParentNode; +import org.simantics.g3d.scenegraph.structural.IStructuralNode; +import org.simantics.g3d.tools.AdaptationUtils; +import org.simantics.selectionview.IPropertyTab; +import org.simantics.selectionview.IPropertyTab2; +import org.simantics.utils.datastructures.Callback; +import org.simantics.utils.datastructures.MapList; + +public class AnnotatedPropertyTabContributorFactory implements PropertyTabContributorFactory { + + private static final boolean DEBUG = false; + + @SuppressWarnings("unchecked") + @Override + public List getContributors(Object input) { + Map items = new LinkedHashMap(); + List blacklist = new ArrayList(); + try { + collectItems(input.getClass(), items); + collectBlacklist(input.getClass(), blacklist); + } catch (InstantiationException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IllegalAccessException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + if (items.size() == 0) + return Collections.EMPTY_LIST; + + MapList tabMap = new MapList(); + List tabs = new ArrayList(); + for (String id : items.keySet()) { + IPropertyItem item = items.get(id); + tabMap.add(item.getTabId(), item); + if (!tabs.contains(item.getTabId())) { + tabs.add(item.getTabId()); + //System.out.println(item.tabId + " " + item.name + " " + item.id); + } + } + for (String s : blacklist) { + tabs.remove(s); + } + + List contributors = new ArrayList(tabs.size()); + for (String tabId : tabs) { + contributors.add(new AnnotatedPropertyTabContributor(tabId, tabMap.getValues(tabId))); + } + + return contributors; + } + + + private static void collectItems(Class clazz, Map items) throws InstantiationException, IllegalAccessException { + Class superclass = clazz.getSuperclass(); + if(superclass != null) + collectItems(superclass, items); + + for (Method m : clazz.getDeclaredMethods()) { + m.setAccessible(true); + for (Annotation annotation : m.getAnnotations()) { + if (annotation.annotationType().equals(GetPropertyValue.class)) { + GetPropertyValue get = (GetPropertyValue)annotation; + PropertyItem item = (PropertyItem)items.get(get.value()); + if (item == null) { + item = new PropertyItem(get.value()); + items.put(item.id, item); + } + + item.getter = m; + item.manipulatorClass = get.manipulator().newInstance().get(m,null); + + item.tabId = get.tabId(); + + item.name = get.name(); + + + } else if (annotation.annotationType().equals(SetPropertyValue.class)) { + SetPropertyValue set = (SetPropertyValue)annotation; + PropertyItem item = (PropertyItem)items.get(set.value()); + if (item == null) { + item = new PropertyItem(set.value()); + items.put(item.id, item); + } + + item.setter = m; + } else if (annotation.annotationType().equals(CompoundGetPropertyValue.class)) { + CompoundGetPropertyValue get = (CompoundGetPropertyValue)annotation; + CompoundPropertyItem item = (CompoundPropertyItem)items.get(get.value()); + if (item == null) { + item = new CompoundPropertyItem(get.value()); + items.put(item.id, item); + } + + item.getter = m; + item.manipulatorFactory = get.manipulator().newInstance(); + + item.tabId = get.tabId(); + + item.name = get.name(); + } else if (annotation.annotationType().equals(CompoundSetPropertyValue.class)) { + CompoundSetPropertyValue set = (CompoundSetPropertyValue)annotation; + CompoundPropertyItem item = (CompoundPropertyItem)items.get(set.value()); + if (item == null) { + item = new CompoundPropertyItem(set.value()); + items.put(item.id, item); + } + + item.setter = m; + } + } + } + + + } + + private static void collectBlacklist(Class clazz, List blacklist) throws InstantiationException, IllegalAccessException { + Class superclass = clazz.getSuperclass(); + if(superclass != null) + collectBlacklist(superclass, blacklist); + + PropertyTabBlacklist ann = clazz.getAnnotation(PropertyTabBlacklist.class); + if (ann == null) + return; + String s = ann.value(); + if (s == null) + return; + if (s.length() == 0) + return; + for (String item : s.split(";")) { + blacklist.add(item); + } + + + } + + private static Map createManipulators(CompoundPropertyItem item, Object obj) { + try { + + Map map = (Map)item.getter.invoke(obj); + Map result = new HashMap(); + for (String key : map.keySet()) { + MethodWithMapValueProvider provider = new MethodWithMapValueProvider(item.getter, item.setter, key); + Class clazz = item.manipulatorFactory.get(null,map.get(key)); + PropertyManipulator manipulator = clazz.getConstructor(ValueProvider.class,Object.class).newInstance(provider,obj); + PropertyItem i = new PropertyItem(item.id+"."+key); + i.getter = item.getter; + i.setter = item.setter; + i.name = key; + i.tabId = item.tabId; + result.put(i,manipulator); + } + return result; + } catch (Exception e) { + e.printStackTrace(); + return Collections.EMPTY_MAP; + } + + } + + private static PropertyManipulator createManipulator(PropertyItem item, Object obj) { + try { + MethodValueProvider provider = new MethodValueProvider(item.getter, item.setter); + PropertyManipulator manipulator = item.manipulatorClass.getConstructor(ValueProvider.class,Object.class).newInstance(provider,obj); + return manipulator; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + private static interface IPropertyItem { + public String getTabId(); + } + + private static class PropertyItem implements IPropertyItem{ + private String id; + private String name; + private String tabId; + private Method getter; + private Method setter; + private Class manipulatorClass; + + + public PropertyItem(String id) { + if (id == null) + throw new NullPointerException(); + this.id = id; + } + + @Override + public int hashCode() { + return id.hashCode(); + } + + @Override + public String getTabId() { + return tabId; + } + } + + private static class CompoundPropertyItem implements IPropertyItem{ + private String id; + private String name; + private String tabId; + private Method getter; + private Method setter; + private PropertyManipulatorFactory manipulatorFactory; + + + public CompoundPropertyItem(String id) { + if (id == null) + throw new NullPointerException(); + this.id = id; + } + + @Override + public int hashCode() { + return id.hashCode(); + } + + @Override + public String getTabId() { + return tabId; + } + } + + private static class AnnotatedPropertyTabContributor implements PropertyTabContributor { + private String id; + List items; + + public AnnotatedPropertyTabContributor(String id, List items) { + if (id == null) + throw new NullPointerException(); + this.id = id; + this.items = items; + } + + @Override + public IPropertyTab create(Composite parent, IWorkbenchSite site, + ISessionContext context, Object input) { + AnnotatedPropertyTab tab = new AnnotatedPropertyTab(id, items); + tab.createControl(parent, context); + return tab; + } + + @Override + public String getId() { + return id; + } + + } + + private static class AnnotatedPropertyTab implements IPropertyTab2, NodeListener { + //private String id; + List contibutedItems; + List resolvedItems = new ArrayList(); + private Map manipulators = new HashMap(); + + private TableViewer viewer; + + private IG3DNode node; + private NodeMap nodeMap; + + private List valueColumns = new ArrayList(); + + public AnnotatedPropertyTab(String id, List items) { + //this.id = id; + this.contibutedItems = items; + + + } + + @Override + public void createControl(Composite parent, ISessionContext context) { + //parent.setLayout(new FillLayout()); + viewer = new TableViewer(parent, SWT.FULL_SELECTION | SWT.SINGLE); + + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(viewer.getTable()); + + //viewer.setLabelProvider(new AnnotatedTableLabelProvider(object)) + + viewer.setContentProvider(new PropertyItemContentsProvider()); + + TableViewerColumn name = new TableViewerColumn(viewer, SWT.LEFT); + //TableViewerColumn value = new TableViewerColumn(viewer, SWT.LEFT); + name.setLabelProvider(new PropertyItemNameProvider()); + //value.setLabelProvider(new PropertyValueLabelProvider(null)); + name.getColumn().setText("Property"); + //value.getColumn().setText("Value"); + name.getColumn().setWidth(200); + //value.getColumn().setWidth(200); + name.getViewer().addSelectionChangedListener(new ISelectionChangedListener() { + + @Override + public void selectionChanged(SelectionChangedEvent event) { + PropertyItem item = AdaptationUtils.adaptToSingle(event.getSelection(),PropertyItem.class); + if (item != null) { + PropertyManipulator manipulator = manipulators.get(item);//createManipulator(item, null); + for (int i = 0; i < valueColumns.size(); i++) { + TableViewerColumn c = valueColumns.get(i); + if (i < manipulator.getValueCount()) { + c.getColumn().setText(manipulator.getDescription(i)); + } else { + c.getColumn().setText(""); + } + } + } + + } + }); + + int valueCount = 0; + for (IPropertyItem item : contibutedItems) { + if (item instanceof PropertyItem) { + PropertyManipulator manipulator = createManipulator((PropertyItem)item, null); + if (manipulator == null) + continue; + if (valueCount < manipulator.getValueCount()) + valueCount = manipulator.getValueCount(); + } else if (item instanceof CompoundPropertyItem) { + if (valueCount < 1) + valueCount = 1; + } + } + for (int i = 0; i < valueCount; i++) { + TableViewerColumn value = new TableViewerColumn(viewer, SWT.LEFT); + //value.getColumn().setText("Value " + (i+1)); + value.getColumn().setText(""); + value.getColumn().setWidth(200); + valueColumns.add(value); + //value.setEditingSupport(new ) + } + viewer.getTable().setHeaderVisible(true); + viewer.getTable().setLinesVisible(true); + viewer.addSelectionChangedListener(new ISelectionChangedListener() { + + @Override + public void selectionChanged(SelectionChangedEvent event) { + PropertyItem item = AdaptationUtils.adaptToSingle(event.getSelection(), PropertyItem.class); + selectedItem = item; + if (!manipulators.get(selectedItem).getEditMode()) + manipulators.get(selectedItem).setEditMode(true); + + + for (IPropertyItem i : delayedUpdate) { + if (!i.equals(selectedItem)) { + manipulators.get(i).setEditMode(false); + viewer.update(i,null); + } + } + if (delayedUpdate.contains(selectedItem)) { + delayedUpdate.clear(); + delayedUpdate.add(selectedItem); + } else { + delayedUpdate.clear(); + } + } + }); + + CellNavigationStrategy nStrategy = new CellNavigationStrategy() { + private ViewerCell internalFindSelectedCell( + ColumnViewer viewer, ViewerCell currentSelectedCell, + Event event) { + switch (event.keyCode) { + case SWT.ARROW_UP: + if (currentSelectedCell != null) { + return getNeighbor(currentSelectedCell, + ViewerCell.ABOVE, false); + } + break; + case SWT.ARROW_DOWN: + if (currentSelectedCell != null) { + return getNeighbor(currentSelectedCell, + ViewerCell.BELOW, false); + } + break; + case SWT.ARROW_LEFT: + if (currentSelectedCell != null) { + return getNeighbor(currentSelectedCell, + ViewerCell.LEFT, true); + } + break; + case SWT.ARROW_RIGHT: + if (currentSelectedCell != null) { + return getNeighbor(currentSelectedCell, + ViewerCell.RIGHT, true); + } + break; + } + return null; + } + + public ViewerCell findSelectedCell(ColumnViewer viewer, + ViewerCell currentSelectedCell, Event event) { + ViewerCell cell = internalFindSelectedCell(viewer, + currentSelectedCell, event); + if (cell != null) { + TableColumn t = AnnotatedPropertyTab.this.viewer.getTable().getColumn( + cell.getColumnIndex()); + AnnotatedPropertyTab.this.viewer.getTable().showColumn(t); + } + return cell; + } + }; + + TableViewerFocusCellManager focusCellManager = new TableViewerFocusCellManager( + viewer, new FocusCellOwnerDrawHighlighter(viewer)); + try { + Field f = focusCellManager.getClass().getSuperclass() + .getDeclaredField("navigationStrategy"); + f.setAccessible(true); + f.set(focusCellManager, nStrategy); + } catch (SecurityException e) { + e.printStackTrace(); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + ColumnViewerEditorActivationStrategy actSupport = new ColumnViewerEditorActivationStrategy( + viewer) { + protected boolean isEditorActivationEvent( + ColumnViewerEditorActivationEvent event) { + return event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL + || event.eventType == ColumnViewerEditorActivationEvent.MOUSE_DOUBLE_CLICK_SELECTION + || (event.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED && event.keyCode == SWT.CR) + || event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC; + } + }; + TableViewerEditor.create(viewer, focusCellManager, actSupport, + ColumnViewerEditor.TABBING_HORIZONTAL + | ColumnViewerEditor.TABBING_MOVE_TO_ROW_NEIGHBOR + | ColumnViewerEditor.TABBING_VERTICAL + | ColumnViewerEditor.KEYBOARD_ACTIVATION); + viewer.getColumnViewerEditor().addEditorActivationListener( + new ColumnViewerEditorActivationListener() { + public void afterEditorActivated( + ColumnViewerEditorActivationEvent event) { + } + + public void afterEditorDeactivated( + ColumnViewerEditorDeactivationEvent event) { + } + + public void beforeEditorActivated( + ColumnViewerEditorActivationEvent event) { + ViewerCell cell = (ViewerCell) event.getSource(); + viewer.getTable().showColumn( + viewer.getTable().getColumn(cell.getColumnIndex())); + } + + public void beforeEditorDeactivated( + ColumnViewerEditorDeactivationEvent event) { + } + }); + } + + + + + private IPropertyItem selectedItem = null; + private Set delayedUpdate = new HashSet(); + + + @Override + public void setInput(ISessionContext context, ISelection selection, + boolean force) { + Collection nodes = AdaptationUtils.adaptToCollection(selection, IG3DNode.class); + if (nodes.size() != 1) { + if (node != null) { + node.removeListener(this); + node = null; + } + return; + } + IG3DNode n = nodes.iterator().next(); + if (node != null) { + if (!node.equals(n)) { + node.removeListener(this); + setInput(n); + } + } else { + setInput(n); + } + } + + + + private void setInput(IG3DNode node) { + this.node = node; + this.node.addListener(this); + // resolve nodemap + IG3DNode n = node; + while (true) { + if (n == null) { + nodeMap = null; + break; + } + if (n instanceof NodeMapProvider) { + nodeMap = ((NodeMapProvider) n).getNodeMap(); + if (nodeMap != null) + break; + } + n = (IG3DNode)n.getParent(); + } + boolean readOnly = (node instanceof IStructuralNode && ((IStructuralNode)node).isPartOfInstantiatedModel() && !((IStructuralNode)node).isInstantiatedModelRoot()); + // create label providers + PropertyValueLabelProvider2 p = new PropertyValueLabelProvider2(this); + int index = 0; + for (TableViewerColumn c : valueColumns) { + c.setLabelProvider(p); + if (!readOnly) { + PropertyEditingSupport support = new PropertyEditingSupport(this, viewer, index++, nodeMap); + c.setEditingSupport(support); + } + } + resolvedItems.clear(); + manipulators.clear(); + for (IPropertyItem item : contibutedItems) { + if (item instanceof PropertyItem) { + resolvedItems.add((PropertyItem)item); + manipulators.put((PropertyItem)item, createManipulator((PropertyItem)item, node)); + } + else { + CompoundPropertyItem compound = (CompoundPropertyItem)item; + Map manipulators = createManipulators(compound, node); + for (PropertyItem i : manipulators.keySet()) { + resolvedItems.add(i); + this.manipulators.put(i, manipulators.get(i)); + } + } + } + + viewer.getTable().setEnabled(!readOnly); + viewer.setInput(resolvedItems); + } + + @Override + public void requestFocus() { + viewer.getTable().forceFocus(); + } + + @Override + public void dispose() { + if (node != null) { + node.removeListener(this); + node = null; + } + + } + + @Override + public Control getControl() { + return viewer.getTable(); + } + + @Override + public ISelectionProvider getSelectionProvider() { + return null; + } + + @Override + public boolean isDisposed() { + return viewer.getTable().isDisposed(); + } + + @Override + public void nodeAdded(ParentNode node, + INode child, String rel) { + + } + + @Override + public void nodeRemoved(ParentNode node, + INode child, String rel) { + + } + + @Override + public void propertyChanged(INode node, final String id) { +// for (final PropertyItem item : items) { +// if (item.id.equals(id)) { +// Display.getDefault().asyncExec(new Runnable() { +// +// @Override +// public void run() { +// viewer.update(item, null); +// +// } +// }); +// } +// } + if (Thread.currentThread() == Display.getDefault().getThread()) { + if (DEBUG)System.out.println("Viewer refresh " + id); + for (PropertyItem item : resolvedItems) + if (!item.equals(selectedItem)) + viewer.refresh(item); + if (selectedItem != null) + delayedUpdate.add(selectedItem); + } else if (!editing){ + // running delayed refresh when a cell editor is active would cancel cell editing. + Display.getDefault().asyncExec(new Runnable() { + @Override + public void run() { + if (viewer.getTable().isDisposed()) { + if (AnnotatedPropertyTab.this.node != null) + AnnotatedPropertyTab.this.node.removeListener(AnnotatedPropertyTab.this); + return; + } + if (DEBUG) System.out.println("Viewer threaded refresh " + id); + for (PropertyItem item : resolvedItems) + if (!item.equals(selectedItem)) + viewer.refresh(item); + if (selectedItem != null) + delayedUpdate.add(selectedItem); + + } + }); + } else { + for (PropertyItem item : resolvedItems) { + delayedUpdate.add(item); + } + } + + } + + + + @Override + public void updatePartName(Consumer updateCallback) { + if (node != null) + updateCallback.accept(node.toString()); + + } + + public PropertyManipulator getManipulator(PropertyItem item) { + return manipulators.get(item); + } + + private boolean editing = false; + + public void setEditing(boolean editing) { + this.editing = editing; + } + } + + + + private static class PropertyEditingSupport extends EditingSupport { + AnnotatedPropertyTab tab; + int index; + NodeMap nodeMap; + TableViewer viewer; + CellEditor editor; + + public PropertyEditingSupport(AnnotatedPropertyTab tab, TableViewer viewer, int index, NodeMap nodeMap) { + super(viewer); + this.tab = tab; + this.index = index; + this.viewer = viewer; + this.nodeMap = nodeMap; + } + + @Override + protected boolean canEdit(Object element) { + PropertyItem item = (PropertyItem)element; + if (tab.getManipulator(item).getValueCount() <= index) + return false; + return (item.setter != null); + } + + @Override + protected CellEditor getCellEditor(Object element) { + + if (tab.getManipulator((PropertyItem)element).getValueCount() <= index) + return null; + if (editor == null) + editor = new TextCellEditor(viewer.getTable(),SWT.NONE) { + @Override + public void activate() { + tab.setEditing(true); + } + + @Override + public void deactivate() { + super.deactivate(); + tab.setEditing(false); + } + }; + if (DEBUG)System.err.println("CELL EDITOR: " + element); + return editor; + } + + @Override + protected Object getValue(Object element) { + PropertyItem item = (PropertyItem)element; + PropertyManipulator manipulator = tab.getManipulator(item);//createManipulator(item, obj); + if (manipulator.getValueCount() <= index) + return null; + Object value = manipulator.getValue(index); + return value; + } + + @Override + protected void setValue(Object element, Object value) { + + PropertyItem item = (PropertyItem)element; + PropertyManipulator manipulator = tab.getManipulator(item);//createManipulator(item, obj); + if (manipulator.getValueCount() <= index) + throw new IllegalAccessError("Editing value in index " + index + " is not allowed."); + if (DEBUG)System.err.println("CELL SET VALUE: " + element + " " + value); + manipulator.setValue((String)value,index); + viewer.refresh(item); + nodeMap.commit(); + + } + + + + } + + private static class PropertyItemNameProvider extends CellLabelProvider { + + + @Override + public void update(ViewerCell cell) { + PropertyItem item = (PropertyItem)cell.getElement(); + + if (item.name.length() > 0) + cell.setText(item.name); + else + cell.setText(item.id); + + + } + } + + private static class PropertyValueLabelProvider2 extends CellLabelProvider { + AnnotatedPropertyTab tab; + //private Object object; + + public PropertyValueLabelProvider2(AnnotatedPropertyTab tab) { + this.tab = tab; + } + + @Override + public void update(ViewerCell cell) { + PropertyItem item = (PropertyItem)cell.getElement(); + int index = cell.getColumnIndex() -1; + PropertyManipulator manipulator = tab.getManipulator(item);//createManipulator(item, object); + if (manipulator.getValueCount() <= index) + return; + cell.setText(manipulator.getValue(index)); + } + } + + private static class PropertyItemContentsProvider implements IStructuredContentProvider { + @SuppressWarnings("unchecked") + @Override + public Object[] getElements(Object inputElement) { + List items = (List)inputElement; + return items.toArray(); + } + + @Override + public void dispose() { + + } + + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + + } + } + + + + private static ViewerCell getNeighbor(ViewerCell currentCell, + int directionMask, boolean sameLevel) { + ViewerRow row; + if ((directionMask & ViewerCell.ABOVE) == ViewerCell.ABOVE) { + row = currentCell.getViewerRow().getNeighbor(ViewerRow.ABOVE, + sameLevel); + } else if ((directionMask & ViewerCell.BELOW) == ViewerCell.BELOW) { + row = currentCell.getViewerRow().getNeighbor(ViewerRow.BELOW, + sameLevel); + } else { + row = currentCell.getViewerRow(); + } + if (row != null) { + int columnIndex; + columnIndex = getVisualIndex(row, currentCell.getColumnIndex()); + int modifier = 0; + if ((directionMask & ViewerCell.LEFT) == ViewerCell.LEFT) { + modifier = -1; + } else if ((directionMask & ViewerCell.RIGHT) == ViewerCell.RIGHT) { + modifier = 1; + } + columnIndex += modifier; + if (columnIndex >= 0 && columnIndex < row.getColumnCount()) { + ViewerCell cell = getCellAtVisualIndex(row, columnIndex); + if (cell != null) { + while (cell != null + && columnIndex < row.getColumnCount() - 1 + && columnIndex > 0) { + if (isVisible(cell)) { + break; + } + columnIndex += modifier; + cell = getCellAtVisualIndex(row, columnIndex); + if (cell == null) { + break; + } + } + } + return cell; + } + } + return null; + } + + + + public static class TableViewerEditor extends ColumnViewerEditor { + /** + * This viewer's table editor. + */ + private TableEditor tableEditor; + private TableViewerFocusCellManager focusCellManager; + private int feature; + + /** + * @param viewer + * the viewer the editor is attached to + * @param focusCellManager + * the cell focus manager if one used or null + * @param editorActivationStrategy + * the strategy used to decide about the editor activation + * @param feature + * the feature mask + */ + TableViewerEditor(TableViewer viewer, + TableViewerFocusCellManager focusCellManager, + ColumnViewerEditorActivationStrategy editorActivationStrategy, + int feature) { + super(viewer, editorActivationStrategy, feature); + this.feature = feature; + tableEditor = new TableEditor(viewer.getTable()); + this.focusCellManager = focusCellManager; + } + + /** + * Create a customized editor with focusable cells + * + * @param viewer + * the viewer the editor is created for + * @param focusCellManager + * the cell focus manager if one needed else + * null + * @param editorActivationStrategy + * activation strategy to control if an editor activated + * @param feature + * bit mask controlling the editor + *
    + *
  • {@link ColumnViewerEditor#DEFAULT}
  • + *
  • {@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}
  • + *
  • {@link ColumnViewerEditor#TABBING_HORIZONTAL}
  • + *
  • + * {@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}
  • + *
  • {@link ColumnViewerEditor#TABBING_VERTICAL}
  • + *
+ * @see #create(TableViewer, ColumnViewerEditorActivationStrategy, int) + */ + public static void create(TableViewer viewer, + TableViewerFocusCellManager focusCellManager, + ColumnViewerEditorActivationStrategy editorActivationStrategy, + int feature) { + TableViewerEditor editor = new TableViewerEditor(viewer, + focusCellManager, editorActivationStrategy, feature); + viewer.setColumnViewerEditor(editor); + if (focusCellManager != null) { + try { + Method m = focusCellManager.getClass().getSuperclass() + .getDeclaredMethod("init", null); + m.setAccessible(true); + m.invoke(focusCellManager, null); + } catch (SecurityException e) { + e.printStackTrace(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + // focusCellManager.init(); + } + } + + /** + * Create a customized editor whose activation process is customized + * + * @param viewer + * the viewer the editor is created for + * @param editorActivationStrategy + * activation strategy to control if an editor activated + * @param feature + * bit mask controlling the editor + *
    + *
  • {@link ColumnViewerEditor#DEFAULT}
  • + *
  • {@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}
  • + *
  • {@link ColumnViewerEditor#TABBING_HORIZONTAL}
  • + *
  • + * {@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}
  • + *
  • {@link ColumnViewerEditor#TABBING_VERTICAL}
  • + *
+ */ + public static void create(TableViewer viewer, + ColumnViewerEditorActivationStrategy editorActivationStrategy, + int feature) { + create(viewer, null, editorActivationStrategy, feature); + } + + protected void setEditor(Control w, Item item, int columnNumber) { + tableEditor.setEditor(w, (TableItem) item, columnNumber); + } + + protected void setLayoutData(LayoutData layoutData) { + tableEditor.grabHorizontal = layoutData.grabHorizontal; + tableEditor.horizontalAlignment = layoutData.horizontalAlignment; + tableEditor.minimumWidth = layoutData.minimumWidth; + } + + public ViewerCell getFocusCell() { + if (focusCellManager != null) { + return focusCellManager.getFocusCell(); + } + return super.getFocusCell(); + } + + protected void updateFocusCell(ViewerCell focusCell, + ColumnViewerEditorActivationEvent event) { + // Update the focus cell when we activated the editor with these 2 + // events + if (event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC + || event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL) { + if (focusCellManager != null) { + try { + if (DEBUG)System.err.println("FOCUS CELL: " + focusCell); + + Method m = AbstractTableViewer.class.getDeclaredMethod( + "getSelectionFromWidget", null); + m.setAccessible(true); + List l = (List) m.invoke(getViewer(), null); + if (focusCellManager != null) { + m = focusCellManager + .getClass() + .getSuperclass() + .getDeclaredMethod("setFocusCell", + new Class[] { ViewerCell.class }); + m.setAccessible(true); + m.invoke(focusCellManager, + new Object[] { focusCell }); + } + if (!l.contains(focusCell.getElement())) { + getViewer().setSelection( + new StructuredSelection(focusCell + .getElement())); + } + } catch (SecurityException e) { + e.printStackTrace(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + } + } + } + + protected void processTraverseEvent(int columnIndex, ViewerRow row, + TraverseEvent event) { + ViewerCell cell2edit = null; + if (event.detail == SWT.TRAVERSE_TAB_PREVIOUS) { + event.doit = false; + if ((event.stateMask & SWT.CTRL) == SWT.CTRL + && (feature & TABBING_VERTICAL) == TABBING_VERTICAL) { + cell2edit = searchCellAboveBelow(row, getViewer(), + columnIndex, true); + } else if ((feature & TABBING_HORIZONTAL) == TABBING_HORIZONTAL) { + cell2edit = searchPreviousCell(row, + row.getCell(columnIndex), row.getCell(columnIndex), + getViewer()); + } + } else if (event.detail == SWT.TRAVERSE_TAB_NEXT) { + event.doit = false; + if ((event.stateMask & SWT.CTRL) == SWT.CTRL + && (feature & TABBING_VERTICAL) == TABBING_VERTICAL) { + cell2edit = searchCellAboveBelow(row, getViewer(), + columnIndex, false); + } else if ((feature & TABBING_HORIZONTAL) == TABBING_HORIZONTAL) { + cell2edit = searchNextCell(row, row.getCell(columnIndex), + row.getCell(columnIndex), getViewer()); + } + } + if (DEBUG) System.err.println("NEXT CELL: " + cell2edit); + if (cell2edit != null) { + getViewer().getControl().setRedraw(false); + ColumnViewerEditorActivationEvent acEvent = new ColumnViewerEditorActivationEvent( + cell2edit, event); + try { + Method m = ColumnViewer.class + .getDeclaredMethod( + "triggerEditorActivationEvent", + new Class[] { ColumnViewerEditorActivationEvent.class }); + m.setAccessible(true); + m.invoke(getViewer(), new Object[] { acEvent }); + } catch (SecurityException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + getViewer().getControl().setRedraw(true); + } + } + + private ViewerCell searchCellAboveBelow(ViewerRow row, + ColumnViewer viewer, int columnIndex, boolean above) { + ViewerCell rv = null; + ViewerRow newRow = null; + if (above) { + newRow = row.getNeighbor(ViewerRow.ABOVE, false); + } else { + newRow = row.getNeighbor(ViewerRow.BELOW, false); + } + try { + if (newRow != null) { + Method m = ColumnViewer.class.getDeclaredMethod( + "getViewerColumn", new Class[] { int.class }); + m.setAccessible(true); + ViewerColumn column = (ViewerColumn) m.invoke(viewer, + new Object[] { new Integer(columnIndex) }); + m = ViewerColumn.class.getDeclaredMethod( + "getEditingSupport", null); + m.setAccessible(true); + EditingSupport es = (EditingSupport) m.invoke(column, null); + if (column != null && es != null) { + m = EditingSupport.class.getDeclaredMethod("canEdit", + new Class[] { Object.class }); + m.setAccessible(true); + Boolean b = (Boolean) m.invoke(es, + new Object[] { newRow.getItem().getData() }); + if (b.booleanValue()) { + rv = newRow.getCell(columnIndex); + } + } else { + rv = searchCellAboveBelow(newRow, viewer, columnIndex, + above); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return rv; + } + + private ViewerCell searchPreviousCell(ViewerRow row, + ViewerCell currentCell, ViewerCell originalCell, + ColumnViewer viewer) { + ViewerCell rv = null; + ViewerCell previousCell; + if (currentCell != null) { + previousCell = getNeighbor(currentCell, ViewerCell.LEFT, true); + } else { + if (row.getColumnCount() != 0) { + previousCell = row.getCell(getCreationIndex(row, + row.getColumnCount() - 1)); + } else { + previousCell = row.getCell(0); + } + } + // No endless loop + if (originalCell.equals(previousCell)) { + return null; + } + if (previousCell != null) { + if (isCellEditable(viewer, previousCell)) { + rv = previousCell; + } else { + rv = searchPreviousCell(row, previousCell, originalCell, + viewer); + } + } else { + if ((feature & TABBING_CYCLE_IN_ROW) == TABBING_CYCLE_IN_ROW) { + rv = searchPreviousCell(row, null, originalCell, viewer); + } else if ((feature & TABBING_MOVE_TO_ROW_NEIGHBOR) == TABBING_MOVE_TO_ROW_NEIGHBOR) { + ViewerRow rowAbove = row + .getNeighbor(ViewerRow.ABOVE, false); + if (rowAbove != null) { + rv = searchPreviousCell(rowAbove, null, originalCell, + viewer); + } + } + } + return rv; + } + + private ViewerCell searchNextCell(ViewerRow row, + ViewerCell currentCell, ViewerCell originalCell, + ColumnViewer viewer) { + ViewerCell rv = null; + ViewerCell nextCell; + if (currentCell != null) { + nextCell = getNeighbor(currentCell, ViewerCell.RIGHT, true); + } else { + nextCell = row.getCell(getCreationIndex(row, 0)); + } + // No endless loop + if (originalCell.equals(nextCell)) { + return null; + } + if (nextCell != null) { + if (isCellEditable(viewer, nextCell)) { + rv = nextCell; + } else { + rv = searchNextCell(row, nextCell, originalCell, viewer); + } + } else { + if ((feature & TABBING_CYCLE_IN_ROW) == TABBING_CYCLE_IN_ROW) { + rv = searchNextCell(row, null, originalCell, viewer); + } else if ((feature & TABBING_MOVE_TO_ROW_NEIGHBOR) == TABBING_MOVE_TO_ROW_NEIGHBOR) { + ViewerRow rowBelow = row + .getNeighbor(ViewerRow.BELOW, false); + if (rowBelow != null) { + rv = searchNextCell(rowBelow, null, originalCell, + viewer); + } + } + } + return rv; + } + + private boolean isCellEditable(ColumnViewer viewer, ViewerCell cell) { + try { + Method m = ColumnViewer.class.getDeclaredMethod( + "getViewerColumn", new Class[] { int.class }); + m.setAccessible(true); + ViewerColumn column = (ViewerColumn) m.invoke(viewer, + new Object[] { new Integer(cell.getColumnIndex()) }); + m = ViewerColumn.class.getDeclaredMethod("getEditingSupport", + null); + m.setAccessible(true); + EditingSupport es = (EditingSupport) m.invoke(column, null); + if (column != null && es != null) { + m = EditingSupport.class.getDeclaredMethod("canEdit", + new Class[] { Object.class }); + m.setAccessible(true); + // return true; + Boolean b = (Boolean) m.invoke(es, + new Object[] { cell.getElement() }); + return b.booleanValue(); + } + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } + } + + // Reimplementation of ViewerCell-Methods + private static int getVisualIndex(ViewerRow row, int creationIndex) { + TableItem item = (TableItem) row.getItem(); + int[] order = item.getParent().getColumnOrder(); + for (int i = 0; i < order.length; i++) { + if (order[i] == creationIndex) { + return i; + } + } + return creationIndex; + } + + private static int getCreationIndex(ViewerRow row, int visualIndex) { + TableItem item = (TableItem) row.getItem(); + if (item != null && !item.isDisposed() /* + * && hasColumns() && + * isValidOrderIndex + * (visualIndex) + */) { + return item.getParent().getColumnOrder()[visualIndex]; + } + return visualIndex; + } + + private static ViewerCell getCellAtVisualIndex(ViewerRow row, + int visualIndex) { + return getCell(row, getCreationIndex(row, visualIndex)); + } + + private static boolean isVisible(ViewerCell cell) { + return getWidth(cell) > 0; + } + + private static int getWidth(ViewerCell cell) { + TableItem item = (TableItem) cell.getViewerRow().getItem(); + return item.getParent().getColumn(cell.getColumnIndex()).getWidth(); + } + + private static ViewerCell getCell(ViewerRow row, int index) { + return row.getCell(index); + } + + +} diff --git a/org.simantics.g3d/src/org/simantics/g3d/scenegraph/NodeMap.java b/org.simantics.g3d/src/org/simantics/g3d/scenegraph/NodeMap.java index 7e0f31e6..6324d90e 100644 --- a/org.simantics.g3d/src/org/simantics/g3d/scenegraph/NodeMap.java +++ b/org.simantics.g3d/src/org/simantics/g3d/scenegraph/NodeMap.java @@ -1,59 +1,62 @@ -/******************************************************************************* - * Copyright (c) 2012, 2013 Association for Decentralized Information Management in - * Industry THTH ry. - * 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.g3d.scenegraph; - -import java.util.Collection; - -import org.simantics.g3d.scenegraph.base.INode; -import org.simantics.g3d.scenegraph.base.NodeListener; -import org.simantics.g3d.scenegraph.base.ParentNode; - -public interface NodeMap { - - public Collection getRenderObjects(INode node); - - public void updateRenderObjectsFor(INode node); - - public INode getNode(T t); - - public ParentNode getRootNode(); - - /** - * Commit changes to the database. - */ - public void commit(); - - - /** - * Deletes (Disposes) the map. - */ - public void delete(); - - - /** - * Track changes that are going to be committed into the database. - * - * Disabling change tracking causes commit() to do nothing. - * @param enabled - */ - public void setChangeTracking(boolean enabled); - public boolean isChangeTracking(); - - - /** - * Add listener for all scene-graph events. - * @param listener - */ - public void addListener(NodeListener listener); - - public void removeListener(NodeListener listener); -} +/******************************************************************************* + * Copyright (c) 2012, 2013 Association for Decentralized Information Management in + * Industry THTH ry. + * 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.g3d.scenegraph; + +import java.util.Collection; + +import org.simantics.g3d.scenegraph.base.INode; +import org.simantics.g3d.scenegraph.base.NodeListener; +import org.simantics.g3d.scenegraph.base.ParentNode; +import org.simantics.objmap.graph.IMapping; + +public interface NodeMap { + + public Collection getRenderObjects(E node); + + public void updateRenderObjectsFor(E node); + + public E getNode(T t); + + public ParentNode getRootNode(); + + /** + * Commit changes to the database. + */ + public void commit(); + + + /** + * Deletes (Disposes) the map. + */ + public void delete(); + + + /** + * Track changes that are going to be committed into the database. + * + * Disabling change tracking causes commit() to do nothing. + * @param enabled + */ + public void setChangeTracking(boolean enabled); + public boolean isChangeTracking(); + + + /** + * Add listener for all scene-graph events. + * @param listener + */ + public void addListener(NodeListener listener); + + public void removeListener(NodeListener listener); + + public IMapping getMapping(); +} diff --git a/org.simantics.g3d/src/org/simantics/g3d/scenegraph/NodeMapProvider.java b/org.simantics.g3d/src/org/simantics/g3d/scenegraph/NodeMapProvider.java index 657fe156..e9b7d4b4 100644 --- a/org.simantics.g3d/src/org/simantics/g3d/scenegraph/NodeMapProvider.java +++ b/org.simantics.g3d/src/org/simantics/g3d/scenegraph/NodeMapProvider.java @@ -1,17 +1,19 @@ -/******************************************************************************* - * Copyright (c) 2012, 2013 Association for Decentralized Information Management in - * Industry THTH ry. - * 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.g3d.scenegraph; - -public interface NodeMapProvider { - - public NodeMap getNodeMap(); -} +/******************************************************************************* + * Copyright (c) 2012, 2013 Association for Decentralized Information Management in + * Industry THTH ry. + * 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.g3d.scenegraph; + +import org.simantics.g3d.scenegraph.base.INode; + +public interface NodeMapProvider { + + public NodeMap getNodeMap(); +} diff --git a/org.simantics.g3d/src/org/simantics/g3d/scenegraph/base/INode.java b/org.simantics.g3d/src/org/simantics/g3d/scenegraph/base/INode.java index d3d276d3..a8379433 100644 --- a/org.simantics.g3d/src/org/simantics/g3d/scenegraph/base/INode.java +++ b/org.simantics.g3d/src/org/simantics/g3d/scenegraph/base/INode.java @@ -1,66 +1,66 @@ -/******************************************************************************* - * Copyright (c) 2012, 2013 Association for Decentralized Information Management in - * Industry THTH ry. - * 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.g3d.scenegraph.base; - -import java.util.List; - - - - - -public interface INode { - - /** - * - * @return unique node identifier - */ - public Long getId(); - - /** - * @return root node of the scene graph or null if this node is - * not part of a properly rooted scene graph hierarchy - */ - public ParentNode getRootNode(); - - /** - * @return Parent node reference or null if not set - */ - public ParentNode getParent(); - - public String getParentRel(); - /** - * Set parent node. This method is for scene graph internal use only and - * should not be called outside the scene graph structure. This method - * simply sets the parent node parent field, and does not affect on parent - * node (i.e., should be called only from parent node). - */ - public void setParent(ParentNode parent, String name); - - /** - * Perform cleanup for this node and for the child nodes. Any resources - * (including child nodes) related to this node are unusable after this - * operation. This method is for scene graph internal use only, thus should - * not be called outside the scene graph structure. - */ - public void cleanup(); - /** - * Remove this node and its children from the scene graph. - */ - public void remove(); - - - public void addListener(NodeListener listener); - public void removeListener(NodeListener listener); - - public List getListeners(); - -} +/******************************************************************************* + * Copyright (c) 2012, 2013 Association for Decentralized Information Management in + * Industry THTH ry. + * 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.g3d.scenegraph.base; + +import java.util.List; + + + + + +public interface INode { + + /** + * + * @return unique node identifier + */ + // public Long getId(); + + /** + * @return root node of the scene graph or null if this node is + * not part of a properly rooted scene graph hierarchy + */ + public ParentNode getRootNode(); + + /** + * @return Parent node reference or null if not set + */ + public ParentNode getParent(); + + public String getParentRel(); + /** + * Set parent node. This method is for scene graph internal use only and + * should not be called outside the scene graph structure. This method + * simply sets the parent node parent field, and does not affect on parent + * node (i.e., should be called only from parent node). + */ + public void setParent(ParentNode parent, String name); + + /** + * Perform cleanup for this node and for the child nodes. Any resources + * (including child nodes) related to this node are unusable after this + * operation. This method is for scene graph internal use only, thus should + * not be called outside the scene graph structure. + */ + public void cleanup(); + /** + * Remove this node and its children from the scene graph. + */ + public void remove(); + + + public void addListener(NodeListener listener); + public void removeListener(NodeListener listener); + + public List getListeners(); + +}