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