From 5e33af894dacbfe97c3ba1a154ba7503f21f43e5 Mon Sep 17 00:00:00 2001 From: lempinen Date: Wed, 29 Dec 2010 12:08:15 +0000 Subject: [PATCH] Removed old Connect tool that used diagram mutator. New Connect tool does not use mutator and should in the future have better routing algorithms for flows. git-svn-id: https://www.simantics.org/svn/simantics/sysdyn/trunk@19226 ac1ea38d-2e2b-0410-8846-a27921b304fc --- .../sysdyn/ui/editor/DiagramViewer.java | 12 +- .../ui/editor/SysdynConnectionAdvisor.java | 25 +- .../ui/editor/participant/ConnectTool.java | 726 ------------------ .../editor/participant/PointerInteractor.java | 35 +- .../editor/participant/SysdynConnectTool.java | 398 ++++++++++ .../participant/SysdynConnectionBuilder.java | 235 ++++++ .../SysdynElementClassProviders.java | 69 ++ .../ui/editor/routing/DependencyRouter.java | 34 + .../ui/elements2/SysdynElementClasses.java | 8 + .../sysdyn/ui/utils/ExpressionUtils.java | 19 +- 10 files changed, 799 insertions(+), 762 deletions(-) delete mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/participant/ConnectTool.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/participant/SysdynConnectTool.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/participant/SysdynConnectionBuilder.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/participant/SysdynElementClassProviders.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/routing/DependencyRouter.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynElementClasses.java diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/DiagramViewer.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/DiagramViewer.java index 85b88f8a..b0b181cf 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/DiagramViewer.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/DiagramViewer.java @@ -39,7 +39,7 @@ import org.simantics.g2d.diagram.DiagramHints; import org.simantics.g2d.diagram.participant.DeleteHandler; import org.simantics.g2d.diagram.participant.ElementPainter; import org.simantics.g2d.diagram.participant.pointertool.PointerInteractor; -import org.simantics.g2d.element.ElementClassProviders; +import org.simantics.g2d.element.ElementClass; import org.simantics.g2d.element.ElementClasses; import org.simantics.g2d.element.ElementHints; import org.simantics.g2d.element.IElement; @@ -55,8 +55,10 @@ import org.simantics.structural2.modelingRules.IModelingRules; import org.simantics.sysdyn.SysdynResource; import org.simantics.sysdyn.ui.editor.participant.CreateVariablesShortcutParticipant; import org.simantics.sysdyn.ui.editor.participant.SysdynComponentCopyAdvisor; +import org.simantics.sysdyn.ui.editor.participant.SysdynElementClassProviders; import org.simantics.sysdyn.ui.editor.participant.SysdynPopulateElementDropParticipant; import org.simantics.sysdyn.ui.elements2.CloudFactory; +import org.simantics.sysdyn.ui.elements2.SysdynElementClasses; import org.simantics.sysdyn.ui.elements2.SysdynElementFactory; import org.simantics.sysdyn.ui.elements2.connections.ConnectionClasses; import org.simantics.sysdyn.ui.elements2.connections.SysdynConnectionClass; @@ -99,11 +101,13 @@ public class DiagramViewer extends org.simantics.modeling.ui.diagramEditor.Diagr @Override protected IElementClassProvider createElementClassProvider(ReadGraph graph) { SysdynResource sr = SysdynResource.getInstance(graph); - return ElementClassProviders.mappedProvider( - ElementClasses.CONNECTION, SysdynConnectionClass.CLASS.newClassWith(new StaticObjectAdapter(sr.FlowConnection)), + ElementClass dependencyClass = SysdynConnectionClass.CLASS.newClassWith(new StaticObjectAdapter(sr.DependencyConnection)); + return SysdynElementClassProviders.mappedProvider( + ElementClasses.CONNECTION, dependencyClass, ElementClasses.FLAG, CloudFactory.createElementClass(sr.CloudSymbol, SysdynElementFactory.createTerminals(graph, sr.CloudSymbol)), ConnectionClasses.FLOW, SysdynConnectionClass.CLASS.newClassWith(new StaticObjectAdapter(sr.FlowConnection)), - ConnectionClasses.DEPENDENCY, SysdynConnectionClass.CLASS.newClassWith(new StaticObjectAdapter(sr.DependencyConnection)) + ConnectionClasses.DEPENDENCY, dependencyClass, + SysdynElementClasses.VALVE, CloudFactory.createElementClass(sr.ValveSymbol, SysdynElementFactory.createTerminals(graph, sr.ValveSymbol)) ); diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/SysdynConnectionAdvisor.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/SysdynConnectionAdvisor.java index f9c3e21d..e21d6abb 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/SysdynConnectionAdvisor.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/SysdynConnectionAdvisor.java @@ -26,6 +26,7 @@ import org.simantics.g2d.connection.IConnectionAdvisor; import org.simantics.g2d.diagram.handler.Topology.Terminal; import org.simantics.g2d.element.ElementUtils; import org.simantics.g2d.element.IElement; +import org.simantics.g2d.element.handler.impl.StaticObjectAdapter; import org.simantics.structural.stubs.StructuralResource2; import org.simantics.structural2.modelingRules.CPIgnore; import org.simantics.structural2.modelingRules.ConnectionJudgement; @@ -77,26 +78,26 @@ public class SysdynConnectionAdvisor implements IConnectionAdvisor { @Override public Object perform(ReadGraph g) throws DatabaseException { if(element1 != null && term1 != null && element2 != null && term2 != null) { - SysdynResource sr = SysdynResource.getInstance(g); + StaticObjectAdapter soa = element1.getElementClass().getSingleItem(StaticObjectAdapter.class); + Resource startElementResource = soa.adapt(Resource.class); + soa = element2.getElementClass().getSingleItem(StaticObjectAdapter.class); + Resource endElementResource = soa.adapt(Resource.class); + DiagramResource dr = DiagramResource.getInstance(g); StructuralResource2 str2 = StructuralResource2.getInstance(g); Resource terminal2 = ((ResourceTerminal) term2).getResource(); + SysdynResource sr = SysdynResource.getInstance(g); + Resource connectionVariable = g.getPossibleObject(terminal2, dr.HasConnectionVariable); if(!g.hasStatement(connectionVariable, str2.Binds, sr.IsHeadOfTerminal)) { return null; } - - // Only one incoming dependency allowed in inputs. That dependency must be from a module - Object obj1 = ElementUtils.getObject(element1); - Object obj2 = ElementUtils.getObject(element2); - if(obj1 instanceof Resource && obj2 instanceof Resource) { - Resource startElementResource = (Resource)obj1; - Resource endElementResource = (Resource)obj2; - if(g.isInstanceOf(endElementResource, sr.InputSymbol)) { - if(g.isInheritedFrom(startElementResource, sr.ModuleSymbol)) return null; - if(g.getObjects(endElementResource, sr.IsHeadOfTerminal).size() > 0) return null; - } + + if(g.isInstanceOf(endElementResource, sr.InputSymbol)) { + if(g.isInheritedFrom(startElementResource, sr.ModuleSymbol)) return null; + if(g.getObjects(endElementResource, sr.IsHeadOfTerminal).size() > 0) return null; } + } ArrayList cps = new ArrayList(); diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/participant/ConnectTool.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/participant/ConnectTool.java deleted file mode 100644 index c90d1173..00000000 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/participant/ConnectTool.java +++ /dev/null @@ -1,726 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010 Association for Decentralized Information Management in - * Industry THTH ry. - * 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.sysdyn.ui.editor.participant; - -import java.awt.AlphaComposite; -import java.awt.Composite; -import java.awt.geom.Point2D; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Deque; -import java.util.HashMap; -import java.util.List; - -import org.simantics.db.ReadGraph; -import org.simantics.db.Resource; -import org.simantics.db.common.request.Queries; -import org.simantics.db.exception.DatabaseException; -import org.simantics.db.request.Read; -import org.simantics.diagram.query.DiagramRequests; -import org.simantics.g2d.canvas.ICanvasContext; -import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency; -import org.simantics.g2d.canvas.impl.SGNodeReflection.SGCleanup; -import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit; -import org.simantics.g2d.command.CommandEvent; -import org.simantics.g2d.command.Commands; -import org.simantics.g2d.connection.IConnectionAdvisor; -import org.simantics.g2d.connection.handler.ConnectionHandler; -import org.simantics.g2d.diagram.DiagramHints; -import org.simantics.g2d.diagram.DiagramMutator; -import org.simantics.g2d.diagram.DiagramUtils; -import org.simantics.g2d.diagram.IDiagram; -import org.simantics.g2d.diagram.handler.PickContext; -import org.simantics.g2d.diagram.handler.Topology; -import org.simantics.g2d.diagram.handler.Topology.Connection; -import org.simantics.g2d.diagram.handler.Topology.Terminal; -import org.simantics.g2d.diagram.handler.TransactionContext.TransactionType; -import org.simantics.g2d.diagram.impl.Diagram; -import org.simantics.g2d.diagram.impl.MutatedDiagram; -import org.simantics.g2d.diagram.participant.AbstractDiagramParticipant; -import org.simantics.g2d.diagram.participant.ElementPainter; -import org.simantics.g2d.diagram.participant.TerminalPainter; -import org.simantics.g2d.diagram.participant.TerminalPainter.TerminalHoverStrategy; -import org.simantics.g2d.diagram.participant.pointertool.PointerInteractor; -import org.simantics.g2d.diagram.participant.pointertool.TerminalUtil.TerminalInfo; -import org.simantics.g2d.element.ElementClass; -import org.simantics.g2d.element.ElementHints; -import org.simantics.g2d.element.ElementUtils; -import org.simantics.g2d.element.IElement; -import org.simantics.g2d.element.SceneGraphNodeKey; -import org.simantics.g2d.element.handler.Parent; -import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd; -import org.simantics.g2d.element.handler.impl.StaticObjectAdapter; -import org.simantics.g2d.elementclass.BranchPoint; -import org.simantics.g2d.elementclass.FlagClass; -import org.simantics.g2d.event.Event; -import org.simantics.g2d.event.KeyEvent; -import org.simantics.g2d.event.MouseEvent; -import org.simantics.g2d.event.EventHandlerReflection.EventHandler; -import org.simantics.g2d.event.KeyEvent.KeyPressedEvent; -import org.simantics.g2d.event.MouseEvent.MouseButtonEvent; -import org.simantics.g2d.event.MouseEvent.MouseButtonPressedEvent; -import org.simantics.g2d.event.MouseEvent.MouseMovedEvent; -import org.simantics.g2d.participant.KeyUtil; -import org.simantics.g2d.participant.MouseUtil; -import org.simantics.g2d.participant.TransformUtil; -import org.simantics.g2d.snap.ISnapAdvisor; -import org.simantics.scenegraph.Node; -import org.simantics.scenegraph.g2d.G2DParentNode; -import org.simantics.sysdyn.SysdynResource; -import org.simantics.sysdyn.ui.elements2.ValveFactory; -import org.simantics.ui.SimanticsUI; -import org.simantics.utils.datastructures.hints.IHintContext.Key; - -public class ConnectTool extends AbstractDiagramParticipant { - - - public static final Key KEY_SG_NODE = new SceneGraphNodeKey(Node.class, "CONNECT_SG_NODE"); - - @Dependency TransformUtil util; - @Dependency ElementPainter diagramPainter; - @Dependency PickContext pickContext; - @Dependency PointerInteractor pi; - @Dependency MouseUtil mouseUtil; - @Dependency KeyUtil keys; - - boolean createFlags; - Point2D startPos; - - IElement startElement; - Terminal startTerminal; - - IElement endElement; - Terminal endTerminal; - - IDiagram inputDiagram; - MutatedDiagram ghostDiagram; - Topology topology; - - ElementClass connectionClass; - - ConnectionHandler connectionHandler; - IElement connection; - Deque edges = new ArrayDeque(); - Deque controlPoints = new ArrayDeque(); - int mouseId; - - Collection terminals = new ArrayList(); - - TerminalHoverStrategy originalStrategy = null; - TerminalHoverStrategy terminalHoverStrategy = new TerminalHoverStrategy() { - @Override - public boolean highlightEnabled() { - return true; - } - - @Override - public boolean highlight(TerminalInfo ti) { - IConnectionAdvisor advisor = diagram.getHint(DiagramHints.CONNECTION_ADVISOR); - return canConnect(advisor, ti.e, ti.t) != null; - } - }; - - public ConnectTool(IDiagram diagram, ElementClass connectionClass, IElement startElement, Terminal startTerminal, int mouseId, Point2D mouseDiagramPos) - { - this.inputDiagram = diagram; - this.connectionClass = connectionClass; - this.mouseId = mouseId; - this.startPos = mouseDiagramPos; - this.startElement = startElement; - this.startTerminal = startTerminal; - } - - @Override - public void addedToContext(ICanvasContext ctx) { - - super.addedToContext(ctx); - - // Force terminals to always be highlighted. - originalStrategy = getHint(TerminalPainter.TERMINAL_HOVER_STRATEGY); - setHint(TerminalPainter.TERMINAL_HOVER_STRATEGY, terminalHoverStrategy); - - // See if flags should be created or not - this.createFlags = Boolean.TRUE.equals(inputDiagram.getHint(DiagramHints.KEY_USE_CONNECTION_FLAGS)); - - // Mutate current diagram and add the connection to the mutated copy. - ghostDiagram = Diagram.mutate(inputDiagram); - topology = ghostDiagram.getDiagramClass().getSingleItem(Topology.class); - - IElement firstElement = null; - Terminal firstTerminal = null; - - // Where is start terminal? - if (startElement != null && startTerminal != null) { - // Whoomp, there it is. Is it already a part of an existing connection? - assert ElementUtils.peekDiagram(startElement) == inputDiagram; - IElement possibleConnection = getConnectionFromPart(startElement); - if (possibleConnection != null) { - // TODO: broken. - connection = ghostDiagram.getMutatedCorrespondence(possibleConnection); - } else { - connection = createConnection(connectionClass); - ghostDiagram.addElement(connection); - } - - connectionHandler = connection.getElementClass().getSingleItem(ConnectionHandler.class); - assert connectionHandler != null; - - firstElement = ghostDiagram.getMutatedCorrespondence(startElement); - firstTerminal = startTerminal; - } else { - connection = createConnection(connectionClass); - connectionHandler = connection.getElementClass().getSingleItem(ConnectionHandler.class); - assert connectionHandler != null; - ghostDiagram.addElement(connection); - - firstElement = createBranchPointOrFlag(startPos, EdgeEnd.Begin); - ArrayList terminals = new ArrayList(); - ElementUtils.getTerminals(firstElement, terminals, false); - firstTerminal = terminals.get(1); - startElement = firstElement; - startTerminal = firstTerminal; - } - - IElement secondElement = connectionHandler.newBranchPoint(connection); - controlPoints.add(secondElement); - // ghostDiagram.addElement(secondElement); - ElementUtils.setPos(secondElement, startPos); - Terminal secondTerminal = ElementUtils.getSingleTerminal(secondElement); - - IElement edge = connectionHandler.newEdge(connection); - edges.add(edge); - // ghostDiagram.addElement(edge); - - topology.connect(edge, EdgeEnd.Begin, firstElement, firstTerminal); - topology.connect(edge, EdgeEnd.End, secondElement, secondTerminal); - - } - - - - private void createValveIfNecessary(Point2D point) { - if (!isCurrentConnectionFlow()) return; - - if(startElement != null && startElement.getElementClass().getId().equals(ValveFactory.class.getSimpleName())) { - return; - } else if(endElement != null && endElement.getElementClass().getId().equals(ValveFactory.class.getSimpleName())) { - return; - } - - ArrayList terminals = new ArrayList(); - - if(endElement != null) { - // Disconnect from end element - topology.disconnect(edges.peekLast(), EdgeEnd.End, ghostDiagram.getMutatedCorrespondence(endElement), endTerminal); - } else { - IElement lastBp = controlPoints.pollLast(); - ElementUtils.getTerminals(lastBp, terminals, true); - topology.disconnect(edges.peekLast(), EdgeEnd.End, lastBp, terminals.get(0)); - connectionHandler.removeBranchPoint(connection, lastBp); - } - - - // Set the position in the middle of the route - double startX = startPos.getX(); - double startY = startPos.getY(); - double x = point.getX(); - double y = point.getY(); - Point2D pos = new Point2D.Double((startX + x) / 2, (startY + y) / 2); - - // Create the valve - IElement end = createValve(pos); - - ElementUtils.getTerminals(end, terminals, true); - topology.connect(edges.peekLast(), EdgeEnd.End, end, terminals.get(0)); - DiagramUtils.validateAndFix(ghostDiagram, getContext()); - - - IElement firstElement = end; - Terminal firstTerminal = terminals.get(1); - - - IElement possibleConnection = getConnectionFromPart(firstElement); - if (possibleConnection != null) { - // TODO: broken. - connection = ghostDiagram.getMutatedCorrespondence(possibleConnection); - } else { - connection = createConnection(connectionClass); - ghostDiagram.addElement(connection); - } - - connectionHandler = connection.getElementClass().getSingleItem(ConnectionHandler.class); - - - - - IElement edge = connectionHandler.newEdge(connection); - edges.add(edge); - - topology.connect(edge, EdgeEnd.Begin, firstElement, firstTerminal); - - if (endElement != null) { - IConnectionAdvisor advisor = diagram.getHint(DiagramHints.CONNECTION_ADVISOR); - Object canConnect = canConnect(advisor, ghostDiagram.getMutatedCorrespondence(endElement), endTerminal); - if(canConnect != null) { - connection.setHint(ElementHints.KEY_CONNECTION_TYPE, canConnect); - } - topology.connect(edge, EdgeEnd.End, ghostDiagram.getMutatedCorrespondence(endElement), endTerminal); - } else { - IElement secondElement = connectionHandler.newBranchPoint(connection); - controlPoints.add(secondElement); - ElementUtils.setPos(secondElement, pos); - Terminal secondTerminal = ElementUtils.getSingleTerminal(secondElement); - IConnectionAdvisor advisor = diagram.getHint(DiagramHints.CONNECTION_ADVISOR); - Object canConnect = canConnect(advisor, null, null); - if(canConnect != null) { - connection.setHint(ElementHints.KEY_CONNECTION_TYPE, canConnect); - } - - topology.connect(edge, EdgeEnd.End, secondElement, secondTerminal); - } - DiagramUtils.validateAndFix(ghostDiagram, getContext()); - - } - - @Override - public void removedFromContext(ICanvasContext ctx) { - - if (getHint(TerminalPainter.TERMINAL_HOVER_STRATEGY) == terminalHoverStrategy) { - if (originalStrategy != null) - setHint(TerminalPainter.TERMINAL_HOVER_STRATEGY, originalStrategy); - else - removeHint(TerminalPainter.TERMINAL_HOVER_STRATEGY); - } - - ghostDiagram.destroy(); - - super.removedFromContext(ctx); - } - - final static Composite ALPHA_COMPOSITE = AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.75f); - public static final int PAINT_PRIORITY = ElementPainter.ELEMENT_PAINT_PRIORITY + 5; - - protected G2DParentNode node = null; - - @SGInit - public void initSG(G2DParentNode parent) { - node = parent.addNode(G2DParentNode.class); - node.setZIndex(PAINT_PRIORITY); - update(); - } - - public void update() { - if (ghostDiagram != null) { - diagramPainter.paintDiagram(node, ghostDiagram, ghostDiagram.getDifferences(), KEY_SG_NODE); - } - } - - @SGCleanup - public void cleanupSG() { - node.remove(); - node = null; - } - - boolean cancelPreviousBend() { - if (!routePointsAllowed()) - return false; - - // If not at the first branch point, remove the last branch - // point and edge. Otherwise, cancel action - if (controlPoints.size() < 2) { - DiagramMutator mutator = diagram.getHint(DiagramHints.KEY_MUTATOR); - mutator.clear(); - update(); - remove(); - return true; - } - // Cancel prev bend - IElement lastControlPoint = controlPoints.removeLast(); - IElement prevControlPoint = controlPoints.peekLast(); - IElement lastEdge = edges.removeLast(); - for (Terminal t : ElementUtils.getTerminals(lastControlPoint, terminals, true)) - topology.disconnect(lastEdge, EdgeEnd.End, lastControlPoint, t); - for (Terminal t : ElementUtils.getTerminals(prevControlPoint, terminals, true)) - topology.disconnect(lastEdge, EdgeEnd.Begin, prevControlPoint, t); - connectionHandler.removeBranchPoint(connection, lastControlPoint); - connectionHandler.removeEdge(connection, lastEdge); - Point2D mousePos = mouseUtil.getMouseInfo(mouseId).canvasPosition; - ElementUtils.setPos(controlPoints.peekLast(), mousePos); - DiagramUtils.validateAndFix(ghostDiagram, getContext()); - update(); - setDirty(); - return true; - } - - @EventHandler(priority = 20) - public boolean handleEvent(Event e) { - // Back-space, cancel prev bend - if (e instanceof KeyPressedEvent) { - KeyEvent ke = (KeyEvent) e; - if (ke.keyCode == java.awt.event.KeyEvent.VK_BACK_SPACE) { - return cancelPreviousBend(); - } - } - if (e instanceof CommandEvent) { - CommandEvent ce = (CommandEvent) e; - if (ce.command.equals(Commands.CANCEL)) - { - DiagramMutator mutator = diagram.getHint(DiagramHints.KEY_MUTATOR); - mutator.clear(); - update(); - remove(); - return true; - } - } - if (e instanceof MouseMovedEvent) { - MouseMovedEvent me = (MouseMovedEvent) e; - Point2D mouseControlPos = me.controlPosition; - Point2D mouseCanvasPos = util.controlToCanvas(mouseControlPos, new Point2D.Double()); - - ISnapAdvisor snapAdvisor = getHint(DiagramHints.SNAP_ADVISOR); - if (snapAdvisor != null) - snapAdvisor.snap(mouseCanvasPos); - - List tiList = ((org.simantics.sysdyn.ui.editor.participant.PointerInteractor)pi).pickTerminals(me.controlPosition); - TerminalInfo ti = null; - - IConnectionAdvisor advisor = diagram.getHint(DiagramHints.CONNECTION_ADVISOR); - for(TerminalInfo info : tiList) { - if(advisor == null || info.e == null || info.t == null) - continue; - Object canConnect = canConnect(advisor, info.e, info.t); - if (canConnect != null) { - connection.setHint(ElementHints.KEY_CONNECTION_TYPE, canConnect); - ti = info; - } - } - - if (ti != null && !(ti.e == startElement && ti.t == startTerminal)) { - if (endElement == null) { - endElement = ti.e; - endTerminal = ti.t; - - IElement lastControlPoint = controlPoints.pollLast(); - topology.disconnect(edges.peekLast(), EdgeEnd.End, lastControlPoint, ElementUtils.getSingleTerminal(lastControlPoint)); - connectionHandler.removeBranchPoint(connection, lastControlPoint); - - topology.connect(edges.peekLast(), EdgeEnd.End, ghostDiagram.getMutatedCorrespondence(endElement), endTerminal); - DiagramUtils.validateAndFix(ghostDiagram, getContext()); - } else if (!ti.e.equals(endElement) || !ti.t.equals(endTerminal)) { - topology.disconnect(edges.peekLast(), EdgeEnd.End, ghostDiagram.getMutatedCorrespondence(endElement), endTerminal); - - endElement = ti.e; - endTerminal = ti.t; - - topology.connect(edges.peekLast(), EdgeEnd.End, ghostDiagram.getMutatedCorrespondence(endElement), endTerminal); - DiagramUtils.validateAndFix(ghostDiagram, getContext()); - } - - update(); - setDirty(); - return false; - } - - { - connection.removeHint(ElementHints.KEY_CONNECTION_TYPE); - if (endElement != null) { - topology.disconnect(edges.peekLast(), EdgeEnd.End, ghostDiagram.getMutatedCorrespondence(endElement), endTerminal); - - endElement = null; - endTerminal = null; - - IElement bp = connectionHandler.newBranchPoint(connection); - controlPoints.add(bp); - // ghostDiagram.addElement(bp); - ElementUtils.setPos(controlPoints.peekLast(), mouseCanvasPos); - - topology.connect(edges.peekLast(), EdgeEnd.End, bp, ElementUtils.getSingleTerminal(bp)); - } else { - ElementUtils.setPos(controlPoints.peekLast(), mouseCanvasPos); - } - DiagramUtils.validateAndFix(ghostDiagram, getContext()); - - update(); - setDirty(); - } - - } - - if (e instanceof MouseButtonPressedEvent) { - MouseButtonEvent me = (MouseButtonEvent) e; - if (me.button==MouseEvent.LEFT_BUTTON && me.mouseId==mouseId) { - Point2D mouseControlPos = me.controlPosition; - final Point2D mouseCanvasPos = util.getInverseTransform().transform(mouseControlPos, new Point2D.Double()); - - ISnapAdvisor snapAdvisor = getHint(DiagramHints.SNAP_ADVISOR); - if (snapAdvisor != null) - snapAdvisor.snap(mouseCanvasPos); - - // Clicked on a terminal .. End connection, End mode - if (endElement != null) { - attachToBranchPoint(); - createValveIfNecessary(mouseCanvasPos); - commitDiagram(); - update(); - remove(); // Remove ConnectTool participant - return true; - } else { - if (isCurrentConnectionFlow() && startElement != null && startTerminal != null) { - - IConnectionAdvisor advisor = diagram.getHint(DiagramHints.CONNECTION_ADVISOR); - Object canConnect = canConnect(advisor, null, null); - if (canConnect != null) - connection.setHint(ElementHints.KEY_CONNECTION_TYPE, canConnect); - - createValveIfNecessary(mouseCanvasPos); - - // Finish connection in thin air only if the - // connection was started from a valid terminal. - if (createFlags) { - // Replace the last branch point with a flag. - IElement lastBp = controlPoints.pollLast(); - topology.disconnect(edges.peekLast(), EdgeEnd.End, lastBp, ElementUtils.getSingleTerminal(lastBp)); - connectionHandler.removeBranchPoint(connection, lastBp); - - IElement end = createFlag(mouseCanvasPos, EdgeEnd.End); - ArrayList terminals = new ArrayList(); - ElementUtils.getTerminals(end, terminals, false); - Terminal endTerminal = terminals.get(0); - topology.connect(edges.peekLast(), EdgeEnd.End, end, endTerminal); - } - - DiagramUtils.validateAndFix(ghostDiagram, getContext()); - commitDiagram(); - update(); - remove(); // Remove ConnectTool participant - return true; - } - } - - } - } - - // Don't let any events slip to creator participant - if (e instanceof MouseEvent) { - MouseEvent me = (MouseEvent) e; - return me.mouseId == mouseId; - } - - return false; - } - - - - - private boolean isCurrentConnectionFlow() { - // Could this check be done better? - Boolean isFlow = false; - try { - isFlow = SimanticsUI.getSession().syncRequest(new Read() { - - @Override - public Boolean perform(ReadGraph graph) - throws DatabaseException { - SysdynResource sr = SysdynResource.getInstance(graph); - StaticObjectAdapter soa = connectionClass.getSingleItem(StaticObjectAdapter.class); - Resource conntype = soa.adapt(Resource.class); - return conntype.equals(sr.FlowConnection); - } - - }); - } catch (DatabaseException e1) { - e1.printStackTrace(); - } - return isFlow; - } - - - private void attachToBranchPoint() { - DiagramUtils.inDiagramTransaction(diagram, TransactionType.WRITE, new Runnable() { - - @Override - public void run() { - - // We are attaching to a branch point - some reordering is needed! - if (endElement.getElementClass().containsClass(BranchPoint.class)) { - - // Scrap everything we have and reconstruct - DiagramMutator mutator = diagram.getHint(DiagramHints.KEY_MUTATOR); - mutator.clear(); - - HashMap bps = new HashMap(); - for(IElement bp : controlPoints) { - IElement nbp = mutator.newBranchPoint(endElement); - Point2D pos = ElementUtils.getPos(bp); - ElementUtils.setPos(nbp, pos); - bps.put(bp, nbp); - } - - for(IElement edge : edges) { - IElement newEdge = mutator.newEdge(endElement); - - // Disconnect and remove old edge - Connection b = mutator.getConnection(edge, EdgeEnd.Begin); - Connection e = mutator.getConnection(edge, EdgeEnd.End); - - IElement mappedB = bps.get(b.node) != null ? bps.get(b.node) : b.node; - IElement mappedE = bps.get(e.node) != null ? bps.get(e.node) : e.node; - - mutator.connect(newEdge, b.end, mappedB, b.terminal); - mutator.connect(newEdge, e.end, mappedE, e.terminal); - } - } - } - }); - } - - private void commitDiagram() { - DiagramMutator mutator = diagram.getHint(DiagramHints.KEY_MUTATOR); - mutator.commit(); - } - - FlagClass.Type endToFlagType(EdgeEnd end) { - switch (end) { - case Begin: return FlagClass.Type.In; - case End: return FlagClass.Type.Out; - default: throw new IllegalArgumentException("unrecognized edge end: " + end); - } - } - - IElement createConnection(ElementClass element) { - DiagramMutator mutator = diagram.getHint(DiagramHints.KEY_MUTATOR); - return mutator.newConnection(element); - } - - IElement createFlag(Point2D pos, EdgeEnd connectionEnd) { - - DiagramMutator mutator = diagram.getHint(DiagramHints.KEY_MUTATOR); - IElement e = mutator.newFlag(); - - // FlagHandler fh = e.getElementClass().getSingleItem(FlagHandler.class); - // fh.setType(e, endToFlagType(connectionEnd)); - //fh.setMode(e, FlagClass.Mode.Internal); - ElementUtils.setPos(e, pos); - - ghostDiagram.addElement(e); - - return e; - - } - - IElement createValve(Point2D pos) { - DiagramMutator mutator = diagram.getHint(DiagramHints.KEY_MUTATOR); - - ElementClass ec = null; - try { - Resource r = SimanticsUI - .getSession() - .syncRequest( - Queries - .resource(SysdynResource.URIs.ValveSymbol)); - ec = SimanticsUI.getSession().syncRequest( - DiagramRequests.getElementClass(r, diagram)); - - } catch (DatabaseException e1) { - e1.printStackTrace(); - } - - if(ec == null) return null; - - IElement e = mutator.newElement(ec); - - ElementUtils.setPos(e, pos); - - ghostDiagram.addElement(e); - - return e; - } - - /** - * Try to get a connection element from a connection part element. - * - * @param e a potential connection part - * @return null if e is not part a part of a connection. - */ - IElement getConnectionFromPart(IElement e) { - Parent parent = e.getElementClass().getAtMostOneItemOfClass(Parent.class); - if (parent == null) - return null; - IElement p = parent.getParent(e); - if (!p.getElementClass().containsClass(ConnectionHandler.class)) - return null; - return p; - } - - IElement createBranchPointOrFlag(Point2D pos, EdgeEnd connectionEnd) { - IElement e = null; - if (createFlags) { - e = createFlag(pos, connectionEnd); - } else { - e = connectionHandler.newBranchPoint(connection); - ElementUtils.setPos(e, pos); - } - return e; - } - - boolean routePointsAllowed() { - return Boolean.TRUE.equals(diagram.getHint(DiagramHints.KEY_ALLOW_ROUTE_POINTS)); - } - - Object canConnect(final IConnectionAdvisor advisor, final IElement endElement, final Terminal endTerminal) { - if(startElement.equals(endElement)) return null; - - if(endElement == null && endTerminal == null) - return advisor.canBeConnected(null, startElement, startTerminal, endElement, endTerminal); - - try { - return SimanticsUI.getSession().syncRequest(new Read() { - - @Override - public Object perform(ReadGraph g) throws DatabaseException { - - // Checking if connection type can be connected to the intended endElement - SysdynResource sr = SysdynResource.getInstance(g); - if(connection != null) { - StaticObjectAdapter soa = connectionClass.getSingleItem(StaticObjectAdapter.class); - Resource conntype = soa.adapt(Resource.class); - soa = endElement.getElementClass().getSingleItem(StaticObjectAdapter.class); - Resource end = soa.adapt(Resource.class); - if(conntype.equals(sr.DependencyConnection)) { - if(end.equals(sr.CloudSymbol)) return null; - soa = startElement.getElementClass().getSingleItem(StaticObjectAdapter.class); - Resource start = soa.adapt(Resource.class); - if(g.isInheritedFrom(start, sr.ModuleSymbol) && !end.equals(sr.InputSymbol)) return null; - } else if (conntype.equals(sr.FlowConnection)) { - if(!(end.equals(sr.StockSymbol) || end.equals(sr.ValveSymbol) || end.equals(sr.CloudSymbol))) return null; - } else { - return null; - } - } - - - if (advisor == null) - return Boolean.TRUE; - return advisor.canBeConnected(g, startElement, startTerminal, endElement, endTerminal); - } - - }); - } catch(DatabaseException e) { - e.printStackTrace(); - return null; - } - - } - - -} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/participant/PointerInteractor.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/participant/PointerInteractor.java index 633b47ac..58860991 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/participant/PointerInteractor.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/participant/PointerInteractor.java @@ -16,6 +16,7 @@ import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.List; +import org.simantics.diagram.participant.ConnectTool2; import org.simantics.g2d.canvas.Hints; import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency; import org.simantics.g2d.canvas.impl.DependencyReflection.Reference; @@ -26,17 +27,19 @@ import org.simantics.g2d.diagram.participant.Selection; import org.simantics.g2d.diagram.participant.TerminalPainter; import org.simantics.g2d.diagram.participant.pointertool.TerminalUtil; import org.simantics.g2d.diagram.participant.pointertool.TerminalUtil.TerminalInfo; -import org.simantics.g2d.element.ElementClass; +import org.simantics.g2d.element.ElementClasses; import org.simantics.g2d.element.IElement; import org.simantics.g2d.element.IElementClassProvider; -import org.simantics.g2d.event.MouseEvent; import org.simantics.g2d.event.EventHandlerReflection.EventHandler; +import org.simantics.g2d.event.MouseEvent; import org.simantics.g2d.event.MouseEvent.MouseButtonPressedEvent; import org.simantics.g2d.participant.KeyUtil; import org.simantics.g2d.participant.MouseUtil; import org.simantics.g2d.participant.TransformUtil; import org.simantics.g2d.routing.RouterFactory; import org.simantics.g2d.utils.GeometryUtils; +import org.simantics.sysdyn.ui.editor.participant.SysdynElementClassProviders.ISysdynElementClassProvider; +import org.simantics.sysdyn.ui.editor.routing.DependencyRouter; import org.simantics.sysdyn.ui.elements2.AuxiliaryFactory; import org.simantics.sysdyn.ui.elements2.CloudFactory; import org.simantics.sysdyn.ui.elements2.InputFactory; @@ -66,7 +69,7 @@ public class PointerInteractor extends org.simantics.g2d.diagram.participant.poi @Dependency PickContext pickContext; @Dependency MouseUtil mice; @Reference TerminalPainter terminalPainter; - + public PointerInteractor(boolean clickSelect, boolean boxSelect, boolean dragElement, boolean dndDragElement, boolean connect, boolean doubleClickEdit, IElementClassProvider newConnectionClassProvider) { super(clickSelect, boxSelect, dragElement, dndDragElement, connect, doubleClickEdit, newConnectionClassProvider); } @@ -85,29 +88,36 @@ public class PointerInteractor extends org.simantics.g2d.diagram.participant.poi if((me.stateMask & MouseEvent.ALT_MASK) == 0) return false; if(elementClassProvider != null) { - ConnectTool bsi = null; + ConnectTool2 bsi = null; if (ti != null) { IElement terminalElement = ti.e; - ElementClass connectionClass = null; +// ElementClass connectionClass = null; if( me.button == MouseEvent.LEFT_BUTTON) { if(terminalElement.getElementClass().getId().equals(CloudFactory.class.getSimpleName())) return false; - diagram.setHint(DiagramHints.ROUTE_ALGORITHM, RouterFactory.create(false, false)); - connectionClass = elementClassProvider.get(ConnectionClasses.DEPENDENCY); +// diagram.setHint(DiagramHints.ROUTE_ALGORITHM, RouterFactory.create(false, false)); + diagram.setHint(DiagramHints.ROUTE_ALGORITHM, new DependencyRouter()); +// connectionClass = elementClassProvider.get(ConnectionClasses.DEPENDENCY); + ISysdynElementClassProvider secp = (ISysdynElementClassProvider)elementClassProvider; + secp.put(ElementClasses.CONNECTION, elementClassProvider.get(ConnectionClasses.DEPENDENCY)); } else if (me.button == MouseEvent.RIGHT_BUTTON) { String id = terminalElement.getElementClass().getId(); if(id.equals(AuxiliaryFactory.class.getSimpleName()) || id.equals(InputFactory.class.getSimpleName()) || id.equals(ModuleFactory.class.getSimpleName())) return false; - diagram.setHint(DiagramHints.ROUTE_ALGORITHM, RouterFactory.create(true, true)); - connectionClass = elementClassProvider.get(ConnectionClasses.FLOW); + diagram.setHint(DiagramHints.ROUTE_ALGORITHM, RouterFactory.create(true, false)); +// connectionClass = elementClassProvider.get(ConnectionClasses.FLOW); + ISysdynElementClassProvider secp = (ISysdynElementClassProvider)elementClassProvider; + secp.put(ElementClasses.CONNECTION, elementClassProvider.get(ConnectionClasses.FLOW)); + } IConnectionAdvisor advisor = diagram.getHint(DiagramHints.CONNECTION_ADVISOR); if (advisor == null || (advisor != null && advisor.canBeginConnection(null, ti.e, ti.t))) { - bsi = new ConnectTool(diagram, connectionClass, ti.e, ti.t, me.mouseId, curCanvasPos); +// bsi = new ConnectTool(diagram, connectionClass, ti.e, ti.t, me.mouseId, curCanvasPos); + bsi = new SysdynConnectTool(ti, me.mouseId, curCanvasPos); } } else if (me.button == MouseEvent.RIGHT_BUTTON) { @@ -119,7 +129,10 @@ public class PointerInteractor extends org.simantics.g2d.diagram.participant.poi */ // Start connection out of thin air, without a terminal. diagram.setHint(DiagramHints.ROUTE_ALGORITHM, RouterFactory.create(true, true)); - bsi = new ConnectTool(diagram, elementClassProvider.get(ConnectionClasses.FLOW), null, null, me.mouseId, curCanvasPos); +// bsi = new ConnectTool(diagram, elementClassProvider.get(ConnectionClasses.FLOW), null, null, me.mouseId, curCanvasPos); + ISysdynElementClassProvider secp = (ISysdynElementClassProvider)elementClassProvider; + secp.put(ElementClasses.CONNECTION, elementClassProvider.get(ConnectionClasses.FLOW)); + bsi = new SysdynConnectTool(null, me.mouseId, curCanvasPos); // FIXME } if (bsi != null) { diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/participant/SysdynConnectTool.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/participant/SysdynConnectTool.java new file mode 100644 index 00000000..d1a98dfe --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/participant/SysdynConnectTool.java @@ -0,0 +1,398 @@ +package org.simantics.sysdyn.ui.editor.participant; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.geom.AffineTransform; +import java.awt.geom.Path2D; +import java.awt.geom.Point2D; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Deque; +import java.util.Iterator; +import java.util.List; + +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.UndoContext; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.UndoWriteRequest; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.request.Read; +import org.simantics.diagram.participant.ConnectTool2; +import org.simantics.diagram.participant.ConnectionBuilder; +import org.simantics.diagram.participant.ControlPoint; +import org.simantics.diagram.ui.DiagramModelHints; +import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit; +import org.simantics.g2d.connection.IConnectionAdvisor; +import org.simantics.g2d.diagram.DiagramHints; +import org.simantics.g2d.diagram.DiagramUtils; +import org.simantics.g2d.diagram.handler.Topology.Terminal; +import org.simantics.g2d.diagram.participant.pointertool.TerminalUtil; +import org.simantics.g2d.diagram.participant.pointertool.TerminalUtil.TerminalInfo; +import org.simantics.g2d.element.ElementClass; +import org.simantics.g2d.element.ElementClasses; +import org.simantics.g2d.element.ElementUtils; +import org.simantics.g2d.element.IElement; +import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd; +import org.simantics.g2d.element.handler.impl.StaticObjectAdapter; +import org.simantics.g2d.element.impl.Element; +import org.simantics.g2d.elementclass.BranchPoint; +import org.simantics.g2d.elementclass.BranchPointClass; +import org.simantics.g2d.elementclass.FlagClass; +import org.simantics.g2d.event.MouseEvent.MouseButtonEvent; +import org.simantics.g2d.event.MouseEvent.MouseButtonPressedEvent; +import org.simantics.g2d.event.MouseEvent.MouseMovedEvent; +import org.simantics.g2d.routing.ConnectionDirectionUtil; +import org.simantics.g2d.routing.Constants; +import org.simantics.g2d.routing.IConnection; +import org.simantics.g2d.routing.IRouter2; +import org.simantics.g2d.routing.TrivialRouter2; +import org.simantics.g2d.snap.ISnapAdvisor; +import org.simantics.g2d.utils.GeometryUtils; +import org.simantics.scenegraph.g2d.G2DParentNode; +import org.simantics.scenegraph.g2d.nodes.ShapeNode; +import org.simantics.structural2.modelingRules.ConnectionJudgement; +import org.simantics.sysdyn.SysdynResource; +import org.simantics.sysdyn.ui.elements2.connections.ConnectionClasses; +import org.simantics.ui.SimanticsUI; +import org.simantics.utils.datastructures.Callback; +import org.simantics.utils.ui.ExceptionUtils; + +public class SysdynConnectTool extends ConnectTool2 { + + public SysdynConnectTool(TerminalInfo startTerminal, int mouseId, + Point2D startCanvasPos) { + super(startTerminal, mouseId, startCanvasPos); + } + + + @SGInit + public void initSG(G2DParentNode parent) { + ghostNode = parent.addNode(G2DParentNode.class); + ghostNode.setZIndex(PAINT_PRIORITY); + + ShapeNode pathNode = ghostNode.getOrCreateNode("path", ShapeNode.class); + pathNode.setColor(Color.BLACK); + pathNode.setStroke(new BasicStroke(1f)); + pathNode.setScaleStroke(true); + pathNode.setZIndex(0); + + G2DParentNode points = ghostNode.getOrCreateNode("points", G2DParentNode.class); + points.setZIndex(1); + + updateSG(); + } + + + protected TerminalInfo createFlag(EdgeEnd connectionEnd) { + ElementClass flagClass = elementClassProvider.get(ElementClasses.FLAG); + IElement e = Element.spawnNew(flagClass); + + e.setHint(FlagClass.KEY_FLAG_TYPE, endToFlagType(connectionEnd)); + e.setHint(FlagClass.KEY_FLAG_MODE, FlagClass.Mode.Internal); + + TerminalInfo ti = new TerminalInfo(); + ti.e = e; + + // start: this part changed to support overlapping terminals + ArrayList terminals = new ArrayList(); + ElementUtils.getTerminals(e, terminals, false); + ti.t = terminals.get(0); + // end + + ti.posElem = TerminalUtil.getTerminalPosOnElement(e, ti.t); + ti.posDia = TerminalUtil.getTerminalPosOnDiagram(e, ti.t); + + return ti; + } + + static class Segment { + public final ControlPoint begin; + public final ControlPoint end; + public Path2D path; + + public Segment(ControlPoint begin, ControlPoint end) { + this.begin = begin; + this.end = end; + } + + @Override + public String toString() { + return "Segment[begin=" + begin + ", end=" + end + ", path=" + path + "]"; + } + } + + private List toSegments(Deque points) { + if (points.isEmpty()) + return Collections.emptyList(); + + List segments = new ArrayList(); + + Iterator it = points.iterator(); + ControlPoint prev = it.next(); + while (it.hasNext()) { + ControlPoint next = it.next(); + segments.add(new Segment(prev, next)); + prev = next; + } + + return segments; + } + + protected void updateSG() { + if (controlPoints.isEmpty()) + return; + + // Route connection segments separately + IRouter2 router = ElementUtils.getHintOrDefault(diagram, DiagramHints.ROUTE_ALGORITHM, TrivialRouter2.INSTANCE); + final List segments = toSegments(controlPoints); + //System.out.println("controlpoints: " + controlPoints); + //System.out.println("segments: " + segments); + router.route(new IConnection() { + @Override + public Collection getSegments() { + return segments; + } + + @Override + public Connector getBegin(Object seg) { + return getConnector(((Segment) seg).begin); + } + + @Override + public Connector getEnd(Object seg) { + return getConnector(((Segment) seg).end); + } + + private Connector getConnector(ControlPoint cp) { + Connector c = new Connector(); + c.x = cp.getPosition().getX(); + c.y = cp.getPosition().getY(); + + TerminalInfo ti = cp.getAttachedTerminal(); + if (ti != null && (ti == startFlag || ti != endFlag)) { + //System.out.println("CP1: " + cp); + c.parentObstacle = DiagramUtils.getObstacleShape(ti.e); + ConnectionDirectionUtil.determineAllowedDirections(c); + } else { + //System.out.println("CP2: " + cp); + c.parentObstacle = GeometryUtils.transformRectangle(AffineTransform.getTranslateInstance(c.x, c.y), + BranchPointClass.DEFAULT_IMAGE2.getBounds()); + c.allowedDirections = toAllowedDirections(cp.getDirection()); + } + + return c; + } + + @Override + public void setPath(Object seg, Path2D path) { + ((Segment) seg).path = (Path2D) path.clone(); + } + + private int toAllowedDirections(BranchPoint.Direction direction) { + switch (direction) { + case Any: + return 0xf; + case Horizontal: + return Constants.EAST_FLAG | Constants.WEST_FLAG; + case Vertical: + return Constants.NORTH_FLAG | Constants.SOUTH_FLAG; + default: + throw new IllegalArgumentException("unrecognized direction: " + direction); + } + } + }); + + // Combine the routed paths + Path2D path = new Path2D.Double(); + for (Segment seg : segments) { + //System.out.println("SEG: " + seg); + if (seg.path != null) + path.append(seg.path.getPathIterator(null), true); + } + + // Create scene graph to visualize the connection. + ShapeNode pathNode = ghostNode.getOrCreateNode("path", ShapeNode.class); + pathNode.setShape(path); + + /* + * Removed Points + */ + + setDirty(); + } + + + @Override + protected Object canConnect(final IConnectionAdvisor advisor, final IElement endElement, final Terminal endTerminal) { + final IElement se = startTerminal != null ? startTerminal.e : startFlag.e; + final Terminal st = startTerminal != null ? startTerminal.t : null; + + if(se.equals(endElement)) return null; + + if(endElement == null && endTerminal == null) + return advisor.canBeConnected(null, se, st, endElement, endTerminal); + + try { + return SimanticsUI.getSession().syncRequest(new Read() { + + @Override + public Object perform(ReadGraph g) throws DatabaseException { + + // Checking if connection type can be connected to the intended endElement + SysdynResource sr = SysdynResource.getInstance(g); + StaticObjectAdapter soa = endElement.getElementClass().getSingleItem(StaticObjectAdapter.class); + Resource end = soa.adapt(Resource.class); + ElementClass dependency = elementClassProvider.get(ConnectionClasses.DEPENDENCY); + ElementClass flow = elementClassProvider.get(ConnectionClasses.FLOW); + ElementClass currentConnection = elementClassProvider.get(ElementClasses.CONNECTION); + if(currentConnection.equals(dependency)) { + if(end.equals(sr.CloudSymbol)) return null; + soa = se.getElementClass().getSingleItem(StaticObjectAdapter.class); + Resource start = soa.adapt(Resource.class); + if(g.isInheritedFrom(start, sr.ModuleSymbol) && !end.equals(sr.InputSymbol)) return null; + if(end.equals(sr.StockSymbol)) return null; + } else if (currentConnection.equals(flow)) { + if(!(end.equals(sr.StockSymbol) || end.equals(sr.ValveSymbol) || end.equals(sr.CloudSymbol))) return null; + } else { + return null; + } + + + if (advisor == null) + return Boolean.TRUE; + return advisor.canBeConnected(g, se, st, endElement, endTerminal); + } + + }); + } catch(DatabaseException e) { + e.printStackTrace(); + return null; + } + + } + + protected boolean processMouseMove(MouseMovedEvent me) { + mouseHasMoved = true; + + Point2D mouseControlPos = me.controlPosition; + Point2D mouseCanvasPos = util.controlToCanvas(mouseControlPos, new Point2D.Double()); + + ISnapAdvisor snapAdvisor = getHint(DiagramHints.SNAP_ADVISOR); + if (snapAdvisor != null) + snapAdvisor.snap(mouseCanvasPos); + + // Record last snapped canvas position of mouse. + this.lastMouseCanvasPos.setLocation(mouseCanvasPos); + + if (isEndingInFlag()) { + endFlagNode.setTransform(AffineTransform.getTranslateInstance(mouseCanvasPos.getX(), mouseCanvasPos.getY())); + } + + List tiList = ((org.simantics.sysdyn.ui.editor.participant.PointerInteractor)pi).pickTerminals(me.controlPosition); + TerminalInfo ti = null; + + IConnectionAdvisor advisor = diagram.getHint(DiagramHints.CONNECTION_ADVISOR); + for(TerminalInfo info : tiList) { + if(advisor == null || info.e == null || info.t == null) + continue; + Object canConnect = canConnect(advisor, info.e, info.t); + if (canConnect != null) { + ti = info; + break; + } + } + + if (ti != null && !isStartTerminal(ti.e, ti.t)) { + Object canConnect = canConnect(ti.e, ti.t); + if (canConnect != null) { + connectionJudgment = (ConnectionJudgement) canConnect; + + if (!isEndingInFlag() || !TerminalUtil.isSameTerminal(ti, endTerminal)) { + controlPoints.getLast() + .setPosition(ti.posDia) + .setAttachedToTerminal(ti); + + endTerminal = ti; + System.out.println("ENDTERMINAL MOUSEMOVESTA: " + ti + ", " + ti.t); + } + + // Make sure that we are ending with a flag if ALT is pressed + // and no end terminal is defined. + endWithoutTerminal(lastMouseCanvasPos, shouldEndWithFlag(me)); + + updateSG(); + return false; + } + } + + connectionJudgment = null; + if (isEndTerminalDefined()) { + // CASE: Mouse was previously on top of a valid terminal to end + // the connection. Now the mouse has been moved where there is + // no longer a terminal to connect to. + // + // => Disconnect the last edge segment from the previous + // terminal, mark endElement/endTerminal non-existent + // and connect the disconnected edge to a new branch point. + + controlPoints.getLast() + .setPosition(mouseCanvasPos) + .setDirection(calculateCurrentBranchPointDirection()) + .setAttachedToTerminal(null); + + endTerminal = null; + } else { + // CASE: Mouse was not previously on top of a valid ending + // element terminal. + // + // => Move and re-orient last branch point. + + controlPoints.getLast() + .setPosition(mouseCanvasPos) + .setDirection(calculateCurrentBranchPointDirection()); + } + + // Make sure that we are ending with a flag if ALT is pressed and no end + // terminal is defined. + endWithoutTerminal(lastMouseCanvasPos, shouldEndWithFlag(me)); + + updateSG(); + + return false; + } + + Point2D mousePosition; + + protected boolean processMouseButtonPress(MouseButtonPressedEvent e) { + MouseButtonEvent me = e; + mousePosition = me.controlPosition; + return super.processMouseButtonPress(e); + } + + @Override + protected void createConnection() { + + final UndoContext uctx = diagram.getHint(DiagramModelHints.KEY_UNDO_CONTEXT); + final ConnectionJudgement judgment = this.connectionJudgment; + // ConnectionBuilder changed to SysdynconnectionBuilder to support overlapping terminals and valve creation + final ConnectionBuilder builder = new SysdynConnectionBuilder(this.diagram); + final Deque controlPoints = this.controlPoints; + final TerminalInfo startTerminal = this.startTerminal; + final TerminalInfo endTerminal = this.endTerminal; + + SimanticsUI.getSession().asyncRequest(new UndoWriteRequest(uctx, true) { + @Override + public void perform(WriteGraph graph) throws DatabaseException { + builder.create(graph, judgment, controlPoints, startTerminal, endTerminal); + } + }, new Callback() { + @Override + public void run(DatabaseException parameter) { + if (parameter != null) + ExceptionUtils.logAndShowError(parameter); + } + }); + } +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/participant/SysdynConnectionBuilder.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/participant/SysdynConnectionBuilder.java new file mode 100644 index 00000000..ed27b294 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/participant/SysdynConnectionBuilder.java @@ -0,0 +1,235 @@ +package org.simantics.sysdyn.ui.editor.participant; + +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.util.ArrayList; +import java.util.Deque; +import java.util.Iterator; +import java.util.List; + +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.utils.OrderedSetUtils; +import org.simantics.db.exception.DatabaseException; +import org.simantics.diagram.content.ConnectionUtil; +import org.simantics.diagram.participant.ConnectionBuilder; +import org.simantics.diagram.participant.ControlPoint; +import org.simantics.diagram.stubs.DiagramResource; +import org.simantics.diagram.stubs.G2DResource; +import org.simantics.diagram.ui.DiagramModelHints; +import org.simantics.g2d.diagram.IDiagram; +import org.simantics.g2d.diagram.handler.Topology.Terminal; +import org.simantics.g2d.diagram.participant.pointertool.TerminalUtil; +import org.simantics.g2d.diagram.participant.pointertool.TerminalUtil.TerminalInfo; +import org.simantics.g2d.element.ElementClass; +import org.simantics.g2d.element.ElementClasses; +import org.simantics.g2d.element.ElementHints; +import org.simantics.g2d.element.ElementUtils; +import org.simantics.g2d.element.IElement; +import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd; +import org.simantics.g2d.element.impl.Element; +import org.simantics.g2d.elementclass.FlagClass; +import org.simantics.layer0.Layer0; +import org.simantics.structural2.modelingRules.ConnectionJudgement; +import org.simantics.structural2.modelingRules.IModelingRules; +import org.simantics.sysdyn.ui.elements2.SysdynElementClasses; +import org.simantics.sysdyn.ui.elements2.ValveFactory; +import org.simantics.sysdyn.ui.elements2.connections.ConnectionClasses; +import org.simantics.utils.datastructures.Pair; + +public class SysdynConnectionBuilder extends ConnectionBuilder{ + + public SysdynConnectionBuilder(IDiagram diagram) { + super(diagram); + } + + /** + * @param graph + * @param judgment + * @param controlPoints + * @param startTerminal + * @param endTerminal + * @throws DatabaseException + */ + public void create(WriteGraph graph, ConnectionJudgement judgment, Deque controlPoints, + TerminalInfo startTerminal, TerminalInfo endTerminal) throws DatabaseException { + // If needs a valve, we will create two separate connections + if(needsValve(startTerminal, endTerminal)) { + createValveAndConnections(graph, judgment, controlPoints, startTerminal, endTerminal); + } + // If no need for valve, just call createConnection with false on createValve parameter + else { + createConnection(graph, judgment, controlPoints, startTerminal, endTerminal, false); + } + } + /** + * @param graph + * @param judgment + * @param controlPoints + * @param startTerminal + * @param endTerminal + * @throws DatabaseException + */ + public TerminalInfo createConnection(WriteGraph graph, ConnectionJudgement judgment, Deque controlPoints, + TerminalInfo startTerminal, TerminalInfo endTerminal, boolean createValve) throws DatabaseException { + TerminalInfo newValve = null; + + this.cu = new ConnectionUtil(graph); + + // 1. Get diagram connection to construct. + Resource connection = getOrCreateConnection(graph, startTerminal, endTerminal); + + // 2. Add branch points + List> bps = createBranchPoints(graph, connection, controlPoints); + + // 3. Create edges between branch points. + Resource firstBranchPoint = null; + Resource lastBranchPoint = null; + if (!bps.isEmpty()) { + Iterator> it = bps.iterator(); + Pair prev = it.next(); + firstBranchPoint = prev.second; + while (it.hasNext()) { + Pair next = it.next(); + cu.connect(prev.second, next.second); + prev = next; + } + lastBranchPoint = prev.second; + } + + // 4. Connect start/end terminals if those exist. + // If first/lastBranchPoint != null, connect to those. + // Otherwise connect the start/end terminals together. + Resource startConnector = null; + Resource endConnector = null; + IElement startFlag = null; + IElement endFlag = null; + + if (startTerminal != null) { + startConnector = createConnectorForNode(graph, connection, startTerminal, EdgeEnd.Begin, judgment); + } else if (createFlags) { + startFlag = createFlag(graph, connection, EdgeEnd.Begin, controlPoints.getFirst(), FlagClass.Type.In); + ArrayList terminals = new ArrayList(); + ElementUtils.getTerminals(startFlag, terminals, false); + Terminal st = terminals.get(1); + startConnector = createConnectorForNode(graph, connection, (Resource) ElementUtils.getObject(startFlag), + st, EdgeEnd.Begin, judgment); + } + + if (endTerminal != null) { + endConnector = createConnectorForNode(graph, connection, endTerminal, EdgeEnd.End, judgment); + } else if (createFlags) { + if(createValve) + endFlag = createValveElement(graph, connection, EdgeEnd.End, controlPoints.getLast()); + else + endFlag = createFlag(graph, connection, EdgeEnd.End, controlPoints.getLast(), FlagClass.Type.Out); + ArrayList terminals = new ArrayList(); + ElementUtils.getTerminals(endFlag, terminals, false); + Terminal et = terminals.get(0); + endConnector = createConnectorForNode(graph, connection, (Resource) ElementUtils.getObject(endFlag), + et, EdgeEnd.End, judgment); + + if(createValve) { + newValve = new TerminalInfo(); + newValve.e = endFlag; + newValve.t = terminals.get(1); + newValve.posElem = TerminalUtil.getTerminalPosOnElement(endFlag, newValve.t); + newValve.posDia = TerminalUtil.getTerminalPosOnDiagram(endFlag, newValve.t); + } + } + + if (firstBranchPoint == null || lastBranchPoint == null) { + cu.connect(startConnector, endConnector); + } else { + cu.connect(startConnector, firstBranchPoint); + cu.connect(lastBranchPoint, endConnector); + } + + // 5. Finally, set connection type according to modelling rules + IModelingRules modelingRules = diagram.getHint(DiagramModelHints.KEY_MODELING_RULES); + if (judgment.connectionType != null && modelingRules != null) { + modelingRules.setConnectionType(graph, connection, judgment.connectionType); + } + + this.cu = null; + return newValve; + } + + + /** + * @param graph + * @param connection + * @param end + * @param cp + * @param type + * @return an element describing the new created flag resource + * @throws DatabaseException + */ + public IElement createValveElement(WriteGraph graph, Resource connection, EdgeEnd end, ControlPoint cp) throws DatabaseException { + ElementClass valveClass = elementClassProvider.get(SysdynElementClasses.VALVE); + IElement valveElement = Element.spawnNew(valveClass); + Resource valveClassResource = ElementUtils.checkedAdapt(valveClass, Resource.class); + + Layer0 L0 = Layer0.getInstance(graph); + G2DResource G2D = G2DResource.getInstance(graph); + DiagramResource DIA = DiagramResource.getInstance(graph); + + Resource valve = graph.newResource(); + graph.claim(valve, L0.InstanceOf, null, valveClassResource); + valveElement.setHint(ElementHints.KEY_OBJECT, valve); + + OrderedSetUtils.add(graph, diagramResource, valve); + + AffineTransform at = AffineTransform.getTranslateInstance(cp.getPosition().getX(), cp.getPosition().getY()); + valveElement.setHint(ElementHints.KEY_TRANSFORM, at); + double[] matrix = new double[6]; + at.getMatrix(matrix); + graph.claimLiteral(valve, DIA.HasTransform, G2D.Transform, matrix); + + // Put the element on all the currently active layers if possible. + if (layerManager != null) { + layerManager.removeFromAllLayers(graph, valve); + layerManager.putElementOnVisibleLayers(diagram, graph, valve); + } + + return valveElement; + } + + private boolean needsValve(TerminalInfo startTerminal, TerminalInfo endTerminal) { + if (!elementClassProvider.get(ElementClasses.CONNECTION) + .equals(elementClassProvider.get(ConnectionClasses.FLOW))) + return false; + if(startTerminal != null && startTerminal.e != null && startTerminal.e.getElementClass().getId().equals(ValveFactory.class.getSimpleName())) { + return false; + } else if(endTerminal != null && endTerminal.e != null && endTerminal.e.getElementClass().getId().equals(ValveFactory.class.getSimpleName())) { + return false; + } + return true; + } + + private void createValveAndConnections(WriteGraph graph, ConnectionJudgement judgment, Deque controlPoints, + TerminalInfo startTerminal, TerminalInfo endTerminal) throws DatabaseException { + + ControlPoint cpfirst = controlPoints.getFirst(); + ControlPoint cplast = controlPoints.getLast(); + + // Set the position in the middle of the route + double startX = cpfirst.getPosition().getX(); + double startY = cpfirst.getPosition().getY(); + double x = cplast.getPosition().getX(); + double y = cplast.getPosition().getY(); + Point2D pos = new Point2D.Double(startX - (startX - x) / 2, startY - (startY - y) / 2); + + // Replace the last control point with the control point in the middle + controlPoints.getLast().setPosition(pos); + + // Create a connection to a new valve and get the new valve + TerminalInfo newValve = createConnection(graph, judgment, controlPoints, startTerminal, null, true); + + // Replace the last control point with the original control point + controlPoints.getLast().setPosition(x, y); + + // Create a connection starting from the new valve + createConnection(graph, judgment, controlPoints, newValve, endTerminal, false); + } +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/participant/SysdynElementClassProviders.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/participant/SysdynElementClassProviders.java new file mode 100644 index 00000000..93c5675c --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/participant/SysdynElementClassProviders.java @@ -0,0 +1,69 @@ +package org.simantics.sysdyn.ui.editor.participant; + +import java.util.HashMap; +import java.util.Map; + +import org.simantics.g2d.element.ElementClass; +import org.simantics.g2d.element.ElementClassProviders; +import org.simantics.g2d.element.IElementClassProvider; + +public class SysdynElementClassProviders extends ElementClassProviders { + + /** + * Create an element class provider that based on the specified map. The + * provider will directly access the map with the received keys. The + * argument map will be copied. + * + * @param map the map to use for element class provision + * @return null if there is no provider for the specified key + */ + public static IElementClassProvider mappedProvider(Map map) { + // Copy the map as a safety measure + final Map copy = new HashMap(map); + return new ISysdynElementClassProvider() { + @Override + public ElementClass get(Object key) { + return copy.get(key); + } + + @Override + public void put(Object key, ElementClass value) { + copy.put(key, value); + } + + }; + } + + /** + * Does the same as {@link #mappedProvider(Map)}, the map is simply provided + * differently. The specified array must contain + * [key, ElementClass, key, ElementClass, ...]. + * + * @param map the map to use for element class provision + * @return null if there is no provider for the specified key + */ + public static IElementClassProvider mappedProvider(Object... keyClassPairs) { + if (keyClassPairs.length % 2 != 0) + throw new IllegalArgumentException(); + Map map = new HashMap(); + int n = keyClassPairs.length / 2; + for (int i = 0; i < n; ++i) { + Object key = keyClassPairs[i * 2]; + Object elementClass = keyClassPairs[i*2+1]; + if (!(elementClass instanceof ElementClass)) + throw new IllegalArgumentException("not an ElementClass instance: " + elementClass); + map.put(key, (ElementClass) elementClass); + } + return mappedProvider(map); + } + + + public interface ISysdynElementClassProvider extends IElementClassProvider { + + /** + * Update a value in an IElementClassProvider + */ + void put(Object key, ElementClass value); + + } +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/routing/DependencyRouter.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/routing/DependencyRouter.java new file mode 100644 index 00000000..24e527a9 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/routing/DependencyRouter.java @@ -0,0 +1,34 @@ +package org.simantics.sysdyn.ui.editor.routing; + +import java.awt.geom.Arc2D; +import java.awt.geom.Path2D; + +import org.simantics.g2d.routing.IConnection; +import org.simantics.g2d.routing.IConnection.Connector; +import org.simantics.g2d.routing.IRouter2; +import org.simantics.sysdyn.ui.elements2.connections.Dependencies; +import org.simantics.utils.datastructures.Pair; + +public class DependencyRouter implements IRouter2 { + + @Override + public void route(IConnection connection) { + if(connection.getSegments().isEmpty()) + return; + Object seg = connection.getSegments().iterator().next(); + Connector begin = connection.getBegin(seg); + Connector end = connection.getEnd(seg); + + Pair shapes = new Pair(new Arc2D.Double(), new Path2D.Double()); + Dependencies.createArrowShape(shapes, + begin.parentObstacle, + end.parentObstacle, + 0.1); + + Path2D path = new Path2D.Double(); + path.append(shapes.first, false); + path.append(shapes.second, false); + connection.setPath(seg, path); + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynElementClasses.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynElementClasses.java new file mode 100644 index 00000000..6dc0364f --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynElementClasses.java @@ -0,0 +1,8 @@ +package org.simantics.sysdyn.ui.elements2; + +public class SysdynElementClasses { + + public static final Object VALVE = new Object() { + public String toString() { return "VALVE"; } + }; +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ExpressionUtils.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ExpressionUtils.java index f52455bb..097ff3cb 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ExpressionUtils.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ExpressionUtils.java @@ -80,9 +80,6 @@ public class ExpressionUtils { */ static public void validateExpressionFields(final Resource variable, IExpression expression, Table variableTable) { - TableItem[] connectedVariables = variableTable.getItems(); - - Resource configuration = null; try { configuration = SimanticsUI.getSession().syncRequest(new Read() { @@ -188,12 +185,16 @@ public class ExpressionUtils { // Check that the variables that exist have connections and the connected variables have references in the expressions if(!(expression instanceof StockExpression)) { - for(TableItem ti : connectedVariables) { - if(!variables.contains(ti.getText())) { - ti.setForeground(new Color(ti.getDisplay(), 255, 0, 0)); - } else { - ti.setForeground(new Color(ti.getDisplay(), 0, 0, 0)); - variables.remove(ti.getText()); + + if(variableTable != null && !variableTable.isDisposed()) { + TableItem[] connectedVariables = variableTable.getItems(); + for(TableItem ti : connectedVariables) { + if(!variables.contains(ti.getText())) { + ti.setForeground(new Color(ti.getDisplay(), 255, 0, 0)); + } else { + ti.setForeground(new Color(ti.getDisplay(), 0, 0, 0)); + variables.remove(ti.getText()); + } } } -- 2.47.1