package org.simantics.plant3d.editor; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Menu; import org.eclipse.ui.views.contentoutline.IContentOutlinePage; import org.simantics.Simantics; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.Session; import org.simantics.db.common.request.ParametrizedRead; import org.simantics.db.common.request.ReadRequest; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.request.combinations.Combinators; import org.simantics.g3d.scenegraph.NodeMap; import org.simantics.g3d.scenegraph.RenderListener; import org.simantics.g3d.scenegraph.base.INode; import org.simantics.g3d.scenegraph.base.ParentNode; import org.simantics.g3d.toolbar.ToolComposite; import org.simantics.g3d.vtk.action.RemoveAction; import org.simantics.g3d.vtk.common.HoverHighlighter; import org.simantics.g3d.vtk.common.NodeSelectionProvider2; import org.simantics.g3d.vtk.common.SelectionHighlighter; import org.simantics.g3d.vtk.common.VtkView; import org.simantics.g3d.vtk.shape.vtkShape; import org.simantics.g3d.vtk.swt.ContextMenuListener; import org.simantics.g3d.vtk.swt.FocusAction; import org.simantics.g3d.vtk.swt.InteractiveVtkComposite; import org.simantics.g3d.vtk.swt.RotateAction; import org.simantics.g3d.vtk.swt.TranslateAction; import org.simantics.g3d.vtk.swt.vtkCameraAndSelectorAction; import org.simantics.g3d.vtk.utils.AxesDisplay; import org.simantics.objmap.graph.IMapping; import org.simantics.objmap.graph.Mappings; import org.simantics.objmap.graph.schema.IMappingSchema; import org.simantics.plant3d.actions.AddComponentAction; import org.simantics.plant3d.actions.AddEquipmentAction; import org.simantics.plant3d.actions.AddNozzleAction; import org.simantics.plant3d.actions.RemoveAndSplitAction; import org.simantics.plant3d.actions.ReversePipeRunAction; import org.simantics.plant3d.actions.RoutePipeAction; import org.simantics.plant3d.actions.TranslateFreeVariableLengthAction; import org.simantics.plant3d.actions.TranslateInlineAction; import org.simantics.plant3d.ontology.Plant3D; import org.simantics.plant3d.scenegraph.EndComponent; import org.simantics.plant3d.scenegraph.Equipment; import org.simantics.plant3d.scenegraph.IP3DNode; import org.simantics.plant3d.scenegraph.IP3DVisualNode; import org.simantics.plant3d.scenegraph.InlineComponent; import org.simantics.plant3d.scenegraph.Nozzle; import org.simantics.plant3d.scenegraph.P3DRootNode; import org.simantics.plant3d.scenegraph.PipeRun; import org.simantics.plant3d.scenegraph.PipelineComponent; import org.simantics.plant3d.scenegraph.SchemaBuilder; import org.simantics.plant3d.scenegraph.TurnComponent; import org.simantics.plant3d.scenegraph.controlpoint.ControlPointFactory; import org.simantics.plant3d.scenegraph.controlpoint.PipingRules; import org.simantics.plant3d.utils.ComponentUtils; import org.simantics.plant3d.utils.Item; import org.simantics.plant3d.utils.P3DUtil; import org.simantics.selectionview.StandardPropertyPage; import org.simantics.ui.workbench.IPropertyPage; import org.simantics.ui.workbench.IResourceEditorInput; import org.simantics.ui.workbench.ResourceEditorPart; import org.simantics.ui.workbench.editor.input.InputValidationCombinators; import org.simantics.utils.threads.ThreadUtils; import org.simantics.utils.ui.ExceptionUtils; import vtk.vtkActor; import vtk.vtkCameraPass; import vtk.vtkDefaultPass; import vtk.vtkGaussianBlurPass; import vtk.vtkLightsPass; import vtk.vtkProp3D; import vtk.vtkRenderPassCollection; import vtk.vtkRenderer; import vtk.vtkSSAAPass; import vtk.vtkSequencePass; import vtk.vtkSimpleMotionBlurPass; public class Plant3DEditor extends ResourceEditorPart { private Composite parent; protected ToolComposite toolComposite; private Resource input; // private InteractiveVtkPanel panel; // private SWTAWTComponent component; private InteractiveVtkComposite panel; private P3DRootNode rootNode; private IMapping mapping; protected NodeSelectionProvider2 selectionProvider; protected vtkCameraAndSelectorAction cameraAction; protected FocusAction focusAction; protected TranslateAction translateAction; protected TranslateInlineAction translateInlineAction; protected TranslateFreeVariableLengthAction translateFreeVariableLengthAction; protected RotateAction rotateAction; protected RemoveAction removeAction; protected RemoveAndSplitAction removeSplitAction; protected RoutePipeAction routePipeAction; protected AddComponentAction addComponentAction; protected ReversePipeRunAction reversePipeRunAction; private P3DNodeMap nodeMap; /** Constants for selecting the up-direction */ public static final int X = 0, Y = 1, Z = 2; protected int upDirection = 1; ParametrizedRead INPUT_VALIDATOR = Combinators.compose( InputValidationCombinators.hasURI(), InputValidationCombinators.extractInputResource() ); @Override protected ParametrizedRead getInputValidator() { return INPUT_VALIDATOR; } @Override public void createPartControl(Composite parent) { this.parent = parent; activateValidation(); IResourceEditorInput rei = (IResourceEditorInput)getEditorInput(); input = rei.getResource(); toolComposite = new ToolComposite(parent, SWT.BORDER); toolComposite.setVisible(true); panel = new InteractiveVtkComposite(parent); GridLayoutFactory.fillDefaults().margins(0, 0).spacing(0, 0).applyTo(parent); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(getPanel().getComponent()); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false).applyTo(toolComposite); //IActionBars actionBars = getEditorSite().getActionBars(); hookContextMenu(); createScene(); new ContextMenuListener(panel, contextMenu); cameraAction = createCameraAction(); switch (upDirection) { case 0: cameraAction.setUpDirection(new double[] { 1, 0, 0 }); break; case 1: cameraAction.setUpDirection(new double[] { 0, 1, 0 }); break; case 2: cameraAction.setUpDirection(new double[] { 0, 0, 1 }); break; } panel.setDefaultAction(cameraAction); panel.useDefaultAction(); panel.setPickType(4); try { ControlPointFactory.preloadCache(Simantics.getSession(), getLibraryUri()); ComponentUtils.preloadCache(Simantics.getSession()); } catch (Exception e) { ExceptionUtils.logAndShowError("Cannot open Plant3D editor",e); return; } try { getSession().syncRequest(new ReadRequest() { @Override public void run(ReadGraph graph) throws DatabaseException { //System.out.println("START PLANT3D LOAD"); PipingRules.setEnabled(false); IMappingSchema schema = getSchema(graph); mapping = Mappings.createWithListening(schema); rootNode = (P3DRootNode)mapping.map(graph, input); // update control points. // TODO : this should be optimized. try { P3DUtil.finalizeDBLoad(rootNode); nodeMap = createNodeMap(getSession(), mapping, panel,rootNode); } catch (Exception e) { throw new DatabaseException(e); } //System.out.println("END PLANT3D LOAD"); } }); if (rootNode == null) throw new RuntimeException("Scenegraph loading failed."); populate(); selectionProvider = createSelectionProvider(); cameraAction.addSelectionChangedListener(selectionProvider); cameraAction.addHoverChangedListener(createHoverHighlhighter()); selectionProvider.addSelectionChangedListener(createSelectionHighlighter()); getSite().setSelectionProvider(selectionProvider); getSite().getPage().addPostSelectionListener(selectionProvider); //outlinePage = new ScenegraphOutlinePage(rootNode); parent.addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent e) { getSite().getPage().removePostSelectionListener(selectionProvider); PipingRules.setEnabled(false); nodeMap.delete(); PipingRules.setEnabled(true); mapping.dispose(); // component.dispose(); //panel.getComponent().dispose(); } }); } catch (DatabaseException e1) { ExceptionUtils.logAndShowError("Cannot open Plant3D editor",e1); return; } createActions(); } public void setUpDirection(int upDirection) { this.upDirection = upDirection; } protected vtkCameraAndSelectorAction createCameraAction() { return new vtkCameraAndSelectorAction(panel); } protected NodeSelectionProvider2 createSelectionProvider() { return new NodeSelectionProvider2(this,mapping,nodeMap); } protected HoverHighlighter createHoverHighlhighter() { return new HoverHighlighter<>(panel,nodeMap); } protected SelectionHighlighter createSelectionHighlighter() { return new SelectionHighlighter(panel,nodeMap); } protected String getLibraryUri() { return Plant3D.URIs.Builtin; } protected void createActions() { focusAction = new FocusAction(panel, cameraAction); translateAction = new TranslateAction(panel,nodeMap,toolComposite); translateInlineAction = new TranslateInlineAction(panel, nodeMap,toolComposite); translateFreeVariableLengthAction = new TranslateFreeVariableLengthAction(panel, getRootNode(), toolComposite); rotateAction = new RotateAction(panel,nodeMap,toolComposite); removeAction = new RemoveAction(nodeMap); removeSplitAction = new RemoveAndSplitAction(nodeMap); routePipeAction = new RoutePipeAction(panel,rootNode, toolComposite); addComponentAction = new AddComponentAction(panel, rootNode, getLibraryUri()); reversePipeRunAction = new ReversePipeRunAction(nodeMap); } public void populate() { ThreadUtils.asyncExec(panel.getThreadQueue(), new Runnable() { @Override public void run() { nodeMap.setChangeTracking(false); nodeMap.populate(); nodeMap.setChangeTracking(true); panel.addListener(new RenderListener() { @Override public void preRender() { } @Override public void postRender() { panel.removeListener(this); try { P3DUtil.finalizeDBLoad2(rootNode); if (nodeMap.getMapping().isRangeModified()) nodeMap.commit("Load sync"); } catch (Exception e) { ExceptionUtils.logAndShowError("Failed to load model correctly", e); } onEditorInitializationComplete(); List props = new ArrayList<>(); collectProps(rootNode, props); fitToWindow(props); } }); } }); } /** * This can be overridden by clients to perform extra initialization tasks */ protected void onEditorInitializationComplete() { } protected IMappingSchema getSchema(ReadGraph graph) throws DatabaseException { IMappingSchema schema = SchemaBuilder.getSchema(graph); return schema; } protected P3DNodeMap createNodeMap(Session session, IMapping mapping, VtkView panel, P3DRootNode rootNode) { return new P3DNodeMap(session, mapping, panel,rootNode); } @Override public void setFocus() { //component.setFocus(); panel.getComponent().setFocus(); } protected void createScene() { vtkRenderer ren1 = panel.getRenderer(); boolean multiPass = false; boolean blur = false; boolean ssaa = false; //boolean sobel = true; boolean mblur = false; if (multiPass) { vtkLightsPass lightsPass = new vtkLightsPass(); vtkDefaultPass defaultPass = new vtkDefaultPass(); vtkRenderPassCollection passes = new vtkRenderPassCollection(); passes.AddItem(lightsPass); passes.AddItem(defaultPass); vtkSequencePass seq = new vtkSequencePass(); seq.SetPasses(passes); vtkCameraPass cameraPass = new vtkCameraPass(); cameraPass.SetDelegatePass(seq); if (blur) { vtkGaussianBlurPass blurPass = new vtkGaussianBlurPass(); blurPass.SetDelegatePass(cameraPass); ren1.SetPass(blurPass); } else if (ssaa) { vtkSSAAPass ssaaPass = new vtkSSAAPass(); ssaaPass.SetDelegatePass(cameraPass); ren1.SetPass(ssaaPass); } else if (mblur) { vtkSimpleMotionBlurPass mBlurPass = new vtkSimpleMotionBlurPass(); mBlurPass.SetDelegatePass(cameraPass); ren1.SetPass(mBlurPass); // } else if (sobel) { // vtkSobelGradientMagnitudePass sobelPass = new vtkSobelGradientMagnitudePass(); // sobelPass.SetDelegatePass(sobelPass); // ren1.SetPass(sobelPass); } else { ren1.SetPass(cameraPass); } } // ren1.GetRenderWindow().LineSmoothingOn(); // ren1.GetRenderWindow().PointSmoothingOn(); // ren1.GetRenderWindow().PolygonSmoothingOn(); // ren1.GetRenderWindow().SetMultiSamples(2); ren1.SetBackground2(1,1,1); // background color white ren1.SetBackground(0.9,0.9,0.9); ren1.SetGradientBackground(true); // vtkActor grid = vtkShape.createGridActor(8,1.0,1|2|4); int dir = 1 << upDirection; vtkActor grid = vtkShape.createGridActor(8, 1.0, dir); grid.SetPickable(0); ren1.AddActor(grid); panel.addDeletable(grid); AxesDisplay axesDisplay = new AxesDisplay(panel); axesDisplay.show(); } protected Menu contextMenu; protected void hookContextMenu() { MenuManager menuMgr = new MenuManager("#PopupMenu"); menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(new IMenuListener() { public void menuAboutToShow(IMenuManager manager) { createContextMenu(manager); } }); contextMenu = menuMgr.createContextMenu(parent); } protected void createContextMenu(IMenuManager m) { List selected = selectionProvider.getSelectedNodes(); createFocusMenu(m, selected); m.add(new Separator()); try { if (selected.size() == 0) { m.add(new AddEquipmentAction(rootNode, getLibraryUri())); // for (Item eq : P3DUtil.getEquipments(getLibraryUri())) { // m.add(new AddEquipmentAction(rootNode, eq)); // } } else if (selected.size() == 1) { IP3DNode node = (IP3DNode)selected.get(0); if (node instanceof Equipment) { m.add(translateAction); m.add(rotateAction); for (Item eq : P3DUtil.getNozzles(Simantics.getSession(), getLibraryUri())) { AddNozzleAction add = new AddNozzleAction(rootNode, eq); add.setEquipment((Equipment)node); m.add(add); } m.add(removeAction); } else if (node instanceof Nozzle) { Nozzle nozzle = (Nozzle)node; if (!nozzle.isFixed()) { m.add(translateAction); m.add(rotateAction); } m.add(routePipeAction); routePipeAction.setComponent(nozzle); routePipeAction.setEnabled(nozzle.getNext() == null && nozzle.getPrevious() == null); m.add(addComponentAction); addComponentAction.setComponent(nozzle); m.add(removeAction); } else if (node instanceof TurnComponent) { m.add(translateAction); TurnComponent component = (TurnComponent)node; m.add(routePipeAction); routePipeAction.setComponent(component); routePipeAction.setEnabled(component.getNext() == null || component.getPrevious() == null); m.add(addComponentAction); addComponentAction.setComponent(component); m.add(removeAction); m.add(removeSplitAction); removeSplitAction.setNode(node); } else if (node instanceof EndComponent) { m.add(translateAction); m.add(addComponentAction); addComponentAction.setComponent((PipelineComponent)node); m.add(removeAction); } else if (node instanceof InlineComponent) { //m.add(translateInlineAction); InlineComponent component = (InlineComponent)node; if (component.isVariableLength()) m.add(translateFreeVariableLengthAction); else m.add(translateInlineAction); m.add(routePipeAction); routePipeAction.setComponent(component); m.add(addComponentAction); addComponentAction.setComponent(component); m.add(removeAction); m.add(removeSplitAction); removeSplitAction.setNode(node); } else if (node instanceof PipeRun) { m.add(reversePipeRunAction); m.add(removeAction); reversePipeRunAction.setNode(node); } else { m.add(removeAction); } translateAction.setNode(node); translateInlineAction.setNode(node); translateFreeVariableLengthAction.setNode(node); rotateAction.setNode(node); removeAction.setNode(node); } } catch (DatabaseException e) { ExceptionUtils.logAndShowError(e); } } protected class FitToWindow extends Action { private List selected; public FitToWindow(List selected) { super("Fit to Window"); this.selected = selected; //setAccelerator('1'); } @Override public void run() { List props = new ArrayList<>(); final Collection collection = !selected.isEmpty() ? selected : getRootNode().getChild(); for (INode n : collection) collectProps(n, props); fitToWindow(props); getPanel().refresh(); } } protected void createFocusMenu(IMenuManager m, List selected) { m.add(createFitToWindowAction(selected)); if (!selected.isEmpty()) { List actors = new ArrayList<>(); for (INode n : selected) collectProps(n, actors); if (actors.size() > 0) { focusAction.setProps(new ArrayList<>(actors)); m.add(focusAction); } } } protected IAction createFitToWindowAction(List selected) { return new FitToWindow(selected); } private IContentOutlinePage createOutline() { if (rootNode == null || selectionProvider == null) return null; //IContentOutlinePage outlinePage = new VTKContentOutlinePage(rootNode, selectionProvider); IContentOutlinePage outlinePage = new P3DContentOutlinePage(rootNode, selectionProvider) { protected void createContextMenu(IMenuManager manager) { Plant3DEditor.this.createContextMenu(manager); }; }; outlinePage.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { selectionProvider.selectionChanged(event); } }); return outlinePage; } @Override public T getAdapter(Class adapter) { if (adapter.isAssignableFrom(IPropertyPage.class)) return adapter.cast(new StandardPropertyPage(getSite(),getPropertyContexts())); if (adapter.isAssignableFrom(ISelectionProvider.class)) return adapter.cast(selectionProvider); if (adapter.isAssignableFrom(IContentOutlinePage.class)) { return adapter.cast(createOutline()); } if (adapter.isAssignableFrom(NodeMap.class)) { return adapter.cast(nodeMap); } if (adapter.isAssignableFrom(INode.class)) { return adapter.cast(rootNode); } if (adapter.isAssignableFrom(IMapping.class)) { return adapter.cast(mapping); } // if (adapter.isAssignableFrom(InteractiveVtkPanel.class)) { // return adapter.cast(panel); // } if (adapter.isAssignableFrom(VtkView.class)) return adapter.cast(panel); return super.getAdapter(adapter); } public Set getPropertyContexts() { Set result = new HashSet(); result.add("http://www.simantics.org/Project-1.0/ProjectBrowseContext"); return result; } public InteractiveVtkComposite getPanel() { return panel; } public P3DRootNode getRootNode() { return rootNode; } public IMapping getMapping() { return mapping; } public P3DNodeMap getNodeMap() { return nodeMap; } public void fitToWindow(Collection props) { cameraAction.fitToView(props); } protected static void collectProps(INode node, List props) { if (node instanceof IP3DVisualNode) props.addAll(((IP3DVisualNode) node).getActors()); if (node instanceof ParentNode) for (INode n : ((ParentNode) node).getNodes()) { collectProps(n, props); } } }