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