1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.g2d.elementclass;
14 import java.awt.Shape;
15 import java.awt.geom.Rectangle2D;
16 import java.util.Collection;
17 import java.util.Collections;
19 import org.simantics.diagram.connection.RouteGraph;
20 import org.simantics.diagram.connection.rendering.IRouteGraphRenderer;
21 import org.simantics.g2d.connection.ConnectionEntity;
22 import org.simantics.g2d.connection.handler.ConnectionHandler;
23 import org.simantics.g2d.diagram.handler.PickRequest.PickPolicy;
24 import org.simantics.g2d.diagram.handler.Topology.Connection;
25 import org.simantics.g2d.element.ElementClass;
26 import org.simantics.g2d.element.ElementHints;
27 import org.simantics.g2d.element.ElementUtils;
28 import org.simantics.g2d.element.IElement;
29 import org.simantics.g2d.element.SceneGraphNodeKey;
30 import org.simantics.g2d.element.handler.InternalSize;
31 import org.simantics.g2d.element.handler.Outline;
32 import org.simantics.g2d.element.handler.Pick;
33 import org.simantics.g2d.element.handler.SceneGraph;
34 import org.simantics.g2d.element.handler.SelectionOutline;
35 import org.simantics.g2d.element.handler.impl.ConfigurableEdgeVisuals;
36 import org.simantics.g2d.element.handler.impl.ConnectionSelectionOutline;
37 import org.simantics.g2d.element.handler.impl.FillColorImpl;
38 import org.simantics.g2d.element.handler.impl.SimpleElementLayers;
39 import org.simantics.g2d.element.handler.impl.TextImpl;
40 import org.simantics.g2d.elementclass.connection.EdgeClass.FixedTransform;
41 import org.simantics.scenegraph.g2d.G2DParentNode;
42 import org.simantics.scenegraph.g2d.nodes.connection.IRouteGraphListener;
43 import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphNode;
44 import org.simantics.utils.datastructures.hints.IHintContext.Key;
45 import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;
48 * An element class for single connection entity elements. A connection entity
49 * consists of connection edge segments and branch points as its children.
51 * @author Tuukka Lehtonen
53 public class RouteGraphConnectionClass {
55 public static final Key KEY_ROUTEGRAPH = new KeyOf(RouteGraph.class, "ROUTE_GRAPH");
56 public static final Key KEY_RENDERER = new KeyOf(IRouteGraphRenderer.class, "ROUTE_GRAPH_RENDERER");
57 public static final Key KEY_PICK_TOLERANCE = new KeyOf(Double.class, "PICK_TOLERANCE");
58 public static final Key KEY_USE_TOLERANCE_IN_SELECTION = new KeyOf(Boolean.class, "PICK_TOLERANCE_SELECTION");
59 public static final Key KEY_RG_LISTENER = new KeyOf(IRouteGraphListener.class, "ROUTE_GRAPH_LISTENER");
60 public static final Key KEY_RG_NODE = new SceneGraphNodeKey(RouteGraphNode.class, "ROUTE_GRAPH_NODE");
62 public static final double BOUND_TOLERANCE = 0.9;
64 public static final ElementClass CLASS =
68 FixedTransform.INSTANCE,
70 ConnectionBoundsAndPick.INSTANCE,
71 ConnectionSelectionOutline.INSTANCE,
72 ConnectionHandlerImpl.INSTANCE,
73 ConnectionSceneGraph.INSTANCE,
74 SimpleElementLayers.INSTANCE,
76 // Exists only loading connection visuals through ConnectionVisualsLoader
77 ConfigurableEdgeVisuals.DEFAULT,
79 ).setId(RouteGraphConnectionClass.class.getSimpleName());
82 static class ConnectionHandlerImpl implements ConnectionHandler {
84 public static final ConnectionHandlerImpl INSTANCE = new ConnectionHandlerImpl();
86 private static final long serialVersionUID = 3267139233182458330L;
89 public Collection<IElement> getBranchPoints(IElement connection, Collection<IElement> result) {
90 return Collections.<IElement>emptySet();
94 public Collection<IElement> getChildren(IElement connection, Collection<IElement> result) {
95 return Collections.emptySet();
99 public Collection<IElement> getSegments(IElement connection, Collection<IElement> result) {
100 return Collections.<IElement>emptySet();
104 public Collection<Connection> getTerminalConnections(IElement connection, Collection<Connection> result) {
105 ConnectionEntity ce = connection.getHint(ElementHints.KEY_CONNECTION_ENTITY);
107 return Collections.<Connection>emptySet();
108 return ce.getTerminalConnections(result);
113 static final class ConnectionSceneGraph implements SceneGraph {
115 public static final ConnectionSceneGraph INSTANCE = new ConnectionSceneGraph();
117 private static final long serialVersionUID = 4232871859964883266L;
120 public void init(IElement connection, G2DParentNode parent) {
121 RouteGraph rg = connection.getHint(KEY_ROUTEGRAPH);
122 IRouteGraphRenderer renderer = connection.getHint(KEY_RENDERER);
123 if (rg == null || renderer == null) {
126 RouteGraphNode rgn = connection.getHint(KEY_RG_NODE);
128 rgn = parent.addNode(ElementUtils.generateNodeId(connection), RouteGraphNode.class);
129 connection.setHint(KEY_RG_NODE, rgn);
131 rgn.setRouteGraph(rg);
132 rgn.setRenderer(renderer);
134 IRouteGraphListener listener = connection.getHint(KEY_RG_LISTENER);
135 rgn.setRouteGraphListener(listener);
137 Double tolerance = connection.getHint(KEY_PICK_TOLERANCE);
138 if (tolerance != null)
139 rgn.setPickTolerance(tolerance);
144 public void cleanup(IElement connection) {
145 ElementUtils.removePossibleNode(connection, KEY_RG_NODE);
146 connection.removeHint(KEY_RG_NODE);
150 static final class ConnectionBoundsAndPick implements InternalSize, Outline, Pick {
152 private static final long serialVersionUID = 4232871859964883266L;
154 public static final ConnectionBoundsAndPick INSTANCE = new ConnectionBoundsAndPick();
156 // Single-threaded system, should be fine to use this for everything.
157 Rectangle2D temp = new Rectangle2D.Double();
159 private Shape getSelectionShape(IElement e) {
160 for (SelectionOutline so : e.getElementClass().getItemsByClass(SelectionOutline.class)) {
161 Shape shape = so.getSelectionShape(e);
165 // Using on-diagram coordinates because neither connections nor
166 // edges have a non-identity transform which means that
167 // coordinates are always absolute. Therefore branch point
168 // shape also needs to be calculated in absolute coordinates.
169 Shape shape = ElementUtils.getElementShapeOrBoundsOnDiagram(e);
174 public boolean pickTest(IElement e, Shape s, PickPolicy policy) {
175 RouteGraphNode rgn = e.getHint(KEY_RG_NODE);
179 RouteGraph rg = getRouteGraph(e);
183 Rectangle2D bounds = getBounds(s);
185 case PICK_CONTAINED_OBJECTS:
186 Shape selectionShape = getSelectionShape(e);
187 return bounds.contains(selectionShape.getBounds2D());
188 case PICK_INTERSECTING_OBJECTS:
189 double tolerance = 0.0;
190 if (e.containsHint(KEY_USE_TOLERANCE_IN_SELECTION))
191 tolerance = getTolerance(e);
193 tolerance = Math.max((bounds.getHeight()+bounds.getHeight()) * 0.25, rgn.getSelectionStrokeWidth() / 2);
194 Object node = rg.pickLine(bounds.getCenterX(), bounds.getCenterY(), tolerance);
201 public Rectangle2D getBounds(IElement e, Rectangle2D size) {
202 RouteGraphNode rgn = e.getHint(KEY_RG_NODE);
205 size = new Rectangle2D.Double();
206 size.setRect(rgn.getBoundsInLocal());
212 public Shape getElementShape(IElement e) {
213 RouteGraph rg = getRouteGraph(e);
214 return rg == null ? null : rg.getPath2D();
217 private Rectangle2D getBounds(Shape shape) {
218 if (shape instanceof Rectangle2D)
219 return (Rectangle2D) shape;
220 return shape.getBounds2D();
223 private RouteGraph getRouteGraph(IElement e) {
224 RouteGraphNode rgn = e.getHint(KEY_RG_NODE);
225 return rgn == null ? null : rgn.getRouteGraph();
228 private double getTolerance(IElement e) {
229 RouteGraphNode rgn = e.getHint(KEY_RG_NODE);
230 return rgn.getPickTolerance();
235 public static int shortestDirectionOutOfBounds(double x, double y, Rectangle2D bounds) {
236 double mx = bounds.getMinX();
237 double Mx = bounds.getMaxX();
238 double my = bounds.getMinY();
239 double My = bounds.getMaxY();
242 double down = My - y;
243 double left = x - mx;
244 double right = Mx - x;
247 double[] dists = { right, down, left, up };
248 byte[] masks = { 0x1, 0x2, 0x4, 0x8 };
249 for (int i = 1; i < 4; ++i) {
250 double value = dists[i];
251 byte mask = masks[i];
253 while (j >= 0 && dists[j] > value) {
254 dists[j + 1] = dists[j];
255 masks[j + 1] = masks[j];
258 dists[j + 1] = value;
262 // Construct mask out of the shortest equal directions
264 double value = dists[0] / BOUND_TOLERANCE;
265 for (int i = 1; i < 4; ++i) {
266 if (dists[i] > value)