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%2Fhandler%2FPickRequest.java;h=8d9d814d8eeb3c5599a08a4113e380752a1863b6;hp=4d5d76094ec1e118f20130cf0fefa06405c6fee2;hb=9f479d68d86a8b5116be5a884c220f816e3cbb18;hpb=969bd23cab98a79ca9101af33334000879fb60c5 diff --git a/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/handler/PickRequest.java b/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/handler/PickRequest.java index 4d5d76094..8d9d814d8 100644 --- a/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/handler/PickRequest.java +++ b/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/handler/PickRequest.java @@ -1,165 +1,279 @@ -/******************************************************************************* - * 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.handler; - -import java.awt.Shape; -import java.awt.geom.AffineTransform; -import java.awt.geom.Area; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -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.utils.GeometryUtils; -import org.simantics.scenegraph.utils.TransformedRectangle; - -/** - * - * @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; - - 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 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 { - void sort(List elements); - - // - public static final PickSorter CONNECTIONS_LAST = new PickSorter() { - @Override - public void sort(List elements) { - Collections.sort(elements, new Comparator() { - @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 elements) { - Collections.sort(elements, new Comparator() { - @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; - } - }); - } - }; - } - -} +/******************************************************************************* + * 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 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. + * + *

+ * 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 + * elements + * @param elements the element list to sort + * + * @author Tuukka Lehtonen + * @since 1.43.0, 1.35.3 + */ + default void sort(PickRequest request, List elements) { + sort(elements); + } + + // + public static final PickSorter CONNECTIONS_LAST = new PickSorter() { + @Override + public void sort(List elements) { + Collections.sort(elements, new Comparator() { + @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 elements) { + Collections.sort(elements, new Comparator() { + @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 segments = new ArrayList(); + 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 elements) { + List> 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>() { + @Override + public int compare(Pair connection1, Pair connection2) { + return Double.compare(connection2.first, connection1.first); + } + }); + for (Pair connection : connections) { + elements.set(tail, connection.second); + tail++; + } + connections.clear(); + tail = i + 1; + } + } + } + + @Override + public void sort(PickRequest request, List elements) { + if (sorter != null) + sorter.sort(request, elements); + sortConnections(elements); + } + + @Override + public void sort(List elements) { + if (sorter != null) + sorter.sort(elements); + sortConnections(elements); + } + }; + } + } + +}