/*******************************************************************************
* 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.Collections;
import java.util.HashSet;
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.Path;
import org.eclipse.gef.geometry.planar.Point;
import javafx.scene.Node;
/**
* An {@link IComputationStrategy} that computes anchor position by projecting
* the respective anchored reference point to the outline of the anchorage
* reference geometry so that the respective point has minimal distance to the
* anchored reference point.
*
* In detail, the computation is done as follows:
* <ol>
* <li>Compute the anchorage outlines (in scene) based on the anchorage
* reference geometry,using {@link #getOutlineSegments(IGeometry)}.</li>
* <li>Transform the given anchored reference point to scene coordinates.</li>
* <li>Project the anchored reference point (in scene) onto the anchorage
* outlines.</li>
* <li>Return the nearest projection to the anchored reference point.</li>
* </ol>
*
* @author anyssen
* @author mwienand
*/
public class ProjectionStrategy implements IComputationStrategy {
@Override
public Point computePositionInScene(Node anchorage, Node anchored,
Set<Parameter<?>> parameters) {
// retrieve required computation parameters
IGeometry anchorageReferenceGeometryInLocal = Parameter
.get(parameters, AnchorageReferenceGeometry.class).get();
Point anchoredReferencePointInLocal = Parameter
.get(parameters, AnchoredReferencePoint.class).get();
// determine anchorage geometry in scene
IGeometry anchorageGeometryInScene = NodeUtils.localToScene(anchorage,
anchorageReferenceGeometryInLocal);
// determine anchorage outlines in scene
List<ICurve> anchorageOutlinesInScene = getOutlineSegments(
anchorageGeometryInScene);
// transform anchored reference point to scene
Point anchoredReferencePointInScene = NodeUtils.localToScene(anchored,
anchoredReferencePointInLocal);
// compute nearest projection of the anchored reference point on the
// anchorage outlines
return computeProjectionInScene(anchorageOutlinesInScene,
anchoredReferencePointInScene, parameters);
}
/**
* Computes the anchorage reference position in scene coordinates, based on
* the given anchorage outlines and the given anchored reference point.
*
* @param anchorageOutlinesInScene
* A list of {@link ICurve}s that describe the outline of the
* anchorage.
* @param anchoredReferencePointInScene
* The reference {@link Point} of the anchored for which the
* anchorage reference {@link Point} is to be determined.
* @param parameters
* The parameters available for the computation.
* @return The anchorage reference position.
*/
protected Point computeProjectionInScene(
List<ICurve> anchorageOutlinesInScene,
Point anchoredReferencePointInScene, Set<Parameter<?>> parameters) {
Point[] projections = new Point[anchorageOutlinesInScene.size()];
for (int i = 0; i < anchorageOutlinesInScene.size(); i++) {
ICurve c = anchorageOutlinesInScene.get(i);
projections[i] = c.getProjection(anchoredReferencePointInScene);
}
return Point.nearest(anchoredReferencePointInScene, projections);
}
/**
* Determines the outline of the given {@link IGeometry}, represented as a
* list of {@link ICurve}s.
*
* @param geometry
* The anchorage geometry.
* @return A list of {@link ICurve}s representing the outline of the given
* {@link IGeometry}.
*/
// TODO: Move to utility within GEF Geometry?
protected List<ICurve> getOutlineSegments(IGeometry geometry) {
if (geometry instanceof IShape) {
return Collections.singletonList(((IShape) geometry).getOutline());
} else if (geometry instanceof ICurve) {
return Collections.singletonList((ICurve) geometry);
} else if (geometry instanceof Path) {
return ((Path) geometry).getOutlines();
} else {
throw new IllegalStateException(
"The transformed geometry is neither an ICurve nor an IShape.");
}
}
@Override
public Set<Class<? extends Parameter<?>>> getRequiredParameters() {
Set<Class<? extends Parameter<?>>> parameters = new HashSet<>();
parameters.add(AnchorageReferenceGeometry.class);
parameters.add(AnchoredReferencePoint.class);
return parameters;
}
}