/******************************************************************************* * Copyright (c) 2016 itemis AG and others. * 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: * Alexander Nyßen (itemis AG) - initial API and implementation * Matthias Wienand (itemis AG) - initial API and implementation * *******************************************************************************/ package org.eclipse.gef.fx.anchors; import java.util.List; import java.util.Set; import org.eclipse.gef.fx.anchors.DynamicAnchor.AnchorageReferenceGeometry; import org.eclipse.gef.fx.anchors.DynamicAnchor.AnchoredReferencePoint; import org.eclipse.gef.fx.utils.NodeUtils; import org.eclipse.gef.geometry.planar.ICurve; import org.eclipse.gef.geometry.planar.IGeometry; import org.eclipse.gef.geometry.planar.IShape; import org.eclipse.gef.geometry.planar.Line; import org.eclipse.gef.geometry.planar.Path; import org.eclipse.gef.geometry.planar.Point; import javafx.geometry.Point2D; import javafx.scene.Node; /** * A specific projection strategy that is based on a center-projection of the * given reference point. * * @author anyssen * @author mwienand */ public class ChopBoxStrategy extends ProjectionStrategy { /** * Computes the anchorage reference position within the coordinate system of * the given {@link IGeometry}. Will return the center of a {@link IShape} * or {@link Path} geometry, if it is contained within the shape or path. * Will return <code>null</code> otherwise to indicate that the computation * should fall back to the nearest projection on the anchorage geometry * outline. * * @param anchorage * The anchorage visual. * @param geometryInLocal * The anchorage geometry within the local coordinate system of * the anchorage visual. * @param anchoredReferencePointInAnchorageLocal * Reference point of the anchored for which to determine the * anchorage reference point. Within the local coordinate system * of the anchorage. * @return A position within the given {@link IGeometry}, or * <code>null</code> if the computation should rather fall back to * the nearest projection. */ protected Point computeAnchorageReferencePointInLocal(Node anchorage, IGeometry geometryInLocal, Point anchoredReferencePointInAnchorageLocal) { if (geometryInLocal instanceof IShape) { IShape shape = (IShape) geometryInLocal; // in case of an IShape we can pick the bounds center if it // is contained, or the vertex nearest to the center point Point boundsCenterInLocal = geometryInLocal.getBounds().getCenter(); if (shape.contains(boundsCenterInLocal)) { return boundsCenterInLocal; } } else if (geometryInLocal instanceof Path) { // in case of a Path we can pick the vertex nearest // to the center point Point boundsCenterInLocal = geometryInLocal.getBounds().getCenter(); if (geometryInLocal.contains(boundsCenterInLocal)) { return boundsCenterInLocal; } } return null; } /** * Computes the anchorage reference position in scene coordinates, based on * the given anchorage geometry. * * @see #computeAnchorageReferencePointInLocal(Node, IGeometry, Point) * @param anchorage * The anchorage visual. * @param geometryInLocal * The anchorage geometry within the coordinate system of the * anchorage visual. * @param anchoredReferencePointInScene * The reference {@link Point} of the anchored for which the * anchorage reference {@link Point} is to be determined. * @return The anchorage reference position in scene coordinates or * <code>null</code> if the computation should rather fall back to * the nearest projection. */ protected Point computeAnchorageReferencePointInScene(Node anchorage, IGeometry geometryInLocal, Point anchoredReferencePointInScene) { Point2D anchoredReferencePointInAnchorageLocal = anchorage.sceneToLocal( anchoredReferencePointInScene.x, anchoredReferencePointInScene.y); Point anchorageReferencePointInLocal = computeAnchorageReferencePointInLocal( anchorage, geometryInLocal, new Point(anchoredReferencePointInAnchorageLocal.getX(), anchoredReferencePointInAnchorageLocal.getY())); if (anchorageReferencePointInLocal == null) { return null; } return NodeUtils.localToScene(anchorage, anchorageReferencePointInLocal); } @Override public Point computePositionInScene(Node anchorage, Node anchored, Set<Parameter<?>> parameters) { // obtain required computation parameters IGeometry anchorageReferenceGeometryInLocal = Parameter .get(parameters, AnchorageReferenceGeometry.class).get(); Point anchoredReferencePointInLocal = Parameter .get(parameters, AnchoredReferencePoint.class).get(); Point anchoredReferencePointInScene = NodeUtils.localToScene(anchored, anchoredReferencePointInLocal); Point anchorageReferencePointInScene = computeAnchorageReferencePointInScene( anchorage, anchorageReferenceGeometryInLocal, anchoredReferencePointInScene); if (anchorageReferencePointInScene == null) { return super.computePositionInScene(anchorage, anchored, parameters); } IGeometry anchorageGeometryInScene = NodeUtils.localToScene(anchorage, anchorageReferenceGeometryInLocal); List<ICurve> anchorageOutlinesInScene = getOutlineSegments( anchorageGeometryInScene); Line referenceLineInScene = new Line(anchorageReferencePointInScene, anchoredReferencePointInScene); Point nearestProjectionInScene = null; double nearestDistance = 0d; for (ICurve anchorageOutlineInScene : anchorageOutlinesInScene) { // if the reference point is already on the outline, we may // directly use it if (anchorageOutlineInScene .contains(anchoredReferencePointInScene)) { return anchoredReferencePointInScene; } Point[] intersections = anchorageOutlineInScene .getIntersections(referenceLineInScene); if (intersections.length > 0) { Point nearestIntersection = Point .nearest(anchoredReferencePointInScene, intersections); double distance = anchoredReferencePointInScene .getDistance(nearestIntersection); if (nearestProjectionInScene == null || distance < nearestDistance) { nearestProjectionInScene = nearestIntersection; nearestDistance = distance; } } } if (nearestProjectionInScene != null) { return nearestProjectionInScene; } return super.computePositionInScene(anchorage, anchored, parameters); } }