From: lempinen Date: Tue, 1 Nov 2011 09:56:43 +0000 (+0000) Subject: First take on new route graph for flows X-Git-Tag: simantics-1.6~115 X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=commitdiff_plain;h=5439b365a9fd62d80ce6d586e9060096074b4bfd;p=simantics%2Fsysdyn.git 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 --- diff --git a/org.simantics.sysdyn.ontology/graph.tg b/org.simantics.sysdyn.ontology/graph.tg index 358bb901..1d3d856b 100644 Binary files a/org.simantics.sysdyn.ontology/graph.tg and b/org.simantics.sysdyn.ontology/graph.tg differ diff --git a/org.simantics.sysdyn.ontology/graph/Sysdyn.pgraph b/org.simantics.sysdyn.ontology/graph/Sysdyn.pgraph index 475e3a60..e35c30e6 100644 --- a/org.simantics.sysdyn.ontology/graph/Sysdyn.pgraph +++ b/org.simantics.sysdyn.ontology/graph/Sysdyn.pgraph @@ -392,14 +392,23 @@ SYSDYN.IsTailOfTerminal @MOD.terminalRelation SYSDYN.IsTailOf SYSDYN.SysdynTerminal - 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 {