]> gerrit.simantics Code Review - simantics/sysdyn.git/blob
c04f747aeab1dcdc697d057c58e7124befb94e4d
[simantics/sysdyn.git] /
1 /*******************************************************************************\r
2  * Copyright (c) 2010 Association for Decentralized Information Management in\r
3  * Industry THTH ry.\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
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.sysdyn.ui.elements2.connections;\r
13 \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
26 \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
57 \r
58 public class FlowEdgeClassOld {\r
59 \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
67 \r
68         public static class FlowEdgeSceneGraph extends EdgeSceneGraph {\r
69 \r
70                 private static final long serialVersionUID = -8737581995034992604L;\r
71 \r
72                 public static final EdgeSceneGraph INSTANCE = new FlowEdgeSceneGraph();\r
73 \r
74                 @Override\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
79 \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
86                                                         EdgeEnd.End);\r
87                                         toValve = endConnection.node.getElementClass()\r
88                                                         .containsClass(ValveSceneGraph.class);\r
89                                 }\r
90                         }\r
91 \r
92                         ConfigurableEdgeVisuals cev = e.getElementClass()\r
93                                         .getAtMostOneItemOfClass(ConfigurableEdgeVisuals.class);\r
94                         if (cev != null) {\r
95                                 if (toValve)\r
96                                         cev.setArrowType(e, EdgeEnd.End, ArrowType.None);\r
97                                 else {\r
98                                         cev.setArrowType(e, EdgeEnd.End, ArrowType.Fill);\r
99                                         cev.setArrowSize(e, EdgeEnd.End, 2);\r
100                                 }\r
101                         }\r
102 \r
103                         updateRoute(e);\r
104                         update(e);\r
105 \r
106                 }\r
107 \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
112 \r
113                         double lineTo[] = new double[2];\r
114                         double prevLine[] = new double[2];\r
115                         double dummy[] = new double[2];\r
116 \r
117                         while (!pi.isDone()) {\r
118                                 int type = pi.currentSegment(lineTo);\r
119                                 pi.next();\r
120                                 int nextType = pi.currentSegment(dummy);\r
121 \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
135                                                 }\r
136                                         }\r
137                                 }\r
138 \r
139                                 if (type == 0) {\r
140                                         result.moveTo(lineTo[0], lineTo[1]);\r
141                                 } else if (type == 1) {\r
142                                         result.lineTo(lineTo[0], lineTo[1]);\r
143                                 } else {\r
144                                         throw new UnsupportedOperationException(\r
145                                                         "invalid path segment type: " + type);\r
146                                 }\r
147                                 prevLine[0] = lineTo[0];\r
148                                 prevLine[1] = lineTo[1];\r
149 \r
150                         }\r
151 \r
152                         result.setWindingRule(line.getWindingRule());\r
153                         return result;\r
154                 }\r
155 \r
156                 private void updateRoute(final IElement e) {\r
157 \r
158                         final List<IElement> segments = new ArrayList<IElement>();\r
159                         segments.add(e);\r
160 \r
161                         IRouter2 router = ElementUtils.getHintOrDefault(e,\r
162                                         DiagramHints.ROUTE_ALGORITHM, new FlowRouter());\r
163 \r
164                         router.route(new SysdynConnection() {\r
165 \r
166                                 IDiagram diagram = ElementUtils.peekDiagram(e);\r
167 \r
168                                 final Topology topology = diagram.getDiagramClass()\r
169                                                 .getSingleItem(Topology.class);\r
170 \r
171                                 @Override\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
176                                 }\r
177 \r
178                                 @Override\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
183                                 }\r
184 \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
189                                         \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
194 \r
195                                         if (connection.node.getElementClass().containsClass(\r
196                                                         ValveSceneGraph.class)) {\r
197                                                 Rectangle2D bounds = ElementUtils\r
198                                                                 .getElementBoundsOnDiagram(connection.node)\r
199                                                                 .getBounds2D();\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
205                                         } else {\r
206                                                 c.parentObstacle = ElementUtils\r
207                                                                 .getElementBoundsOnDiagram(connection.node)\r
208                                                                 .getBounds2D();\r
209                                                 \r
210                                         }\r
211 \r
212                                         return c;\r
213                                 }\r
214 \r
215                                 private int toAllowedDirections(BranchPoint.Direction direction) {\r
216                                         switch (direction) {\r
217                                         case Any:\r
218                                                 return 0xf;\r
219                                         case Horizontal:\r
220                                                 return Constants.EAST_FLAG | Constants.WEST_FLAG;\r
221                                         case Vertical:\r
222                                                 return Constants.NORTH_FLAG | Constants.SOUTH_FLAG;\r
223                                         default:\r
224                                                 throw new IllegalArgumentException(\r
225                                                                 "unrecognized direction: " + direction);\r
226                                         }\r
227                                 }\r
228 \r
229                                 @Override\r
230                                 public Collection<? extends Object> getSegments() {\r
231                                         return segments;\r
232                                 }\r
233 \r
234                                 @Override\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
244                                 }\r
245                         });\r
246 \r
247                 }\r
248 \r
249                 public void update(final IElement e) {\r
250                         EdgeNode node = e.getHint(KEY_SG_NODE);\r
251                         if (node == null)\r
252                                 return;\r
253 \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
262 \r
263                         Color c = ElementUtils.getFillColor(e, Color.BLACK);\r
264 \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
272                                                         EdgeEnd.Begin);\r
273                                         Connection endConnection = topology.getConnection(e,\r
274                                                         EdgeEnd.End);\r
275                                         int beginBranchDegree = getBranchPointDegree(\r
276                                                         beginConnection, topology);\r
277                                         int endBranchDegree = getBranchPointDegree(endConnection,\r
278                                                         topology);\r
279                                         if (beginBranchDegree > 0 && beginBranchDegree < 3) {\r
280                                                 at1 = ArrowType.None;\r
281                                         }\r
282                                         if (endBranchDegree > 0 && endBranchDegree < 3) {\r
283                                                 at2 = ArrowType.None;\r
284                                         }\r
285                                 }\r
286                         }\r
287 \r
288                         // Read bends\r
289                         BendsHandler bh = e.getElementClass().getSingleItem(\r
290                                         BendsHandler.class);\r
291                         Path2D line = bh.getPath(e);\r
292 \r
293                         boolean drawArrows = at1 != ArrowType.None || at2 != ArrowType.None;\r
294                         // line = clipLineEnds(line, beginTerminalShape, endTerminalShape);\r
295 \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
302 \r
303                         if (drawArrows) {\r
304                                 line = trimLineToArrows(line, at2, as2);\r
305                         }\r
306 \r
307                         EdgeNode.ArrowType pat1 = convert(at1);\r
308                         EdgeNode.ArrowType pat2 = convert(at2);\r
309 \r
310                         node.init(new GeneralPath(line), stroke, c, dir1, dir2, first,\r
311                                         last, as1, as2, pat1, pat2, null, null);\r
312                 }\r
313 \r
314                 private boolean getPathArrows(PathIterator pi, Point2D begin,\r
315                                 Point2D beginDirection, Point2D end, Point2D endDirection) {\r
316 \r
317                         Iterator<double[]> i = PathUtils.toLineIterator(pi);\r
318 \r
319                         double first1[] = null, last1[] = null;\r
320                         double first2[] = null, last2[] = null;\r
321 \r
322                         // double current[] = new double[2];\r
323 \r
324                         double[] previous = null;\r
325 \r
326                         while (i.hasNext()) {\r
327                                 double[] current = i.next();\r
328 \r
329                                 // Start of the first path\r
330                                 if (first1 == null) {\r
331                                         first1 = current;\r
332                                         last1 = current;\r
333                                 }\r
334 \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
338                                         first2 = current;\r
339                                         last2 = current;\r
340                                 }\r
341 \r
342                                 // first2 == null => still in the first line\r
343                                 else if (first2 == null) {\r
344                                         last1 = current;\r
345                                 }\r
346 \r
347                                 // second path\r
348                                 else if (!i.hasNext()) {\r
349                                         last2 = current;\r
350                                 }\r
351 \r
352                                 previous = current;\r
353                         }\r
354 \r
355                         if (first1 == null || last1 == null || first2 == null\r
356                                         || last2 == null)\r
357                                 return false;\r
358 \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
365 \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
371 \r
372                         return true;\r
373                 }\r
374 \r
375                 private double mean(double c1, double c2) {\r
376                         return c1 + (c2 - c1) / 2;\r
377                 }\r
378 \r
379                 private static EdgeNode.ArrowType convert(ArrowType at) {\r
380                         switch (at) {\r
381                         case None:\r
382                                 return EdgeNode.ArrowType.None;\r
383                         case Stroke:\r
384                                 return EdgeNode.ArrowType.Stroke;\r
385                         case Fill:\r
386                                 return EdgeNode.ArrowType.Fill;\r
387                         default:\r
388                                 throw new IllegalArgumentException("unsupported arrow type: "\r
389                                                 + at);\r
390                         }\r
391                 }\r
392 \r
393                 private final Collection<Connection> connectionsTemp = new ArrayList<Connection>();\r
394 \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
405                                         return degree;\r
406                                 }\r
407                         }\r
408                         return -1;\r
409                 }\r
410 \r
411         }\r
412 \r
413 }\r