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
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.diagram.connection;
\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
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
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
49 * @author Tuukka Lehtonen
\r
51 public class RouteGraphConnectionClass {
\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
60 public static final double BOUND_TOLERANCE = 0.9;
\r
62 public static final ElementClass CLASS =
\r
63 ElementClass.compile(
\r
66 FixedTransform.INSTANCE,
\r
68 ConnectionBoundsAndPick.INSTANCE,
\r
69 ConnectionSelectionOutline.INSTANCE,
\r
70 ConnectionHandlerImpl.INSTANCE,
\r
71 ConnectionSceneGraph.INSTANCE,
\r
72 //SimpleElementLayers.INSTANCE,
\r
74 // Exists only loading connection visuals through ConnectionVisualsLoader
\r
75 ConfigurableEdgeVisuals.DEFAULT,
\r
77 ).setId(RouteGraphConnectionClass.class.getSimpleName());
\r
80 static class ConnectionHandlerImpl implements ConnectionHandler {
\r
82 public static final ConnectionHandlerImpl INSTANCE = new ConnectionHandlerImpl();
\r
84 private static final long serialVersionUID = 3267139233182458330L;
\r
87 public Collection<IElement> getBranchPoints(IElement connection, Collection<IElement> result) {
\r
88 return Collections.<IElement>emptySet();
\r
92 public Collection<IElement> getChildren(IElement connection, Collection<IElement> result) {
\r
93 return Collections.emptySet();
\r
97 public Collection<IElement> getSegments(IElement connection, Collection<IElement> result) {
\r
98 return Collections.<IElement>emptySet();
\r
102 public Collection<Connection> getTerminalConnections(IElement connection, Collection<Connection> result) {
\r
103 ConnectionEntity ce = connection.getHint(ElementHints.KEY_CONNECTION_ENTITY);
\r
105 return Collections.<Connection>emptySet();
\r
106 return ce.getTerminalConnections(result);
\r
111 static final class ConnectionSceneGraph implements SceneGraph {
\r
113 public static final ConnectionSceneGraph INSTANCE = new ConnectionSceneGraph();
\r
115 private static final long serialVersionUID = 4232871859964883266L;
\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
124 RouteGraphNode rgn = connection.getHint(KEY_RG_NODE);
\r
126 rgn = parent.addNode(ElementUtils.generateNodeId(connection), RouteGraphNode.class);
\r
127 connection.setHint(KEY_RG_NODE, rgn);
\r
129 rgn.setRouteGraph(rg);
\r
130 rgn.setRenderer(renderer);
\r
132 IRouteGraphListener listener = connection.getHint(KEY_RG_LISTENER);
\r
133 rgn.setRouteGraphListener(listener);
\r
135 Double tolerance = connection.getHint(KEY_PICK_TOLERANCE);
\r
136 if (tolerance != null)
\r
137 rgn.setPickTolerance(tolerance);
\r
142 public void cleanup(IElement connection) {
\r
143 ElementUtils.removePossibleNode(connection, KEY_RG_NODE);
\r
144 connection.removeHint(KEY_RG_NODE);
\r
148 static final class ConnectionBoundsAndPick implements InternalSize, Outline, Pick {
\r
150 private static final long serialVersionUID = 4232871859964883266L;
\r
152 public static final ConnectionBoundsAndPick INSTANCE = new ConnectionBoundsAndPick();
\r
154 // Single-threaded system, should be fine to use this for everything.
\r
155 Rectangle2D temp = new Rectangle2D.Double();
\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
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
172 public boolean pickTest(IElement e, Shape s, PickPolicy policy) {
\r
173 RouteGraph rg = getRouteGraph(e);
\r
177 Rectangle2D bounds = getBounds(s);
\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
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
195 public Rectangle2D getBounds(IElement e, Rectangle2D size) {
\r
196 RouteGraph rg = getRouteGraph(e);
\r
199 size = new Rectangle2D.Double();
\r
200 rg.getBounds(size);
\r
206 public Shape getElementShape(IElement e) {
\r
207 RouteGraph rg = getRouteGraph(e);
\r
208 return rg == null ? null : rg.getPath2D();
\r
211 private Rectangle2D getBounds(Shape shape) {
\r
212 if (shape instanceof Rectangle2D)
\r
213 return (Rectangle2D) shape;
\r
214 return shape.getBounds2D();
\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
222 private double getTolerance(IElement e) {
\r
223 RouteGraphNode rgn = e.getHint(KEY_RG_NODE);
\r
224 return rgn.getPickTolerance();
\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
235 double up = y - my;
\r
236 double down = My - y;
\r
237 double left = x - mx;
\r
238 double right = Mx - x;
\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
247 while (j >= 0 && dists[j] > value) {
\r
248 dists[j + 1] = dists[j];
\r
249 masks[j + 1] = masks[j];
\r
252 dists[j + 1] = value;
\r
253 masks[j + 1] = mask;
\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