-/*******************************************************************************
- * 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;
- }
-
-}