-/*******************************************************************************\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.handler;\r
-\r
-import java.awt.Shape;\r
-import java.awt.geom.AffineTransform;\r
-import java.awt.geom.Area;\r
-import java.awt.geom.Point2D;\r
-import java.awt.geom.Rectangle2D;\r
-import java.util.Collections;\r
-import java.util.Comparator;\r
-import java.util.List;\r
-\r
-import org.simantics.g2d.connection.handler.ConnectionHandler;\r
-import org.simantics.g2d.element.IElement;\r
-import org.simantics.g2d.element.handler.BendsHandler;\r
-import org.simantics.g2d.element.handler.TerminalTopology;\r
-import org.simantics.g2d.element.handler.impl.ConnectionSelectionOutline;\r
-import org.simantics.g2d.elementclass.BranchPoint;\r
-import org.simantics.g2d.elementclass.MonitorHandler;\r
-import org.simantics.g2d.utils.GeometryUtils;\r
-import org.simantics.scenegraph.utils.TransformedRectangle;\r
-\r
-/**\r
- *\r
- * @See {@link GeometryUtils} for intersect and contains tests\r
- * @See {@link TransformedRectangle}\r
- * @See {@link Area} intersects operations for complex shapes\r
- * @author Toni Kalajainen\r
- */\r
-public class PickRequest {\r
-\r
- public static enum PickPolicy {\r
- PICK_INTERSECTING_OBJECTS,\r
- PICK_CONTAINED_OBJECTS\r
- }\r
-\r
- public Shape pickArea;\r
- public PickPolicy pickPolicy = PickPolicy.PICK_INTERSECTING_OBJECTS;\r
- /** Pick filter (null == everything is accepted) */\r
- public PickFilter pickFilter = null;\r
- public PickSorter pickSorter = null;\r
-\r
- public PickRequest(double x, double y)\r
- {\r
- pickArea = new Rectangle2D.Double(x, y, 1, 1);\r
- }\r
- public PickRequest(Point2D p)\r
- {\r
- pickArea = new Rectangle2D.Double(p.getX(), p.getY(), 0.0001, 0.0001);\r
- }\r
- public PickRequest(Shape shape)\r
- {\r
- pickArea = shape;\r
- }\r
- public PickRequest(Shape shape, AffineTransform transform)\r
- {\r
- pickArea = GeometryUtils.transformShape(shape, transform);\r
- }\r
-\r
- public static interface PickFilter {\r
- boolean accept(IElement e);\r
-\r
- public static final PickFilter FILTER_ALL = new PickFilter() {\r
- @Override\r
- public boolean accept(IElement e) {\r
- return true;\r
- }\r
- };\r
- // Connections\r
- public static final PickFilter FILTER_CONNECTIONS = new PickFilter() {\r
- @Override\r
- public boolean accept(IElement e) {\r
- return e.getElementClass().containsClass(ConnectionHandler.class);\r
- }\r
- @Override\r
- public String toString() { return "FILTER_CONNECTIONS"; }\r
- };\r
- // Connection edges\r
- public static final PickFilter FILTER_CONNECTION_EDGES = new PickFilter() {\r
- @Override\r
- public boolean accept(IElement e) {\r
- return e.getElementClass().containsClass(BendsHandler.class) || e.getElementClass().containsClass(ConnectionSelectionOutline.class);\r
- }\r
- @Override\r
- public String toString() { return "FILTER_CONNECTION_EDGES"; }\r
- };\r
- // Connections\r
- public static final PickFilter FILTER_BRANCH_POINT = new PickFilter() {\r
- @Override\r
- public boolean accept(IElement e) {\r
- return e.getElementClass().containsClass(BranchPoint.class);\r
- }\r
- @Override\r
- public String toString() { return "FILTER_BRANCH_POINTS"; }\r
- };\r
- // Anything that has terminals\r
- public static final PickFilter FILTER_NODES = new PickFilter() {\r
- @Override\r
- public boolean accept(IElement e) {\r
- return e.getElementClass().containsClass(TerminalTopology.class);\r
- }\r
- @Override\r
- public String toString() { return "FILTER_NODES"; }\r
- };\r
- public static final PickFilter FILTER_MONITORS = new PickFilter() {\r
- @Override\r
- public boolean accept(IElement e) {\r
- return e.getElementClass().containsClass(MonitorHandler.class);\r
- }\r
- @Override\r
- public String toString() { return "FILTER_MONITORS"; }\r
- };\r
- }\r
-\r
- public static interface PickSorter {\r
- void sort(List<IElement> elements);\r
-\r
- //\r
- public static final PickSorter CONNECTIONS_LAST = new PickSorter() {\r
- @Override\r
- public void sort(List<IElement> elements) {\r
- Collections.sort(elements, new Comparator<IElement>() {\r
- @Override\r
- public int compare(IElement e1, IElement e2) {\r
- boolean isConn1 = PickFilter.FILTER_CONNECTION_EDGES.accept(e1);\r
- boolean isConn2 = PickFilter.FILTER_CONNECTION_EDGES.accept(e2);\r
- if (!isConn1 && isConn2)\r
- return -1;\r
- if (isConn1 && !isConn2)\r
- return 1;\r
- return 0;\r
- }\r
- });\r
- }\r
- };\r
- public static final PickSorter CONNECTIONS_FIRST = new PickSorter() {\r
- @Override\r
- public void sort(List<IElement> elements) {\r
- Collections.sort(elements, new Comparator<IElement>() {\r
- @Override\r
- public int compare(IElement e1, IElement e2) {\r
- boolean isConn1 = PickFilter.FILTER_CONNECTION_EDGES.accept(e1);\r
- boolean isConn2 = PickFilter.FILTER_CONNECTION_EDGES.accept(e2);\r
- if (!isConn1 && isConn2)\r
- return 1;\r
- if (isConn1 && !isConn2)\r
- return -1;\r
- return 0;\r
- }\r
- });\r
- }\r
- };\r
- }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2020 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
+ * Semantum Oy - gitlab #454
+ *******************************************************************************/
+package org.simantics.g2d.diagram.handler;
+
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Area;
+import java.awt.geom.Line2D;
+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.diagram.connection.RouteLine;
+import org.simantics.diagram.connection.RoutePoint;
+import org.simantics.diagram.connection.segments.Segment;
+import org.simantics.g2d.canvas.ICanvasContext;
+import org.simantics.g2d.connection.handler.ConnectionHandler;
+import org.simantics.g2d.element.IElement;
+import org.simantics.g2d.element.handler.BendsHandler;
+import org.simantics.g2d.element.handler.TerminalTopology;
+import org.simantics.g2d.element.handler.impl.ConnectionSelectionOutline;
+import org.simantics.g2d.elementclass.BranchPoint;
+import org.simantics.g2d.elementclass.MonitorHandler;
+import org.simantics.g2d.elementclass.RouteGraphConnectionClass;
+import org.simantics.g2d.utils.GeometryUtils;
+import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphNode;
+import org.simantics.scenegraph.utils.TransformedRectangle;
+import org.simantics.utils.datastructures.Pair;
+
+/**
+ *
+ * @See {@link GeometryUtils} for intersect and contains tests
+ * @See {@link TransformedRectangle}
+ * @See {@link Area} intersects operations for complex shapes
+ * @author Toni Kalajainen
+ */
+public class PickRequest {
+
+ public static enum PickPolicy {
+ PICK_INTERSECTING_OBJECTS,
+ PICK_CONTAINED_OBJECTS
+ }
+
+ public Shape pickArea;
+ public PickPolicy pickPolicy = PickPolicy.PICK_INTERSECTING_OBJECTS;
+ /** Pick filter (null == everything is accepted) */
+ public PickFilter pickFilter = null;
+ public PickSorter pickSorter = null;
+
+ /**
+ * Used to optimize picking if provided via R-tree traversal to find
+ * intersecting elements, not everything.
+ */
+ public ICanvasContext pickContext;
+
+ public PickRequest(double x, double y)
+ {
+ pickArea = new Rectangle2D.Double(x, y, 1, 1);
+ }
+ public PickRequest(Point2D p)
+ {
+ pickArea = new Rectangle2D.Double(p.getX(), p.getY(), 0.0001, 0.0001);
+ }
+ public PickRequest(Shape shape)
+ {
+ pickArea = shape;
+ }
+ public PickRequest(Shape shape, AffineTransform transform)
+ {
+ pickArea = GeometryUtils.transformShape(shape, transform);
+ }
+
+ public PickRequest context(ICanvasContext ctx) {
+ this.pickContext = ctx;
+ return this;
+ }
+
+ public static interface PickFilter {
+ boolean accept(IElement e);
+
+ public static final PickFilter FILTER_ALL = new PickFilter() {
+ @Override
+ public boolean accept(IElement e) {
+ return true;
+ }
+ };
+ // Connections
+ public static final PickFilter FILTER_CONNECTIONS = new PickFilter() {
+ @Override
+ public boolean accept(IElement e) {
+ return e.getElementClass().containsClass(ConnectionHandler.class);
+ }
+ @Override
+ public String toString() { return "FILTER_CONNECTIONS"; }
+ };
+ // Connection edges
+ public static final PickFilter FILTER_CONNECTION_EDGES = new PickFilter() {
+ @Override
+ public boolean accept(IElement e) {
+ return e.getElementClass().containsClass(BendsHandler.class) || e.getElementClass().containsClass(ConnectionSelectionOutline.class);
+ }
+ @Override
+ public String toString() { return "FILTER_CONNECTION_EDGES"; }
+ };
+ // Connections
+ public static final PickFilter FILTER_BRANCH_POINT = new PickFilter() {
+ @Override
+ public boolean accept(IElement e) {
+ return e.getElementClass().containsClass(BranchPoint.class);
+ }
+ @Override
+ public String toString() { return "FILTER_BRANCH_POINTS"; }
+ };
+ // Anything that has terminals
+ public static final PickFilter FILTER_NODES = new PickFilter() {
+ @Override
+ public boolean accept(IElement e) {
+ return e.getElementClass().containsClass(TerminalTopology.class);
+ }
+ @Override
+ public String toString() { return "FILTER_NODES"; }
+ };
+ public static final PickFilter FILTER_MONITORS = new PickFilter() {
+ @Override
+ public boolean accept(IElement e) {
+ return e.getElementClass().containsClass(MonitorHandler.class);
+ }
+ @Override
+ public String toString() { return "FILTER_MONITORS"; }
+ };
+ }
+
+ public static interface PickSorter {
+ /**
+ * Sorts the specified element list.
+ *
+ * @param elements the element list to sort
+ */
+ void sort(List<IElement> elements);
+
+ /**
+ * Extended interface-method that receives the pick request in addition to the
+ * picked elements to be sorted. Allows e.g. looking at the pick area in the
+ * sorter.
+ *
+ * <p>
+ * The default implementation just invokes {@link #sort(List)} ignoring the pick
+ * request. The default implementation also keeps PickSorter API/ABI-compatible.
+ *
+ * @param request the original pick request that produced the hits listed in
+ * <code>elements</code>
+ * @param elements the element list to sort
+ *
+ * @author Tuukka Lehtonen
+ * @since 1.43.0, 1.35.3
+ */
+ default void sort(PickRequest request, List<IElement> elements) {
+ sort(elements);
+ }
+
+ //
+ public static final PickSorter CONNECTIONS_LAST = new PickSorter() {
+ @Override
+ public void sort(List<IElement> elements) {
+ Collections.sort(elements, new Comparator<IElement>() {
+ @Override
+ public int compare(IElement e1, IElement e2) {
+ boolean isConn1 = PickFilter.FILTER_CONNECTION_EDGES.accept(e1);
+ boolean isConn2 = PickFilter.FILTER_CONNECTION_EDGES.accept(e2);
+ if (!isConn1 && isConn2)
+ return -1;
+ if (isConn1 && !isConn2)
+ return 1;
+ return 0;
+ }
+ });
+ }
+ };
+ public static final PickSorter CONNECTIONS_FIRST = new PickSorter() {
+ @Override
+ public void sort(List<IElement> elements) {
+ Collections.sort(elements, new Comparator<IElement>() {
+ @Override
+ public int compare(IElement e1, IElement e2) {
+ boolean isConn1 = PickFilter.FILTER_CONNECTION_EDGES.accept(e1);
+ boolean isConn2 = PickFilter.FILTER_CONNECTION_EDGES.accept(e2);
+ if (!isConn1 && isConn2)
+ return 1;
+ if (isConn1 && !isConn2)
+ return -1;
+ return 0;
+ }
+ });
+ }
+ };
+
+ /*
+ * First use the default sorting if available, then sort successive connections by their distance to cursor.
+ */
+ public static PickSorter connectionSorter(PickSorter sorter, double x, double y) {
+ return new PickSorter() {
+
+ private double getDistanceSqToRouteGraphConnection(RouteGraphNode rgn, double x, double y) {
+ double minDistanceSq = Double.MAX_VALUE;
+ for (RouteLine line : rgn.getRouteGraph().getAllLines()) {
+ ArrayList<Segment> segments = new ArrayList<Segment>();
+ line.collectSegments(segments);
+ for (Segment segment : segments) {
+ RoutePoint p1 = segment.p1;
+ RoutePoint p2 = segment.p2;
+
+ double distanceSq = Line2D.ptSegDistSq(p1.getX(), p1.getY(), p2.getX(), p2.getY(), x, y);
+ if (distanceSq < minDistanceSq) {
+ minDistanceSq = distanceSq;
+ }
+ }
+ }
+ return minDistanceSq;
+ }
+
+ private void sortConnections(List<IElement> elements) {
+ List<Pair<Double, IElement>> connections = new ArrayList<>(elements.size());
+ int tail = 0;
+ for (int i = 0; i < elements.size(); i++) {
+ IElement element = elements.get(i);
+ RouteGraphNode rgn = element.getHint(RouteGraphConnectionClass.KEY_RG_NODE);
+ if (rgn != null) {
+ double distanceSq = getDistanceSqToRouteGraphConnection(rgn, x, y);
+ connections.add(Pair.make(distanceSq, element));
+ }
+
+ if (rgn == null || i == elements.size() - 1) {
+ Collections.sort(connections, new Comparator<Pair<Double, IElement>>() {
+ @Override
+ public int compare(Pair<Double, IElement> connection1, Pair<Double, IElement> connection2) {
+ return Double.compare(connection2.first, connection1.first);
+ }
+ });
+ for (Pair<Double, IElement> connection : connections) {
+ elements.set(tail, connection.second);
+ tail++;
+ }
+ connections.clear();
+ tail = i + 1;
+ }
+ }
+ }
+
+ @Override
+ public void sort(PickRequest request, List<IElement> elements) {
+ if (sorter != null)
+ sorter.sort(request, elements);
+ sortConnections(elements);
+ }
+
+ @Override
+ public void sort(List<IElement> elements) {
+ if (sorter != null)
+ sorter.sort(elements);
+ sortConnections(elements);
+ }
+ };
+ }
+ }
+
+}