From 5439b365a9fd62d80ce6d586e9060096074b4bfd Mon Sep 17 00:00:00 2001 From: lempinen Date: Tue, 1 Nov 2011 09:56:43 +0000 Subject: [PATCH] First take on new route graph for flows git-svn-id: https://www.simantics.org/svn/simantics/sysdyn/trunk@23141 ac1ea38d-2e2b-0410-8846-a27921b304fc --- org.simantics.sysdyn.ontology/graph.tg | Bin 71254 -> 71717 bytes .../graph/Sysdyn.pgraph | 11 +- .../org/simantics/sysdyn/SysdynResource.java | 15 + org.simantics.sysdyn.ui/adapters.xml | 2 +- .../sysdyn/ui/editor/DiagramViewer.java | 4 +- .../editor/participant/PointerInteractor.java | 12 +- .../sysdyn/ui/editor/routing/FlowRouter.java | 162 ++++- .../sysdyn/ui/elements2/Orientation.java | 35 + .../ui/elements2/SysdynElementHints.java | 3 +- .../ui/elements2/SysdynElementUtils.java | 30 +- .../sysdyn/ui/elements2/ValveFactory.java | 36 +- .../connections/FlowArrowLineStyle.java | 169 +++++ ...ory.java => FlowConnectionFactoryOld.java} | 2 +- .../connections/FlowConnectionStyle.java | 143 +++++ ...owEdgeClass.java => FlowEdgeClassOld.java} | 5 +- ...geFactory.java => FlowEdgeFactoryOld.java} | 4 +- ...FlowEdgeNode.java => FlowEdgeNodeOld.java} | 2 +- .../{FlowNode.java => FlowNodeOld.java} | 2 +- .../RouteFlowConnectionFactory.java | 606 ++++++++++++++++++ .../connections/RouteFlowEdgeClass.java | 88 +++ .../connections/RouteFlowEdgeFactory.java | 38 ++ .../ui/properties/VariableInformationTab.java | 6 +- .../widgets/ValveOrientationGroup.java | 136 ++++ .../utils/SheetFormatUtils.java | 2 +- 24 files changed, 1482 insertions(+), 31 deletions(-) create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/Orientation.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowArrowLineStyle.java rename org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/{FlowConnectionFactory.java => FlowConnectionFactoryOld.java} (95%) create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowConnectionStyle.java rename org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/{FlowEdgeClass.java => FlowEdgeClassOld.java} (95%) rename org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/{FlowEdgeFactory.java => FlowEdgeFactoryOld.java} (89%) rename org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/{FlowEdgeNode.java => FlowEdgeNodeOld.java} (92%) rename org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/{FlowNode.java => FlowNodeOld.java} (94%) create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/RouteFlowConnectionFactory.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/RouteFlowEdgeClass.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/RouteFlowEdgeFactory.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/ValveOrientationGroup.java diff --git a/org.simantics.sysdyn.ontology/graph.tg b/org.simantics.sysdyn.ontology/graph.tg index 358bb901b8f779559413619d2561ef0ebbdb95ef..1d3d856bc069ef1d6a7a6853e5e7435103a9011e 100644 GIT binary patch literal 71717 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

Ba6O^j#sf~p>os=P^XWJ= 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 - none 0 fill 1 + none 0 fill 1 \ No newline at end of file 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 8e697c51..e6c5107a 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 @@ -60,6 +60,7 @@ 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.RouteFlowEdgeClass; import org.simantics.sysdyn.ui.elements2.connections.SysdynConnectionClass; import org.simantics.sysdyn.ui.properties.SysdynPropertyPage; import org.simantics.ui.workbench.IPropertyPage; @@ -115,10 +116,11 @@ public class DiagramViewer extends org.simantics.modeling.ui.diagramEditor.Diagr protected IElementClassProvider createElementClassProvider(ReadGraph graph) { SysdynResource sr = SysdynResource.getInstance(graph); ElementClass dependencyClass = SysdynConnectionClass.CLASS.newClassWith(new StaticObjectAdapter(sr.DependencyConnection)); + ElementClass flowClass = RouteFlowEdgeClass.FLOW_CLASS.newClassWith(new StaticObjectAdapter(sr.FlowConnection)); 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.FLOW, flowClass, 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/participant/PointerInteractor.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/participant/PointerInteractor.java index 8af06aac..9f0b53db 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 @@ -34,13 +34,13 @@ import org.simantics.g2d.element.IElementClassProvider; import org.simantics.g2d.participant.KeyUtil; import org.simantics.g2d.participant.MouseUtil; import org.simantics.g2d.participant.TransformUtil; +import org.simantics.g2d.routing.algorithm2.Router4; import org.simantics.g2d.utils.GeometryUtils; -import org.simantics.scenegraph.g2d.events.MouseEvent; import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler; +import org.simantics.scenegraph.g2d.events.MouseEvent; import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonPressedEvent; import org.simantics.sysdyn.ui.editor.participant.SysdynElementClassProviders.ISysdynElementClassProvider; import org.simantics.sysdyn.ui.editor.routing.DependencyRouter; -import org.simantics.sysdyn.ui.editor.routing.FlowRouter; import org.simantics.sysdyn.ui.elements2.AuxiliaryFactory; import org.simantics.sysdyn.ui.elements2.CloudFactory; import org.simantics.sysdyn.ui.elements2.InputFactory; @@ -62,7 +62,7 @@ import org.simantics.sysdyn.ui.elements2.connections.ConnectionClasses; * * @author Toni Kalajainen */ -public class PointerInteractor extends org.simantics.g2d.diagram.participant.pointertool.PointerInteractor { +public class PointerInteractor extends org.simantics.diagram.participant.PointerInteractor2 { @Dependency Selection selection; @Dependency KeyUtil keys; @@ -108,7 +108,8 @@ public class PointerInteractor extends org.simantics.g2d.diagram.participant.poi if(id.equals(AuxiliaryFactory.class.getSimpleName()) || id.equals(InputFactory.class.getSimpleName()) || id.equals(ModuleFactory.class.getSimpleName())) return false; - diagram.setHint(DiagramHints.ROUTE_ALGORITHM, new FlowRouter(false)); +// diagram.setHint(DiagramHints.ROUTE_ALGORITHM, new FlowRouter(false)); + diagram.setHint(DiagramHints.ROUTE_ALGORITHM, new Router4(false)); diagram.setHint(DiagramHints.KEY_USE_CONNECTION_FLAGS, true); ISysdynElementClassProvider secp = (ISysdynElementClassProvider)elementClassProvider; secp.put(ElementClasses.CONNECTION, elementClassProvider.get(ConnectionClasses.FLOW)); @@ -122,7 +123,8 @@ public class PointerInteractor extends org.simantics.g2d.diagram.participant.poi } else if (me.button == MouseEvent.RIGHT_BUTTON) { // Start connection out of thin air, without a terminal. - diagram.setHint(DiagramHints.ROUTE_ALGORITHM, new FlowRouter(false)); +// diagram.setHint(DiagramHints.ROUTE_ALGORITHM, new FlowRouter(false)); + diagram.setHint(DiagramHints.ROUTE_ALGORITHM, new Router4(false)); diagram.setHint(DiagramHints.KEY_USE_CONNECTION_FLAGS, true); ISysdynElementClassProvider secp = (ISysdynElementClassProvider)elementClassProvider; secp.put(ElementClasses.CONNECTION, elementClassProvider.get(ConnectionClasses.FLOW)); diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/routing/FlowRouter.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/routing/FlowRouter.java index 2e67cedc..f8042337 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/routing/FlowRouter.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/routing/FlowRouter.java @@ -1,5 +1,7 @@ package org.simantics.sysdyn.ui.editor.routing; +import gnu.trove.TObjectIntHashMap; + import java.awt.geom.AffineTransform; import java.awt.geom.Path2D; import java.awt.geom.PathIterator; @@ -10,8 +12,6 @@ import org.simantics.g2d.routing.Constants; import org.simantics.g2d.routing.IConnection; import org.simantics.g2d.routing.IConnection.Connector; import org.simantics.g2d.routing.IRouter2; -import org.simantics.sysdyn.ui.editor.participant.SysdynConnectTool.SysdynConnection; -import org.simantics.sysdyn.ui.elements2.connections.Flows; public class FlowRouter implements IRouter2{ @@ -80,7 +80,8 @@ public class FlowRouter implements IRouter2{ } localRouter.route(); - + return localRouter.path; + /* Path2D completePath = new Path2D.Double(); double[] coordinates = new double[localRouter.points.size()]; @@ -109,8 +110,162 @@ public class FlowRouter implements IRouter2{ // Flows.createLines(completePath, false, beginObstacle, endObstacle); return completePath; + */ + } + + @Override + public void route(IConnection connection) { + Collection segments = connection.getSegments(); + if(segments.size() == 1) + for(Object seg : segments) { + Connector begin = connection.getBegin(seg); + Connector end = connection.getEnd(seg); + + double bestLength = Double.POSITIVE_INFINITY; + Path2D bestPath = null; + + for(int sDir : Constants.POSSIBLE_DIRECTIONS[begin.allowedDirections]) + for(int tDir : Constants.POSSIBLE_DIRECTIONS[end.allowedDirections]) { + Path2D path = route(begin.x, begin.y, sDir, begin.parentObstacle, + end.x, end.y, tDir, end.parentObstacle); + + double length = pathCost(path); + if(length < bestLength) { + bestLength = length; + bestPath = localRouter.path; + } + } + + if(bestPath != null) + connection.setPath(seg, bestPath); + } + else { + TObjectIntHashMap leftSegments = new TObjectIntHashMap(); + TObjectIntHashMap rightSegments = new TObjectIntHashMap(); + TObjectIntHashMap upSegments = new TObjectIntHashMap(); + TObjectIntHashMap downSegments = new TObjectIntHashMap(); + TObjectIntHashMap horizontalCount = new TObjectIntHashMap(); + for(Object seg : segments) { + Connector begin = connection.getBegin(seg); + Connector end = connection.getEnd(seg); + if(begin.x < end.x) { + leftSegments.adjustOrPutValue(end, 1, 1); + rightSegments.adjustOrPutValue(begin, 1, 1); + } + else { + leftSegments.adjustOrPutValue(begin, 1, 1); + rightSegments.adjustOrPutValue(end, 1, 1); + } + if(begin.y < end.y) { + upSegments.adjustOrPutValue(end, 1, 1); + downSegments.adjustOrPutValue(begin, 1, 1); + } + else { + upSegments.adjustOrPutValue(begin, 1, 1); + downSegments.adjustOrPutValue(end, 1, 1); + } + if((begin.allowedDirections & 5) != 0) + horizontalCount.adjustOrPutValue(end, 1, 1); + if((begin.allowedDirections & 10) != 0) + horizontalCount.adjustOrPutValue(end, -1, -1); + if((end.allowedDirections & 5) != 0) + horizontalCount.adjustOrPutValue(begin, 1, 1); + if((end.allowedDirections & 10) != 0) + horizontalCount.adjustOrPutValue(begin, -1, -1); + } + for(Object seg : segments) { + Connector begin = connection.getBegin(seg); + Connector end = connection.getEnd(seg); + int allowedBegin = begin.allowedDirections; + int allowedEnd = end.allowedDirections; + + if(horizontalCount.get(begin) + horizontalCount.get(end) >= 0) { + //System.out.println("horizontal"); + if(begin.x < end.x) { + if(allowedBegin == 0xf) { + if(rightSegments.get(begin) <= 1) + allowedBegin = 1; + else + allowedBegin = 11; + } + if(allowedEnd == 0xf) { + if(leftSegments.get(end) <= 1) + allowedEnd = 4; + else + allowedEnd = 14; + } + } + else { + if(allowedBegin == 0xf) { + if(leftSegments.get(begin) <= 1) + allowedBegin = 4; + else + allowedBegin = 14; + } + if(allowedEnd == 0xf) { + if(rightSegments.get(end) <= 1) + allowedEnd = 1; + else + allowedEnd = 11; + } + } + } + else { + //System.out.println("vertical"); + if(begin.y < end.y) { + if(allowedBegin == 0xf) { + if(downSegments.get(begin) <= 1) + allowedBegin = 2; + else + allowedBegin = 7; + } + if(allowedEnd == 0xf) { + if(upSegments.get(end) <= 1) + allowedEnd = 8; + else + allowedEnd = 13; + } + } + else { + if(allowedBegin == 0xf) { + if(upSegments.get(begin) <= 1) + allowedBegin = 8; + else + allowedBegin = 13; + } + if(allowedEnd == 0xf) { + if(downSegments.get(end) <= 1) + allowedEnd = 2; + else + allowedEnd = 7; + } + } + } + + //System.out.println(allowedBegin + " " + allowedEnd); + + double bestLength = Double.POSITIVE_INFINITY; + Path2D bestPath = null; + + for(int sDir : Constants.POSSIBLE_DIRECTIONS[allowedBegin]) + for(int tDir : Constants.POSSIBLE_DIRECTIONS[allowedEnd]) { + Path2D path = route(begin.x, begin.y, sDir, begin.parentObstacle, + end.x, end.y, tDir, end.parentObstacle); + + double length = pathCost(path); + if(length < bestLength) { + bestLength = length; + bestPath = localRouter.path; + } + } + + if(bestPath != null) + connection.setPath(seg, bestPath); + } + } } + /* @Override public void route(IConnection connection) { @@ -142,6 +297,7 @@ public class FlowRouter implements IRouter2{ connection.setPath(seg, bestPath); } } + */ final static AffineTransform IDENTITY = new AffineTransform(); diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/Orientation.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/Orientation.java new file mode 100644 index 00000000..77733071 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/Orientation.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 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.elements2; + +import org.simantics.g2d.element.IElement; +import org.simantics.g2d.element.handler.ElementHandler; + +public class Orientation implements ElementHandler { + + private static final long serialVersionUID = 958120463924210936L; + + public static final Orientation INSTANCE = new Orientation(); + + public String getOrientation(IElement e) { + return e.getHint(SysdynElementHints.KEY_ORIENTATION); + } + + + public void setOrientation(IElement e, String orientation) { + if (orientation != null) + e.setHint(SysdynElementHints.KEY_ORIENTATION, orientation); + else + e.removeHint(SysdynElementHints.KEY_ORIENTATION); + } +} + diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynElementHints.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynElementHints.java index c7f47e7e..bbe07794 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynElementHints.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynElementHints.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2010 Association for Decentralized Information Management in + * Copyright (c) 2010, 2011 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 @@ -17,5 +17,6 @@ import org.simantics.utils.datastructures.hints.IHintContext.KeyOf; public class SysdynElementHints { public static final Key KEY_INPUT_REFERENCE = new KeyOf(String.class, "INPUT_REFERENCE"); + public static final Key KEY_ORIENTATION = new KeyOf(String.class, "ORIENTATION"); } diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynElementUtils.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynElementUtils.java index ec8a5721..1837728c 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynElementUtils.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/SysdynElementUtils.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2010 Association for Decentralized Information Management in + * Copyright (c) 2010, 2011 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 @@ -15,17 +15,37 @@ import org.simantics.g2d.element.IElement; public class SysdynElementUtils { - + public static void setInputReference(IElement e, String inputReference) { Input i = e.getElementClass().getSingleItem(Input.class); - i.setInputReference(e, inputReference); + if(i != null) + i.setInputReference(e, inputReference); } public static String getInputReference(IElement e) { Input i = e.getElementClass().getSingleItem(Input.class); - return i.getInputReference(e); + if(i != null) + return i.getInputReference(e); + else + return null; + } + + public static void setOrientation(IElement e, String orientation) + { + Orientation o = e.getElementClass().getSingleItem(Orientation.class); + if(o != null) + o.setOrientation(e, orientation); } - + + public static String getOrientation(IElement e) + { + Orientation o = e.getElementClass().getSingleItem(Orientation.class); + if(o != null) + return o.getOrientation(e); + else + return null; + } + } diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/ValveFactory.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/ValveFactory.java index 4824b40e..c154c1dc 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/ValveFactory.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/ValveFactory.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2010 Association for Decentralized Information Management in + * Copyright (c) 2010, 2011 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 @@ -18,8 +18,12 @@ import java.awt.geom.Path2D; import java.awt.geom.Rectangle2D; import java.util.Collection; +import org.simantics.db.ReadGraph; import org.simantics.db.Resource; +import org.simantics.db.exception.DatabaseException; import org.simantics.diagram.elements.TextNode; +import org.simantics.g2d.canvas.ICanvasContext; +import org.simantics.g2d.diagram.IDiagram; import org.simantics.g2d.element.ElementClass; import org.simantics.g2d.element.ElementHints; import org.simantics.g2d.element.ElementUtils; @@ -44,10 +48,11 @@ import org.simantics.g2d.image.impl.ShapeImage; import org.simantics.g2d.utils.Alignment; import org.simantics.scenegraph.g2d.G2DParentNode; import org.simantics.scenegraph.g2d.nodes.ShapeNode; -import org.simantics.utils.datastructures.hints.IHintListener; -import org.simantics.utils.datastructures.hints.IHintObservable; +import org.simantics.sysdyn.SysdynResource; import org.simantics.utils.datastructures.hints.IHintContext.Key; import org.simantics.utils.datastructures.hints.IHintContext.KeyOf; +import org.simantics.utils.datastructures.hints.IHintListener; +import org.simantics.utils.datastructures.hints.IHintObservable; public class ValveFactory extends SysdynElementFactory { @@ -77,10 +82,29 @@ public class ValveFactory extends SysdynElementFactory { HoverImpl.INSTANCE, ValveSceneGraph.INSTANCE, BoundsOutline.INSTANCE, + Orientation.INSTANCE, new WholeElementTerminals(terminals) ).setId(ValveFactory.class.getSimpleName()); } + @Override + public void load(ReadGraph graph, final ICanvasContext canvas, final IDiagram diagram, final Resource element, final IElement e) throws DatabaseException { + super.load(graph, canvas, diagram, element, e); + + SysdynResource sr = SysdynResource.getInstance(graph); + + Resource orientation = graph.getPossibleObject(element, sr.HasValveOrientation); + + String orientationText; + if(orientation != null && sr.Vertical.equals(orientation)) { + orientationText = "Vertical"; + } else { + orientationText = "Horizontal"; + } + SysdynElementUtils.setOrientation(e, orientationText); + + } + /** * @param valveSize * @param rotated true for vertical valve, false @@ -126,7 +150,11 @@ public class ValveFactory extends SysdynElementFactory { node.setStroke(STROKE); node.setScaleStroke(true); node.setColor(Color.BLACK); - node.setShape(createShape(VALVE_SIZE, Boolean.TRUE.equals(e.getHint(KEY_ROTATED)))); + boolean rotated = false; + String orientation = SysdynElementUtils.getOrientation(e); + if(orientation != null && orientation.equals("Vertical")) + rotated = true; + node.setShape(createShape(VALVE_SIZE, Boolean.TRUE.equals(rotated))); Boolean hover = e.getHint(ElementHints.KEY_HOVER); node.setHover(hover != null ? hover : false); diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowArrowLineStyle.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowArrowLineStyle.java new file mode 100644 index 00000000..ca2f0921 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowArrowLineStyle.java @@ -0,0 +1,169 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 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.elements2.connections; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; +import java.awt.geom.Path2D; +import java.awt.geom.Rectangle2D; +import java.io.Serializable; +import java.util.StringTokenizer; + +import org.simantics.diagram.connection.rendering.arrows.ILineEndStyle; + + +/** + * Copied from ArrowLLineEndStyle + */ +public class FlowArrowLineStyle implements ILineEndStyle, Serializable { + + private static final long serialVersionUID = 5348566089660986479L; + + public static enum ArrowType { None, Stroke, Fill } + + public static final double length = 8.0; + public static final double width = 4.0; + public static final double space = 0.0; + + protected ArrowType type; + protected Path2D path; + protected double lineEndLength; + + protected Rectangle2D bounds = new Rectangle2D.Double(); + + public FlowArrowLineStyle(String desc, Rectangle2D bounds) { + this.type = ArrowType.None; + this.lineEndLength = 0.0; + + double l = length; + double w = width; + double s = space; + this.bounds = bounds; + + StringTokenizer tokenizer = new StringTokenizer(desc); + if (tokenizer.hasMoreTokens()) { + String type = tokenizer.nextToken(); + this.type = parseType(type); + + if (tokenizer.hasMoreTokens()) { + String ls = tokenizer.nextToken(); + l = parseSize(ls, length); + + if (tokenizer.hasMoreTokens()) { + String ws = tokenizer.nextToken(); + w = parseSize(ws, width); + + if (tokenizer.hasMoreTokens()) { + String ss = tokenizer.nextToken(); + s = parseSize(ss, space); + } + } + } + if (this.type != ArrowType.None) { + this.path = arrow(l, w, s); + lineEndLength = l+s; + } + } + } + + @Override + public void render(Graphics2D g, double x, double y, int dir) { + if (type == ArrowType.None || path == null) + return; + // Calculate coordinates to the border of the terminal + switch(dir) { + case 0: + x = bounds.getMinX(); + break; + case 1: + y = bounds.getMinY(); + break; + case 2: + x = bounds.getMaxX(); + break; + case 3: + y = bounds.getMaxY(); + break; + default: + return; + } + AffineTransform old = g.getTransform(); + g.translate(x, y); + g.rotate(dir*Math.PI*0.5); + g.setColor(Color.BLACK); + + switch (type) { + case Fill: + g.fill(path); + break; + case Stroke: + g.draw(path); + break; + } + + g.setTransform(old); + } + + @Override + public double getLineEndLength(int direction) { + switch(direction) { + case 0: + lineEndLength = bounds.getWidth() / 2.0; + break; + case 1: + lineEndLength = bounds.getHeight() / 2.0; + break; + case 2: + lineEndLength = bounds.getWidth() / 2.0; + break; + case 3: + lineEndLength = bounds.getHeight() / 2.0; + break; + } + return lineEndLength; + } + + private static Path2D arrow(double length, double width, double space) { + Path2D.Double path = new Path2D.Double(); + path.moveTo(-space, 0); + path.lineTo(-length-space, -width); + path.lineTo(-length-space, +width); + path.closePath(); + return path; + } + + private double parseSize(String size, double defaultValue) { + try { + return Double.parseDouble(size); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + private ArrowType parseType(String type) { + String lower = type.toLowerCase(); + if ("none".equals(lower)) + return ArrowType.None; + if ("stroke".equals(lower)) + return ArrowType.Stroke; + if ("fill".equals(lower)) + return ArrowType.Fill; + throw new IllegalArgumentException("unrecognized arrow type: " + type); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[" + type + ", " + path + "]"; + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowConnectionFactory.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowConnectionFactoryOld.java similarity index 95% rename from org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowConnectionFactory.java rename to org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowConnectionFactoryOld.java index c770d602..40d3e080 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowConnectionFactory.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowConnectionFactoryOld.java @@ -31,7 +31,7 @@ import org.simantics.sysdyn.ui.editor.routing.FlowRouter; * * @author Tuukka Lehtonen */ -public class FlowConnectionFactory extends ElementFactoryAdapter { +public class FlowConnectionFactoryOld extends ElementFactoryAdapter { public static final ElementClass CLASS = SysdynConnectionClass.CLASS; diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowConnectionStyle.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowConnectionStyle.java new file mode 100644 index 00000000..b975e17c --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowConnectionStyle.java @@ -0,0 +1,143 @@ +package org.simantics.sysdyn.ui.elements2.connections; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Stroke; +import java.awt.geom.Path2D; +import java.awt.geom.PathIterator; +import java.io.Serializable; + +import org.simantics.diagram.connection.rendering.ConnectionStyle; + +public class FlowConnectionStyle implements ConnectionStyle, Serializable { + + private static final long serialVersionUID = 2777194644079591357L; + + Color lineColor; + Stroke lineStroke; + + public FlowConnectionStyle(Color lineColor, Stroke lineStroke) { + this.lineColor = lineColor; + this.lineStroke = lineStroke; + } + + @Override + public void drawBranchPoint(Graphics2D g, double x, double y) { + } + + @Override + public void drawLine(Graphics2D g, double x1, double y1, double x2, double y2, boolean isTransient) { + System.out.println("DrawLine"); + } + + @Override + public void drawPath(Graphics2D g, Path2D path, boolean isTransient) { + if (lineColor != null) + g.setColor(lineColor); + if (lineStroke != null) + g.setStroke(lineStroke); + + Path2D p1 = createOffsetPath(g, path, 1); + Path2D p2 = createOffsetPath(g, path, -1); + p1.append(p2, false); + g.draw(p1); + } + + @Override + public void drawDegeneratedLine(Graphics2D g, double x, double y, boolean isHorizontal, boolean isTransient) { + } + + @Override + public double getDegeneratedLineLength() { + return 0; + } + + private static int x = 0; + private static int y = 1; + private Path2D createOffsetPath(Graphics2D g, Path2D originalPath, float offset) { + PathIterator pi = originalPath.getPathIterator(null); + Path2D newPath = new Path2D.Double(); + double[] previous = new double[6]; + double[] current = new double[6]; + double[] next = new double[6]; + boolean vertical = false; + pi.currentSegment(current); + pi.next(); + pi.currentSegment(next); + + Direction direction = getDirection(current, next); + + int i = 0; + if(direction == Direction.SOUTH || direction == Direction.NORTH) { + // First line vertical + vertical = true; + current[x] += offset; + newPath.moveTo(current[x], current[y]); + + if(direction == Direction.SOUTH) + offset = -offset; + } else { + // First line horizontal + current[y] += offset; + i = 1; + newPath.moveTo(current[x], current[y]); + if(direction == Direction.WEST) + offset = -offset; + } + + + previous[x] = current[x]; + previous[y] = current[y]; + current[x] = next[x]; + current[y] = next[y]; + + while(!pi.isDone()) { + pi.next(); + pi.currentSegment(next); + if(previous[i] < next[i] ^ (i&1)==1) { + if(vertical) { + if(!pi.isDone()) current[y] += offset; + newPath.lineTo(previous[x], current[y]); + } else { + if(!pi.isDone()) current[x] += offset; + newPath.lineTo(current[x], previous[y]); + } + } else { + if(vertical) { + if(!pi.isDone()) current[y] -= offset; + newPath.lineTo(previous[x], current[y]); + } else { + if(!pi.isDone()) current[x] -= offset; + newPath.lineTo(current[x], previous[y]); + } + } + + previous[x] = current[x]; + previous[y] = current[y]; + current[x] = next[x]; + current[y] = next[y]; + vertical = !vertical; + i = (i + 1) % 2; + } + return newPath; + } + + private enum Direction {NORTH, SOUTH, EAST, WEST}; + + private Direction getDirection(double[] current, double[] next) { + if(current[x] == next[x]) { + // move vertically + if(current[y] < next[y]) + return Direction.SOUTH; + else + return Direction.NORTH; + } else { + //move horizontally + if(current[x] < next[x]) + return Direction.EAST; + else + return Direction.WEST; + } + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowEdgeClass.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowEdgeClassOld.java similarity index 95% rename from org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowEdgeClass.java rename to org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowEdgeClassOld.java index e002cb23..c04f747a 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowEdgeClass.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowEdgeClassOld.java @@ -46,7 +46,6 @@ import org.simantics.g2d.elementclass.BranchPoint; import org.simantics.g2d.elementclass.connection.EdgeClass.EdgeHandler; import org.simantics.g2d.elementclass.connection.EdgeClass.FixedTransform; import org.simantics.g2d.elementclass.connection.EdgeSceneGraph; -import org.simantics.g2d.routing.ConnectionDirectionUtil; import org.simantics.g2d.routing.Constants; import org.simantics.g2d.routing.IRouter2; import org.simantics.g2d.utils.PathUtils; @@ -56,7 +55,7 @@ import org.simantics.sysdyn.ui.editor.participant.SysdynConnectTool.SysdynConnec import org.simantics.sysdyn.ui.editor.routing.FlowRouter; import org.simantics.sysdyn.ui.elements2.ValveFactory.ValveSceneGraph; -public class FlowEdgeClass { +public class FlowEdgeClassOld { // TODO scale, rotate, move, transform public static final ElementClass CLASS = ElementClass.compile( @@ -75,7 +74,7 @@ public class FlowEdgeClass { @Override public void init(IElement e, G2DParentNode parent) { ElementUtils.getOrCreateNode(e, parent, KEY_SG_NODE, - "edge_" + e.hashCode(), FlowEdgeNode.class); + "edge_" + e.hashCode(), FlowEdgeNodeOld.class); final IDiagram diagram = ElementUtils.peekDiagram(e); boolean toValve = false; diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowEdgeFactory.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowEdgeFactoryOld.java similarity index 89% rename from org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowEdgeFactory.java rename to org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowEdgeFactoryOld.java index 6acef9fc..ae6eee98 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowEdgeFactory.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowEdgeFactoryOld.java @@ -24,9 +24,9 @@ import org.simantics.g2d.element.ElementClass; * * @author Tuukka Lehtonen */ -public class FlowEdgeFactory extends ElementFactoryAdapter { +public class FlowEdgeFactoryOld extends ElementFactoryAdapter { - private static final ElementClass CLASS = FlowEdgeClass.CLASS; + private static final ElementClass CLASS = FlowEdgeClassOld.CLASS; @Override public void create(AsyncReadGraph graph, ICanvasContext canvas, IDiagram diagram, Resource elementType, diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowEdgeNode.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowEdgeNodeOld.java similarity index 92% rename from org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowEdgeNode.java rename to org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowEdgeNodeOld.java index e6640f17..81143035 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowEdgeNode.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowEdgeNodeOld.java @@ -11,7 +11,7 @@ import org.simantics.scenegraph.ISelectionPainterNode; import org.simantics.scenegraph.g2d.nodes.EdgeNode; import org.simantics.scenegraph.utils.NodeUtil; -public class FlowEdgeNode extends EdgeNode implements ISelectionPainterNode { +public class FlowEdgeNodeOld extends EdgeNode implements ISelectionPainterNode { private static final long serialVersionUID = -6774653631527343539L; diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowNode.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowNodeOld.java similarity index 94% rename from org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowNode.java rename to org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowNodeOld.java index 25cec640..4f69a765 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowNode.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/FlowNodeOld.java @@ -22,7 +22,7 @@ import org.simantics.scenegraph.ISelectionPainterNode; import org.simantics.scenegraph.g2d.G2DNode; import org.simantics.scenegraph.utils.NodeUtil; -public class FlowNode extends G2DNode implements ISelectionPainterNode { +public class FlowNodeOld extends G2DNode implements ISelectionPainterNode { private static final long serialVersionUID = 328942356917631237L; diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/RouteFlowConnectionFactory.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/RouteFlowConnectionFactory.java new file mode 100644 index 00000000..f9097492 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/RouteFlowConnectionFactory.java @@ -0,0 +1,606 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 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.elements2.connections; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.simantics.databoard.Bindings; +import org.simantics.db.AsyncReadGraph; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.Session; +import org.simantics.db.Statement; +import org.simantics.db.common.procedure.adapter.TransientCacheListener; +import org.simantics.db.common.utils.NameUtils; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.procedure.AsyncProcedure; +import org.simantics.diagram.adapter.SyncElementFactory; +import org.simantics.diagram.connection.ConnectionVisuals; +import org.simantics.diagram.connection.RouteGraph; +import org.simantics.diagram.connection.RouteGraphConnectionClass; +import org.simantics.diagram.connection.RouteLine; +import org.simantics.diagram.connection.RouteNode; +import org.simantics.diagram.connection.RouteTerminal; +import org.simantics.diagram.connection.rendering.ConnectionStyle; +import org.simantics.diagram.connection.rendering.StyledRouteGraphRenderer; +import org.simantics.diagram.connection.rendering.arrows.ILineEndStyle; +import org.simantics.diagram.content.EdgeResource; +import org.simantics.diagram.content.ResourceTerminal; +import org.simantics.diagram.content.TerminalMap; +import org.simantics.diagram.query.DiagramRequests; +import org.simantics.diagram.stubs.DiagramResource; +import org.simantics.diagram.synchronization.graph.DiagramGraphUtil; +import org.simantics.diagram.synchronization.graph.RouteGraphConnection; +import org.simantics.diagram.ui.DiagramModelHints; +import org.simantics.g2d.canvas.ICanvasContext; +import org.simantics.g2d.connection.ConnectionEntity; +import org.simantics.g2d.diagram.DiagramHints; +import org.simantics.g2d.diagram.IDiagram; +import org.simantics.g2d.diagram.handler.DataElementMap; +import org.simantics.g2d.diagram.handler.Topology.Connection; +import org.simantics.g2d.diagram.handler.Topology.Terminal; +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.handler.EdgeVisuals.EdgeEnd; +import org.simantics.g2d.element.handler.TerminalTopology; +import org.simantics.g2d.element.handler.impl.StaticObjectAdapter; +import org.simantics.g2d.routing.algorithm2.Router4; +import org.simantics.g2d.utils.TopologicalSelectionExpander; +import org.simantics.layer0.Layer0; +import org.simantics.modeling.ModelingResources; +import org.simantics.scenegraph.g2d.nodes.connection.IRouteGraphListener; +import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphChangeEvent; +import org.simantics.scenegraph.utils.GeometryUtils; +import org.simantics.structural.stubs.StructuralResource2; +import org.simantics.structural2.modelingRules.CPTerminal; +import org.simantics.structural2.modelingRules.IAttachmentRelationMap; +import org.simantics.structural2.modelingRules.IModelingRules; +import org.simantics.sysdyn.SysdynResource; +import org.simantics.sysdyn.ui.elements2.ValveFactory.ValveSceneGraph; +/** + * An element class for Sysdyn Flow elements. + * Copied from RouteGraphConnectionClassFactory and adapted to Flow needs + * + * @author Tuukka Lehtonen + * + * + * +ts. oma ehdotukseni on tämä: +- etsi miten pystyt syöttämään jokaiselle sysdynin "terminaalille" oman ILineEndStyle:n joka sisältää viitteen mokkulan mittoihin +- lisää direction-parametri getLineEndLength:iin niin että voit palauttaa siellä mokkulan mittojen mukaisen etäisyyden + */ +public class RouteFlowConnectionFactory extends SyncElementFactory { + + public static final ElementClass CLASS = RouteFlowEdgeClass.FLOW_CLASS; + + Layer0 L0; + DiagramResource DIA; + StructuralResource2 STR; + ModelingResources MOD; + + public RouteFlowConnectionFactory(ReadGraph graph) { + this.L0 = Layer0.getInstance(graph); + this.DIA = DiagramResource.getInstance(graph); + this.STR = StructuralResource2.getInstance(graph); + this.MOD = ModelingResources.getInstance(graph); + } + + @Override + public void create(AsyncReadGraph graph, ICanvasContext canvas, IDiagram diagram, Resource elementType, final AsyncProcedure procedure) { + SysdynResource sr = graph.getService(SysdynResource.class); + graph.forSingleType(elementType, sr.FlowConnection, new AsyncProcedure() { + @Override + public void exception(AsyncReadGraph graph, Throwable throwable) { + procedure.exception(graph, throwable); + } + @Override + public void execute(AsyncReadGraph graph, Resource connectionType) { + procedure.execute(graph, CLASS.newClassWith(false, new StaticObjectAdapter(connectionType))); + } + }); + } + + @Override + public void load(ReadGraph graph, final ICanvasContext canvas, final IDiagram diagram, final Resource connection, + IElement element) throws DatabaseException { + + // Do we need this? + element.setHint(DiagramHints.ROUTE_ALGORITHM, new Router4(false)); + + IModelingRules modelingRules = diagram.getHint(DiagramModelHints.KEY_MODELING_RULES); + + IElement mappedElement = ElementUtils.getByData(diagram, connection); + if (mappedElement == null) + // FIXME: With undo this seems to happen, don't know why yet! + return; + + RouteGraph rg = new RouteGraph(); + + Set nodes = new HashSet(); + Set links = new HashSet(); + Map nodeByData = new HashMap(); + + // Needed to support ConnectionEntity#getTerminalConnections + Set backendonnections = new HashSet(); + + // Load all route graph interior RouteNodes: route lines and points + for (Resource interiorNode : graph.getObjects(connection, DIA.HasInteriorRouteNode)) { + if (graph.isInstanceOf(interiorNode, DIA.RouteLine)) { + Boolean isHorizontal = graph.getRelatedValue(interiorNode, DIA.IsHorizontal, Bindings.BOOLEAN); + Double position = graph.getRelatedValue(interiorNode, DIA.HasPosition, Bindings.DOUBLE); + RouteLine line = rg.addLine(isHorizontal, position); + line.setData( RouteGraphConnection.serialize(graph, interiorNode) ); + + nodes.add( interiorNode ); + nodeByData.put( interiorNode, line ); + + for (Resource connectedTo : graph.getObjects(interiorNode, DIA.AreConnected)) { + links.add( new EdgeResource(interiorNode, connectedTo) ); + } + } else if (graph.isInstanceOf(interiorNode, DIA.RoutePoint)) { + // Not supported yet. Ignore. + } + } + + Rectangle2D bounds = new Rectangle2D.Double(); + + // Load all node terminal connections as RouteTerminals + for (Statement toConnector : graph.getStatements(connection, DIA.HasConnector)) { + Resource connector = toConnector.getObject(); + Resource attachmentRelation = toConnector.getPredicate(); + + Statement terminalStm = findTerminalStatement(graph, STR, connection, connector); + if (terminalStm == null) + // Ignore broken connector: attached to the connection but not to any terminal. + continue; + + Resource terminalElement = terminalStm.getObject(); + Resource terminalElementType = graph.getPossibleType(terminalElement, DIA.Element); + if (terminalElementType == null) + // Ignore non-element terminal elements + continue; + + Resource connectionRelation = graph.getInverse(terminalStm.getPredicate()); + + // Discover node and terminal this connector is connected to. + TerminalMap terminals = graph.syncRequest(DiagramRequests.elementTypeTerminals(terminalElementType), + TransientCacheListener. instance()); + Resource terminal = terminals.getTerminal(connectionRelation); + if (terminal == null) { + System.err.println(getClass().getSimpleName() + + ": Could not find terminal for connection point " + + NameUtils.getSafeName(graph, connectionRelation, true) + + " in element " + + NameUtils.getSafeName(graph, terminalElement, true)); + continue; + } + + double[] position = graph.getRelatedValue(connector, DIA.HasRelativeLocation, Bindings.DOUBLE_ARRAY); + if (position.length != 2) + position = new double[] { 0, 0 }; + + //System.out.println("terminalStm: " + NameUtils.toString(graph, terminalStm)); + AffineTransform terminalElementTr = getWorldTransform(graph, terminalElement); + + double x = terminalElementTr.getTranslateX(); + double y = terminalElementTr.getTranslateY(); + double minx = x-1, miny = y-1, maxx = x+1, maxy = y+1; + int direction = 0x0; + + // Use modelingRules to ascertain the proper attachmentRelation + // for this terminal connection, if available. + if (modelingRules != null) { + // Get attachmentRelation from modelingRules if possible. + IAttachmentRelationMap map = modelingRules.getAttachmentRelations(graph, connection); + Resource att = map.get(graph, new CPTerminal(terminalElement, terminal)); + if (att != null) { + //System.out.println("modeling rules attachment: " + NameUtils.getSafeLabel(graph, att)); + attachmentRelation = att; + } + } + //System.out.println("attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation)); + + // Get element bounds to decide allowed terminal direction(s) + IElement te = graph.syncRequest(DiagramRequests.getElement(canvas, diagram, terminalElement, null)); + + + ElementUtils.getElementBounds(te, bounds); + { + Shape shp = org.simantics.g2d.utils.GeometryUtils.transformShape(bounds, terminalElementTr); + bounds.setFrame(shp.getBounds2D()); + } + + // Valve behaves differently. The flow must start inside the valve bounds + if(te.getElementClass().containsClass(ValveSceneGraph.class)) { + bounds.setFrame(new Rectangle2D.Double(bounds.getCenterX() - 1, bounds.getCenterY() - 1, 2, 2)); + } + + x = bounds.getCenterX(); + y = bounds.getCenterY(); + + // Expand bounds by 4mm to make the connections enter the terminals + // at a straight angle and from a distance instead of coming in + // "horizontally". + GeometryUtils.expandRectangle(bounds, 4); + + minx = bounds.getMinX(); + miny = bounds.getMinY(); + maxx = bounds.getMaxX(); + maxy = bounds.getMaxY(); + + + Integer allowedDirections = graph.getPossibleRelatedValue(terminal, DIA.Terminal_AllowedDirections, Bindings.INTEGER); + + // Valve behaves differently. Allowed directions depend on the orientation of the valve + if(te.getElementClass().containsClass(ValveSceneGraph.class)) { + SysdynResource sr = SysdynResource.getInstance(graph); + if(graph.hasStatement(terminalElement, sr.HasValveOrientation, sr.Vertical)) { + allowedDirections = 10; // Directions up and down (1010) + } else { + allowedDirections = 5; // Directions left and right (0101) + } + } + if (allowedDirections != null) { + direction |= allowedDirections; + } else { + direction |= RouteGraphConnectionClass.shortestDirectionOutOfBounds(x, y, bounds); + } + + backendonnections.add( + new BackendConnection( + toEdgeEnd(graph, attachmentRelation, EdgeEnd.Begin), + terminalElement, + terminal) + ); + + if (direction == 0) + // Accept any horizontal/vertical direction if nothing is defined + direction = 0xf; + + //System.out.println("load line style: " + NameUtils.getSafeLabel(graph, attachmentRelation)); + ILineEndStyle endStyle = loadLineEndStyle(graph, te, attachmentRelation, new Rectangle2D.Double( + bounds.getX(), + bounds.getY(), + bounds.getWidth(), + bounds.getHeight()) + ); + + RouteTerminal routeTerminal = rg.addTerminal(x, y, minx, miny, maxx, maxy, direction, endStyle); + routeTerminal.setData( RouteGraphConnection.serialize(graph, connector) ); + + nodes.add( connector ); + nodeByData.put( connector, routeTerminal ); + + for (Resource connectedTo : graph.getObjects(connector, DIA.AreConnected)) { + links.add( new EdgeResource(connectedTo, connector) ); + } + } + + // Finish route graph loading by Linking route nodes together + for (EdgeResource link : links) { + RouteNode n1 = nodeByData.get(link.first()); + RouteNode n2 = nodeByData.get(link.second()); + if (n1 == null || n2 == null) { + System.err.println("Stray connection link found: " + link.toString(graph)); + continue; + } + rg.link(n1, n2); + } + + // Load connection line style + ConnectionStyle style = readConnectionStyle(graph, modelingRules, connection, element); + StyledRouteGraphRenderer renderer = new StyledRouteGraphRenderer(style); + + // Finish element load + element.setHint(RouteGraphConnectionClass.KEY_ROUTEGRAPH, rg); + element.setHint(RouteGraphConnectionClass.KEY_RENDERER, renderer); + element.setHint(RouteGraphConnectionClass.KEY_PICK_TOLERANCE, 0.5); + + // Initialize ConnectionEntity in element + // NOTE: MUST use the mapped element with class CE, not the connection (element) were loading into. + // GDS will synchronize element into mappedElement in a controlled manner. + element.setHint(ElementHints.KEY_CONNECTION_ENTITY, new CE(connection, mappedElement, backendonnections)); + + // Setup graph writeback support for route graph modifications + final Session session = graph.getSession(); + element.setHint(RouteGraphConnectionClass.KEY_RG_LISTENER, new IRouteGraphListener() { + @Override + public void routeGraphChanged(RouteGraphChangeEvent event) { + scheduleSynchronize(session, connection, event); + } + }); + } + + private EdgeEnd toEdgeEnd(ReadGraph graph, Resource attachmentRelation, EdgeEnd defaultValue) + throws DatabaseException { + if (graph.isSubrelationOf(attachmentRelation, DIA.IsTailConnectorOf)) + return EdgeEnd.Begin; + if (graph.isSubrelationOf(attachmentRelation, DIA.IsHeadConnectorOf)) + return EdgeEnd.End; + return defaultValue; + } + + private ConnectionStyle readConnectionStyle(ReadGraph graph, IModelingRules modelingRules, Resource connection, + IElement element) throws DatabaseException { + Resource connectionType = null; + if (modelingRules != null) + connectionType = modelingRules.getConnectionType(graph, connection); + if (connectionType == null) + connectionType = graph.getPossibleObject(connection, STR.HasConnectionType); + + ConnectionVisuals cv = null; + if (connectionType != null) + cv = graph.syncRequest(DiagramRequests.getConnectionVisuals(connectionType), + TransientCacheListener. instance()); + + Color lineColor = cv != null ? cv.toColor() : null; + if (lineColor == null) + lineColor = Color.DARK_GRAY; + Stroke lineStroke = cv != null ? cv.stroke : null; + if (lineStroke == null) + lineStroke = new BasicStroke(0.1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10, null, 0); + + return new FlowConnectionStyle( + lineColor, + lineStroke); + } + + /** + * @param graph + * @param STR + * @param connection + * @param connector + * @return connection relation statement from diagram connection connector + * to a node + * @throws DatabaseException + */ + private static Statement findTerminalStatement(ReadGraph graph, StructuralResource2 STR, Resource connection, + Resource connector) throws DatabaseException { + for (Statement stm : graph.getStatements(connector, STR.Connects)) { + if (connection.equals(stm.getObject())) + continue; + return stm; + } + return null; + } + + public ILineEndStyle loadLineEndStyle(ReadGraph graph, IElement te, Resource attachmentRelation, Rectangle2D bounds) + throws DatabaseException { + ILineEndStyle style; + // TODO: change bounds according to terminal type: Very small rectangle for Valves, Text box size for Stocks and Clouds + if(te.getElementClass().containsClass(ValveSceneGraph.class)) { + GeometryUtils.expandRectangle(bounds, -4); + style = new FlowArrowLineStyle("none 0 0 0", bounds); + } else { + if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector)) { + GeometryUtils.expandRectangle(bounds, -2.5); + style = new FlowArrowLineStyle("fill 2 2 -1.5", bounds); + } else { + GeometryUtils.expandRectangle(bounds, -4); + style = new FlowArrowLineStyle("none 0 0 0", bounds); + } + } + return style; + } + + /** + * @param graph + * @param element + * @return + * @throws DatabaseException + */ + private static AffineTransform getWorldTransform(ReadGraph graph, Resource element) throws DatabaseException { + ModelingResources MOD = ModelingResources.getInstance(graph); + AffineTransform result = DiagramGraphUtil.getAffineTransform(graph, element); + while (true) { + Resource parentComponent = graph.getPossibleObject(element, MOD.HasParentComponent); + if (parentComponent == null) + return result; + element = graph.getPossibleObject(parentComponent, MOD.ComponentToElement); + if (element == null) + return result; + AffineTransform tr = DiagramGraphUtil.getAffineTransform(graph, element); + tr.setToTranslation(tr.getTranslateX(), tr.getTranslateY()); + result.preConcatenate(tr); + } + } + +// private int directionSetToToDirectionMask(DirectionSet directions) { +// if (directions == DirectionSet.ANY) +// return 0xf; +// int mask = 0; +// for (Double d : directions) { +// mask |= compassAngleToToDirectionMask(d); +// } +// return mask; +// } +// +// private int compassAngleToToDirectionMask(double compassAngle) { +// Double d = DirectionSet.NESW.getClosestDirection(compassAngle); +// int id = (int) Math.round(d); +// // bits: +// // 0 right (1,0) +// // 1 down (0,1) +// // 2 left (-1,0) +// // 3 up (0,-1) +// switch (id) { +// case 0: return (1 << 3); +// case 90: return (1 << 2); +// case 180: return (1 << 1); +// case 270: return (1 << 0); +// } +// return 0xf; +// } + + protected void scheduleSynchronize(Session session, Resource connection, RouteGraphChangeEvent event) { + session.asyncRequest(RouteGraphConnection.synchronizer(connection, event)); + } + + /** + * Must have this in order for {@link TopologicalSelectionExpander} to work. + * Otherwise this is pretty useless and should be deprecated altogether. + * + * @see ElementHints#KEY_CONNECTION_ENTITY + */ + static class CE implements ConnectionEntity { + + /** + * The connection instance resource in the graph backend. + */ + final Resource connection; + + /** + * The connection entity element which is a part of the diagram. + */ + final IElement connectionElement; + + /** + * @see #getTerminalConnections(Collection) + */ + final Set backendConnections; + + /** + * Cache. + */ + Set terminalConnections; + + CE(Resource connection, IElement connectionElement, Set backendConnections) { + this.connection = connection; + this.connectionElement = connectionElement; + this.backendConnections = backendConnections; + } + + @Override + public IElement getConnection() { + return connectionElement; + } + + public Object getConnectionObject() { + return connection; + } + + public IElement getConnectionElement() { + return connectionElement; + } + + @Override + public Collection getBranchPoints(Collection result) { + return result != null ? result : Collections. emptyList(); + } + + @Override + public Collection getSegments(Collection result) { + return result != null ? result : Collections. emptyList(); + } + + @Override + public Collection getTerminalConnections(Collection result) { + if (terminalConnections == null) + terminalConnections = calculateTerminalConnections(); + if (result == null) + result = new ArrayList(terminalConnections); + else + result.addAll(terminalConnections); + return terminalConnections; + } + + private Set calculateTerminalConnections() { + IDiagram diagram = connectionElement.getDiagram(); + DataElementMap dem = diagram.getDiagramClass().getSingleItem(DataElementMap.class); + Set result = new HashSet(); + ArrayList ts = new ArrayList(); + for (BackendConnection bc : backendConnections) { + IElement e = dem.getElement(diagram, bc.node); + if (e == null) + continue; + TerminalTopology tt = e.getElementClass().getSingleItem(TerminalTopology.class); + tt.getTerminals(e, ts); + for (Terminal t : ts) { + if (t instanceof ResourceTerminal) { + ResourceTerminal rt = (ResourceTerminal) t; + if (bc.terminal.equals(rt.getResource())) { + result.add(new Connection(connectionElement, bc.end, e, t)); + break; + } + } + } + } + return result; + } + + @Override + public void setListener(ConnectionListener listener) { + throw new UnsupportedOperationException(); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[resource=" + connection + ", connectionElement=" + connectionElement + + "]"; + } + + } + + public static class BackendConnection { + public final Resource node; + public final Resource terminal; + public final EdgeEnd end; + public BackendConnection(EdgeEnd end, Resource node, Resource terminal) { + assert end != null; + assert node != null; + assert terminal != null; + this.end = end; + this.node = node; + this.terminal = terminal; + } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!(obj instanceof Connection)) + return false; + Connection other = (Connection) obj; + return other.terminal == terminal + && other.node == node + && other.end == end; + } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + end.hashCode(); + result = prime * result + ((node == null) ? 0 : node.hashCode()); + result = prime * result + ((terminal == null) ? 0 : terminal.hashCode()); + return result; + } + @Override + public String toString() { + return "BackendConnection[node=" + node + ", terminal=" + terminal + ", end=" + end + "]"; + } + } + +} + diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/RouteFlowEdgeClass.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/RouteFlowEdgeClass.java new file mode 100644 index 00000000..638e3177 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/RouteFlowEdgeClass.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 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.elements2.connections; + +import java.util.ArrayList; +import java.util.List; + +import org.simantics.diagram.connection.RouteGraph; +import org.simantics.diagram.connection.RouteGraphConnectionClass; +import org.simantics.diagram.connection.rendering.IRouteGraphRenderer; +import org.simantics.g2d.element.ElementClass; +import org.simantics.g2d.element.ElementUtils; +import org.simantics.g2d.element.IElement; +import org.simantics.g2d.element.handler.ElementHandler; +import org.simantics.g2d.element.handler.SceneGraph; +import org.simantics.scenegraph.g2d.G2DParentNode; +import org.simantics.scenegraph.g2d.nodes.connection.IRouteGraphListener; +import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphNode; + + + +public class RouteFlowEdgeClass extends RouteGraphConnectionClass { + + public static final ElementClass FLOW_CLASS = getElementClass(); + + + public static final ElementClass getElementClass() { + List oldList = CLASS.getAll(); + ArrayList list = new ArrayList(); + list.add(FlowConnectionSceneGraph.INSTANCE); + for(ElementHandler eh : oldList) { + if(!(eh instanceof SceneGraph)) { + list.add(eh); + } + } + return ElementClass.compile(list); + } + + + static final class FlowConnectionSceneGraph implements SceneGraph { + + public static final FlowConnectionSceneGraph INSTANCE = new FlowConnectionSceneGraph(); + + private static final long serialVersionUID = 1865920472882420644L; + + @Override + public void init(IElement connection, G2DParentNode parent) { + RouteGraph rg = connection.getHint(KEY_ROUTEGRAPH); + IRouteGraphRenderer renderer = connection.getHint(KEY_RENDERER); + if (rg == null || renderer == null) { + cleanup(connection); + } else { + RouteGraphNode rgn = connection.getHint(KEY_RG_NODE); + if (rgn == null) { + rgn = parent.addNode(ElementUtils.generateNodeId(connection), RouteGraphNode.class); + connection.setHint(KEY_RG_NODE, rgn); + } + rgn.setRouteGraph(rg); + rgn.setRenderer(renderer); + + IRouteGraphListener listener = connection.getHint(KEY_RG_LISTENER); + rgn.setRouteGraphListener(listener); + + Double tolerance = connection.getHint(KEY_PICK_TOLERANCE); + if (tolerance != null) + rgn.setPickTolerance(tolerance); + } + } + + @Override + public void cleanup(IElement connection) { + ElementUtils.removePossibleNode(connection, KEY_RG_NODE); + connection.removeHint(KEY_RG_NODE); + } + } + +} + + diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/RouteFlowEdgeFactory.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/RouteFlowEdgeFactory.java new file mode 100644 index 00000000..8ee7934b --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/connections/RouteFlowEdgeFactory.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 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.elements2.connections; + +import org.simantics.db.AsyncReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.procedure.AsyncProcedure; +import org.simantics.diagram.adapter.ElementFactoryAdapter; +import org.simantics.g2d.canvas.ICanvasContext; +import org.simantics.g2d.diagram.IDiagram; +import org.simantics.g2d.element.ElementClass; + +public class RouteFlowEdgeFactory extends ElementFactoryAdapter { + + private static final ElementClass CLASS = RouteFlowEdgeClass.FLOW_CLASS; + + @Override + public void create(AsyncReadGraph graph, ICanvasContext canvas, IDiagram diagram, Resource elementType, + AsyncProcedure procedure) { + procedure.execute(graph, CLASS); + } + + @Override + public void getClass(AsyncReadGraph graph, ICanvasContext canvas, IDiagram diagram, Resource elementResource, + AsyncProcedure procedure) { + throw new UnsupportedOperationException(); + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/VariableInformationTab.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/VariableInformationTab.java index 9a3b21e8..17bcb250 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/VariableInformationTab.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/VariableInformationTab.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2010 Association for Decentralized Information Management in + * Copyright (c) 2010, 2011 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 @@ -26,6 +26,7 @@ import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupport; import org.simantics.db.management.ISessionContext; import org.simantics.layer0.Layer0; import org.simantics.sysdyn.SysdynResource; +import org.simantics.sysdyn.ui.properties.widgets.ValveOrientationGroup; import org.simantics.sysdyn.ui.properties.widgets.factories.DoublePropertyFactory; import org.simantics.sysdyn.ui.properties.widgets.factories.DoublePropertyModifier; @@ -80,6 +81,9 @@ public class VariableInformationTab extends LabelPropertyTabContributor { rangeStep.addModifyListener(new DoublePropertyModifier(context, SysdynResource.URIs.HasRangeStep)); rangeStep.setInputValidator(new DoubleValidator()); GridDataFactory.fillDefaults().grab(true, false).applyTo(rangeStep.getWidget()); + + + new ValveOrientationGroup(composite, context, support, SWT.NONE); } diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/ValveOrientationGroup.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/ValveOrientationGroup.java new file mode 100644 index 00000000..436e8ba8 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/ValveOrientationGroup.java @@ -0,0 +1,136 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 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.properties.widgets; + +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.layout.GridLayoutFactory; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Group; +import org.simantics.browsing.ui.swt.widgets.Button; +import org.simantics.browsing.ui.swt.widgets.WidgetImpl; +import org.simantics.browsing.ui.swt.widgets.impl.ReadFactoryImpl; +import org.simantics.browsing.ui.swt.widgets.impl.SelectionListenerImpl; +import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupport; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.management.ISessionContext; +import org.simantics.modeling.ModelingResources; +import org.simantics.sysdyn.SysdynResource; +import org.simantics.utils.datastructures.Triple; + +public class ValveOrientationGroup extends WidgetImpl { + + Group group; + Button vertical, horizontal; + + public ValveOrientationGroup(Composite parent, ISessionContext context, WidgetSupport support, int style) { + super(support); + support.register(this); + group = new Group(parent, SWT.NONE); + group.setText("Valve orientation"); + GridDataFactory.fillDefaults().applyTo(group); + GridLayoutFactory.fillDefaults().margins(3, 3).applyTo(group); + + horizontal = new Button(group, support, SWT.RADIO); + horizontal.setText("Horizontal"); + horizontal.setSelectionFactory(new OrientationSelectionFactory(SysdynResource.URIs.Horizontal, true)); + horizontal.addSelectionListener(new OrientationSelectionListener(context, SysdynResource.URIs.Horizontal)); + + vertical = new Button(group, support, SWT.RADIO); + vertical.setText("Vertical"); + vertical.setSelectionFactory(new OrientationSelectionFactory(SysdynResource.URIs.Vertical)); + vertical.addSelectionListener(new OrientationSelectionListener(context, SysdynResource.URIs.Vertical)); + } + + @Override + public void setInput(ISessionContext context, Object input) { + + + } + + @Override + public Control getControl() { + return this.group; + } + + private class OrientationSelectionFactory extends ReadFactoryImpl { + + boolean defaultSelected; + String uri; + + public OrientationSelectionFactory(String uri) { + this(uri, false); + } + + public OrientationSelectionFactory(String uri, boolean defaultSelected) { + this.uri = uri; + this.defaultSelected = defaultSelected; + } + + public Object getIdentity(Object inputContents) { + return new Triple>(uri, defaultSelected, getClass()); + } + + @Override + public Boolean perform(ReadGraph graph, Resource valve) throws DatabaseException { + SysdynResource sr = SysdynResource.getInstance(graph); + ModelingResources mr = ModelingResources.getInstance(graph); + if(!graph.isInstanceOf(valve, sr.Valve)) + return Boolean.FALSE; + + Resource symbol = graph.getPossibleObject(valve, mr.ComponentToElement); + if(symbol == null) + return Boolean.FALSE; + + Resource orientation = graph.getPossibleObject(symbol, sr.HasValveOrientation); + + if(orientation == null) + return defaultSelected; + + return orientation.equals(graph.getResource(uri)); + } + + } + + + private class OrientationSelectionListener extends SelectionListenerImpl { + + String uri; + + public OrientationSelectionListener(ISessionContext context, String uri) { + super(context); + this.uri = uri; + } + + @Override + public void apply(WriteGraph graph, Resource valve) throws DatabaseException { + SysdynResource sr = SysdynResource.getInstance(graph); + ModelingResources mr = ModelingResources.getInstance(graph); + if(!graph.isInstanceOf(valve, sr.Valve)) + return; + + Resource symbol = graph.getPossibleObject(valve, mr.ComponentToElement); + if(symbol == null) + return; + + if(graph.hasStatement(symbol, sr.HasValveOrientation)) + graph.deny(symbol, sr.HasValveOrientation); + graph.claim(symbol, sr.HasValveOrientation, graph.getResource(uri)); + } + + } + +} diff --git a/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/utils/SheetFormatUtils.java b/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/utils/SheetFormatUtils.java index fd04c6c0..5923c194 100644 --- a/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/utils/SheetFormatUtils.java +++ b/org.simantics.sysdyn/src/org/simantics/sysdyn/representation/utils/SheetFormatUtils.java @@ -17,7 +17,7 @@ import org.simantics.sysdyn.representation.Variable; public class SheetFormatUtils { public static String reformatSheetReferences(Variable v, String expression) { - if(!expression.contains("(")) + if(expression == null || !expression.contains("(")) return expression; ExpressionParser parser = new ExpressionParser(new StringReader(expression)); try { -- 2.47.1