--- /dev/null
+/*******************************************************************************
+ * 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.g2d.elementclass;
+
+import java.awt.Shape;
+import java.awt.geom.Rectangle2D;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.simantics.diagram.connection.RouteGraph;
+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;
+ }
+
+}