1 /*******************************************************************************
\r
2 * Copyright (c) 2007 VTT Technical Research Centre of Finland and others.
\r
3 * All rights reserved. This program and the accompanying materials
\r
4 * are made available under the terms of the Eclipse Public License v1.0
\r
5 * which accompanies this distribution, and is available at
\r
6 * http://www.eclipse.org/legal/epl-v10.html
\r
9 * VTT Technical Research Centre of Finland - initial API and implementation
\r
10 *******************************************************************************/
\r
11 package org.simantics.proconf.g3d.shapeeditor.views;
\r
13 import java.util.Collection;
\r
14 import java.util.HashSet;
\r
15 import java.util.Iterator;
\r
16 import java.util.List;
\r
18 import org.eclipse.jface.action.Action;
\r
19 import org.eclipse.jface.action.IStatusLineManager;
\r
20 import org.eclipse.jface.viewers.ISelection;
\r
21 import org.eclipse.jface.viewers.ISelectionChangedListener;
\r
22 import org.eclipse.jface.viewers.SelectionChangedEvent;
\r
23 import org.eclipse.swt.widgets.Composite;
\r
24 import org.eclipse.ui.IWorkbenchPart;
\r
25 import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
\r
26 import org.simantics.db.ContextGraph;
\r
27 import org.simantics.db.Graph;
\r
28 import org.simantics.db.GraphRequestAdapter;
\r
29 import org.simantics.db.GraphRequestStatus;
\r
30 import org.simantics.db.GraphRequestWithResult;
\r
31 import org.simantics.db.Resource;
\r
32 import org.simantics.db.Session;
\r
33 import org.simantics.db.management.ISessionContext;
\r
34 import org.simantics.equation.solver.Solver;
\r
35 import org.simantics.layer0.stubs.Property;
\r
36 import org.simantics.layer0.utils.EntityFactory;
\r
37 import org.simantics.layer0.utils.IEntity;
\r
38 import org.simantics.proconf.g3d.actions.InteractiveAction;
\r
39 import org.simantics.proconf.g3d.actions.TranslateAction;
\r
40 import org.simantics.proconf.g3d.base.G3DTools;
\r
41 import org.simantics.proconf.g3d.base.JmeRenderingComponent;
\r
42 import org.simantics.proconf.g3d.base.ScenegraphAdapter;
\r
43 import org.simantics.proconf.g3d.base.ScenegraphAdapterImpl;
\r
44 import org.simantics.proconf.g3d.base.SelectionAdapter;
\r
45 import org.simantics.proconf.g3d.base.ThreeDimensionalEditorBase;
\r
46 import org.simantics.proconf.g3d.base.ThreeDimensionalEditorProvider;
\r
47 import org.simantics.proconf.g3d.common.StructuredResourceSelection;
\r
48 import org.simantics.proconf.g3d.csg.stubs.CSGModel;
\r
49 import org.simantics.proconf.g3d.csg.stubs.Primitive;
\r
50 import org.simantics.proconf.g3d.dnd.DropListener;
\r
51 import org.simantics.proconf.g3d.scenegraph.AbstractGraphicsNode;
\r
52 import org.simantics.proconf.g3d.scenegraph.IGeometryNode;
\r
53 import org.simantics.proconf.g3d.scenegraph.IGraphicsNode;
\r
54 import org.simantics.proconf.g3d.scenegraph.ISelectableNode;
\r
55 import org.simantics.proconf.g3d.shapeeditor.ShapeEditorResources;
\r
56 import org.simantics.proconf.g3d.shapeeditor.actions.ExportAction;
\r
57 import org.simantics.proconf.g3d.shapeeditor.actions.ImportAction;
\r
58 import org.simantics.proconf.g3d.shapeeditor.scenegraph.CSGShapeNode;
\r
59 import org.simantics.proconf.g3d.shapeeditor.tools.AnimationContribution;
\r
60 import org.simantics.proconf.g3d.shapeeditor.tools.CSGModellingContribution;
\r
61 import org.simantics.proconf.g3d.shapeeditor.tools.ParameterizationContribution;
\r
62 import org.simantics.proconf.g3d.shapes.GridShape;
\r
63 import org.simantics.proconf.g3d.stubs.G3DModel;
\r
64 import org.simantics.proconf.g3d.stubs.G3DNode;
\r
65 import org.simantics.proconf.g3d.stubs.Shape;
\r
66 import org.simantics.utils.ErrorLogger;
\r
67 import org.simantics.utils.ui.jface.MenuTools;
\r
69 public class ShapeEditorBase extends ThreeDimensionalEditorBase {
\r
71 // currently each 3D-model has a root object which is ShapeGroup
\r
72 protected Resource model = null;
\r
74 protected boolean isParameterized;
\r
76 private Action exportAction;
\r
77 private Action importAction;
\r
79 public ShapeEditorBase(ISessionContext session) {
\r
81 addEditorContribution(new CSGModellingContribution(this));
\r
82 addEditorContribution(new AnimationContribution(this));
\r
83 addEditorContribution(new ParameterizationContribution(this));
\r
86 public ShapeEditorBase(ISessionContext session, JmeRenderingComponent component) {
\r
87 super(session,component);
\r
88 addEditorContribution(new CSGModellingContribution(this));
\r
89 addEditorContribution(new AnimationContribution(this));
\r
90 addEditorContribution(new ParameterizationContribution(this));
\r
94 protected ScenegraphAdapter createScenegraphAdapter() {
\r
95 return new ShapeEditorAdapter(session, getRenderingComponent());
\r
99 public void createControl(Graph graph,Composite parent) {
\r
100 super.createControl(graph,parent);
\r
101 getRenderingComponent().getNoCastRoot().attachChild(GridShape.getShape(getRenderingComponent().getDisplaySystem().getRenderer(), 10, 1.f));
\r
104 // private void loadGroup(Graph graph) {
\r
105 // assert (model != null);
\r
106 // adapter.addOutbound(EntityFactory.create(graph,model));
\r
107 // //assert (abstractGraphicsNodes.size() == 1);
\r
111 protected void makeActions(Graph graph) {
\r
112 super.makeActions(graph);
\r
113 exportAction = new ExportAction(this);
\r
114 importAction = new ImportAction(this);
\r
118 protected void fillLocalPullDown() {
\r
119 super.fillLocalPullDown();
\r
120 MenuTools.getOrCreate(getMenuID(),"Model", menuManager).add(exportAction);
\r
121 MenuTools.getOrCreate(getMenuID(),"Model", menuManager).add(importAction);
\r
127 * These are used for updating CSG models geometry when internal shapes are moved. Interactive update is not possible
\r
128 * because recalculation of geometry takes too much time. There are several problems in this method:
\r
129 * 1. it relies on instanceof check
\r
130 * 2. when shape is moved, transformations of its children are updated, which causes all
\r
131 * child geometries to be updated, which is not necessary. We want to update only moved
\r
132 * shape, since updateAllGemetry method takes care of parents.
\r
134 * TODO : this functionality should be moved to TranslateAction
\r
135 * TODO : prevent moved shape's children to be updated.
\r
139 public void setCurrentAction(InteractiveAction action) {
\r
140 if (getCurrentAction() == action)
\r
142 if (getCurrentAction() != null && getCurrentAction() instanceof TranslateAction) {
\r
143 runGeometryUpdates();
\r
145 super.setCurrentAction(action);
\r
148 private void runGeometryUpdates() {
\r
149 // now we'll just filter out all parents so that they won't be updated multiple times.
\r
150 HashSet<CSGShapeNode> parents = new HashSet<CSGShapeNode>();
\r
151 for (CSGShapeNode n : geometryUpdates) {
\r
152 IGraphicsNode parent = n.getParent();
\r
153 if (parent instanceof CSGShapeNode)
\r
154 parents.add((CSGShapeNode)parent);
\r
156 for (CSGShapeNode n : geometryUpdates) {
\r
157 if (!parents.contains(n))
\r
158 n.updateAllGeometry();
\r
160 geometryUpdates.clear();
\r
163 private HashSet<CSGShapeNode> geometryUpdates = new HashSet<CSGShapeNode>();
\r
165 private void geometryUpdate(CSGShapeNode shape) {
\r
167 if (!(getCurrentAction() instanceof TranslateAction)) {
\r
168 shape.updateAllGeometry();
\r
170 geometryUpdates.add(shape);
\r
174 public Graph createParameterization(Graph g) {
\r
175 if (isParameterized) {
\r
176 ContextGraph graph;
\r
177 if (!(g instanceof ContextGraph)) {
\r
178 graph = new ContextGraph(g);
\r
179 graph.setContext(model);
\r
181 graph = (ContextGraph)g;
\r
183 Solver solver = new Solver();
\r
184 Collection<org.simantics.layer0.utils.Property> parameters = getModel(graph).getRelatedProperties(ShapeEditorResources.g3dResource.HasSizingParameter);
\r
185 for (org.simantics.layer0.utils.Property p : parameters) {
\r
186 IEntity t = EntityFactory.create(graph, p.getResource());
\r
187 Collection<IEntity> exp = t.getRelatedObjects(ShapeEditorResources.equationResource.HasTarget);
\r
188 if (exp.size() > 0) {
\r
189 Iterator<IEntity> i = exp.iterator();
\r
190 while (i.hasNext())
\r
191 solver.evaluate(i.next());
\r
193 ErrorLogger.defaultLogWarning("Model property " + p + " is not bound to a expression", null);
\r
196 solver.pushToGraph(graph);
\r
205 protected class ShapeEditorAdapter extends ScenegraphAdapterImpl {
\r
207 public ShapeEditorAdapter(Session session,JmeRenderingComponent component) {
\r
208 super(session,component);
\r
212 public synchronized void updateGeometry(Graph graph) {
\r
213 if (isParameterized) {
\r
214 graph = createParameterization(graph);
\r
216 super.updateGeometry(graph);
\r
221 protected AbstractGraphicsNode instantiateNode(IGraphicsNode comp,
\r
223 CSGShapeNode mo = new CSGShapeNode(ShapeEditorBase.this, comp, node.getGraph(),node.getResource());
\r
224 updateGeometry(mo);
\r
229 private class ShapeEditorScenegraphQuery extends ScenegraphQuery {
\r
231 public ShapeEditorScenegraphQuery(Resource nodeResource) {
\r
232 super(nodeResource);
\r
236 public void shapeAdded(Graph graph,IGraphicsNode n) {
\r
237 updateGeometry((CSGShapeNode) n);
\r
239 if (n.getG3DNode(graph).getParent() == null) {
\r
240 if (DEBUG)System.out.println("ShapeSubnodeListener "
\r
242 + " has no parent");
\r
245 if (DEBUG) System.out.print("ShapeSubnodeListener " + n.getResource());
\r
246 if (n.getG3DNode(graph).getRelatedObjects(ShapeEditorResources.g3dResource.GeometryDefinitionOf).size() == 0) {
\r
247 if (DEBUG) System.out.println(" visible");
\r
248 ((ISelectableNode)n).setVisible(true);
\r
250 if (DEBUG) System.out.println(" invisible");
\r
251 ((ISelectableNode)n).setVisible(false);
\r
256 // public NodeQuery instantiateQuery(Resource node) {
\r
257 // return new ShapeEditorScenegraphQuery(node);
\r
262 protected ScenegraphQuery newSubnodeListener(G3DNode node) {
\r
263 return new ShapeEditorScenegraphQuery(node.getResource());
\r
266 private class ShapeEditorNodePropertyQuery extends NodePropertyQuery {
\r
267 public ShapeEditorNodePropertyQuery(Resource nodeResource) {
\r
268 super(nodeResource);
\r
272 public void shapeUpdated(Graph graph,final IGraphicsNode shape) {
\r
273 if (DEBUG) System.out.println("Tri - Shape id " + shape + " modified");
\r
274 ((CSGShapeNode) shape).updateAllGeometry();
\r
278 // public NodeQuery instantiateQuery(Resource node) {
\r
279 // return new ShapeEditorNodePropertyQuery(node);
\r
284 protected NodePropertyQuery newPropertyListener(G3DNode node) {
\r
285 return new ShapeEditorNodePropertyQuery(node.getResource());
\r
288 private class ShapeEditorNodeTransformationQuery extends NodeTransformationQuery {
\r
289 public ShapeEditorNodeTransformationQuery(Resource nodeResource) {
\r
290 super(nodeResource);
\r
294 public void shapeUpdated(Graph graph,final IGraphicsNode shape) {
\r
295 if (DEBUG) System.out.println("Tra - Shape id " + shape + " modified");
\r
296 ((CSGShapeNode) shape).updateTransform(graph);
\r
297 geometryUpdate((CSGShapeNode)shape);
\r
301 // public NodeQuery instantiateQuery(Resource node) {
\r
302 // return new ShapeEditorNodePropertyQuery(node);
\r
307 protected NodeTransformationQuery newTransformationListener(G3DNode node) {
\r
308 return new ShapeEditorNodeTransformationQuery(node.getResource());
\r
311 private class ShapeEditorRootPropertyQuery extends NodePropertyQuery {
\r
312 public ShapeEditorRootPropertyQuery(Resource nodeResource) {
\r
313 super(nodeResource);
\r
317 public void shapeUpdated(Graph graph, final IGraphicsNode shape) {
\r
318 if (DEBUG)System.out.println("Tri - Shape id " + shape + " modified");
\r
320 updateParameterizationStatus(graph);
\r
321 if (isParameterized) {
\r
322 for (IGraphicsNode n : getNodes())
\r
323 if (n instanceof IGeometryNode)
\r
324 updateGeometry((IGeometryNode) n);
\r
329 // public NodeQuery instantiateQuery(Resource node) {
\r
330 // return new ShapeEditorRootPropertyQuery(node);
\r
335 protected NodePropertyQuery newRootPropertyListener(G3DNode root) {
\r
336 return new ShapeEditorRootPropertyQuery(root.getResource());
\r
341 protected void contributeStatusBar(IStatusLineManager manager) {
\r
345 * Loads the initial scene: all further updates to the view are done by
\r
346 * listening changes in the shapes and int the shape group
\r
350 protected void reloadFrom(IEntity thing) {
\r
351 if (model != null) {
\r
352 throw new UnsupportedOperationException(
\r
353 "Reloading instantiated viewer not supported");
\r
356 if (thing.isInstanceOf(ShapeEditorResources.csgResource.CSGModel)) {
\r
357 //System.out.print("ShapeEditorView.reloadFrom() : model");
\r
358 Graph g = thing.getGraph();
\r
359 model = thing.getResource();
\r
360 //System.out.println(" " + model.getResource());
\r
361 adapter.setRootNode(new G3DNode(thing));
\r
362 updateParameterizationStatus(g);
\r
364 //loadGroup(thing.getGraph());
\r
367 throw new UnsupportedOperationException("Cannot load ShapeViewer for Resource:" + thing);
\r
372 private void updateParameterizationStatus(Graph graph) {
\r
373 G3DModel model = getModel(graph);
\r
374 if(model.getRelatedObjects(ShapeEditorResources.g3dResource.HasSizingParameter).size() > 0) {
\r
375 isParameterized = true;
\r
377 isParameterized = false;
\r
379 parent.getDisplay().asyncExec(new Runnable() {
\r
381 public void run() {
\r
382 // for (Action a : addActions)
\r
383 // a.setEnabled(!isParameterized);
\r
384 // unionAction.setEnabled(!isParameterized);
\r
385 // differenceAction.setEnabled(!isParameterized);
\r
386 // intersectionAction.setEnabled(!isParameterized);
\r
387 // linkAction.setEnabled(!isParameterized);
\r
388 // unlinkAction.setEnabled(!isParameterized);
\r
389 // translateAction.setEnabled(!isParameterized);
\r
390 // rotateAction.setEnabled(!isParameterized);
\r
391 // removeAction.setEnabled(!isParameterized);
\r
397 public Resource getModelResource() {
\r
401 public G3DModel getModel(Graph graph) {
\r
402 return new G3DModel(graph, model);
\r
406 protected SelectionAdapter createSelectionAdapter() {
\r
407 return new ShapeEditorSelectionAdapter(adapter);
\r
410 protected class ShapeEditorSelectionAdapter extends SelectionAdapter {
\r
412 public ShapeEditorSelectionAdapter(ScenegraphAdapter adapter) {
\r
416 public void setEditorSelection() {
\r
417 List<IGraphicsNode> sel = getSelectedObjects();
\r
418 for (IGraphicsNode o : adapter.getNodes())
\r
419 if (o instanceof ISelectableNode) {
\r
420 ISelectableNode n = (ISelectableNode)o;
\r
421 if (sel.contains(o))
\r
422 n.setSelected(true);
\r
424 n.setSelected(false);
\r
426 List<Resource> selected = getSelectedResources();
\r
427 for (Resource r : selected) {
\r
428 if (!adapter.hasNode(r)) {
\r
429 //adapter.addInbound(r).setSelected(true);
\r
434 public void setEditorHighlightSelection() {
\r
435 List<IGraphicsNode> sel = getInteractiveSelectedObjects();
\r
436 for (IGraphicsNode o : adapter.getNodes())
\r
437 if (o instanceof CSGShapeNode) {
\r
438 if (sel.contains(o))
\r
439 ((CSGShapeNode) o).setHighlighted(true);
\r
441 ((CSGShapeNode) o).setHighlighted(false);
\r
445 public void setEditorSelection(boolean addShapes) {
\r
447 List<IGraphicsNode> sel = getSelectedObjects();
\r
448 for (IGraphicsNode o : adapter.getNodes())
\r
449 if (o instanceof ISelectableNode) {
\r
450 ISelectableNode n = (ISelectableNode)o;
\r
451 if (sel.contains(o))
\r
452 n.setSelected(true);
\r
454 n.setSelected(false);
\r
456 viewChanged = true;
\r
458 session.syncRead(new GraphRequestAdapter() {
\r
460 public GraphRequestStatus perform(Graph g) throws Exception {
\r
461 List<Resource> selected = getSelectedResources();
\r
462 for (Resource r : selected) {
\r
463 if (!adapter.hasNode(r)) {
\r
464 IEntity t = EntityFactory.create(g, r);
\r
465 if (t.isInstanceOf(ShapeEditorResources.g3dResource.Shape)) {
\r
466 G3DNode group = G3DTools.getModelFromResource(g,r);
\r
468 && group.getResource().equals(model.getResource())) {
\r
469 //adapter.addInbound(g).setSelected(true);
\r
475 return GraphRequestStatus.transactionComplete();
\r
484 * Receives selection changes
\r
489 protected void pageSelectionChanged(IWorkbenchPart part, ISelection selection) {
\r
491 StructuredResourceSelection s = SelectionAdapter.transformSelection(selection);
\r
492 //System.out.println("ShapeEditorBase.pageSelectionChanged " + s);
\r
493 selectionAdapter.setCurrentSelection(s);
\r
495 if (!(part instanceof ThreeDimensionalEditorProvider)) {
\r
496 ((ShapeEditorSelectionAdapter) selectionAdapter).setEditorSelection(true);
\r
499 ThreeDimensionalEditorBase e = ((ThreeDimensionalEditorProvider)part).getEditor();
\r
500 if (!(e instanceof ShapeEditorBase)) {
\r
501 ((ShapeEditorSelectionAdapter) selectionAdapter).setEditorSelection(true);
\r
505 ShapeEditorBase editor = (ShapeEditorBase)e;
\r
507 if (!editor.getModelResource().equals(model.getResource())) {
\r
508 selectionAdapter.setCurrentSelection(new StructuredResourceSelection());
\r
509 ((ShapeEditorSelectionAdapter) selectionAdapter).setEditorSelection(false);
\r
512 selectionAdapter.setEditorSelection();
\r
516 protected void hookDragAndDrop() {
\r
517 super.hookDragAndDrop();
\r
518 dropTarget.addDropListener(new DropListener() {
\r
519 public boolean acceptDrop(StructuredResourceSelection s, Resource[] ids) {
\r
524 if (ids.length != 1)
\r
526 final Resource r = ids[0];
\r
527 GraphRequestWithResult<Boolean> rq = new GraphRequestWithResult<Boolean>() {
\r
529 public Boolean performWithResult(Graph g) throws Exception {
\r
530 IEntity t = EntityFactory.create(g, r);
\r
531 return t.isInstanceOf(ShapeEditorResources.csgResource.Primitive);
\r
534 session.syncRead(rq);
\r
535 return rq.getResult();
\r
538 public void doDrop(StructuredResourceSelection s, Resource[] ids) {
\r
539 session.asyncWrite(new GraphRequestAdapter() {
\r
542 public GraphRequestStatus perform(Graph g) throws Exception {
\r
543 IEntity type = EntityFactory.create(g);
\r
544 IEntity instance = type.instantiate();
\r
545 Shape shape = new Shape(instance);
\r
547 CSGModel m = new CSGModel(g, model);
\r
548 m.getChild().add(shape.toG3DNode()); // FIXME : stubcast
\r
549 return GraphRequestStatus.transactionComplete();
\r
554 public void requestCompleted(GraphRequestStatus status) {
\r
556 .updateSelection(new StructuredResourceSelection(r));
\r
557 super.requestCompleted(status);
\r
566 private void resetShape(Shape shape) {
\r
567 G3DTools.resetTransformation(shape);
\r
568 Graph graph = shape.getGraph();
\r
569 if (shape.isInstanceOf(ShapeEditorResources.csgResource.Primitive)) {
\r
570 Primitive prim = new Primitive(shape);
\r
571 Collection<Property> c = prim.getSizingProperty();
\r
573 ErrorLogger.getDefault().logWarning("Shape does not contain sizing properties.", null);
\r
575 for (Property p : c) {
\r
576 if (p.isInstanceOf(graph.getBuiltins().Double)) {
\r
577 graph.setScalarDouble(p.getResource(), 1.0);
\r
578 } else if (p.isInstanceOf(graph.getBuiltins().Integer)) {
\r
579 graph.setScalarInteger(p.getResource(), 1);
\r
581 ErrorLogger.getDefault().logWarning("Cannot handle sizing property " + p.getName() , null);
\r
589 public Object getAdapter(Class adapter) {
\r
590 if (adapter == IContentOutlinePage.class) {
\r
591 if (getModelResource() == null)
\r
593 final StructureOutlinePage page = new StructureOutlinePage(sessionContext,getModelResource());
\r
595 getSelectionAdapter().addSelectionChangedListener(new ISelectionChangedListener() {
\r
597 public void selectionChanged(SelectionChangedEvent event) {
\r
598 page.setSelection(event.getSelection());
\r
602 parent.getDisplay().asyncExec(new Runnable() {
\r
604 public void run() {
\r
605 page.addSelectionChangedListener(new ISelectionChangedListener() {
\r
607 public void selectionChanged(SelectionChangedEvent event) {
\r
608 selectionAdapter.setSelection(SelectionAdapter.transformSelection(event.getSelection()));
\r