/******************************************************************************* * 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.impl; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.simantics.g2d.diagram.DiagramHints; import org.simantics.g2d.diagram.IDiagram; import org.simantics.g2d.diagram.handler.PickContext; import org.simantics.g2d.diagram.handler.PickRequest; import org.simantics.g2d.diagram.handler.PickRequest.PickPolicy; import org.simantics.g2d.element.ElementClass; import org.simantics.g2d.element.ElementUtils; import org.simantics.g2d.element.IElement; import org.simantics.g2d.element.handler.ElementLayers; import org.simantics.g2d.element.handler.InternalSize; import org.simantics.g2d.element.handler.Outline; import org.simantics.g2d.element.handler.Pick; import org.simantics.g2d.element.handler.Pick2; import org.simantics.g2d.element.handler.Transform; import org.simantics.g2d.layers.ILayers; import org.simantics.g2d.utils.GeometryUtils; /** * @author Toni Kalajainen */ public class PickContextImpl implements PickContext { public static final PickContextImpl INSTANCE = new PickContextImpl(); @Override public void pick( IDiagram diagram, PickRequest request, Collection finalResult) { assert(diagram!=null); assert(request!=null); assert(finalResult!=null); ILayers layers = diagram.getHint(DiagramHints.KEY_LAYERS); Collection result = finalResult; if (request.pickSorter != null) { // Need a temporary List for PickSorter result = new ArrayList(); } Rectangle2D elementBounds = new Rectangle2D.Double(); nextElement: for (IElement e : diagram.getSnapshot()) { // Ignore hidden elements. if (ElementUtils.isHidden(e)) continue; ElementClass ec = e.getElementClass(); if(layers != null && !layers.getIgnoreFocusSettings()) { ElementLayers el = ec.getAtMostOneItemOfClass(ElementLayers.class); if(el != null) { if(!el.isFocusable(e, layers)) continue; } } if (request.pickFilter!=null && !request.pickFilter.accept(e)) continue; Transform t = e.getElementClass().getSingleItem(Transform.class); AffineTransform canvasToElement = t.getTransform(e); if (canvasToElement==null) continue; double det = canvasToElement.getDeterminant(); if (det == 0) { // Singular transform, just reset the rotation/scaling part to // allow picking to proceed. // TODO: this may modify the internal transform value of an element which is not intended canvasToElement.setToTranslation( canvasToElement.getTranslateX(), canvasToElement.getTranslateY()); } // Get bounds, ignore elements that have no bounds InternalSize b = e.getElementClass().getAtMostOneItemOfClass(InternalSize.class); if (b==null) continue; elementBounds.setFrame(Double.NaN, Double.NaN, Double.NaN, Double.NaN); b.getBounds(e, elementBounds); if (Double.isNaN(elementBounds.getWidth()) || Double.isNaN(elementBounds.getHeight())) continue; Shape elementBoundsOnCanvas = GeometryUtils.transformShape(elementBounds, canvasToElement); if (elementBoundsOnCanvas instanceof Rectangle2D) elementBoundsOnCanvas = elementBoundsOnCanvas.getBounds2D(); elementBoundsOnCanvas = elementBoundsOnCanvas.getBounds2D(); org.simantics.scenegraph.utils.GeometryUtils.expandRectangle((Rectangle2D)elementBoundsOnCanvas, 1e-3, 1e-3, 1e-3, 1e-3); // Pick with pick handler(s) List pickHandlers = e.getElementClass().getItemsByClass(Pick.class); if (!pickHandlers.isEmpty()) { // Rough filtering with bounds if (!GeometryUtils.intersects(request.pickArea, elementBoundsOnCanvas)) { // System.out.println("Element bounds discards " + e.getElementClass()); continue; } // Convert pick shape to element coordinates // AffineTransform elementToCanvas; // try { // elementToCanvas = canvasToElement.createInverse(); // } catch (NoninvertibleTransformException e1) { // throw new RuntimeException(e1); // } // Shape pickShapeInElementCoords = GeometryUtils.transformShape(request.pickArea, elementToCanvas); for (Pick p : pickHandlers) { if (p instanceof Pick2) { Pick2 p2 = (Pick2) p; //if (p2.pick(e, pickShapeInElementCoords, request.pickPolicy, result) > 0) if (p2.pick(e, request.pickArea, request.pickPolicy, result) > 0) continue nextElement; } else { //if (p.pickTest(e, pickShapeInElementCoords, request.pickPolicy)) { if (p.pickTest(e, request.pickArea, request.pickPolicy)) { result.add(e); continue nextElement; } } } continue nextElement; } // Pick with shape handler(s) List 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); } Shape pickShapeInElementCoords = GeometryUtils.transformShape(request.pickArea, elementToCanvas); // Intersection with one shape is enough if (request.pickPolicy == PickPolicy.PICK_INTERSECTING_OBJECTS) { for (Outline es : shapeHandlers) { Shape elementShape = es.getElementShape(e); if (elementShape==null) continue nextElement; if (GeometryUtils.intersects(pickShapeInElementCoords, elementShape)) { result.add(e); continue nextElement; } } continue nextElement; } // Contains of all shapes is required if (request.pickPolicy == PickPolicy.PICK_CONTAINED_OBJECTS) { for (Outline es : shapeHandlers) { Shape elementShape = es.getElementShape(e); if (!GeometryUtils.contains(pickShapeInElementCoords, elementShape)) continue nextElement; } result.add(e); continue nextElement; } continue nextElement; } // Pick by rectangle if (request.pickPolicy == PickPolicy.PICK_INTERSECTING_OBJECTS) { if (GeometryUtils.intersects(request.pickArea, elementBoundsOnCanvas)) result.add(e); } else if (request.pickPolicy == PickPolicy.PICK_CONTAINED_OBJECTS) { if (GeometryUtils.contains(request.pickArea, elementBoundsOnCanvas)) result.add(e); } } if (request.pickSorter != null) { request.pickSorter.sort((List) result); finalResult.addAll(result); } } }