X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.g2d%2Fsrc%2Forg%2Fsimantics%2Fg2d%2Fdiagram%2Fparticipant%2Fpointertool%2FTerminalUtil.java;h=85f75a06638150f83af0fc4f413836a7f6a6d0b9;hp=409af79d69e4e0febb7fd69375ccb8f76d867fe1;hb=refs%2Fchanges%2F38%2F238%2F2;hpb=24e2b34260f219f0d1644ca7a138894980e25b14 diff --git a/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/pointertool/TerminalUtil.java b/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/pointertool/TerminalUtil.java index 409af79d6..85f75a066 100644 --- a/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/pointertool/TerminalUtil.java +++ b/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/pointertool/TerminalUtil.java @@ -1,447 +1,447 @@ -/******************************************************************************* - * 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> TERMINALS = new ThreadLocal>() { - @Override - protected ArrayList initialValue() { - return new ArrayList(); - } - }; - - /** - * Thread local element list for keeping memory allocations down. - */ - private static final ThreadLocal> ELEMENTS = new ThreadLocal>() { - @Override - protected ArrayList initialValue() { - return new ArrayList(); - } - }; - - 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 ASCENDING_DISTANCE_ORDER = new Comparator() { - @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 pickTerminals(IDiagram d, Shape pickShape, boolean pickPointTerminals, boolean pickAreaTerminals) - { - boolean clearElements = false; - List 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 result = new ArrayList(); - ArrayList 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 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 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 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 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 tls = e.getElementClass().getItemsByClass(TerminalLayout.class); - return getTerminalShape(tls, e, t); - } - - private static Shape getTerminalShape(List 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 elements = ELEMENTS.get(); - elements.clear(); - PickRequest req = new PickRequest(pickShape); - DiagramUtils.pick(diagram, req, elements); - - ArrayList bends = new ArrayList(); - 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 true if the element and terminal instances of both - * structures are the same, false 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 - *
    - *
  1. nearest and equal in distance (see TerminalInfo.distance)
  2. - *
  3. have the same absolute diagram position
  4. - *
- * - * @param tis the picked terminals to examine - * @return the nearest position-wise overlapping terminals - */ - public static List findNearestOverlappingTerminals(List 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 result = new ArrayList(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; - } - -} +/******************************************************************************* + * 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> TERMINALS = new ThreadLocal>() { + @Override + protected ArrayList initialValue() { + return new ArrayList(); + } + }; + + /** + * Thread local element list for keeping memory allocations down. + */ + private static final ThreadLocal> ELEMENTS = new ThreadLocal>() { + @Override + protected ArrayList initialValue() { + return new ArrayList(); + } + }; + + 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 ASCENDING_DISTANCE_ORDER = new Comparator() { + @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 pickTerminals(IDiagram d, Shape pickShape, boolean pickPointTerminals, boolean pickAreaTerminals) + { + boolean clearElements = false; + List 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 result = new ArrayList(); + ArrayList 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 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 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 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 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 tls = e.getElementClass().getItemsByClass(TerminalLayout.class); + return getTerminalShape(tls, e, t); + } + + private static Shape getTerminalShape(List 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 elements = ELEMENTS.get(); + elements.clear(); + PickRequest req = new PickRequest(pickShape); + DiagramUtils.pick(diagram, req, elements); + + ArrayList bends = new ArrayList(); + 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 true if the element and terminal instances of both + * structures are the same, false 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 + *
    + *
  1. nearest and equal in distance (see TerminalInfo.distance)
  2. + *
  3. have the same absolute diagram position
  4. + *
+ * + * @param tis the picked terminals to examine + * @return the nearest position-wise overlapping terminals + */ + public static List findNearestOverlappingTerminals(List 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 result = new ArrayList(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; + } + +}