/*******************************************************************************
* Copyright (c) 2017 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:
* Matthias Wienand (itemis AG) - initial API and implementation
*
*******************************************************************************/
package org.eclipse.gef.mvc.fx.parts;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.gef.fx.nodes.Connection;
import org.eclipse.gef.geometry.planar.Point;
import org.eclipse.gef.mvc.fx.models.SnappingModel.SnappingLocation;
import javafx.collections.ObservableList;
import javafx.geometry.Bounds;
import javafx.geometry.Orientation;
import javafx.geometry.Point2D;
import javafx.scene.Node;
/**
* The {@link SnappingUtil} class is a collection of utility methods that can be
* used to provide {@link SnappingLocation}s for an {@link ISnappablePart}.
*
* In general, the snapping locations can be provided for ratios which are
* applied to the width/height of the {@link ISnappablePart}'s visual:
* {@link #computeSnappingLocations(ISnappablePart, Orientation, double...)}.
*
* Additionally, there is a dedicated method for determining the
* {@link SnappingLocation}s for a part that uses a {@link Connection} for its
* visualization:
* {@link #computeSnappingLocations(ISnappablePart, Orientation)}.
*/
public class SnappingUtil {
/**
* Returns the {@link SnappingLocation}s for the individual start/end points
* of the {@link Connection} that visualizes the given
* {@link ISnappablePart}. Depending on the given {@link Orientation},
* either the horizontal or vertical coordinates are evaluated for the
* {@link SnappingLocation}s.
*
* @param part
* The {@link ISnappablePart} for which to determine the
* {@link SnappingLocation}s.
* @param orientation
* The {@link Orientation} for the {@link SnappingLocation}s.
* @return A {@link List} containing the determined
* {@link SnappingLocation}s according to the given parameters.
*/
public static List<SnappingLocation> computeSnappingLocations(
ISnappablePart<? extends Connection> part,
Orientation orientation) {
List<SnappingLocation> locations = new ArrayList<>();
// consider all segments of the connection
ObservableList<Point> points = part.getVisual().getPointsUnmodifiable();
for (int i = 0; i < points.size() - 1; i++) {
// current segment from point(i) to point(i+1)
Point start = points.get(i);
Point end = points.get(i + 1);
// determine orientation
Orientation segmentOrientation = start.x == end.x
? Orientation.VERTICAL : Orientation.HORIZONTAL;
if (segmentOrientation != orientation) {
continue;
}
// transform position to scene
Point2D startInScene = part.getVisual().localToScene(start.x,
start.y);
// save location
locations.add(new SnappingLocation(part, segmentOrientation,
orientation == Orientation.VERTICAL ? startInScene.getX()
: startInScene.getY()));
}
return locations;
}
/**
* Returns the {@link SnappingLocation}s for the given
* {@link ISnappablePart}, {@link Orientation}, and size ratios. The size
* ratios are applied to the width or height of the {@link ISnappablePart}'s
* visual in order to determine the actual {@link SnappingLocation}s. The
* given {@link Orientation} determines if horizontal or vertical
* {@link SnappingLocation}s are determined.
*
* @param part
* The {@link ISnappablePart} for which to determine the
* {@link SnappingLocation}s.
* @param orientation
* The {@link Orientation} for the {@link SnappingLocation}s.
* @param ratios
* The width/height ratios for the {@link SnappingLocation}s.
* @return A {@link List} containing the determined
* {@link SnappingLocation}s according to the given parameters.
*/
public static List<SnappingLocation> computeSnappingLocations(
ISnappablePart<? extends Node> part, Orientation orientation,
double... ratios) {
Bounds boundsInScene = part.getVisual()
.localToScene(part.getVisual().getLayoutBounds());
List<SnappingLocation> locations = new ArrayList<>();
for (int i = 0; i < ratios.length; i++) {
double min, size;
if (orientation == Orientation.VERTICAL) {
min = boundsInScene.getMinX();
size = boundsInScene.getWidth();
} else {
min = boundsInScene.getMinY();
size = boundsInScene.getHeight();
}
double location = min + size * ratios[i];
locations.add(new SnappingLocation(part, orientation, location));
}
return locations;
}
}