]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.diagram/src/org/simantics/diagram/connection/RouteGraphConnectionClass.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / connection / RouteGraphConnectionClass.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in 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.diagram.connection;\r
13 \r
14 import java.awt.Shape;\r
15 import java.awt.geom.Rectangle2D;\r
16 import java.util.Collection;\r
17 import java.util.Collections;\r
18 \r
19 import org.simantics.diagram.connection.rendering.IRouteGraphRenderer;\r
20 import org.simantics.g2d.connection.ConnectionEntity;\r
21 import org.simantics.g2d.connection.handler.ConnectionHandler;\r
22 import org.simantics.g2d.diagram.handler.PickRequest.PickPolicy;\r
23 import org.simantics.g2d.diagram.handler.Topology.Connection;\r
24 import org.simantics.g2d.element.ElementClass;\r
25 import org.simantics.g2d.element.ElementHints;\r
26 import org.simantics.g2d.element.ElementUtils;\r
27 import org.simantics.g2d.element.IElement;\r
28 import org.simantics.g2d.element.SceneGraphNodeKey;\r
29 import org.simantics.g2d.element.handler.InternalSize;\r
30 import org.simantics.g2d.element.handler.Outline;\r
31 import org.simantics.g2d.element.handler.Pick;\r
32 import org.simantics.g2d.element.handler.SceneGraph;\r
33 import org.simantics.g2d.element.handler.SelectionOutline;\r
34 import org.simantics.g2d.element.handler.impl.ConfigurableEdgeVisuals;\r
35 import org.simantics.g2d.element.handler.impl.ConnectionSelectionOutline;\r
36 import org.simantics.g2d.element.handler.impl.FillColorImpl;\r
37 import org.simantics.g2d.element.handler.impl.TextImpl;\r
38 import org.simantics.g2d.elementclass.connection.EdgeClass.FixedTransform;\r
39 import org.simantics.scenegraph.g2d.G2DParentNode;\r
40 import org.simantics.scenegraph.g2d.nodes.connection.IRouteGraphListener;\r
41 import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphNode;\r
42 import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
43 import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;\r
44 \r
45 /**\r
46  * An element class for single connection entity elements. A connection entity\r
47  * consists of connection edge segments and branch points as its children.\r
48  * \r
49  * @author Tuukka Lehtonen\r
50  */\r
51 public class RouteGraphConnectionClass {\r
52 \r
53     public static final Key          KEY_ROUTEGRAPH     = new KeyOf(RouteGraph.class, "ROUTE_GRAPH");\r
54     public static final Key          KEY_RENDERER       = new KeyOf(IRouteGraphRenderer.class, "ROUTE_GRAPH_RENDERER");\r
55     public static final Key          KEY_PICK_TOLERANCE = new KeyOf(Double.class, "PICK_TOLERANCE");\r
56     public static final Key          KEY_USE_TOLERANCE_IN_SELECTION  = new KeyOf(Boolean.class, "PICK_TOLERANCE_SELECTION");\r
57     public static final Key          KEY_RG_LISTENER    = new KeyOf(IRouteGraphListener.class, "ROUTE_GRAPH_LISTENER");\r
58     public static final Key          KEY_RG_NODE        = new SceneGraphNodeKey(RouteGraphNode.class, "ROUTE_GRAPH_NODE");\r
59     \r
60     public static final double       BOUND_TOLERANCE = 0.9;\r
61 \r
62     public static final ElementClass CLASS =\r
63         ElementClass.compile(\r
64                 TextImpl.INSTANCE,\r
65 \r
66                 FixedTransform.INSTANCE,\r
67 \r
68                 ConnectionBoundsAndPick.INSTANCE,\r
69                 ConnectionSelectionOutline.INSTANCE,\r
70                 ConnectionHandlerImpl.INSTANCE,\r
71                 ConnectionSceneGraph.INSTANCE,\r
72                 //SimpleElementLayers.INSTANCE,\r
73 \r
74                 // Exists only loading connection visuals through ConnectionVisualsLoader\r
75                 ConfigurableEdgeVisuals.DEFAULT,\r
76                 FillColorImpl.BLACK\r
77         ).setId(RouteGraphConnectionClass.class.getSimpleName());\r
78 \r
79 \r
80     static class ConnectionHandlerImpl implements ConnectionHandler {\r
81 \r
82         public static final ConnectionHandlerImpl INSTANCE = new ConnectionHandlerImpl();\r
83 \r
84         private static final long serialVersionUID = 3267139233182458330L;\r
85 \r
86         @Override\r
87         public Collection<IElement> getBranchPoints(IElement connection, Collection<IElement> result) {\r
88             return Collections.<IElement>emptySet();\r
89         }\r
90 \r
91         @Override\r
92         public Collection<IElement> getChildren(IElement connection, Collection<IElement> result) {\r
93             return Collections.emptySet();\r
94         }\r
95 \r
96         @Override\r
97         public Collection<IElement> getSegments(IElement connection, Collection<IElement> result) {\r
98             return Collections.<IElement>emptySet();\r
99         }\r
100 \r
101         @Override\r
102         public Collection<Connection> getTerminalConnections(IElement connection, Collection<Connection> result) {\r
103             ConnectionEntity ce = connection.getHint(ElementHints.KEY_CONNECTION_ENTITY);\r
104             if (ce == null)\r
105                 return Collections.<Connection>emptySet();\r
106             return ce.getTerminalConnections(result);\r
107         }\r
108 \r
109     }\r
110 \r
111     static final class ConnectionSceneGraph implements SceneGraph {\r
112 \r
113         public static final ConnectionSceneGraph INSTANCE = new ConnectionSceneGraph();\r
114 \r
115         private static final long serialVersionUID = 4232871859964883266L;\r
116 \r
117         @Override\r
118         public void init(IElement connection, G2DParentNode parent) {\r
119             RouteGraph rg = connection.getHint(KEY_ROUTEGRAPH);\r
120             IRouteGraphRenderer renderer = connection.getHint(KEY_RENDERER);\r
121             if (rg == null || renderer == null) {\r
122                 cleanup(connection);\r
123             } else {\r
124                 RouteGraphNode rgn = connection.getHint(KEY_RG_NODE);\r
125                 if (rgn == null) {\r
126                     rgn = parent.addNode(ElementUtils.generateNodeId(connection), RouteGraphNode.class);\r
127                     connection.setHint(KEY_RG_NODE, rgn);\r
128                 }\r
129                 rgn.setRouteGraph(rg);\r
130                 rgn.setRenderer(renderer);\r
131 \r
132                 IRouteGraphListener listener = connection.getHint(KEY_RG_LISTENER);\r
133                 rgn.setRouteGraphListener(listener);\r
134 \r
135                 Double tolerance = connection.getHint(KEY_PICK_TOLERANCE);\r
136                 if (tolerance != null)\r
137                     rgn.setPickTolerance(tolerance);\r
138             }\r
139         }\r
140 \r
141         @Override\r
142         public void cleanup(IElement connection) {\r
143             ElementUtils.removePossibleNode(connection, KEY_RG_NODE);\r
144             connection.removeHint(KEY_RG_NODE);\r
145         }\r
146     }\r
147 \r
148     static final class ConnectionBoundsAndPick implements InternalSize, Outline, Pick {\r
149 \r
150         private static final long serialVersionUID = 4232871859964883266L;\r
151 \r
152         public static final ConnectionBoundsAndPick INSTANCE = new ConnectionBoundsAndPick();\r
153 \r
154         // Single-threaded system, should be fine to use this for everything.\r
155         Rectangle2D temp = new Rectangle2D.Double();\r
156 \r
157         private Shape getSelectionShape(IElement e) {\r
158             for (SelectionOutline so : e.getElementClass().getItemsByClass(SelectionOutline.class)) {\r
159                 Shape shape = so.getSelectionShape(e);\r
160                 if (shape != null)\r
161                     return shape;\r
162             }\r
163             // Using on-diagram coordinates because neither connections nor\r
164             // edges have a non-identity transform which means that\r
165             // coordinates are always absolute. Therefore branch point\r
166             // shape also needs to be calculated in absolute coordinates.\r
167             Shape shape = ElementUtils.getElementShapeOrBoundsOnDiagram(e);\r
168             return shape;\r
169         }\r
170 \r
171         @Override\r
172         public boolean pickTest(IElement e, Shape s, PickPolicy policy) {\r
173             RouteGraph rg = getRouteGraph(e);\r
174             if (rg == null)\r
175                 return false;\r
176 \r
177             Rectangle2D bounds = getBounds(s);\r
178             switch (policy) {\r
179                 case PICK_CONTAINED_OBJECTS:\r
180                     Shape selectionShape = getSelectionShape(e);\r
181                     return bounds.contains(selectionShape.getBounds2D());\r
182                 case PICK_INTERSECTING_OBJECTS:\r
183                         double tolerance = 0.0;\r
184                         if (e.containsHint(KEY_USE_TOLERANCE_IN_SELECTION))\r
185                                 tolerance = getTolerance(e);\r
186                         else\r
187                                 tolerance = (bounds.getHeight()+bounds.getHeight()) * 0.25;\r
188                         Object node = rg.pickLine(bounds.getCenterX(), bounds.getCenterY(), tolerance);\r
189                     return node != null;\r
190             }\r
191             return false;\r
192         }\r
193 \r
194         @Override\r
195         public Rectangle2D getBounds(IElement e, Rectangle2D size) {\r
196             RouteGraph rg = getRouteGraph(e);\r
197             if (rg != null) {\r
198                 if (size == null)\r
199                     size = new Rectangle2D.Double();\r
200                 rg.getBounds(size);\r
201             }\r
202             return size;\r
203         }\r
204 \r
205         @Override\r
206         public Shape getElementShape(IElement e) {\r
207             RouteGraph rg = getRouteGraph(e);\r
208             return rg == null ? null : rg.getPath2D();\r
209         }\r
210 \r
211         private Rectangle2D getBounds(Shape shape) {\r
212             if (shape instanceof Rectangle2D)\r
213                 return (Rectangle2D) shape;\r
214             return shape.getBounds2D();\r
215         }\r
216 \r
217         private RouteGraph getRouteGraph(IElement e) {\r
218             RouteGraphNode rgn = e.getHint(KEY_RG_NODE);\r
219             return rgn == null ? null : rgn.getRouteGraph();\r
220         }\r
221         \r
222         private double getTolerance(IElement e) {\r
223                 RouteGraphNode rgn = e.getHint(KEY_RG_NODE);\r
224                 return rgn.getPickTolerance();\r
225         }\r
226 \r
227     }\r
228 \r
229     public static int shortestDirectionOutOfBounds(double x, double y, Rectangle2D bounds) {\r
230         double mx = bounds.getMinX();\r
231         double Mx = bounds.getMaxX();\r
232         double my = bounds.getMinY();\r
233         double My = bounds.getMaxY();\r
234 \r
235         double up = y - my;\r
236         double down = My - y;\r
237         double left = x - mx;\r
238         double right = Mx - x;\r
239 \r
240         // Insertion sort\r
241         double[] dists = { right, down, left, up };\r
242         byte[] masks = { 0x1, 0x2, 0x4, 0x8 };\r
243         for (int i = 1; i < 4; ++i) {\r
244             double value = dists[i];\r
245             byte mask = masks[i];\r
246             int j = i - 1;\r
247             while (j >= 0 && dists[j] > value) {\r
248                 dists[j + 1] = dists[j];\r
249                 masks[j + 1] = masks[j];\r
250                 --j;\r
251             }\r
252             dists[j + 1] = value;\r
253             masks[j + 1] = mask;\r
254         }\r
255 \r
256         // Construct mask out of the shortest equal directions \r
257         int mask = masks[0];\r
258         double value = dists[0] / BOUND_TOLERANCE;\r
259         for (int i = 1; i < 4; ++i) {\r
260             if (dists[i] > value)\r
261                 break;\r
262             mask |= masks[i];\r
263         }\r
264         return mask;\r
265     }\r
266 \r
267 }\r