-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.diagram.connection;\r
-\r
-import java.awt.Shape;\r
-import java.awt.geom.Rectangle2D;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-\r
-import org.simantics.diagram.connection.rendering.IRouteGraphRenderer;\r
-import org.simantics.g2d.connection.ConnectionEntity;\r
-import org.simantics.g2d.connection.handler.ConnectionHandler;\r
-import org.simantics.g2d.diagram.handler.PickRequest.PickPolicy;\r
-import org.simantics.g2d.diagram.handler.Topology.Connection;\r
-import org.simantics.g2d.element.ElementClass;\r
-import org.simantics.g2d.element.ElementHints;\r
-import org.simantics.g2d.element.ElementUtils;\r
-import org.simantics.g2d.element.IElement;\r
-import org.simantics.g2d.element.SceneGraphNodeKey;\r
-import org.simantics.g2d.element.handler.InternalSize;\r
-import org.simantics.g2d.element.handler.Outline;\r
-import org.simantics.g2d.element.handler.Pick;\r
-import org.simantics.g2d.element.handler.SceneGraph;\r
-import org.simantics.g2d.element.handler.SelectionOutline;\r
-import org.simantics.g2d.element.handler.impl.ConfigurableEdgeVisuals;\r
-import org.simantics.g2d.element.handler.impl.ConnectionSelectionOutline;\r
-import org.simantics.g2d.element.handler.impl.FillColorImpl;\r
-import org.simantics.g2d.element.handler.impl.TextImpl;\r
-import org.simantics.g2d.elementclass.connection.EdgeClass.FixedTransform;\r
-import org.simantics.scenegraph.g2d.G2DParentNode;\r
-import org.simantics.scenegraph.g2d.nodes.connection.IRouteGraphListener;\r
-import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphNode;\r
-import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
-import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;\r
-\r
-/**\r
- * An element class for single connection entity elements. A connection entity\r
- * consists of connection edge segments and branch points as its children.\r
- * \r
- * @author Tuukka Lehtonen\r
- */\r
-public class RouteGraphConnectionClass {\r
-\r
- public static final Key KEY_ROUTEGRAPH = new KeyOf(RouteGraph.class, "ROUTE_GRAPH");\r
- public static final Key KEY_RENDERER = new KeyOf(IRouteGraphRenderer.class, "ROUTE_GRAPH_RENDERER");\r
- public static final Key KEY_PICK_TOLERANCE = new KeyOf(Double.class, "PICK_TOLERANCE");\r
- public static final Key KEY_USE_TOLERANCE_IN_SELECTION = new KeyOf(Boolean.class, "PICK_TOLERANCE_SELECTION");\r
- public static final Key KEY_RG_LISTENER = new KeyOf(IRouteGraphListener.class, "ROUTE_GRAPH_LISTENER");\r
- public static final Key KEY_RG_NODE = new SceneGraphNodeKey(RouteGraphNode.class, "ROUTE_GRAPH_NODE");\r
- \r
- public static final double BOUND_TOLERANCE = 0.9;\r
-\r
- public static final ElementClass CLASS =\r
- ElementClass.compile(\r
- TextImpl.INSTANCE,\r
-\r
- FixedTransform.INSTANCE,\r
-\r
- ConnectionBoundsAndPick.INSTANCE,\r
- ConnectionSelectionOutline.INSTANCE,\r
- ConnectionHandlerImpl.INSTANCE,\r
- ConnectionSceneGraph.INSTANCE,\r
- //SimpleElementLayers.INSTANCE,\r
-\r
- // Exists only loading connection visuals through ConnectionVisualsLoader\r
- ConfigurableEdgeVisuals.DEFAULT,\r
- FillColorImpl.BLACK\r
- ).setId(RouteGraphConnectionClass.class.getSimpleName());\r
-\r
-\r
- static class ConnectionHandlerImpl implements ConnectionHandler {\r
-\r
- public static final ConnectionHandlerImpl INSTANCE = new ConnectionHandlerImpl();\r
-\r
- private static final long serialVersionUID = 3267139233182458330L;\r
-\r
- @Override\r
- public Collection<IElement> getBranchPoints(IElement connection, Collection<IElement> result) {\r
- return Collections.<IElement>emptySet();\r
- }\r
-\r
- @Override\r
- public Collection<IElement> getChildren(IElement connection, Collection<IElement> result) {\r
- return Collections.emptySet();\r
- }\r
-\r
- @Override\r
- public Collection<IElement> getSegments(IElement connection, Collection<IElement> result) {\r
- return Collections.<IElement>emptySet();\r
- }\r
-\r
- @Override\r
- public Collection<Connection> getTerminalConnections(IElement connection, Collection<Connection> result) {\r
- ConnectionEntity ce = connection.getHint(ElementHints.KEY_CONNECTION_ENTITY);\r
- if (ce == null)\r
- return Collections.<Connection>emptySet();\r
- return ce.getTerminalConnections(result);\r
- }\r
-\r
- }\r
-\r
- static final class ConnectionSceneGraph implements SceneGraph {\r
-\r
- public static final ConnectionSceneGraph INSTANCE = new ConnectionSceneGraph();\r
-\r
- private static final long serialVersionUID = 4232871859964883266L;\r
-\r
- @Override\r
- public void init(IElement connection, G2DParentNode parent) {\r
- RouteGraph rg = connection.getHint(KEY_ROUTEGRAPH);\r
- IRouteGraphRenderer renderer = connection.getHint(KEY_RENDERER);\r
- if (rg == null || renderer == null) {\r
- cleanup(connection);\r
- } else {\r
- RouteGraphNode rgn = connection.getHint(KEY_RG_NODE);\r
- if (rgn == null) {\r
- rgn = parent.addNode(ElementUtils.generateNodeId(connection), RouteGraphNode.class);\r
- connection.setHint(KEY_RG_NODE, rgn);\r
- }\r
- rgn.setRouteGraph(rg);\r
- rgn.setRenderer(renderer);\r
-\r
- IRouteGraphListener listener = connection.getHint(KEY_RG_LISTENER);\r
- rgn.setRouteGraphListener(listener);\r
-\r
- Double tolerance = connection.getHint(KEY_PICK_TOLERANCE);\r
- if (tolerance != null)\r
- rgn.setPickTolerance(tolerance);\r
- }\r
- }\r
-\r
- @Override\r
- public void cleanup(IElement connection) {\r
- ElementUtils.removePossibleNode(connection, KEY_RG_NODE);\r
- connection.removeHint(KEY_RG_NODE);\r
- }\r
- }\r
-\r
- static final class ConnectionBoundsAndPick implements InternalSize, Outline, Pick {\r
-\r
- private static final long serialVersionUID = 4232871859964883266L;\r
-\r
- public static final ConnectionBoundsAndPick INSTANCE = new ConnectionBoundsAndPick();\r
-\r
- // Single-threaded system, should be fine to use this for everything.\r
- Rectangle2D temp = new Rectangle2D.Double();\r
-\r
- private Shape getSelectionShape(IElement e) {\r
- for (SelectionOutline so : e.getElementClass().getItemsByClass(SelectionOutline.class)) {\r
- Shape shape = so.getSelectionShape(e);\r
- if (shape != null)\r
- return shape;\r
- }\r
- // Using on-diagram coordinates because neither connections nor\r
- // edges have a non-identity transform which means that\r
- // coordinates are always absolute. Therefore branch point\r
- // shape also needs to be calculated in absolute coordinates.\r
- Shape shape = ElementUtils.getElementShapeOrBoundsOnDiagram(e);\r
- return shape;\r
- }\r
-\r
- @Override\r
- public boolean pickTest(IElement e, Shape s, PickPolicy policy) {\r
- RouteGraph rg = getRouteGraph(e);\r
- if (rg == null)\r
- return false;\r
-\r
- Rectangle2D bounds = getBounds(s);\r
- switch (policy) {\r
- case PICK_CONTAINED_OBJECTS:\r
- Shape selectionShape = getSelectionShape(e);\r
- return bounds.contains(selectionShape.getBounds2D());\r
- case PICK_INTERSECTING_OBJECTS:\r
- double tolerance = 0.0;\r
- if (e.containsHint(KEY_USE_TOLERANCE_IN_SELECTION))\r
- tolerance = getTolerance(e);\r
- else\r
- tolerance = (bounds.getHeight()+bounds.getHeight()) * 0.25;\r
- Object node = rg.pickLine(bounds.getCenterX(), bounds.getCenterY(), tolerance);\r
- return node != null;\r
- }\r
- return false;\r
- }\r
-\r
- @Override\r
- public Rectangle2D getBounds(IElement e, Rectangle2D size) {\r
- RouteGraph rg = getRouteGraph(e);\r
- if (rg != null) {\r
- if (size == null)\r
- size = new Rectangle2D.Double();\r
- rg.getBounds(size);\r
- }\r
- return size;\r
- }\r
-\r
- @Override\r
- public Shape getElementShape(IElement e) {\r
- RouteGraph rg = getRouteGraph(e);\r
- return rg == null ? null : rg.getPath2D();\r
- }\r
-\r
- private Rectangle2D getBounds(Shape shape) {\r
- if (shape instanceof Rectangle2D)\r
- return (Rectangle2D) shape;\r
- return shape.getBounds2D();\r
- }\r
-\r
- private RouteGraph getRouteGraph(IElement e) {\r
- RouteGraphNode rgn = e.getHint(KEY_RG_NODE);\r
- return rgn == null ? null : rgn.getRouteGraph();\r
- }\r
- \r
- private double getTolerance(IElement e) {\r
- RouteGraphNode rgn = e.getHint(KEY_RG_NODE);\r
- return rgn.getPickTolerance();\r
- }\r
-\r
- }\r
-\r
- public static int shortestDirectionOutOfBounds(double x, double y, Rectangle2D bounds) {\r
- double mx = bounds.getMinX();\r
- double Mx = bounds.getMaxX();\r
- double my = bounds.getMinY();\r
- double My = bounds.getMaxY();\r
-\r
- double up = y - my;\r
- double down = My - y;\r
- double left = x - mx;\r
- double right = Mx - x;\r
-\r
- // Insertion sort\r
- double[] dists = { right, down, left, up };\r
- byte[] masks = { 0x1, 0x2, 0x4, 0x8 };\r
- for (int i = 1; i < 4; ++i) {\r
- double value = dists[i];\r
- byte mask = masks[i];\r
- int j = i - 1;\r
- while (j >= 0 && dists[j] > value) {\r
- dists[j + 1] = dists[j];\r
- masks[j + 1] = masks[j];\r
- --j;\r
- }\r
- dists[j + 1] = value;\r
- masks[j + 1] = mask;\r
- }\r
-\r
- // Construct mask out of the shortest equal directions \r
- int mask = masks[0];\r
- double value = dists[0] / BOUND_TOLERANCE;\r
- for (int i = 1; i < 4; ++i) {\r
- if (dists[i] > value)\r
- break;\r
- mask |= masks[i];\r
- }\r
- return mask;\r
- }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.diagram.connection;
+
+import java.awt.Shape;
+import java.awt.geom.Rectangle2D;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.simantics.diagram.connection.rendering.IRouteGraphRenderer;
+import org.simantics.g2d.connection.ConnectionEntity;
+import org.simantics.g2d.connection.handler.ConnectionHandler;
+import org.simantics.g2d.diagram.handler.PickRequest.PickPolicy;
+import org.simantics.g2d.diagram.handler.Topology.Connection;
+import org.simantics.g2d.element.ElementClass;
+import org.simantics.g2d.element.ElementHints;
+import org.simantics.g2d.element.ElementUtils;
+import org.simantics.g2d.element.IElement;
+import org.simantics.g2d.element.SceneGraphNodeKey;
+import org.simantics.g2d.element.handler.InternalSize;
+import org.simantics.g2d.element.handler.Outline;
+import org.simantics.g2d.element.handler.Pick;
+import org.simantics.g2d.element.handler.SceneGraph;
+import org.simantics.g2d.element.handler.SelectionOutline;
+import org.simantics.g2d.element.handler.impl.ConfigurableEdgeVisuals;
+import org.simantics.g2d.element.handler.impl.ConnectionSelectionOutline;
+import org.simantics.g2d.element.handler.impl.FillColorImpl;
+import org.simantics.g2d.element.handler.impl.TextImpl;
+import org.simantics.g2d.elementclass.connection.EdgeClass.FixedTransform;
+import org.simantics.scenegraph.g2d.G2DParentNode;
+import org.simantics.scenegraph.g2d.nodes.connection.IRouteGraphListener;
+import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphNode;
+import org.simantics.utils.datastructures.hints.IHintContext.Key;
+import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;
+
+/**
+ * An element class for single connection entity elements. A connection entity
+ * consists of connection edge segments and branch points as its children.
+ *
+ * @author Tuukka Lehtonen
+ */
+public class RouteGraphConnectionClass {
+
+ public static final Key KEY_ROUTEGRAPH = new KeyOf(RouteGraph.class, "ROUTE_GRAPH");
+ public static final Key KEY_RENDERER = new KeyOf(IRouteGraphRenderer.class, "ROUTE_GRAPH_RENDERER");
+ public static final Key KEY_PICK_TOLERANCE = new KeyOf(Double.class, "PICK_TOLERANCE");
+ public static final Key KEY_USE_TOLERANCE_IN_SELECTION = new KeyOf(Boolean.class, "PICK_TOLERANCE_SELECTION");
+ public static final Key KEY_RG_LISTENER = new KeyOf(IRouteGraphListener.class, "ROUTE_GRAPH_LISTENER");
+ public static final Key KEY_RG_NODE = new SceneGraphNodeKey(RouteGraphNode.class, "ROUTE_GRAPH_NODE");
+
+ public static final double BOUND_TOLERANCE = 0.9;
+
+ public static final ElementClass CLASS =
+ ElementClass.compile(
+ TextImpl.INSTANCE,
+
+ FixedTransform.INSTANCE,
+
+ ConnectionBoundsAndPick.INSTANCE,
+ ConnectionSelectionOutline.INSTANCE,
+ ConnectionHandlerImpl.INSTANCE,
+ ConnectionSceneGraph.INSTANCE,
+ //SimpleElementLayers.INSTANCE,
+
+ // Exists only loading connection visuals through ConnectionVisualsLoader
+ ConfigurableEdgeVisuals.DEFAULT,
+ FillColorImpl.BLACK
+ ).setId(RouteGraphConnectionClass.class.getSimpleName());
+
+
+ static class ConnectionHandlerImpl implements ConnectionHandler {
+
+ public static final ConnectionHandlerImpl INSTANCE = new ConnectionHandlerImpl();
+
+ private static final long serialVersionUID = 3267139233182458330L;
+
+ @Override
+ public Collection<IElement> getBranchPoints(IElement connection, Collection<IElement> result) {
+ return Collections.<IElement>emptySet();
+ }
+
+ @Override
+ public Collection<IElement> getChildren(IElement connection, Collection<IElement> result) {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Collection<IElement> getSegments(IElement connection, Collection<IElement> result) {
+ return Collections.<IElement>emptySet();
+ }
+
+ @Override
+ public Collection<Connection> getTerminalConnections(IElement connection, Collection<Connection> result) {
+ ConnectionEntity ce = connection.getHint(ElementHints.KEY_CONNECTION_ENTITY);
+ if (ce == null)
+ return Collections.<Connection>emptySet();
+ return ce.getTerminalConnections(result);
+ }
+
+ }
+
+ static final class ConnectionSceneGraph implements SceneGraph {
+
+ public static final ConnectionSceneGraph INSTANCE = new ConnectionSceneGraph();
+
+ private static final long serialVersionUID = 4232871859964883266L;
+
+ @Override
+ public void init(IElement connection, G2DParentNode parent) {
+ RouteGraph rg = connection.getHint(KEY_ROUTEGRAPH);
+ IRouteGraphRenderer renderer = connection.getHint(KEY_RENDERER);
+ if (rg == null || renderer == null) {
+ cleanup(connection);
+ } else {
+ RouteGraphNode rgn = connection.getHint(KEY_RG_NODE);
+ if (rgn == null) {
+ rgn = parent.addNode(ElementUtils.generateNodeId(connection), RouteGraphNode.class);
+ connection.setHint(KEY_RG_NODE, rgn);
+ }
+ rgn.setRouteGraph(rg);
+ rgn.setRenderer(renderer);
+
+ IRouteGraphListener listener = connection.getHint(KEY_RG_LISTENER);
+ rgn.setRouteGraphListener(listener);
+
+ Double tolerance = connection.getHint(KEY_PICK_TOLERANCE);
+ if (tolerance != null)
+ rgn.setPickTolerance(tolerance);
+ }
+ }
+
+ @Override
+ public void cleanup(IElement connection) {
+ ElementUtils.removePossibleNode(connection, KEY_RG_NODE);
+ connection.removeHint(KEY_RG_NODE);
+ }
+ }
+
+ static final class ConnectionBoundsAndPick implements InternalSize, Outline, Pick {
+
+ private static final long serialVersionUID = 4232871859964883266L;
+
+ public static final ConnectionBoundsAndPick INSTANCE = new ConnectionBoundsAndPick();
+
+ // Single-threaded system, should be fine to use this for everything.
+ Rectangle2D temp = new Rectangle2D.Double();
+
+ private Shape getSelectionShape(IElement e) {
+ for (SelectionOutline so : e.getElementClass().getItemsByClass(SelectionOutline.class)) {
+ Shape shape = so.getSelectionShape(e);
+ if (shape != null)
+ return shape;
+ }
+ // Using on-diagram coordinates because neither connections nor
+ // edges have a non-identity transform which means that
+ // coordinates are always absolute. Therefore branch point
+ // shape also needs to be calculated in absolute coordinates.
+ Shape shape = ElementUtils.getElementShapeOrBoundsOnDiagram(e);
+ return shape;
+ }
+
+ @Override
+ public boolean pickTest(IElement e, Shape s, PickPolicy policy) {
+ RouteGraph rg = getRouteGraph(e);
+ if (rg == null)
+ return false;
+
+ Rectangle2D bounds = getBounds(s);
+ switch (policy) {
+ case PICK_CONTAINED_OBJECTS:
+ Shape selectionShape = getSelectionShape(e);
+ return bounds.contains(selectionShape.getBounds2D());
+ case PICK_INTERSECTING_OBJECTS:
+ double tolerance = 0.0;
+ if (e.containsHint(KEY_USE_TOLERANCE_IN_SELECTION))
+ tolerance = getTolerance(e);
+ else
+ tolerance = (bounds.getHeight()+bounds.getHeight()) * 0.25;
+ Object node = rg.pickLine(bounds.getCenterX(), bounds.getCenterY(), tolerance);
+ return node != null;
+ }
+ return false;
+ }
+
+ @Override
+ public Rectangle2D getBounds(IElement e, Rectangle2D size) {
+ RouteGraph rg = getRouteGraph(e);
+ if (rg != null) {
+ if (size == null)
+ size = new Rectangle2D.Double();
+ rg.getBounds(size);
+ }
+ return size;
+ }
+
+ @Override
+ public Shape getElementShape(IElement e) {
+ RouteGraph rg = getRouteGraph(e);
+ return rg == null ? null : rg.getPath2D();
+ }
+
+ private Rectangle2D getBounds(Shape shape) {
+ if (shape instanceof Rectangle2D)
+ return (Rectangle2D) shape;
+ return shape.getBounds2D();
+ }
+
+ private RouteGraph getRouteGraph(IElement e) {
+ RouteGraphNode rgn = e.getHint(KEY_RG_NODE);
+ return rgn == null ? null : rgn.getRouteGraph();
+ }
+
+ private double getTolerance(IElement e) {
+ RouteGraphNode rgn = e.getHint(KEY_RG_NODE);
+ return rgn.getPickTolerance();
+ }
+
+ }
+
+ public static int shortestDirectionOutOfBounds(double x, double y, Rectangle2D bounds) {
+ double mx = bounds.getMinX();
+ double Mx = bounds.getMaxX();
+ double my = bounds.getMinY();
+ double My = bounds.getMaxY();
+
+ double up = y - my;
+ double down = My - y;
+ double left = x - mx;
+ double right = Mx - x;
+
+ // Insertion sort
+ double[] dists = { right, down, left, up };
+ byte[] masks = { 0x1, 0x2, 0x4, 0x8 };
+ for (int i = 1; i < 4; ++i) {
+ double value = dists[i];
+ byte mask = masks[i];
+ int j = i - 1;
+ while (j >= 0 && dists[j] > value) {
+ dists[j + 1] = dists[j];
+ masks[j + 1] = masks[j];
+ --j;
+ }
+ dists[j + 1] = value;
+ masks[j + 1] = mask;
+ }
+
+ // Construct mask out of the shortest equal directions
+ int mask = masks[0];
+ double value = dists[0] / BOUND_TOLERANCE;
+ for (int i = 1; i < 4; ++i) {
+ if (dists[i] > value)
+ break;
+ mask |= masks[i];
+ }
+ return mask;
+ }
+
+}