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