/******************************************************************************* * Copyright (c) 2011, 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: * Matthias Wienand (itemis AG) - initial API and implementation * *******************************************************************************/ package org.eclipse.gef.geometry.planar; import java.util.Arrays; import java.util.HashSet; import java.util.Set; /** * The {@link CurveUtils} class provides functionality that can be used for all * {@link ICurve}s, independent on their construction kind. * * @author mwienand * */ class CurveUtils { /** * Creates copies of the given {@link BezierCurve}s. * * @param curves * the {@link BezierCurve}s to copy * @return an array containing the copies */ public static BezierCurve[] getCopy(BezierCurve... curves) { BezierCurve[] copies = new BezierCurve[curves.length]; for (int i = 0; i < curves.length; i++) { copies[i] = curves[i].getCopy(); } return copies; } /** * Delegates to the {@link BezierCurve#getIntersections(ICurve)} method. * * @param curve1 * The first {@link ICurve} to intersect * @param curve2 * The second {@link ICurve} to intersect * @return An array of intersection {@link Point}s */ public static Point[] getIntersections(ICurve curve1, ICurve curve2) { Set<Point> intersections = new HashSet<>(); for (BezierCurve bezier : curve1.toBezier()) { intersections .addAll(Arrays.asList(bezier.getIntersections(curve2))); } return intersections.toArray(new Point[] {}); } /** * Delegates to the appropriate getIntersections() method for the passed-in * {@link IGeometry} depending on its type. * * @param curve * the {@link ICurve} to intersect * @param geom * the {@link IGeometry} to intersect * @return points of intersection * @see #getIntersections(ICurve, ICurve) * @see #getIntersections(ICurve, IShape) * @see #getIntersections(ICurve, IMultiShape) */ public static Point[] getIntersections(ICurve curve, IGeometry geom) { if (geom instanceof ICurve) { return getIntersections(curve, (ICurve) geom); } else if (geom instanceof IShape) { return getIntersections(curve, (IShape) geom); } else if (geom instanceof IMultiShape) { return getIntersections(curve, (IMultiShape) geom); } else { throw new UnsupportedOperationException("Not yet implemented."); } } /** * Delegates to the {@link #getIntersections(ICurve, IShape)} method. * * @param curve * the {@link ICurve} to intersect * @param multiShape * the {@link IMultiShape} of which the outline is intersected * @return an array of intersection {@link Point}s */ public static Point[] getIntersections(ICurve curve, IMultiShape multiShape) { Set<Point> intersections = new HashSet<>(); for (IShape shape : multiShape.getShapes()) { intersections.addAll(Arrays.asList(getIntersections(curve, shape))); } return intersections.toArray(new Point[] {}); } /** * Delegates to the {@link #getIntersections(ICurve, ICurve)} method. * * @param curve * the {@link ICurve} to intersect * @param shape * the {@link IShape} of which the outline is intersected * @return an array of intersection {@link Point}s */ public static Point[] getIntersections(ICurve curve, IShape shape) { Set<Point> intersections = new HashSet<>(); for (ICurve curve2 : shape.getOutlineSegments()) { intersections .addAll(Arrays.asList(getIntersections(curve, curve2))); } return intersections.toArray(new Point[] {}); } /** * Delegates to the {@link #getIntersections(ICurve, IGeometry)} method. * * @param geom1 * the first {@link IGeometry} to intersect * @param geom2 * the second {@link IGeometry} to intersect * @return points of intersection */ public static Point[] getIntersections(IGeometry geom1, IGeometry geom2) { if (geom1 instanceof ICurve) { return getIntersections((ICurve) geom1, geom2); } else { Set<Point> intersections = new HashSet<>(); if (geom1 instanceof IShape) { for (ICurve curve : ((IShape) geom1).getOutlineSegments()) { intersections.addAll( Arrays.asList(getIntersections(curve, geom2))); } } else if (geom1 instanceof IMultiShape) { for (IShape shape : ((IMultiShape) geom1).getShapes()) { for (ICurve curve : shape.getOutlineSegments()) { intersections.addAll( Arrays.asList(getIntersections(curve, geom2))); } } } else { throw new UnsupportedOperationException("Not yet implemented."); } return intersections.toArray(new Point[] {}); } } /** * Returns the overlaps between the given two {@link ICurve curves}, if the * given two {@link ICurve curves} overlap. * * @param curve1 * The first {@link ICurve}, for which to compute overlaps. * @param curve2 * The second {@link ICurve}, for which to compute overlaps. * @return An array of {@link ICurve curves} representing overlaps between * the two given curves. An empty array in case the curves do not * overlap. */ public static ICurve[] getOverlaps(ICurve curve1, ICurve curve2) { Set<ICurve> overlaps = new HashSet<>(); for (BezierCurve bezier1 : curve1.toBezier()) { for (BezierCurve bezier2 : curve2.toBezier()) { BezierCurve overlap = bezier1.getOverlap(bezier2); if (overlap != null) { overlaps.add(overlap); } } } return overlaps.toArray(new ICurve[] {}); } /** * Checks if the given {@link ICurve}s intersect in a finite number of * {@link Point}s. * * @param c1 * The first {@link ICurve} to check for intersection * {@link Point}s * @param c2 * The second {@link ICurve} to check for intersection * {@link Point}s * @return <code>true</code> if both {@link ICurve}s have a finite set of * intersection {@link Point}s, otherwise <code>false</code> */ public static boolean intersect(ICurve c1, ICurve c2) { return getIntersections(c1, c2).length > 0; } /** * Checks if the given {@link ICurve}s overlap, i.e. both {@link ICurve}s * have an infinite number of intersection {@link Point}s. * * @param c1 * the first {@link ICurve} to check for overlap * @param c2 * the second {@link ICurve} to check for overlap * @return <code>true</code> if both {@link ICurve}s overlap, otherwise * <code>false</code> */ public static boolean overlap(ICurve c1, ICurve c2) { for (BezierCurve seg1 : c1.toBezier()) { for (BezierCurve seg2 : c2.toBezier()) { if (seg1.overlaps(seg2)) { return true; } } } return false; } /** * Builds up a {@link Path} from the given {@link ICurve}s. Only * {@link Line}, {@link QuadraticCurve} and {@link CubicCurve} objects can * be integrated into the constructed {@link Path}. * * @param curves * the {@link ICurve}s from which the {@link Path} is constructed * @return a new {@link Path} representing the given {@link ICurve}s */ public static final Path toPath(ICurve... curves) { Path p = new Path(); for (int i = 0; i < curves.length; i++) { ICurve c = curves[i]; if (i == 0) { p.moveTo(c.getX1(), c.getY1()); } if (c instanceof Line) { p.lineTo(c.getX2(), c.getY2()); } else if (c instanceof QuadraticCurve) { p.quadTo(((QuadraticCurve) c).getCtrlX(), ((QuadraticCurve) c).getCtrlY(), c.getX2(), c.getY2()); } else if (c instanceof CubicCurve) { p.cubicTo(((CubicCurve) c).getCtrlX1(), ((CubicCurve) c).getCtrlY1(), ((CubicCurve) c).getCtrlX2(), ((CubicCurve) c).getCtrlY2(), ((CubicCurve) c).getX2(), ((CubicCurve) c).getY2()); } else if (c instanceof BezierCurve) { Point[] points = ((BezierCurve) c).getPoints(); int length = points.length; if (length == 1) { p.moveTo(c.getX1(), c.getY1()); } else if (length == 2) { p.lineTo(c.getX2(), c.getY2()); } else if (length == 3) { p.quadTo(points[1].x, points[1].y, points[2].x, points[2].y); } else if (length == 4) { p.cubicTo(points[1].x, points[1].y, points[2].x, points[2].y, points[3].x, points[3].y); } else { throw new UnsupportedOperationException( "A BezierCurve with more than 4 control points cannot be integrated in a Path. Only singular, linear, quadratic, or cubic BezierCurves can be integrated in a Path."); } } else { throw new UnsupportedOperationException( "This type of ICurve is not yet implemented: toPath(" + Arrays.asList(curves) + ")"); } } return p; } /** * Transforms a sequence of {@link Point}s into a sequence of {@link Line} * segments, by creating a {@link Line} segment for each two adjacent * {@link Point}s in the given array. In case it is specified to close the * segment list, another {@link Line} segment is created that connects the * last and the first {@link Point} in the array. * * @param points * the array of {@link Point}s to convert * @param close * a flag indicating whether a {@link Line} segment will be * created from the last {@link Point} in the list back to the * first one * @return an array of {@link Line} segments, which is created by creating a * {@link Line} for each two adjacent {@link Point}s in the given * array, which includes a {@link Line} segment from the last * {@link Point} in the given array to the first one if the * <i>close</i> flag is set to <code>true</code> */ public static Line[] toSegmentsArray(Point[] points, boolean close) { int segmentCount = close ? points.length : points.length - 1; Line[] segments = new Line[segmentCount]; for (int i = 0; i < segmentCount; i++) { segments[i] = new Line(points[i], points[i + 1 < points.length ? i + 1 : 0]); } return segments; } private CurveUtils() { // this class should not be instantiated by clients } }