]> gerrit.simantics Code Review - simantics/3d.git/blob - org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/common/AbstractVTKNodeMap.java
3D framework (Simca 2012)
[simantics/3d.git] / org.simantics.g3d.vtk / src / org / simantics / g3d / vtk / common / AbstractVTKNodeMap.java
1 package org.simantics.g3d.vtk.common;\r
2 \r
3 import java.util.ArrayList;\r
4 import java.util.Collection;\r
5 import java.util.HashMap;\r
6 import java.util.HashSet;\r
7 import java.util.List;\r
8 import java.util.Map;\r
9 import java.util.Set;\r
10 import java.util.Stack;\r
11 \r
12 import org.simantics.db.ReadGraph;\r
13 import org.simantics.db.Resource;\r
14 import org.simantics.db.Session;\r
15 import org.simantics.db.WriteGraph;\r
16 import org.simantics.db.common.request.ReadRequest;\r
17 import org.simantics.db.common.request.WriteRequest;\r
18 import org.simantics.db.exception.DatabaseException;\r
19 import org.simantics.g3d.ontology.G3D;\r
20 import org.simantics.g3d.scenegraph.IG3DNode;\r
21 import org.simantics.g3d.scenegraph.RenderListener;\r
22 import org.simantics.g3d.scenegraph.base.INode;\r
23 import org.simantics.g3d.scenegraph.base.NodeListener;\r
24 import org.simantics.g3d.scenegraph.base.ParentNode;\r
25 import org.simantics.objmap.graph.IMapping;\r
26 import org.simantics.objmap.graph.IMappingListener;\r
27 import org.simantics.utils.datastructures.Callback;\r
28 import org.simantics.utils.datastructures.MapList;\r
29 import org.simantics.utils.datastructures.MapSet;\r
30 import org.simantics.utils.datastructures.Pair;\r
31 import org.simantics.utils.ui.ExceptionUtils;\r
32 \r
33 import vtk.vtkProp;\r
34 \r
35 public abstract class AbstractVTKNodeMap<E extends INode> implements VTKNodeMap, IMappingListener, RenderListener, NodeListener{\r
36 \r
37         private static final boolean DEBUG = false;\r
38         \r
39         protected Session session;\r
40         protected IMapping<Object,E> mapping;\r
41         protected InteractiveVtkPanel panel;\r
42         \r
43         protected MapList<E, vtkProp> nodeToActor = new MapList<E, vtkProp>();\r
44         protected Map<vtkProp,E> actorToNode = new HashMap<vtkProp, E>();\r
45 \r
46         protected ParentNode<E> rootNode;\r
47         \r
48         public AbstractVTKNodeMap(Session session, IMapping<Object,E> mapping, InteractiveVtkPanel panel, ParentNode<E> rootNode) {\r
49                 this.session = session;\r
50                 this.mapping = mapping;\r
51                 this.panel = panel;\r
52                 this.rootNode = rootNode;\r
53                 panel.addListener(this);\r
54                 mapping.addMappingListener(this);\r
55                 rootNode.addListener(this);\r
56         }\r
57         \r
58         \r
59         protected abstract void addActor(E node);\r
60         protected abstract void removeActor(E node);\r
61         protected abstract void updateActor(E node,Set<String> ids);\r
62         \r
63         public void repaint() {\r
64                 panel.repaint();\r
65         }\r
66         \r
67         public void populate() {\r
68                 for (E node : rootNode.getNodes()) {\r
69                         receiveAdd(node, node.getParentRel(),true);\r
70                 }\r
71                 repaint();\r
72         }\r
73         \r
74         @Override\r
75         public INode getNode(vtkProp prop) {\r
76                 return actorToNode.get(prop);\r
77         }\r
78         \r
79         @SuppressWarnings("unchecked")\r
80         @Override\r
81         public Collection<vtkProp> getRenderObjects(INode node) {\r
82                 return nodeToActor.getValues((E)node);\r
83         }\r
84         \r
85         @SuppressWarnings("unchecked")\r
86         @Override\r
87         public ParentNode<IG3DNode> getRootNode() {\r
88                 return (ParentNode<IG3DNode>)rootNode;\r
89         }\r
90         \r
91         \r
92         \r
93         @Override\r
94         public boolean isChangeTracking() {\r
95                 return changeTracking;\r
96         }\r
97         \r
98         @Override\r
99         public void setChangeTracking(boolean enabled) {\r
100                 changeTracking = enabled;\r
101         }\r
102         \r
103         private boolean changeTracking = true;\r
104         \r
105         protected Object syncMutex = new Object(); \r
106         \r
107 \r
108         private List<Pair<E,String>> added = new ArrayList<Pair<E,String>>();\r
109         private List<Pair<E,String>> removed = new ArrayList<Pair<E,String>>();\r
110         //private List<Pair<E,String>> updated = new ArrayList<Pair<E,String>>();\r
111         private MapSet<E, String> updated = new MapSet.Hash<E, String>();\r
112 \r
113         private boolean rangeModified = false;\r
114         \r
115         @SuppressWarnings("unchecked")\r
116         @Override\r
117         public void updateRenderObjectsFor(INode node) {\r
118                 List<vtkProp> toDelete = new ArrayList<vtkProp>();\r
119                 for (vtkProp prop : nodeToActor.getValues((E)node)) {\r
120                         if (prop.GetVTKId() != 0) {\r
121                                 panel.GetRenderer().RemoveActor(prop);\r
122                                 //prop.Delete();\r
123                                 toDelete.add(prop);\r
124                         }\r
125                         actorToNode.remove(prop);\r
126                 }\r
127                 nodeToActor.remove((E)node);\r
128                 Collection<vtkProp> coll = getActors((E)node);\r
129                 if (coll == null)\r
130                         return;\r
131                 for (vtkProp prop : coll) {\r
132                         nodeToActor.add((E)node,prop);\r
133                         actorToNode.put(prop, (E)node);\r
134                         toDelete.remove(prop);\r
135                 }\r
136                 for (vtkProp p : toDelete)\r
137                         p.Delete();\r
138         }\r
139         \r
140         protected abstract  Collection<vtkProp> getActors(E node);\r
141         \r
142         @SuppressWarnings("unchecked")\r
143         private void receiveAdd(E node, String id, boolean db) {\r
144                 if (DEBUG) System.out.println("receiveAdd " + node  + " " + id + " " + db);\r
145                 synchronized (syncMutex) {\r
146                         for (Pair<E, String> n : added) {\r
147                                 if (n.first.equals(node))\r
148                                         return;\r
149                         }\r
150                         if (changeTracking) {\r
151                                 mapping.rangeModified((E)node.getParent());\r
152                         }\r
153                         added.add(new Pair<E, String>(node, id));\r
154                         rangeModified = true;\r
155                 }\r
156                 panel.repaint();\r
157         }\r
158         \r
159         @SuppressWarnings("unchecked")\r
160         private void receiveRemove(E node, String id, boolean db) {\r
161                 if (DEBUG) System.out.println("receiveRemove " + node  + " " + id + " " + db);\r
162                 synchronized (syncMutex) {\r
163                         for (Pair<E, String> n : removed) {\r
164                                 if (n.first.equals(node))\r
165                                         return;\r
166                         }\r
167                         if (changeTracking && !db)\r
168                                 mapping.rangeModified((E)node.getParent());\r
169                         removed.add(new Pair<E, String>(node, id));\r
170                         rangeModified = true;\r
171                 }\r
172                 panel.repaint();\r
173         }\r
174         \r
175         @SuppressWarnings("unchecked")\r
176         private void receiveUpdate(E node, String id, boolean db) {\r
177                 if (DEBUG) System.out.println("receiveUpdate " + node  + " " + id + " " + db);\r
178                 synchronized (syncMutex) {\r
179 //                      for (Pair<E, String> n : updated) {\r
180 //                              if (n.first.equals(node))\r
181 //                                      return;\r
182 //                      }\r
183                         if (changeTracking && !db)\r
184                                 mapping.rangeModified(node);\r
185                         //updated.add(new Pair<E, String>(node, id));\r
186                         updated.add(node, id);\r
187                         rangeModified = true;\r
188                 }\r
189                 panel.repaint();\r
190         }\r
191         \r
192         private boolean graphUpdates = false;\r
193         private Set<E> graphModified = new HashSet<E>();\r
194         \r
195         private boolean requestCommit = false;\r
196         \r
197         @Override\r
198         public void commit() {\r
199                 requestCommit = true;\r
200         }\r
201         \r
202         protected void doCommit() {\r
203                 session.asyncRequest(new WriteRequest() {\r
204                         \r
205                         @Override\r
206                         public void perform(WriteGraph graph) throws DatabaseException {\r
207                                 commit(graph);\r
208                         }\r
209                         \r
210                 }, new Callback<DatabaseException>() {\r
211                         \r
212                         @Override\r
213                         public void run(DatabaseException parameter) {\r
214                                 if (parameter != null)\r
215                                         ExceptionUtils.logAndShowError("Cannot commit editor changes", parameter);\r
216                         }\r
217                 });\r
218         }\r
219         \r
220         protected void commit(WriteGraph graph) throws DatabaseException {\r
221                 synchronized(syncMutex) {\r
222                         if (DEBUG) System.out.println("Commit");\r
223                         graphUpdates = true;\r
224                         mapping.updateDomain(graph);\r
225                         graphUpdates = false;\r
226                 }\r
227         }\r
228         \r
229         @Override\r
230         public void domainModified() {\r
231                 if (graphUpdates)\r
232                         return;\r
233                 if (DEBUG)System.out.println("domainModified");\r
234                 session.asyncRequest(new ReadRequest() {\r
235                         \r
236                         @SuppressWarnings("unchecked")\r
237                         @Override\r
238                         public void run(ReadGraph graph) throws DatabaseException {\r
239                                 update(graph);\r
240                         }\r
241                 });\r
242                 \r
243         }\r
244         \r
245         protected void update(ReadGraph graph) throws DatabaseException {\r
246                 synchronized (syncMutex) {\r
247                         graphUpdates = true;\r
248                         for (Object domainObject : mapping.getDomainModified()) {\r
249                                 E rangeObject = mapping.get(domainObject);\r
250                                 if (rangeObject != null)\r
251                                         graphModified.add(rangeObject);\r
252                         }\r
253                         mapping.updateRange(graph);\r
254                         graphModified.clear();\r
255                         graphUpdates = false;\r
256                 }\r
257                 \r
258                 if (mapping.isRangeModified())\r
259                         commit();\r
260         }\r
261         \r
262         @Override\r
263         public void rangeModified() {\r
264                 //System.out.println("rangeModified");\r
265 \r
266         }\r
267         \r
268         @Override\r
269         public void postRender() {\r
270                 // Commit changes if\r
271                 // 1. Commit has been requested\r
272                 // 2. There are no pending changes that should be processed in preRender() \r
273                 if (requestCommit && !rangeModified) { // FIXME : not thread safe.\r
274                         requestCommit = false;\r
275                         doCommit();\r
276                 }\r
277         }\r
278         \r
279         List<Pair<E, String>> rem = new ArrayList<Pair<E,String>>();\r
280         List<Pair<E, String>> add = new ArrayList<Pair<E,String>>();\r
281         MapSet<E, String> mod = new MapSet.Hash<E, String>();\r
282         Set<E> propagation = new HashSet<E>();\r
283         Stack<E> stack = new Stack<E>();\r
284         \r
285         \r
286         @Override\r
287         public synchronized void preRender() {\r
288                 updateCycle();\r
289         }\r
290         \r
291         @SuppressWarnings("unchecked")\r
292         protected void updateCycle() {\r
293                 rem.clear();\r
294                 add.clear();\r
295                 mod.clear();\r
296                 propagation.clear();\r
297                 \r
298                 synchronized (syncMutex) {\r
299                         rem.addAll(removed);\r
300                         add.addAll(added);\r
301                         for (E e : updated.getKeys()) {\r
302                                 for (String s : updated.getValues(e)) {\r
303                                         mod.add(e, s);\r
304                                 }\r
305                         }\r
306                         \r
307                         removed.clear();\r
308                         added.clear();\r
309                         updated.clear();\r
310                 }\r
311                 \r
312                 for (Pair<E, String> n : rem) {\r
313                         stopListening(n.first);\r
314                         removeActor(n.first);\r
315                 \r
316                 }\r
317                 \r
318                 for (Pair<E, String> n : add) {\r
319                         addActor(n.first);\r
320                         listen(n.first);\r
321                 }\r
322                 \r
323                 for (E e : mod.getKeys()) {\r
324                         Set<String> ids = mod.getValues(e);\r
325                         if (ids.contains(G3D.URIs.hasPosition) || ids.contains(G3D.URIs.hasOrientation)) {\r
326                                 if (!propagation.contains(e))\r
327                                         propagation.add(e);\r
328                         }\r
329                 }\r
330 \r
331                 if (propagation.size() > 0) {\r
332                         stack.clear();\r
333                         stack.addAll(propagation);\r
334                         propagation.clear();\r
335                         while (!stack.isEmpty()) {\r
336                                 E node = stack.pop();\r
337                                 if (propagation.contains(node))\r
338                                         continue;\r
339                                 propagation.add(node);\r
340                                 for (NodeListener l : node.getListeners()) {\r
341                                         if (l == this) {\r
342                                                 //changeTracking = false;\r
343                                                 //l.propertyChanged(node, G3D.URIs.hasPosition);\r
344                                                 //changeTracking = true;\r
345                                         } else {\r
346                                                 l.propertyChanged(node, G3D.URIs.hasWorldPosition);\r
347                                         }\r
348                                 }\r
349                                 if (node instanceof ParentNode) {\r
350                                         stack.addAll(((ParentNode<E>)node).getNodes());\r
351                                 }\r
352                         }\r
353                 }\r
354                 \r
355 //              synchronized (syncMutex) {\r
356 //                      rem.addAll(removed);\r
357 //                      add.addAll(added);\r
358 //                      //mod.addAll(updated);\r
359 //                      for (E e : updated.getKeys()) {\r
360 //                              for (String s : updated.getValues(e))\r
361 //                                      mod.add(e, s);\r
362 //                      }\r
363 //                      \r
364 //                      removed.clear();\r
365 //                      added.clear();\r
366 //                      updated.clear();\r
367 //              }\r
368                 \r
369                 for (E e : mod.getKeys()) {\r
370                         Set<String> ids = mod.getValues(e);\r
371                         updateActor(e,ids);\r
372                 }\r
373                 \r
374                 \r
375                 for (Pair<E, String> n : rem) {\r
376                         for (NodeListener l : nodeListeners)\r
377                                 l.nodeRemoved(null, n.first, n.second);\r
378                 }\r
379                 for (Pair<E, String> n : add) {\r
380                         for (NodeListener l : nodeListeners)\r
381                                 l.nodeAdded(n.first.getParent(), n.first, n.second);\r
382                 }\r
383 //              for (Pair<E, String> n : mod) {\r
384 //                      for (NodeListener l : nodeListeners)\r
385 //                              l.propertyChanged(n.first, n.second);\r
386 //              }\r
387                 for (E e : mod.getKeys()) {\r
388                         for (NodeListener l : nodeListeners)\r
389                                 for (String s : mod.getValues(e))\r
390                                         l.propertyChanged(e, s);\r
391                 }\r
392                 synchronized (syncMutex) {\r
393                         if (added.isEmpty() && removed.isEmpty() && updated.getKeys().size() == 0)\r
394                                 rangeModified = false;\r
395                 }\r
396         }\r
397         \r
398         @SuppressWarnings("unchecked")\r
399         private void listen(INode node) {\r
400                 node.addListener(this);\r
401                 if (node instanceof ParentNode<?>) {\r
402                         ParentNode<INode> parentNode = (ParentNode<INode>)node;\r
403                         for (INode n : parentNode.getNodes())\r
404                                 listen(n);\r
405                 }\r
406         }\r
407         \r
408         private void stopListening(INode node) {\r
409                 node.removeListener(this);\r
410                 if (node instanceof ParentNode<?>) {\r
411                         @SuppressWarnings("unchecked")\r
412                         ParentNode<INode> parentNode = (ParentNode<INode>)node;\r
413                         for (INode n : parentNode.getNodes())\r
414                                 stopListening(n);\r
415                 }\r
416         }\r
417         \r
418         @SuppressWarnings("unchecked")\r
419         @Override\r
420         public void propertyChanged(INode node, String id) {\r
421                 //receiveUpdate((E)node, id, graphUpdates);\r
422                 receiveUpdate((E)node, id, graphModified.contains(node));\r
423                 \r
424         }\r
425         \r
426         @SuppressWarnings("unchecked")\r
427         @Override\r
428         public <T extends INode> void nodeAdded(ParentNode<T> node, INode child,\r
429                         String rel) {\r
430                 if (DEBUG) System.out.println("Node added " + child + " parent " + node);\r
431                 //receiveAdd((E)child, rel ,graphUpdates);\r
432                 receiveAdd((E)child, rel ,graphModified.contains(node));\r
433                 \r
434         }\r
435         \r
436         @SuppressWarnings("unchecked")\r
437         @Override\r
438         public <T extends INode> void nodeRemoved(ParentNode<T> node, INode child,\r
439                         String rel) {\r
440                 if (DEBUG) System.out.println("Node removed " + child + " parent " + node);\r
441                 //receiveRemove((E)child, rel, graphUpdates);\r
442                 receiveRemove((E)child, rel, graphModified.contains(node));\r
443                 \r
444                 //FIXME : sometimes removed structural models cause ObjMap to add their children again.\r
445                 //        removing the listener here prevents corruption of visual model, but better fix is needed.\r
446                 stopListening(child);\r
447         }\r
448         \r
449         @Override\r
450         public void delete() {\r
451                 changeTracking = false;\r
452                 panel.removeListener(this);\r
453                 mapping.removeMappingListener(this);\r
454 \r
455                 List<E> nodes = new ArrayList<E>(nodeToActor.getKeySize());\r
456                 nodes.addAll(nodeToActor.getKeys());\r
457                 for (E node : nodes) {\r
458                         node.removeListener(this);\r
459                         removeActor(node);\r
460                         node.cleanup();\r
461                 }\r
462                 for (vtkProp prop : actorToNode.keySet()) {\r
463                         if (prop.GetVTKId() != 0) \r
464                                 prop.Delete();\r
465                 }\r
466                 actorToNode.clear();\r
467                 nodeToActor.clear();\r
468                 \r
469         }\r
470         \r
471         \r
472         private List<NodeListener> nodeListeners = new ArrayList<NodeListener>();\r
473         @Override\r
474         public void addListener(NodeListener listener) {\r
475                 nodeListeners.add(listener);\r
476                 \r
477         }\r
478         \r
479         @Override\r
480         public void removeListener(NodeListener listener) {\r
481                 nodeListeners.remove(listener);\r
482                 \r
483         }\r
484         \r
485         public IMapping<Object,E> getMapping() {\r
486                 return mapping;\r
487         }\r
488         \r
489         \r
490 }\r