]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/pointertool/TerminalUtil.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / diagram / participant / pointertool / TerminalUtil.java
index 409af79d69e4e0febb7fd69375ccb8f76d867fe1..85f75a06638150f83af0fc4f413836a7f6a6d0b9 100644 (file)
-/*******************************************************************************\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.g2d.diagram.participant.pointertool;\r
-\r
-import java.awt.Shape;\r
-import java.awt.geom.AffineTransform;\r
-import java.awt.geom.Point2D;\r
-import java.awt.geom.Rectangle2D;\r
-import java.util.ArrayList;\r
-import java.util.Collections;\r
-import java.util.Comparator;\r
-import java.util.List;\r
-\r
-import org.simantics.g2d.diagram.DiagramUtils;\r
-import org.simantics.g2d.diagram.IDiagram;\r
-import org.simantics.g2d.diagram.handler.PickRequest;\r
-import org.simantics.g2d.diagram.handler.Topology.Terminal;\r
-import org.simantics.g2d.element.ElementUtils;\r
-import org.simantics.g2d.element.IElement;\r
-import org.simantics.g2d.element.handler.BendsHandler;\r
-import org.simantics.g2d.element.handler.BendsHandler.Bend;\r
-import org.simantics.g2d.element.handler.TerminalLayout;\r
-import org.simantics.g2d.element.handler.TerminalTopology;\r
-import org.simantics.g2d.utils.GeometryUtils;\r
-import org.simantics.g2d.utils.geom.DirectionSet;\r
-\r
-/**\r
- * @author Toni Kalajainen\r
- */\r
-public class TerminalUtil {\r
-\r
-    /**\r
-     * Thread local terminal list for keeping memory allocations down.\r
-     */\r
-    private static final ThreadLocal<ArrayList<Terminal>> TERMINALS = new ThreadLocal<ArrayList<Terminal>>() {\r
-        @Override\r
-        protected ArrayList<Terminal> initialValue() {\r
-            return new ArrayList<Terminal>();\r
-        }\r
-    };\r
-\r
-    /**\r
-     * Thread local element list for keeping memory allocations down.\r
-     */\r
-    private static final ThreadLocal<ArrayList<IElement>> ELEMENTS = new ThreadLocal<ArrayList<IElement>>() {\r
-        @Override\r
-        protected ArrayList<IElement> initialValue() {\r
-            return new ArrayList<IElement>();\r
-        }\r
-    };\r
-\r
-    public static class TerminalInfo {\r
-        public IElement e;\r
-        public Terminal t;\r
-        public AffineTransform posElem; // on element\r
-        public AffineTransform posDia; // on diagram\r
-        public Shape shape; // Shape or null\r
-        public double distance; // Distance of terminal from pick point in millimeters\r
-\r
-        @Override\r
-        public String toString() {\r
-            StringBuilder sb = new StringBuilder();\r
-            sb.append('[')\r
-            .append("element=").append(e)\r
-            .append(", terminal=").append(t)\r
-            .append(", posDia=").append(posDia)\r
-            .append(", shape=").append(shape)\r
-            .append(", distance=").append(distance)\r
-            .append(']');\r
-            return sb.toString();\r
-        }\r
-\r
-        public static TerminalInfo create(Point2D p, IElement e, Terminal t, Shape terminalShape) {\r
-            AffineTransform at = AffineTransform.getTranslateInstance(p.getX(), p.getY());\r
-            TerminalInfo ti = new TerminalInfo();\r
-            ti.e = e;\r
-            ti.t = t;\r
-            ti.posElem = at;\r
-            ti.posDia = at;\r
-            ti.shape = terminalShape;\r
-            return ti;\r
-        }\r
-    }\r
-    private static final Rectangle2D POINT_PICK_SHAPE = new Rectangle2D.Double(0, 0, 0.001, 0.001);\r
-\r
-    public static final Comparator<TerminalInfo> ASCENDING_DISTANCE_ORDER = new Comparator<TerminalInfo>() {\r
-        @Override\r
-        public int compare(TerminalInfo o1, TerminalInfo o2) {\r
-            double d1 = o1.distance;\r
-            double d2 = o2.distance;\r
-            if (d1 < d2)\r
-                return -1;\r
-            if (d1 > d2)\r
-                return 1;\r
-            return 0;\r
-        }\r
-    };\r
-\r
-    public static class BendsInfo {\r
-        public IElement e;\r
-        public Bend b;\r
-    }\r
-\r
-    /**\r
-     * Pick terminals\r
-     * @param d diagram\r
-     * @param pickShape pick area or null for the whole canvas (return all terminals)\r
-     * @param pickPointTerminals pick terminals of a single point\r
-     * @param pickAreaTerminals pick terminals that have a shape\r
-     * @return terminals in z-order (bottom to top)\r
-     */\r
-    public static List<TerminalInfo> pickTerminals(IDiagram d, Shape pickShape, boolean pickPointTerminals, boolean pickAreaTerminals)\r
-    {\r
-        boolean clearElements = false;\r
-        List<IElement> elements = null;\r
-        // Pick\r
-        if (pickShape != null) {\r
-            elements = ELEMENTS.get();\r
-            elements.clear();\r
-            clearElements = true;\r
-            PickRequest req = new PickRequest(pickShape);\r
-            DiagramUtils.pick(d, req, elements);\r
-        } else {\r
-            // Select all terminals\r
-            elements = d.getElements();\r
-        }\r
-        if (elements.isEmpty())\r
-            return Collections.emptyList();\r
-\r
-        double pickCenterX = 0;\r
-        double pickCenterY = 0;\r
-        if (pickShape != null) {\r
-            Rectangle2D bounds = pickShape.getBounds2D();\r
-            pickCenterX = bounds.getCenterX();\r
-            pickCenterY = bounds.getCenterY();\r
-        }\r
-\r
-        List<TerminalInfo> result = new ArrayList<TerminalInfo>();\r
-        ArrayList<Terminal> terminals = TERMINALS.get();\r
-        for (IElement e : elements)\r
-        {\r
-            TerminalTopology tt = e.getElementClass().getAtMostOneItemOfClass(TerminalTopology.class);\r
-            if (tt==null) continue;\r
-            terminals.clear();\r
-            tt.getTerminals(e, terminals);\r
-            if (terminals.isEmpty()) continue;\r
-\r
-            List<TerminalLayout> tls = e.getElementClass().getItemsByClass(TerminalLayout.class);\r
-\r
-            for (Terminal t : terminals)\r
-            {\r
-                Shape terminalShape = getTerminalShape(tls, e, t);\r
-                if ( terminalShape==null /* point terminal */ && !pickPointTerminals ) continue;\r
-                if ( terminalShape!=null /* are terminal */ && !pickAreaTerminals ) continue;\r
-                AffineTransform terminalToDiagram = getTerminalPosOnDiagram(e, t);\r
-\r
-                // Pick distance will is set to 0 if there was no pick shape,\r
-                // i.e. everything is picked.\r
-                double pickDist = 0;\r
-                if (pickShape != null) {\r
-                    Shape pickTargetShape = terminalShape != null ? terminalShape : POINT_PICK_SHAPE;\r
-                    // Point Terminal uses a very small box as pick shape\r
-                    pickTargetShape = GeometryUtils.transformShape(pickTargetShape, terminalToDiagram);\r
-                    if (!GeometryUtils.intersects(pickShape, pickTargetShape)) continue;\r
-\r
-                    pickDist = Point2D.distance(pickCenterX, pickCenterY, terminalToDiagram.getTranslateX(), terminalToDiagram.getTranslateY());\r
-                }\r
-\r
-                AffineTransform terminalToElement = getTerminalPosOnElement(e, t);\r
-                TerminalInfo ti = new TerminalInfo();\r
-                ti.e = e;\r
-                ti.posDia = terminalToDiagram;\r
-                ti.posElem = terminalToElement;\r
-                ti.t = t;\r
-                ti.shape = terminalShape;\r
-                ti.distance = pickDist;\r
-                result.add(ti);\r
-            }\r
-        }\r
-\r
-        if (clearElements)\r
-            elements.clear();\r
-        terminals.clear();\r
-\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * Pick terminals\r
-     * @param d diagram\r
-     * @param pickShape pick area (in diagram coordinate system)\r
-     * @return terminals in z-order (bottom to top)\r
-     */\r
-    public static TerminalInfo pickTerminal(IDiagram diagram, Shape pickShape)\r
-    {\r
-        ArrayList<IElement> elements = ELEMENTS.get();\r
-        elements.clear();\r
-        PickRequest req = new PickRequest(pickShape);\r
-        DiagramUtils.pick(diagram, req, elements);\r
-        if (elements.isEmpty())\r
-            return null;\r
-\r
-        TerminalInfo  result = new TerminalInfo();\r
-        double        bestShortestDist = Double.MAX_VALUE;\r
-        Rectangle2D   bounds = pickShape.getBounds2D();\r
-        double        pickCenterX = bounds.getCenterX();\r
-        double        pickCenterY = bounds.getCenterY();\r
-\r
-        ArrayList<Terminal> terminals = TERMINALS.get();\r
-        for (IElement e : elements)\r
-        {\r
-            TerminalTopology tt = e.getElementClass().getAtMostOneItemOfClass(TerminalTopology.class);\r
-            if (tt==null) continue;\r
-            terminals.clear();\r
-            tt.getTerminals(e, terminals);\r
-            for (Terminal t : terminals)\r
-            {\r
-                Shape terminalShape = getTerminalShape(e, t);\r
-                AffineTransform terminalToDiagram = getTerminalPosOnDiagram(e, t);\r
-                Shape pickTargetShape = terminalShape != null ? terminalShape : POINT_PICK_SHAPE;\r
-                pickTargetShape = GeometryUtils.transformShape(pickTargetShape, terminalToDiagram);\r
-                if (!GeometryUtils.intersects(pickShape, pickTargetShape)) continue;\r
-\r
-                double pickDist = Point2D.distanceSq(pickCenterX, pickCenterY, terminalToDiagram.getTranslateX(), terminalToDiagram.getTranslateY());\r
-                if (pickDist>bestShortestDist) continue;\r
-\r
-                result.e = e;\r
-                result.posDia = terminalToDiagram;\r
-                result.posElem = getTerminalPosOnElement(e, t);\r
-                result.t = t;\r
-                result.shape = terminalShape;\r
-                result.distance = Math.sqrt(pickDist);\r
-                bestShortestDist = pickDist;\r
-            }\r
-        }\r
-        elements.clear();\r
-        terminals.clear();\r
-        if (bestShortestDist==Double.MAX_VALUE) return null;\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * Get directions\r
-     * @param e\r
-     * @param t\r
-     * @param directions null or direction set\r
-     * @return\r
-     */\r
-    public static DirectionSet getTerminalDirectionSet(IElement e, Terminal t, DirectionSet directions)\r
-    {\r
-        if (directions == null) directions = new DirectionSet();\r
-        for (TerminalLayout tl : e.getElementClass().getItemsByClass(TerminalLayout.class))\r
-            tl.getTerminalDirection(e, t, directions);\r
-        return directions;\r
-    }\r
-\r
-    /**\r
-     * Get directions\r
-     * @param e\r
-     * @param t\r
-     * @param directions null or direction set\r
-     * @return\r
-     */\r
-    public static DirectionSet getTerminalPosition(IElement e, Terminal t, DirectionSet directions)\r
-    {\r
-        if (directions == null) directions = new DirectionSet();\r
-        for (TerminalLayout tl : e.getElementClass().getItemsByClass(TerminalLayout.class))\r
-            tl.getTerminalDirection(e, t, directions);\r
-        return directions;\r
-    }\r
-\r
-    public static Point2D getTerminalCenterPosOnDiagram(IElement e, Terminal t)\r
-    {\r
-        Shape shape = getTerminalShape(e, t);\r
-        Point2D terminalCenterPos = new Point2D.Double();\r
-        if (shape!=null) {\r
-            Rectangle2D rect = shape.getBounds2D();\r
-            terminalCenterPos.setLocation(rect.getCenterX(), rect.getCenterY());\r
-        }\r
-        // Transform to diagram\r
-        AffineTransform at = getTerminalPosOnDiagram(e, t);\r
-        at.transform(terminalCenterPos, terminalCenterPos);\r
-        return terminalCenterPos;\r
-    }\r
-\r
-    /**\r
-     * Get position of a terminal on diagram\r
-     * @param e element\r
-     * @param t terminal\r
-     * @return position of a terminal on diagram\r
-     */\r
-    public static AffineTransform getTerminalPosOnDiagram(IElement e, Terminal t)\r
-    {\r
-        AffineTransform        pos     = getTerminalPosOnElement(e, t);\r
-        AffineTransform        at      = ElementUtils.getTransform(e);\r
-        AffineTransform result = new AffineTransform(at);\r
-        result.concatenate(pos);\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * Get position of a terminal in element\r
-     * @param e element\r
-     * @param t terminal\r
-     * @return Transform of a terminal\r
-     */\r
-    public static AffineTransform getTerminalPosOnElement(IElement e, Terminal t)\r
-    {\r
-        List<TerminalLayout>   tls = e.getElementClass().getItemsByClass(TerminalLayout.class);\r
-        AffineTransform                        result = null;\r
-        for (TerminalLayout tl : tls) {\r
-            result = tl.getTerminalPosition(e, t);\r
-            if (result!=null) return result;\r
-        }\r
-        return null;\r
-    }\r
-\r
-    /**\r
-     * Get terminal shape\r
-     * @param e element\r
-     * @param t terminal\r
-     * @return terminal shape or null\r
-     */\r
-    public static Shape getTerminalShape(IElement e, Terminal t)\r
-    {\r
-        List<TerminalLayout> tls = e.getElementClass().getItemsByClass(TerminalLayout.class);\r
-        return getTerminalShape(tls, e, t);\r
-    }\r
-\r
-    private static Shape getTerminalShape(List<TerminalLayout> tls, IElement e, Terminal t)\r
-    {\r
-        for (TerminalLayout tl : tls) {\r
-            Shape result = tl.getTerminalShape(e, t);\r
-            if (result != null) return result;\r
-        }\r
-        return null;\r
-    }\r
-\r
-    /**\r
-     * \r
-     * @param diagram\r
-     * @param pickShape\r
-     * @return bends or null\r
-     */\r
-    public BendsInfo pickBends(IDiagram diagram, Shape pickShape)\r
-    {\r
-        BendsInfo              result = null;\r
-        double                         bestShortestDist = Double.MAX_VALUE;\r
-        Rectangle2D    pickShapeBounds = pickShape.getBounds2D();\r
-        Point2D                pickShapeCenter = new Point2D.Double(pickShapeBounds.getCenterX(), pickShapeBounds.getCenterY());\r
-\r
-        ArrayList<IElement> elements = ELEMENTS.get();\r
-        elements.clear();\r
-        PickRequest req = new PickRequest(pickShape);\r
-        DiagramUtils.pick(diagram, req, elements);\r
-\r
-        ArrayList<Bend> bends = new ArrayList<Bend>();\r
-        Point2D bendPos = new Point2D.Double();\r
-        for (IElement e : diagram.getElements())\r
-        {\r
-            AffineTransform elementToDiagram = ElementUtils.getTransform(e);\r
-            BendsHandler bh = e.getElementClass().getSingleItem(BendsHandler.class);\r
-            if (bh==null) continue;\r
-            bends.clear(); bh.getBends(e, bends);\r
-            for (Bend b : bends)\r
-            {\r
-                bh.getBendPosition(e, b, bendPos);\r
-                elementToDiagram.transform(bendPos, bendPos);\r
-                if (!pickShape.contains(bendPos)) continue;\r
-                double dist = bendPos.distance(pickShapeCenter);\r
-                if (dist>bestShortestDist) continue;\r
-                dist = bestShortestDist;\r
-                result = new BendsInfo();\r
-                result.e = e;\r
-                result.b = b;\r
-            }\r
-        }\r
-        elements.clear();\r
-        if (bestShortestDist==Double.MAX_VALUE) return null;\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * Checks whether the element/terminal information of the two specified\r
-     * TerminalInfo structures match.\r
-     * \r
-     * @param t1\r
-     * @param t2\r
-     * @return <code>true</code> if the element and terminal instances of both\r
-     *         structures are the same, <code>false</code> otherwise\r
-     */\r
-    public static boolean isSameTerminal(TerminalInfo t1, TerminalInfo t2) {\r
-        if (t1 == null || t2 == null)\r
-            return false;\r
-        return t1.e.equals(t2.e) && t1.t.equals(t2.e);\r
-    }\r
-\r
-    /**\r
-     * Finds those terminals among the specified set that are\r
-     * <ol>\r
-     * <li>nearest and equal in distance (see TerminalInfo.distance)</li>\r
-     * <li>have the same absolute diagram position</li>\r
-     * </ol>\r
-     * \r
-     * @param tis the picked terminals to examine\r
-     * @return the nearest position-wise overlapping terminals\r
-     */\r
-    public static List<TerminalInfo> findNearestOverlappingTerminals(List<TerminalInfo> tis) {\r
-        int len = tis.size();\r
-        if (len < 2)\r
-            return tis;\r
-\r
-        // Only gather the nearest terminals that are\r
-        // directly on top of each other\r
-\r
-        TerminalInfo nearest = null;\r
-        for (int i = 0; i < len; ++i) {\r
-            TerminalInfo ti = tis.get(i);\r
-            if (nearest == null || ti.distance < nearest.distance) {\r
-                nearest = ti;\r
-            }\r
-        }\r
-\r
-        ArrayList<TerminalInfo> result = new ArrayList<TerminalInfo>(len);\r
-        for (int i = 0; i < len; ++i) {\r
-            TerminalInfo ti = tis.get(i);\r
-            if (ti.distance == nearest.distance\r
-                    //&& ti.e.equals(nearest.e)\r
-                    && ti.posDia.equals(nearest.posDia))\r
-            {\r
-                result.add(ti);\r
-            }\r
-        }\r
-\r
-        return result;\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.g2d.diagram.participant.pointertool;
+
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.simantics.g2d.diagram.DiagramUtils;
+import org.simantics.g2d.diagram.IDiagram;
+import org.simantics.g2d.diagram.handler.PickRequest;
+import org.simantics.g2d.diagram.handler.Topology.Terminal;
+import org.simantics.g2d.element.ElementUtils;
+import org.simantics.g2d.element.IElement;
+import org.simantics.g2d.element.handler.BendsHandler;
+import org.simantics.g2d.element.handler.BendsHandler.Bend;
+import org.simantics.g2d.element.handler.TerminalLayout;
+import org.simantics.g2d.element.handler.TerminalTopology;
+import org.simantics.g2d.utils.GeometryUtils;
+import org.simantics.g2d.utils.geom.DirectionSet;
+
+/**
+ * @author Toni Kalajainen
+ */
+public class TerminalUtil {
+
+    /**
+     * Thread local terminal list for keeping memory allocations down.
+     */
+    private static final ThreadLocal<ArrayList<Terminal>> TERMINALS = new ThreadLocal<ArrayList<Terminal>>() {
+        @Override
+        protected ArrayList<Terminal> initialValue() {
+            return new ArrayList<Terminal>();
+        }
+    };
+
+    /**
+     * Thread local element list for keeping memory allocations down.
+     */
+    private static final ThreadLocal<ArrayList<IElement>> ELEMENTS = new ThreadLocal<ArrayList<IElement>>() {
+        @Override
+        protected ArrayList<IElement> initialValue() {
+            return new ArrayList<IElement>();
+        }
+    };
+
+    public static class TerminalInfo {
+        public IElement e;
+        public Terminal t;
+        public AffineTransform posElem; // on element
+        public AffineTransform posDia; // on diagram
+        public Shape shape; // Shape or null
+        public double distance; // Distance of terminal from pick point in millimeters
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append('[')
+            .append("element=").append(e)
+            .append(", terminal=").append(t)
+            .append(", posDia=").append(posDia)
+            .append(", shape=").append(shape)
+            .append(", distance=").append(distance)
+            .append(']');
+            return sb.toString();
+        }
+
+        public static TerminalInfo create(Point2D p, IElement e, Terminal t, Shape terminalShape) {
+            AffineTransform at = AffineTransform.getTranslateInstance(p.getX(), p.getY());
+            TerminalInfo ti = new TerminalInfo();
+            ti.e = e;
+            ti.t = t;
+            ti.posElem = at;
+            ti.posDia = at;
+            ti.shape = terminalShape;
+            return ti;
+        }
+    }
+    private static final Rectangle2D POINT_PICK_SHAPE = new Rectangle2D.Double(0, 0, 0.001, 0.001);
+
+    public static final Comparator<TerminalInfo> ASCENDING_DISTANCE_ORDER = new Comparator<TerminalInfo>() {
+        @Override
+        public int compare(TerminalInfo o1, TerminalInfo o2) {
+            double d1 = o1.distance;
+            double d2 = o2.distance;
+            if (d1 < d2)
+                return -1;
+            if (d1 > d2)
+                return 1;
+            return 0;
+        }
+    };
+
+    public static class BendsInfo {
+        public IElement e;
+        public Bend b;
+    }
+
+    /**
+     * Pick terminals
+     * @param d diagram
+     * @param pickShape pick area or null for the whole canvas (return all terminals)
+     * @param pickPointTerminals pick terminals of a single point
+     * @param pickAreaTerminals pick terminals that have a shape
+     * @return terminals in z-order (bottom to top)
+     */
+    public static List<TerminalInfo> pickTerminals(IDiagram d, Shape pickShape, boolean pickPointTerminals, boolean pickAreaTerminals)
+    {
+        boolean clearElements = false;
+        List<IElement> elements = null;
+        // Pick
+        if (pickShape != null) {
+            elements = ELEMENTS.get();
+            elements.clear();
+            clearElements = true;
+            PickRequest req = new PickRequest(pickShape);
+            DiagramUtils.pick(d, req, elements);
+        } else {
+            // Select all terminals
+            elements = d.getElements();
+        }
+        if (elements.isEmpty())
+            return Collections.emptyList();
+
+        double pickCenterX = 0;
+        double pickCenterY = 0;
+        if (pickShape != null) {
+            Rectangle2D bounds = pickShape.getBounds2D();
+            pickCenterX = bounds.getCenterX();
+            pickCenterY = bounds.getCenterY();
+        }
+
+        List<TerminalInfo> result = new ArrayList<TerminalInfo>();
+        ArrayList<Terminal> terminals = TERMINALS.get();
+        for (IElement e : elements)
+        {
+            TerminalTopology tt = e.getElementClass().getAtMostOneItemOfClass(TerminalTopology.class);
+            if (tt==null) continue;
+            terminals.clear();
+            tt.getTerminals(e, terminals);
+            if (terminals.isEmpty()) continue;
+
+            List<TerminalLayout> tls = e.getElementClass().getItemsByClass(TerminalLayout.class);
+
+            for (Terminal t : terminals)
+            {
+                Shape terminalShape = getTerminalShape(tls, e, t);
+                if ( terminalShape==null /* point terminal */ && !pickPointTerminals ) continue;
+                if ( terminalShape!=null /* are terminal */ && !pickAreaTerminals ) continue;
+                AffineTransform terminalToDiagram = getTerminalPosOnDiagram(e, t);
+
+                // Pick distance will is set to 0 if there was no pick shape,
+                // i.e. everything is picked.
+                double pickDist = 0;
+                if (pickShape != null) {
+                    Shape pickTargetShape = terminalShape != null ? terminalShape : POINT_PICK_SHAPE;
+                    // Point Terminal uses a very small box as pick shape
+                    pickTargetShape = GeometryUtils.transformShape(pickTargetShape, terminalToDiagram);
+                    if (!GeometryUtils.intersects(pickShape, pickTargetShape)) continue;
+
+                    pickDist = Point2D.distance(pickCenterX, pickCenterY, terminalToDiagram.getTranslateX(), terminalToDiagram.getTranslateY());
+                }
+
+                AffineTransform terminalToElement = getTerminalPosOnElement(e, t);
+                TerminalInfo ti = new TerminalInfo();
+                ti.e = e;
+                ti.posDia = terminalToDiagram;
+                ti.posElem = terminalToElement;
+                ti.t = t;
+                ti.shape = terminalShape;
+                ti.distance = pickDist;
+                result.add(ti);
+            }
+        }
+
+        if (clearElements)
+            elements.clear();
+        terminals.clear();
+
+        return result;
+    }
+
+    /**
+     * Pick terminals
+     * @param d diagram
+     * @param pickShape pick area (in diagram coordinate system)
+     * @return terminals in z-order (bottom to top)
+     */
+    public static TerminalInfo pickTerminal(IDiagram diagram, Shape pickShape)
+    {
+        ArrayList<IElement> elements = ELEMENTS.get();
+        elements.clear();
+        PickRequest req = new PickRequest(pickShape);
+        DiagramUtils.pick(diagram, req, elements);
+        if (elements.isEmpty())
+            return null;
+
+        TerminalInfo  result = new TerminalInfo();
+        double        bestShortestDist = Double.MAX_VALUE;
+        Rectangle2D   bounds = pickShape.getBounds2D();
+        double        pickCenterX = bounds.getCenterX();
+        double        pickCenterY = bounds.getCenterY();
+
+        ArrayList<Terminal> terminals = TERMINALS.get();
+        for (IElement e : elements)
+        {
+            TerminalTopology tt = e.getElementClass().getAtMostOneItemOfClass(TerminalTopology.class);
+            if (tt==null) continue;
+            terminals.clear();
+            tt.getTerminals(e, terminals);
+            for (Terminal t : terminals)
+            {
+                Shape terminalShape = getTerminalShape(e, t);
+                AffineTransform terminalToDiagram = getTerminalPosOnDiagram(e, t);
+                Shape pickTargetShape = terminalShape != null ? terminalShape : POINT_PICK_SHAPE;
+                pickTargetShape = GeometryUtils.transformShape(pickTargetShape, terminalToDiagram);
+                if (!GeometryUtils.intersects(pickShape, pickTargetShape)) continue;
+
+                double pickDist = Point2D.distanceSq(pickCenterX, pickCenterY, terminalToDiagram.getTranslateX(), terminalToDiagram.getTranslateY());
+                if (pickDist>bestShortestDist) continue;
+
+                result.e = e;
+                result.posDia = terminalToDiagram;
+                result.posElem = getTerminalPosOnElement(e, t);
+                result.t = t;
+                result.shape = terminalShape;
+                result.distance = Math.sqrt(pickDist);
+                bestShortestDist = pickDist;
+            }
+        }
+        elements.clear();
+        terminals.clear();
+        if (bestShortestDist==Double.MAX_VALUE) return null;
+        return result;
+    }
+
+    /**
+     * Get directions
+     * @param e
+     * @param t
+     * @param directions null or direction set
+     * @return
+     */
+    public static DirectionSet getTerminalDirectionSet(IElement e, Terminal t, DirectionSet directions)
+    {
+        if (directions == null) directions = new DirectionSet();
+        for (TerminalLayout tl : e.getElementClass().getItemsByClass(TerminalLayout.class))
+            tl.getTerminalDirection(e, t, directions);
+        return directions;
+    }
+
+    /**
+     * Get directions
+     * @param e
+     * @param t
+     * @param directions null or direction set
+     * @return
+     */
+    public static DirectionSet getTerminalPosition(IElement e, Terminal t, DirectionSet directions)
+    {
+        if (directions == null) directions = new DirectionSet();
+        for (TerminalLayout tl : e.getElementClass().getItemsByClass(TerminalLayout.class))
+            tl.getTerminalDirection(e, t, directions);
+        return directions;
+    }
+
+    public static Point2D getTerminalCenterPosOnDiagram(IElement e, Terminal t)
+    {
+        Shape shape = getTerminalShape(e, t);
+        Point2D terminalCenterPos = new Point2D.Double();
+        if (shape!=null) {
+            Rectangle2D rect = shape.getBounds2D();
+            terminalCenterPos.setLocation(rect.getCenterX(), rect.getCenterY());
+        }
+        // Transform to diagram
+        AffineTransform at = getTerminalPosOnDiagram(e, t);
+        at.transform(terminalCenterPos, terminalCenterPos);
+        return terminalCenterPos;
+    }
+
+    /**
+     * Get position of a terminal on diagram
+     * @param e element
+     * @param t terminal
+     * @return position of a terminal on diagram
+     */
+    public static AffineTransform getTerminalPosOnDiagram(IElement e, Terminal t)
+    {
+        AffineTransform        pos     = getTerminalPosOnElement(e, t);
+        AffineTransform        at      = ElementUtils.getTransform(e);
+        AffineTransform result = new AffineTransform(at);
+        result.concatenate(pos);
+        return result;
+    }
+
+    /**
+     * Get position of a terminal in element
+     * @param e element
+     * @param t terminal
+     * @return Transform of a terminal
+     */
+    public static AffineTransform getTerminalPosOnElement(IElement e, Terminal t)
+    {
+        List<TerminalLayout>   tls = e.getElementClass().getItemsByClass(TerminalLayout.class);
+        AffineTransform                        result = null;
+        for (TerminalLayout tl : tls) {
+            result = tl.getTerminalPosition(e, t);
+            if (result!=null) return result;
+        }
+        return null;
+    }
+
+    /**
+     * Get terminal shape
+     * @param e element
+     * @param t terminal
+     * @return terminal shape or null
+     */
+    public static Shape getTerminalShape(IElement e, Terminal t)
+    {
+        List<TerminalLayout> tls = e.getElementClass().getItemsByClass(TerminalLayout.class);
+        return getTerminalShape(tls, e, t);
+    }
+
+    private static Shape getTerminalShape(List<TerminalLayout> tls, IElement e, Terminal t)
+    {
+        for (TerminalLayout tl : tls) {
+            Shape result = tl.getTerminalShape(e, t);
+            if (result != null) return result;
+        }
+        return null;
+    }
+
+    /**
+     * 
+     * @param diagram
+     * @param pickShape
+     * @return bends or null
+     */
+    public BendsInfo pickBends(IDiagram diagram, Shape pickShape)
+    {
+        BendsInfo              result = null;
+        double                         bestShortestDist = Double.MAX_VALUE;
+        Rectangle2D    pickShapeBounds = pickShape.getBounds2D();
+        Point2D                pickShapeCenter = new Point2D.Double(pickShapeBounds.getCenterX(), pickShapeBounds.getCenterY());
+
+        ArrayList<IElement> elements = ELEMENTS.get();
+        elements.clear();
+        PickRequest req = new PickRequest(pickShape);
+        DiagramUtils.pick(diagram, req, elements);
+
+        ArrayList<Bend> bends = new ArrayList<Bend>();
+        Point2D bendPos = new Point2D.Double();
+        for (IElement e : diagram.getElements())
+        {
+            AffineTransform elementToDiagram = ElementUtils.getTransform(e);
+            BendsHandler bh = e.getElementClass().getSingleItem(BendsHandler.class);
+            if (bh==null) continue;
+            bends.clear(); bh.getBends(e, bends);
+            for (Bend b : bends)
+            {
+                bh.getBendPosition(e, b, bendPos);
+                elementToDiagram.transform(bendPos, bendPos);
+                if (!pickShape.contains(bendPos)) continue;
+                double dist = bendPos.distance(pickShapeCenter);
+                if (dist>bestShortestDist) continue;
+                dist = bestShortestDist;
+                result = new BendsInfo();
+                result.e = e;
+                result.b = b;
+            }
+        }
+        elements.clear();
+        if (bestShortestDist==Double.MAX_VALUE) return null;
+        return result;
+    }
+
+    /**
+     * Checks whether the element/terminal information of the two specified
+     * TerminalInfo structures match.
+     * 
+     * @param t1
+     * @param t2
+     * @return <code>true</code> if the element and terminal instances of both
+     *         structures are the same, <code>false</code> otherwise
+     */
+    public static boolean isSameTerminal(TerminalInfo t1, TerminalInfo t2) {
+        if (t1 == null || t2 == null)
+            return false;
+        return t1.e.equals(t2.e) && t1.t.equals(t2.e);
+    }
+
+    /**
+     * Finds those terminals among the specified set that are
+     * <ol>
+     * <li>nearest and equal in distance (see TerminalInfo.distance)</li>
+     * <li>have the same absolute diagram position</li>
+     * </ol>
+     * 
+     * @param tis the picked terminals to examine
+     * @return the nearest position-wise overlapping terminals
+     */
+    public static List<TerminalInfo> findNearestOverlappingTerminals(List<TerminalInfo> tis) {
+        int len = tis.size();
+        if (len < 2)
+            return tis;
+
+        // Only gather the nearest terminals that are
+        // directly on top of each other
+
+        TerminalInfo nearest = null;
+        for (int i = 0; i < len; ++i) {
+            TerminalInfo ti = tis.get(i);
+            if (nearest == null || ti.distance < nearest.distance) {
+                nearest = ti;
+            }
+        }
+
+        ArrayList<TerminalInfo> result = new ArrayList<TerminalInfo>(len);
+        for (int i = 0; i < len; ++i) {
+            TerminalInfo ti = tis.get(i);
+            if (ti.distance == nearest.distance
+                    //&& ti.e.equals(nearest.e)
+                    && ti.posDia.equals(nearest.posDia))
+            {
+                result.add(ti);
+            }
+        }
+
+        return result;
+    }
+
+}