-
- // Pick with shape handler(s)
- List<Outline> shapeHandlers = e.getElementClass().getItemsByClass(Outline.class);
- if (!shapeHandlers.isEmpty())
- {
- // Rough filtering with bounds
- if (!GeometryUtils.intersects(request.pickArea, elementBoundsOnCanvas)) continue;
-
- // Convert pick shape to element coordinates
- AffineTransform elementToCanvas;
- try {
- elementToCanvas = canvasToElement.createInverse();
- } catch (NoninvertibleTransformException e1) {
- throw new RuntimeException(e1);
+
+ // Pick with Outline handler(s)
+ List<Outline> outlineHandlers = ec.getItemsByClass(Outline.class);
+ if (!outlineHandlers.isEmpty())
+ return pickByOutline(e, request, elementBoundsOnCanvas, canvasToElement, outlineHandlers);
+
+ // Finally, pick by rectangle
+ return pickByBounds(e, request, elementBoundsOnCanvas);
+ }
+
+ }
+
+ private static Rectangle2D toBoundingRectangle(Shape shape) {
+ if (shape instanceof Rectangle2D)
+ return (Rectangle2D) shape;
+ else
+ return shape.getBounds2D();
+ }
+
+ private static List<IElement> pickElements(IDiagram diagram, PickRequest request) {
+ ILayers layers = diagram.getHint(DiagramHints.KEY_LAYERS);
+
+ // Get the scene graph nodes that intersect the pick-requests pick shape
+ INode spatialRoot = request.pickContext != null
+ ? request.pickContext.getSceneGraph().lookupNode(SceneGraphConstants.SPATIAL_ROOT_NODE_ID)
+ : null;
+
+ if (spatialRoot instanceof RTreeNode) {
+ // Optimized picking version that no longer supports Pick2 interface
+ // and therefore doesn't support connections modelled as
+ // branchpoint/edge subelements of connection elements.
+
+ RTreeNode rtree = (RTreeNode) spatialRoot;
+ Map<INode, IElement> nodeToElement = diagram.getHint(DiagramHints.NODE_TO_ELEMENT_MAP);
+ if (nodeToElement != null) {
+ // The most optimized version
+ return rtree.intersectingNodes(toBoundingRectangle(request.pickArea), new ArrayList<>())
+ .stream()
+ .parallel()
+ .map(nodeToElement::get)
+ .filter(Objects::nonNull)
+ .filter(new PickFilter(request, layers))
+ .collect(Collectors.toList());
+ } else {
+ // Slower version for when DiagramHints.NODE_TO_ELEMENT_MAP is not used
+ Set<IG2DNode> nodes =
+ new HashSet<>(
+ rtree.intersectingNodes(
+ toBoundingRectangle(request.pickArea),
+ new ArrayList<>())
+ );
+
+ return diagram.getSnapshot().stream()
+ .parallel()
+ // Choose only elements that are under the pick region bounds-wise
+ .filter(e -> {
+ INode node = e.getHint(ElementHints.KEY_SG_NODE);
+ return nodes.contains(node);
+ })
+ // Perform comprehensive picking
+ .filter(new PickFilter(request, layers))
+ .collect(Collectors.toList());
+ }
+
+ } else {
+
+ // Fall-back logic that ends up processing everything.
+ // This still supports all the old logic and the Pick2
+ // interface in element classes.
+ List<IElement> result = new ArrayList<>();
+ boolean checkLayers = layers != null && !layers.getIgnoreFocusSettings();
+
+ // Do not do this in parallel mode to keep results in proper Z order
+ diagram.getSnapshot().stream().forEachOrdered(e -> {
+ // Ignore hidden elements.
+ if (ElementUtils.isHidden(e))
+ return;
+
+ ElementClass ec = e.getElementClass();
+
+ if (checkLayers) {
+ ElementLayers el = ec.getAtMostOneItemOfClass(ElementLayers.class);
+ if (el != null && !el.isFocusable(e, layers))
+ return;