/******************************************************************************* * 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 fi.vtt.simantics.processeditor.actions; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.List; import javax.vecmath.Point3d; import org.eclipse.swt.widgets.Display; 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.layer0.utils.EntityFactory; import org.simantics.layer0.utils.IEntity; import org.simantics.proconf.g3d.actions.InteractiveAction; import org.simantics.proconf.g3d.base.G3DAPI; import org.simantics.proconf.g3d.base.G3DTools; import org.simantics.proconf.g3d.base.ThreeDimensionalEditorBase; import org.simantics.proconf.g3d.common.StructuredResourceSelection; import org.simantics.proconf.g3d.dnd.DropListener; import org.simantics.proconf.g3d.scenegraph.IGraphicsNode; import org.simantics.utils.datastructures.Pair; import fi.vtt.simantics.processeditor.Activator; import fi.vtt.simantics.processeditor.ProcessResource; import fi.vtt.simantics.processeditor.common.ControlPointTools; import fi.vtt.simantics.processeditor.common.PipingTools2; import fi.vtt.simantics.processeditor.common.PipingTools2.Direction; import fi.vtt.simantics.processeditor.dialogs.PipelineComponentDialog; import fi.vtt.simantics.processeditor.gizmo.PositionSelectionGizmo; import fi.vtt.simantics.processeditor.stubs.PipeControlPoint; import fi.vtt.simantics.processeditor.stubs.PipelineComponent; /** * Action that inserts new components into pipe run. * - VariableLengthInlineComponents cannot be inserted with this action * - Assumes that SizeChangeComponent is dual connected * * * @author Marko Luukkainen * */ public class InsertComponentAction extends InteractiveAction implements DropListener, SplitPointListener { Resource typeResource; // type of inserted component Resource targetResource; // component where we are inserting new one Resource selectedPosition; // selected control point for insertion PositionType selectedType; // selected position type List> insertPositions; List> insertPoints; PositionSelectionGizmo gizmo = null; boolean activated = false; private SelectSplitPointAction splitPointAction; public InsertComponentAction(ThreeDimensionalEditorBase parent) { super(parent); splitPointAction = new SelectSplitPointAction(parent,this); } @Override public void init() { this.setText("Insert Component"); this.setToolTipText("Inserts a Component into Pipeline"); this.setImageDescriptor(Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/Component.png")); } @Override public boolean usable(Graph graph, List resources) { if (!(resources.size() == 1)) { return false; } IEntity target = EntityFactory.create(graph,resources.get(0)); if (!target.isInstanceOf(ProcessResource.plant3Dresource.PipelineComponent)) { return false; } PipelineComponent ic = new PipelineComponent(target); PipeControlPoint pcp = ic.getControlPoint(); // one possibility: adding new component to unconnected position // TODO : how about inserting new components between existing ones. // TODO : can there be fixed length components that can be split (for example fixed length pipe) if (pcp.getNext() == null && pcp.getPrevious() == null) return true; if (ic.isInstanceOf(ProcessResource.plant3Dresource.InlineComponent) && (pcp.getNext() == null || pcp.getPrevious() == null)) return true; for (PipeControlPoint p : pcp.getSubPoint()) // SubPoint's other connection is always null // Exception: size change components offset point (that is subpoint) // does have one or both ends connected, but it won't matter // here because previous test fould return true, if insertion // is possible: // TODO : here we assume that Size Change Component is dual connected if (p.getNext() == null && p.getPrevious() == null) return true; if (target.isInstanceOf(ProcessResource.plant3Dresource.VariableLengthInlineComponent)) { // last option to insert component is split variable length component. // If user chooses to split the component, inserted component must be // inline component, but not size change component return true; } return false; } @Override public void activate() { insertPositions = null; if (targetResource == null) { List mos = parent.getSelectionAdapter().getSelectedObjects(); if (mos.size() != 1) { end(); return; } IGraphicsNode startNode = mos.get(0); targetResource = startNode.getResource(); } if (selectedPosition == null) { updateInsertPositions(); } if (typeResource == null) { List filter = new ArrayList(); filter.add(ProcessResource.plant3Dresource.VariableLengthInlineComponent); boolean containsEnd = false; for (Pair p : insertPoints) { if (p.second == PositionType.NEXT || p.second == PositionType.PREVIOUS) { containsEnd = true; break; } } if(!containsEnd) filter.add(ProcessResource.plant3Dresource.EndComponent); PipelineComponentDialog dialog = new PipelineComponentDialog(Display.getCurrent().getActiveShell(),null, filter , parent.getSession()); if (dialog.open() == PipelineComponentDialog.CANCEL) { end(); return; } typeResource = dialog.getComboValue(); updateInsertPositions(); } activated = true; } private void updateInsertPositions() { parent.getSession().syncRead(new GraphRequestAdapter() { @Override public GraphRequestStatus perform(Graph g) throws Exception { insertPositions = getInsertPositions(g,targetResource,typeResource); insertPoints = new ArrayList>(); for (Pair p : insertPositions) { IEntity entity = EntityFactory.create(g,p.first); Point3d pos = ControlPointTools.getRealPosition(entity, p.second); insertPoints.add(new Pair(pos,p.second)); } return GraphRequestStatus.transactionComplete(); } }); } /** * Finds possible locations of inserted component * * TODO (s): currently allows only inline, non size change components to split variable length component * assumes that size change component is DualConnected component * * @param g * @param target * @param acceptSplit * @return */ private List> getInsertPositions(Graph g, Resource target, Resource typeResource) { boolean acceptSplit = true; boolean checkSubPoints = !g.isInstanceOf(target, ProcessResource.plant3Dresource.DualInlineControlPoint); if (typeResource != null) acceptSplit = g.isInstanceOf(typeResource, ProcessResource.plant3Dresource.InlineComponent) && !g.isInstanceOf(typeResource, ProcessResource.plant3Dresource.SizeChangeComponent); List> insertPositions = new ArrayList>(); PipelineComponent ic = new PipelineComponent(g,target); PipeControlPoint pcp = ic.getControlPoint(); if (pcp.getNext() == null) { insertPositions.add(new Pair(pcp.getResource(),PositionType.NEXT)); } if (pcp.getPrevious() == null) { insertPositions.add(new Pair(pcp.getResource(),PositionType.PREVIOUS)); } if (checkSubPoints) { for (PipeControlPoint p : pcp.getSubPoint()) { if (p.getNext() == null && p.getPrevious() == null) { insertPositions.add(new Pair(p.getResource(),PositionType.PORT)); } } } if (acceptSplit && ic.isInstanceOf(ProcessResource.plant3Dresource.VariableLengthInlineComponent)) { insertPositions.add(new Pair(pcp.getResource(),PositionType.SPLIT)); } return insertPositions; } @Override public void update() { if (splitPointAction.active()) { splitPointAction.update(); return; } if (!activated) { return; } if (insertPositions == null) return; if (insertPositions.size() == 0) { end(); return; } if (insertPositions.size() == 1) { activated = false; insertComponent(insertPositions.get(0)); return; } if (gizmo == null) { gizmo = new PositionSelectionGizmo(parent,insertPoints); parent.setGizmo(gizmo); parent.getRenderingComponent().getNoShadowRoot().attachChild(gizmo.getNode()); } gizmo.update(); if (gizmo.getSelected() >= 0 && input.mouseClicked() && input.clickButton() == MouseEvent.BUTTON1) { activated = false; insertComponent(insertPositions.get(gizmo.getSelected())); } if (input.keyPressed(KeyEvent.VK_ESCAPE)) { end(); } } private void insertComponent(Pair position) { selectedPosition = position.first; selectedType = position.second; switch (selectedType) { case NEXT: case PREVIOUS: case PORT: parent.getSession().asyncWrite(new GraphRequestAdapter() { @Override public GraphRequestStatus perform(Graph g) throws Exception { IEntity pcp = EntityFactory.create(g,selectedPosition); Point3d point = ControlPointTools.getRealPosition(pcp, selectedType); IEntity component = instantiateComponent(g,point); PipingTools2.insertComponent(component, EntityFactory.create(g, targetResource), pcp , Direction.NEXT); endThreaded(); return GraphRequestStatus.transactionComplete(); } }); break; case SPLIT: parent.getSession().asyncRead(new GraphRequestAdapter() { @Override public GraphRequestStatus perform(Graph g) throws Exception { PipeControlPoint pcp = new PipeControlPoint(g,selectedPosition); Point3d p1 = new Point3d(); Point3d p2 = new Point3d(); ControlPointTools.getInlineControlPointEnds(pcp, p1, p2); splitPointAction.setSplit(p1, p2); splitPointAction.activate(); return GraphRequestStatus.transactionComplete(); } }); break; } } private void endThreaded() { parent.getRenderingComposite().getDisplay().asyncExec(new Runnable() { @Override public void run() { end(); } }); } @Override public void setSplitPoint(final Point3d point) { splitPointAction.deactivate(); if (point == null) { end(); return; } parent.getSession().asyncWrite(new GraphRequestAdapter() { @Override public GraphRequestStatus perform(Graph g) throws Exception { IEntity component = instantiateComponent(g,point); PipingTools2.splitVariableLengthComponent(component, EntityFactory.create(g,targetResource)); return GraphRequestStatus.transactionComplete(); } @Override public void requestCompleted(GraphRequestStatus status) { parent.getRenderingComposite().getDisplay().asyncExec(new Runnable(){ @Override public void run() { end(); } }); } }); } /** * instantiates selected component * @param worldPosition position of the new component */ private PipelineComponent instantiateComponent(Graph g,Point3d worldPosition) { PipelineComponent target = new PipelineComponent(g,targetResource); IEntity piperun = target.getParent(); PipelineComponent instance = PipingTools2.instantiatePipelineComponent(g,piperun.getResource(), typeResource); G3DTools.resetTransformation(instance); //G3DAPI.addNodeWorld(piperun, instance); G3DAPI.setWorldPosition(instance, worldPosition); return instance; } @Override public void deactivate() { typeResource = null; targetResource = null; selectedPosition = null; selectedType = null; insertPoints = null; insertPositions = null; if (gizmo != null) { parent.setGizmo(null); gizmo = null; } } public boolean acceptDrop(StructuredResourceSelection s, Resource[] ids) { if(s.size() != 1) return false; if (ids == null) return false; if (ids.length != 1) return false; final Resource type = ids[0]; final Resource target = s.getSelectionList().get(0); GraphRequestWithResult query = new GraphRequestWithResult() { @Override public Boolean performWithResult(Graph g) throws Exception { IEntity entity = EntityFactory.create(g, type); // dropped type must be pipeline component if (!entity.isInstanceOf(ProcessResource.plant3Dresource.PipelineComponent)) return false; // but not variable length inline component if (entity.isInstanceOf(ProcessResource.plant3Dresource.VariableLengthInlineComponent)) return false; if (entity.getRelatedObjects(ProcessResource.plant3Dresource.HasGraphics).size() != 1) return false; List> insertPositions = getInsertPositions(g,target,type); return insertPositions.size() > 0; } }; parent.getSession().syncRead(query); return query.getResult(); } public void doDrop(StructuredResourceSelection s, Resource[] ids) { typeResource = ids[0]; targetResource = s.getSelectionList().get(0); parent.setCurrentAction(this); } }