/******************************************************************************* * Copyright (c) 2007 VTT Technical Research Centre of Finland and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.proconf.g3d.shapeeditor.views; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IStatusLineManager; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.views.contentoutline.IContentOutlinePage; import org.simantics.db.ContextGraph; import org.simantics.db.Graph; import org.simantics.db.GraphRequestAdapter; import org.simantics.db.GraphRequestStatus; import org.simantics.db.GraphRequestWithResult; import org.simantics.db.Resource; import org.simantics.db.Session; import org.simantics.db.management.ISessionContext; import org.simantics.equation.solver.Solver; import org.simantics.layer0.stubs.Property; import org.simantics.layer0.utils.EntityFactory; import org.simantics.layer0.utils.IEntity; import org.simantics.proconf.g3d.actions.InteractiveAction; import org.simantics.proconf.g3d.actions.TranslateAction; import org.simantics.proconf.g3d.base.G3DTools; import org.simantics.proconf.g3d.base.JmeRenderingComponent; import org.simantics.proconf.g3d.base.ScenegraphAdapter; import org.simantics.proconf.g3d.base.ScenegraphAdapterImpl; import org.simantics.proconf.g3d.base.SelectionAdapter; import org.simantics.proconf.g3d.base.ThreeDimensionalEditorBase; import org.simantics.proconf.g3d.base.ThreeDimensionalEditorProvider; import org.simantics.proconf.g3d.common.StructuredResourceSelection; import org.simantics.proconf.g3d.csg.stubs.CSGModel; import org.simantics.proconf.g3d.csg.stubs.Primitive; import org.simantics.proconf.g3d.dnd.DropListener; import org.simantics.proconf.g3d.scenegraph.AbstractGraphicsNode; import org.simantics.proconf.g3d.scenegraph.IGeometryNode; import org.simantics.proconf.g3d.scenegraph.IGraphicsNode; import org.simantics.proconf.g3d.scenegraph.ISelectableNode; import org.simantics.proconf.g3d.shapeeditor.ShapeEditorResources; import org.simantics.proconf.g3d.shapeeditor.actions.ExportAction; import org.simantics.proconf.g3d.shapeeditor.actions.ImportAction; import org.simantics.proconf.g3d.shapeeditor.scenegraph.CSGShapeNode; import org.simantics.proconf.g3d.shapeeditor.tools.AnimationContribution; import org.simantics.proconf.g3d.shapeeditor.tools.CSGModellingContribution; import org.simantics.proconf.g3d.shapeeditor.tools.ParameterizationContribution; import org.simantics.proconf.g3d.shapes.GridShape; import org.simantics.proconf.g3d.stubs.G3DModel; import org.simantics.proconf.g3d.stubs.G3DNode; import org.simantics.proconf.g3d.stubs.Shape; import org.simantics.utils.ErrorLogger; import org.simantics.utils.ui.jface.MenuTools; public class ShapeEditorBase extends ThreeDimensionalEditorBase { // currently each 3D-model has a root object which is ShapeGroup protected Resource model = null; protected boolean isParameterized; private Action exportAction; private Action importAction; public ShapeEditorBase(ISessionContext session) { super(session); addEditorContribution(new CSGModellingContribution(this)); addEditorContribution(new AnimationContribution(this)); addEditorContribution(new ParameterizationContribution(this)); } public ShapeEditorBase(ISessionContext session, JmeRenderingComponent component) { super(session,component); addEditorContribution(new CSGModellingContribution(this)); addEditorContribution(new AnimationContribution(this)); addEditorContribution(new ParameterizationContribution(this)); } @Override protected ScenegraphAdapter createScenegraphAdapter() { return new ShapeEditorAdapter(session, getRenderingComponent()); } @Override public void createControl(Graph graph,Composite parent) { super.createControl(graph,parent); getRenderingComponent().getNoCastRoot().attachChild(GridShape.getShape(getRenderingComponent().getDisplaySystem().getRenderer(), 10, 1.f)); } // private void loadGroup(Graph graph) { // assert (model != null); // adapter.addOutbound(EntityFactory.create(graph,model)); // //assert (abstractGraphicsNodes.size() == 1); // // } protected void makeActions(Graph graph) { super.makeActions(graph); exportAction = new ExportAction(this); importAction = new ImportAction(this); } @Override protected void fillLocalPullDown() { super.fillLocalPullDown(); MenuTools.getOrCreate(getMenuID(),"Model", menuManager).add(exportAction); MenuTools.getOrCreate(getMenuID(),"Model", menuManager).add(importAction); } /* * These are used for updating CSG models geometry when internal shapes are moved. Interactive update is not possible * because recalculation of geometry takes too much time. There are several problems in this method: * 1. it relies on instanceof check * 2. when shape is moved, transformations of its children are updated, which causes all * child geometries to be updated, which is not necessary. We want to update only moved * shape, since updateAllGemetry method takes care of parents. * * TODO : this functionality should be moved to TranslateAction * TODO : prevent moved shape's children to be updated. */ @Override public void setCurrentAction(InteractiveAction action) { if (getCurrentAction() == action) return; if (getCurrentAction() != null && getCurrentAction() instanceof TranslateAction) { runGeometryUpdates(); } super.setCurrentAction(action); } private void runGeometryUpdates() { // now we'll just filter out all parents so that they won't be updated multiple times. HashSet parents = new HashSet(); for (CSGShapeNode n : geometryUpdates) { IGraphicsNode parent = n.getParent(); if (parent instanceof CSGShapeNode) parents.add((CSGShapeNode)parent); } for (CSGShapeNode n : geometryUpdates) { if (!parents.contains(n)) n.updateAllGeometry(); } geometryUpdates.clear(); } private HashSet geometryUpdates = new HashSet(); private void geometryUpdate(CSGShapeNode shape) { if (!(getCurrentAction() instanceof TranslateAction)) { shape.updateAllGeometry(); } else { geometryUpdates.add(shape); } } public Graph createParameterization(Graph g) { if (isParameterized) { ContextGraph graph; if (!(g instanceof ContextGraph)) { graph = new ContextGraph(g); graph.setContext(model); } else { graph = (ContextGraph)g; } Solver solver = new Solver(); Collection parameters = getModel(graph).getRelatedProperties(ShapeEditorResources.g3dResource.HasSizingParameter); for (org.simantics.layer0.utils.Property p : parameters) { IEntity t = EntityFactory.create(graph, p.getResource()); Collection exp = t.getRelatedObjects(ShapeEditorResources.equationResource.HasTarget); if (exp.size() > 0) { Iterator i = exp.iterator(); while (i.hasNext()) solver.evaluate(i.next()); } else { ErrorLogger.defaultLogWarning("Model property " + p + " is not bound to a expression", null); } } solver.pushToGraph(graph); return graph; } else { return g; } } protected class ShapeEditorAdapter extends ScenegraphAdapterImpl { public ShapeEditorAdapter(Session session,JmeRenderingComponent component) { super(session,component); } @Override public synchronized void updateGeometry(Graph graph) { if (isParameterized) { graph = createParameterization(graph); } super.updateGeometry(graph); } protected AbstractGraphicsNode instantiateNode(IGraphicsNode comp, G3DNode node) { CSGShapeNode mo = new CSGShapeNode(ShapeEditorBase.this, comp, node.getGraph(),node.getResource()); updateGeometry(mo); return mo; } private class ShapeEditorScenegraphQuery extends ScenegraphQuery { public ShapeEditorScenegraphQuery(Resource nodeResource) { super(nodeResource); } @Override public void shapeAdded(Graph graph,IGraphicsNode n) { updateGeometry((CSGShapeNode) n); if (n.getG3DNode(graph).getParent() == null) { if (DEBUG)System.out.println("ShapeSubnodeListener " + n.getResource() + " has no parent"); return; } if (DEBUG) System.out.print("ShapeSubnodeListener " + n.getResource()); if (n.getG3DNode(graph).getRelatedObjects(ShapeEditorResources.g3dResource.GeometryDefinitionOf).size() == 0) { if (DEBUG) System.out.println(" visible"); ((ISelectableNode)n).setVisible(true); } else { if (DEBUG) System.out.println(" invisible"); ((ISelectableNode)n).setVisible(false); } } // @Override // public NodeQuery instantiateQuery(Resource node) { // return new ShapeEditorScenegraphQuery(node); // } } @Override protected ScenegraphQuery newSubnodeListener(G3DNode node) { return new ShapeEditorScenegraphQuery(node.getResource()); } private class ShapeEditorNodePropertyQuery extends NodePropertyQuery { public ShapeEditorNodePropertyQuery(Resource nodeResource) { super(nodeResource); } @Override public void shapeUpdated(Graph graph,final IGraphicsNode shape) { if (DEBUG) System.out.println("Tri - Shape id " + shape + " modified"); ((CSGShapeNode) shape).updateAllGeometry(); } // @Override // public NodeQuery instantiateQuery(Resource node) { // return new ShapeEditorNodePropertyQuery(node); // } } @Override protected NodePropertyQuery newPropertyListener(G3DNode node) { return new ShapeEditorNodePropertyQuery(node.getResource()); } private class ShapeEditorNodeTransformationQuery extends NodeTransformationQuery { public ShapeEditorNodeTransformationQuery(Resource nodeResource) { super(nodeResource); } @Override public void shapeUpdated(Graph graph,final IGraphicsNode shape) { if (DEBUG) System.out.println("Tra - Shape id " + shape + " modified"); ((CSGShapeNode) shape).updateTransform(graph); geometryUpdate((CSGShapeNode)shape); } // @Override // public NodeQuery instantiateQuery(Resource node) { // return new ShapeEditorNodePropertyQuery(node); // } } @Override protected NodeTransformationQuery newTransformationListener(G3DNode node) { return new ShapeEditorNodeTransformationQuery(node.getResource()); } private class ShapeEditorRootPropertyQuery extends NodePropertyQuery { public ShapeEditorRootPropertyQuery(Resource nodeResource) { super(nodeResource); } @Override public void shapeUpdated(Graph graph, final IGraphicsNode shape) { if (DEBUG)System.out.println("Tri - Shape id " + shape + " modified"); updateParameterizationStatus(graph); if (isParameterized) { for (IGraphicsNode n : getNodes()) if (n instanceof IGeometryNode) updateGeometry((IGeometryNode) n); } } // @Override // public NodeQuery instantiateQuery(Resource node) { // return new ShapeEditorRootPropertyQuery(node); // } } @Override protected NodePropertyQuery newRootPropertyListener(G3DNode root) { return new ShapeEditorRootPropertyQuery(root.getResource()); } } protected void contributeStatusBar(IStatusLineManager manager) { } /** * Loads the initial scene: all further updates to the view are done by * listening changes in the shapes and int the shape group * * @param resource */ protected void reloadFrom(IEntity thing) { if (model != null) { throw new UnsupportedOperationException( "Reloading instantiated viewer not supported"); } if (thing.isInstanceOf(ShapeEditorResources.csgResource.CSGModel)) { //System.out.print("ShapeEditorView.reloadFrom() : model"); Graph g = thing.getGraph(); model = thing.getResource(); //System.out.println(" " + model.getResource()); adapter.setRootNode(new G3DNode(thing)); updateParameterizationStatus(g); //loadGroup(thing.getGraph()); } else { throw new UnsupportedOperationException("Cannot load ShapeViewer for Resource:" + thing); } } private void updateParameterizationStatus(Graph graph) { G3DModel model = getModel(graph); if(model.getRelatedObjects(ShapeEditorResources.g3dResource.HasSizingParameter).size() > 0) { isParameterized = true; } else { isParameterized = false; } parent.getDisplay().asyncExec(new Runnable() { @Override public void run() { // for (Action a : addActions) // a.setEnabled(!isParameterized); // unionAction.setEnabled(!isParameterized); // differenceAction.setEnabled(!isParameterized); // intersectionAction.setEnabled(!isParameterized); // linkAction.setEnabled(!isParameterized); // unlinkAction.setEnabled(!isParameterized); // translateAction.setEnabled(!isParameterized); // rotateAction.setEnabled(!isParameterized); // removeAction.setEnabled(!isParameterized); } }); } public Resource getModelResource() { return model; } public G3DModel getModel(Graph graph) { return new G3DModel(graph, model); } @Override protected SelectionAdapter createSelectionAdapter() { return new ShapeEditorSelectionAdapter(adapter); } protected class ShapeEditorSelectionAdapter extends SelectionAdapter { public ShapeEditorSelectionAdapter(ScenegraphAdapter adapter) { super(adapter); } public void setEditorSelection() { List sel = getSelectedObjects(); for (IGraphicsNode o : adapter.getNodes()) if (o instanceof ISelectableNode) { ISelectableNode n = (ISelectableNode)o; if (sel.contains(o)) n.setSelected(true); else n.setSelected(false); } List selected = getSelectedResources(); for (Resource r : selected) { if (!adapter.hasNode(r)) { //adapter.addInbound(r).setSelected(true); } } } public void setEditorHighlightSelection() { List sel = getInteractiveSelectedObjects(); for (IGraphicsNode o : adapter.getNodes()) if (o instanceof CSGShapeNode) { if (sel.contains(o)) ((CSGShapeNode) o).setHighlighted(true); else ((CSGShapeNode) o).setHighlighted(false); } } public void setEditorSelection(boolean addShapes) { List sel = getSelectedObjects(); for (IGraphicsNode o : adapter.getNodes()) if (o instanceof ISelectableNode) { ISelectableNode n = (ISelectableNode)o; if (sel.contains(o)) n.setSelected(true); else n.setSelected(false); } viewChanged = true; if (addShapes) { session.syncRead(new GraphRequestAdapter() { @Override public GraphRequestStatus perform(Graph g) throws Exception { List selected = getSelectedResources(); for (Resource r : selected) { if (!adapter.hasNode(r)) { IEntity t = EntityFactory.create(g, r); if (t.isInstanceOf(ShapeEditorResources.g3dResource.Shape)) { G3DNode group = G3DTools.getModelFromResource(g,r); if (group != null && group.getResource().equals(model.getResource())) { //adapter.addInbound(g).setSelected(true); } } } } return GraphRequestStatus.transactionComplete(); } }); } } public StructuredResourceSelection filterSelection(ISelection selection) { if (!(selection instanceof StructuredResourceSelection)) return new StructuredResourceSelection(); return (StructuredResourceSelection) selection; } } /** * Receives selection changes * * @param part * @param selection */ protected void pageSelectionChanged(IWorkbenchPart part, ISelection selection) { StructuredResourceSelection s = SelectionAdapter.transformSelection(selection); //System.out.println("ShapeEditorBase.pageSelectionChanged " + s); selectionAdapter.setCurrentSelection(s); if (!(part instanceof ThreeDimensionalEditorProvider)) { ((ShapeEditorSelectionAdapter) selectionAdapter).setEditorSelection(true); return; } ThreeDimensionalEditorBase e = ((ThreeDimensionalEditorProvider)part).getEditor(); if (!(e instanceof ShapeEditorBase)) { ((ShapeEditorSelectionAdapter) selectionAdapter).setEditorSelection(true); return; } ShapeEditorBase editor = (ShapeEditorBase)e; if (!editor.getModelResource().equals(model.getResource())) { selectionAdapter.setCurrentSelection(new StructuredResourceSelection()); ((ShapeEditorSelectionAdapter) selectionAdapter).setEditorSelection(false); return; } selectionAdapter.setEditorSelection(); } @Override protected void hookDragAndDrop() { super.hookDragAndDrop(); dropTarget.addDropListener(new DropListener() { public boolean acceptDrop(StructuredResourceSelection s, Resource[] ids) { if (!s.isEmpty()) return false; if (ids == null) return false; if (ids.length != 1) return false; final Resource r = ids[0]; GraphRequestWithResult rq = new GraphRequestWithResult() { @Override public Boolean performWithResult(Graph g) throws Exception { IEntity t = EntityFactory.create(g, r); return t.isInstanceOf(ShapeEditorResources.csgResource.Primitive); } }; session.syncRead(rq); return rq.getResult(); } public void doDrop(StructuredResourceSelection s, Resource[] ids) { session.asyncWrite(new GraphRequestAdapter() { Resource r; public GraphRequestStatus perform(Graph g) throws Exception { IEntity type = EntityFactory.create(g); IEntity instance = type.instantiate(); Shape shape = new Shape(instance); resetShape(shape); CSGModel m = new CSGModel(g, model); m.getChild().add(shape.toG3DNode()); // FIXME : stubcast return GraphRequestStatus.transactionComplete(); }; @Override public void requestCompleted(GraphRequestStatus status) { selectionAdapter .updateSelection(new StructuredResourceSelection(r)); super.requestCompleted(status); } } ); } }); } private void resetShape(Shape shape) { G3DTools.resetTransformation(shape); Graph graph = shape.getGraph(); if (shape.isInstanceOf(ShapeEditorResources.csgResource.Primitive)) { Primitive prim = new Primitive(shape); Collection c = prim.getSizingProperty(); if (c.size() == 0) ErrorLogger.getDefault().logWarning("Shape does not contain sizing properties.", null); for (Property p : c) { if (p.isInstanceOf(graph.getBuiltins().Double)) { graph.setScalarDouble(p.getResource(), 1.0); } else if (p.isInstanceOf(graph.getBuiltins().Integer)) { graph.setScalarInteger(p.getResource(), 1); } else { ErrorLogger.getDefault().logWarning("Cannot handle sizing property " + p.getName() , null); } } } } @Override public Object getAdapter(Class adapter) { if (adapter == IContentOutlinePage.class) { if (getModelResource() == null) return null; final StructureOutlinePage page = new StructureOutlinePage(sessionContext,getModelResource()); getSelectionAdapter().addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { page.setSelection(event.getSelection()); } }); parent.getDisplay().asyncExec(new Runnable() { @Override public void run() { page.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { selectionAdapter.setSelection(SelectionAdapter.transformSelection(event.getSelection())); } }); } }); return page; } return null; } }