1 /*******************************************************************************
\r
2 * Copyright (c) 2010 Association for Decentralized Information Management in
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.sysdyn.ui.elements2.connections;
\r
14 import java.awt.Color;
\r
15 import java.awt.Stroke;
\r
16 import java.awt.geom.AffineTransform;
\r
17 import java.awt.geom.GeneralPath;
\r
18 import java.awt.geom.Path2D;
\r
19 import java.awt.geom.PathIterator;
\r
20 import java.awt.geom.Point2D;
\r
21 import java.awt.geom.Rectangle2D;
\r
22 import java.util.ArrayList;
\r
23 import java.util.Collection;
\r
24 import java.util.Iterator;
\r
25 import java.util.List;
\r
27 import org.simantics.g2d.diagram.DiagramHints;
\r
28 import org.simantics.g2d.diagram.IDiagram;
\r
29 import org.simantics.g2d.diagram.handler.Topology;
\r
30 import org.simantics.g2d.diagram.handler.Topology.Connection;
\r
31 import org.simantics.g2d.diagram.participant.pointertool.TerminalUtil;
\r
32 import org.simantics.g2d.element.ElementClass;
\r
33 import org.simantics.g2d.element.ElementUtils;
\r
34 import org.simantics.g2d.element.IElement;
\r
35 import org.simantics.g2d.element.handler.BendsHandler;
\r
36 import org.simantics.g2d.element.handler.EdgeVisuals;
\r
37 import org.simantics.g2d.element.handler.EdgeVisuals.ArrowType;
\r
38 import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;
\r
39 import org.simantics.g2d.element.handler.impl.ConfigurableEdgeVisuals;
\r
40 import org.simantics.g2d.element.handler.impl.ConnectionSelectionOutline;
\r
41 import org.simantics.g2d.element.handler.impl.FillColorImpl;
\r
42 import org.simantics.g2d.element.handler.impl.ParentImpl;
\r
43 import org.simantics.g2d.element.handler.impl.ShapePick;
\r
44 import org.simantics.g2d.element.handler.impl.SimpleElementLayers;
\r
45 import org.simantics.g2d.elementclass.BranchPoint;
\r
46 import org.simantics.g2d.elementclass.connection.EdgeClass.EdgeHandler;
\r
47 import org.simantics.g2d.elementclass.connection.EdgeClass.FixedTransform;
\r
48 import org.simantics.g2d.elementclass.connection.EdgeSceneGraph;
\r
49 import org.simantics.g2d.routing.Constants;
\r
50 import org.simantics.g2d.routing.IRouter2;
\r
51 import org.simantics.g2d.utils.PathUtils;
\r
52 import org.simantics.scenegraph.g2d.G2DParentNode;
\r
53 import org.simantics.scenegraph.g2d.nodes.EdgeNode;
\r
54 import org.simantics.sysdyn.ui.editor.participant.SysdynConnectTool.SysdynConnection;
\r
55 import org.simantics.sysdyn.ui.editor.routing.FlowRouter;
\r
56 import org.simantics.sysdyn.ui.elements2.ValveFactory.ValveSceneGraph;
\r
58 public class FlowEdgeClassOld {
\r
60 // TODO scale, rotate, move, transform
\r
61 public static final ElementClass CLASS = ElementClass.compile(
\r
62 FlowEdgeSceneGraph.INSTANCE, EdgeHandler.INSTANCE,
\r
63 ConfigurableEdgeVisuals.DEFAULT, FillColorImpl.BLACK,
\r
64 FixedTransform.INSTANCE, ShapePick.INSTANCE,
\r
65 ConnectionSelectionOutline.INSTANCE, SimpleElementLayers.INSTANCE,
\r
66 ParentImpl.INSTANCE).setId("FlowEdgeClass");
\r
68 public static class FlowEdgeSceneGraph extends EdgeSceneGraph {
\r
70 private static final long serialVersionUID = -8737581995034992604L;
\r
72 public static final EdgeSceneGraph INSTANCE = new FlowEdgeSceneGraph();
\r
75 public void init(IElement e, G2DParentNode parent) {
\r
76 ElementUtils.getOrCreateNode(e, parent, KEY_SG_NODE,
\r
77 "edge_" + e.hashCode(), FlowEdgeNodeOld.class);
\r
78 final IDiagram diagram = ElementUtils.peekDiagram(e);
\r
80 boolean toValve = false;
\r
81 if (diagram != null) {
\r
82 Topology topology = diagram.getDiagramClass()
\r
83 .getAtMostOneItemOfClass(Topology.class);
\r
84 if (topology != null) {
\r
85 Connection endConnection = topology.getConnection(e,
\r
87 toValve = endConnection.node.getElementClass()
\r
88 .containsClass(ValveSceneGraph.class);
\r
92 ConfigurableEdgeVisuals cev = e.getElementClass()
\r
93 .getAtMostOneItemOfClass(ConfigurableEdgeVisuals.class);
\r
96 cev.setArrowType(e, EdgeEnd.End, ArrowType.None);
\r
98 cev.setArrowType(e, EdgeEnd.End, ArrowType.Fill);
\r
99 cev.setArrowSize(e, EdgeEnd.End, 2);
\r
108 private static Path2D trimLineToArrows(Path2D line,
\r
109 ArrowType endArrowType, double endArrowSize) {
\r
110 Path2D result = new Path2D.Double();
\r
111 PathIterator pi = line.getPathIterator(null);
\r
113 double lineTo[] = new double[2];
\r
114 double prevLine[] = new double[2];
\r
115 double dummy[] = new double[2];
\r
117 while (!pi.isDone()) {
\r
118 int type = pi.currentSegment(lineTo);
\r
120 int nextType = pi.currentSegment(dummy);
\r
122 if (type == PathIterator.SEG_LINETO
\r
123 && nextType == PathIterator.SEG_MOVETO) {
\r
124 // this is the end of one line
\r
125 if (endArrowType == ArrowType.Fill) {
\r
126 double dx = (lineTo[0] - prevLine[0]);
\r
127 double dy = (lineTo[1] - prevLine[1]);
\r
128 double x2 = dx * dx;
\r
129 double y2 = dy * dy;
\r
130 double len = Math.sqrt(x2 + y2);
\r
131 if (len > endArrowSize) {
\r
132 double scale = endArrowSize / len;
\r
133 lineTo[0] -= dx * scale;
\r
134 lineTo[1] -= dy * scale;
\r
140 result.moveTo(lineTo[0], lineTo[1]);
\r
141 } else if (type == 1) {
\r
142 result.lineTo(lineTo[0], lineTo[1]);
\r
144 throw new UnsupportedOperationException(
\r
145 "invalid path segment type: " + type);
\r
147 prevLine[0] = lineTo[0];
\r
148 prevLine[1] = lineTo[1];
\r
152 result.setWindingRule(line.getWindingRule());
\r
156 private void updateRoute(final IElement e) {
\r
158 final List<IElement> segments = new ArrayList<IElement>();
\r
161 IRouter2 router = ElementUtils.getHintOrDefault(e,
\r
162 DiagramHints.ROUTE_ALGORITHM, new FlowRouter());
\r
164 router.route(new SysdynConnection() {
\r
166 IDiagram diagram = ElementUtils.peekDiagram(e);
\r
168 final Topology topology = diagram.getDiagramClass()
\r
169 .getSingleItem(Topology.class);
\r
172 public Connector getBegin(Object seg) {
\r
173 IElement e = (IElement) seg;
\r
174 Connection begin = topology.getConnection(e, EdgeEnd.Begin);
\r
175 return getConnector(begin);
\r
179 public Connector getEnd(Object seg) {
\r
180 IElement e = (IElement) seg;
\r
181 Connection end = topology.getConnection(e, EdgeEnd.End);
\r
182 return getConnector(end);
\r
185 private Connector getConnector(Connection connection) {
\r
186 Connector c = new Connector();
\r
187 c.allowedDirections = Constants.EAST_FLAG | Constants.WEST_FLAG
\r
188 | Constants.NORTH_FLAG | Constants.SOUTH_FLAG;
\r
190 AffineTransform at = TerminalUtil.getTerminalPosOnDiagram(
\r
191 connection.node, connection.terminal);
\r
192 c.x = at.getTranslateX();
\r
193 c.y = at.getTranslateY();
\r
195 if (connection.node.getElementClass().containsClass(
\r
196 ValveSceneGraph.class)) {
\r
197 Rectangle2D bounds = ElementUtils
\r
198 .getElementBoundsOnDiagram(connection.node)
\r
200 c.parentObstacle = new Rectangle2D.Double(bounds
\r
201 .getCenterX() - FlowRouter.OFFSET, bounds
\r
202 .getCenterY() - FlowRouter.OFFSET,
\r
203 FlowRouter.OFFSET * 2, FlowRouter.OFFSET * 2);
\r
204 c.allowedDirections = Constants.EAST_FLAG | Constants.WEST_FLAG;
\r
206 c.parentObstacle = ElementUtils
\r
207 .getElementBoundsOnDiagram(connection.node)
\r
215 private int toAllowedDirections(BranchPoint.Direction direction) {
\r
216 switch (direction) {
\r
220 return Constants.EAST_FLAG | Constants.WEST_FLAG;
\r
222 return Constants.NORTH_FLAG | Constants.SOUTH_FLAG;
\r
224 throw new IllegalArgumentException(
\r
225 "unrecognized direction: " + direction);
\r
230 public Collection<? extends Object> getSegments() {
\r
235 public void setPath(Object seg, Path2D path) {
\r
236 IElement e = (IElement) seg;
\r
237 BendsHandler bends = e.getElementClass()
\r
238 .getAtMostOneItemOfClass(BendsHandler.class);
\r
239 AffineTransform elementTransform = ElementUtils
\r
240 .getInvTransform(e);
\r
241 path = (Path2D) path.clone();
\r
242 path.transform(elementTransform);
\r
243 bends.setPath(e, path);
\r
249 public void update(final IElement e) {
\r
250 EdgeNode node = e.getHint(KEY_SG_NODE);
\r
254 EdgeVisuals vh = e.getElementClass().getSingleItem(
\r
255 EdgeVisuals.class);
\r
256 ArrowType at1 = vh.getArrowType(e, EdgeEnd.Begin);
\r
257 ArrowType at2 = vh.getArrowType(e, EdgeEnd.End);
\r
258 Stroke stroke = vh.getStroke(e);
\r
259 // StrokeType strokeType = vh.getStrokeType(e);
\r
260 double as1 = vh.getArrowSize(e, EdgeEnd.Begin);
\r
261 double as2 = vh.getArrowSize(e, EdgeEnd.End);
\r
263 Color c = ElementUtils.getFillColor(e, Color.BLACK);
\r
265 // Get terminal shape for clipping the painted edge to its bounds.
\r
266 IDiagram diagram = ElementUtils.peekDiagram(e);
\r
267 if (diagram != null) {
\r
268 Topology topology = diagram.getDiagramClass()
\r
269 .getAtMostOneItemOfClass(Topology.class);
\r
270 if (topology != null) {
\r
271 Connection beginConnection = topology.getConnection(e,
\r
273 Connection endConnection = topology.getConnection(e,
\r
275 int beginBranchDegree = getBranchPointDegree(
\r
276 beginConnection, topology);
\r
277 int endBranchDegree = getBranchPointDegree(endConnection,
\r
279 if (beginBranchDegree > 0 && beginBranchDegree < 3) {
\r
280 at1 = ArrowType.None;
\r
282 if (endBranchDegree > 0 && endBranchDegree < 3) {
\r
283 at2 = ArrowType.None;
\r
289 BendsHandler bh = e.getElementClass().getSingleItem(
\r
290 BendsHandler.class);
\r
291 Path2D line = bh.getPath(e);
\r
293 boolean drawArrows = at1 != ArrowType.None || at2 != ArrowType.None;
\r
294 // line = clipLineEnds(line, beginTerminalShape, endTerminalShape);
\r
296 Point2D first = new Point2D.Double();
\r
297 Point2D dir1 = new Point2D.Double();
\r
298 Point2D last = new Point2D.Double();
\r
299 Point2D dir2 = new Point2D.Double();
\r
300 PathIterator pi = line.getPathIterator(null);
\r
301 drawArrows &= getPathArrows(pi, first, dir1, last, dir2);
\r
304 line = trimLineToArrows(line, at2, as2);
\r
307 EdgeNode.ArrowType pat1 = convert(at1);
\r
308 EdgeNode.ArrowType pat2 = convert(at2);
\r
310 node.init(new GeneralPath(line), stroke, c, dir1, dir2, first,
\r
311 last, as1, as2, pat1, pat2, null, null);
\r
314 private boolean getPathArrows(PathIterator pi, Point2D begin,
\r
315 Point2D beginDirection, Point2D end, Point2D endDirection) {
\r
317 Iterator<double[]> i = PathUtils.toLineIterator(pi);
\r
319 double first1[] = null, last1[] = null;
\r
320 double first2[] = null, last2[] = null;
\r
322 // double current[] = new double[2];
\r
324 double[] previous = null;
\r
326 while (i.hasNext()) {
\r
327 double[] current = i.next();
\r
329 // Start of the first path
\r
330 if (first1 == null) {
\r
335 // Command was moveTo => start of the second path
\r
336 else if (previous != null
\r
337 && (previous[2] != current[0] || previous[3] != current[1])) {
\r
342 // first2 == null => still in the first line
\r
343 else if (first2 == null) {
\r
348 else if (!i.hasNext()) {
\r
352 previous = current;
\r
355 if (first1 == null || last1 == null || first2 == null
\r
359 double[] first = { mean(first1[0], first2[0]),
\r
360 mean(first1[1], first2[1]), mean(first1[2], first2[2]),
\r
361 mean(first1[3], first2[3]) };
\r
362 double[] last = { mean(last1[0], last2[0]),
\r
363 mean(last1[1], last2[1]), mean(last1[2], last2[2]),
\r
364 mean(last1[3], last2[3]) };
\r
366 begin.setLocation(PathUtils.getLinePos(first, 0));
\r
367 beginDirection.setLocation(PathUtils.getLineTangent(first, 0));
\r
368 end.setLocation(PathUtils.getLinePos(last, 1));
\r
369 Point2D endTangent = PathUtils.getLineTangent(last, 1);
\r
370 endDirection.setLocation(-endTangent.getX(), -endTangent.getY());
\r
375 private double mean(double c1, double c2) {
\r
376 return c1 + (c2 - c1) / 2;
\r
379 private static EdgeNode.ArrowType convert(ArrowType at) {
\r
382 return EdgeNode.ArrowType.None;
\r
384 return EdgeNode.ArrowType.Stroke;
\r
386 return EdgeNode.ArrowType.Fill;
\r
388 throw new IllegalArgumentException("unsupported arrow type: "
\r
393 private final Collection<Connection> connectionsTemp = new ArrayList<Connection>();
\r
395 private int getBranchPointDegree(Connection connection,
\r
396 Topology topology) {
\r
397 if (connection != null && connection.node != null) {
\r
398 if (connection.node.getElementClass().containsClass(
\r
399 BranchPoint.class)) {
\r
400 connectionsTemp.clear();
\r
401 topology.getConnections(connection.node,
\r
402 connection.terminal, connectionsTemp);
\r
403 int degree = connectionsTemp.size();
\r
404 connectionsTemp.clear();
\r