org.jhotdraw.geom.ConvexHull Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jhotdraw Show documentation
Show all versions of jhotdraw Show documentation
JHotDraw 7 with openTCS-specific modifications
The newest version!
/*
* @(#)ConvexHull.java
*
* Copyright (c) 1996-2010 by the original authors of JHotDraw and all its
* contributors. All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with the copyright holders. For details
* see accompanying license terms.
*/
package org.jhotdraw.geom;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Shape;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
/**
* Provides utility methods for computing the convex hull from a set of points.
*
* @author Werner Randelshofer
* @version $Id: ConvexHull.java 727 2011-01-09 13:23:59Z rawcoder $
*/
public class ConvexHull {
/**
* Computes the convex hull from a set of points.
*
* @param points
* @return convex hull of the points as a polygon object.
*/
public static Polygon getConvexHullPolygon(List points) {
Polygon convexHull = new Polygon();
for (Point p : getConvexHull(points.toArray(new Point[points.size()]))) {
convexHull.addPoint(p.x, p.y);
}
return convexHull;
}
/**
* Computes the convex hull from a set of points.
*
* @param points
* @return convex hull of the points as a Polygon2D object.
*/
public static Polygon2D.Double getConvexHullPath2D(List points) {
Polygon2D.Double convexHull = new Polygon2D.Double();
for (Point p : getConvexHull(points.toArray(new Point[points.size()]))) {
convexHull.add(p);
}
return convexHull;
}
/**
* Computes the convex hull from a shape.
*
* @param shape an arbitray shape
* @return convex hull of the points as a Polygon2D object.
*/
public static Polygon2D.Double getConvexHullPath2D(Shape shape) {
List points = new LinkedList();
double[] coords = new double[6];
for (PathIterator i = shape.getPathIterator(null); !i.isDone(); i.next()) {
switch (i.currentSegment(coords)) {
case PathIterator.SEG_CLOSE:
break;
case PathIterator.SEG_MOVETO:
case PathIterator.SEG_LINETO:
points.add(new Point2D.Double(coords[0], coords[1]));
break;
case PathIterator.SEG_QUADTO:
points.add(new Point2D.Double(coords[0], coords[1]));
points.add(new Point2D.Double(coords[2], coords[3]));
break;
case PathIterator.SEG_CUBICTO:
points.add(new Point2D.Double(coords[0], coords[1]));
points.add(new Point2D.Double(coords[2], coords[3]));
points.add(new Point2D.Double(coords[4], coords[5]));
break;
}
}
Polygon2D.Double convexHull = new Polygon2D.Double();
for (Point2D.Double p : getConvexHull2D(points.toArray(new Point2D.Double[points.size()]))) {
convexHull.add(p);
}
return convexHull;
}
/**
* Computes the convex hull from a set of points.
*
* @param points
* @return convex hull of the points
*/
public static List getConvexHull(List points) {
return Arrays.asList(getConvexHull(points.toArray(new Point[points.size()])));
}
/**
* Computes the convex hull from a set of points.
*
* @param points
* @return convex hull of the points
*/
public static List getConvexHull2D(List points) {
return Arrays.asList(getConvexHull2D(points.toArray(new Point2D.Double[points.size()])));
}
/**
* Computes the convex hull from a set of points.
*
* @param points
* @return convex hull of the points
*/
public static Point[] getConvexHull(Point[] points) {
// Quickly return if no work is needed
if (points.length < 3) {
return points.clone();
}
// Sort points from left to right O(n log n)
Point[] sorted = points.clone();
Arrays.sort(sorted, new Comparator() {
@Override
public int compare(Point o1, Point o2) {
int v = o1.x - o2.x;
return (v == 0) ? o1.y - o2.y : v;
}
});
Point[] hull = new Point[sorted.length + 2];
// Process upper part of convex hull O(n)
int upper = 0; // Number of points in upper part of convex hull
hull[upper++] = sorted[0];
hull[upper++] = sorted[1];
for (int i = 2; i < sorted.length; i++) {
hull[upper++] = sorted[i];
while (upper > 2 && !isRightTurn(hull[upper - 3], hull[upper - 2], hull[upper - 1])) {
hull[upper - 2] = hull[upper - 1];
upper--;
}
}
// Process lower part of convex hull O(n)
int lower = upper; // (lower - number + 1) = number of points in the lower part of the convex hull
hull[lower++] = sorted[sorted.length - 2];
for (int i = sorted.length - 3; i >= 0; i--) {
hull[lower++] = sorted[i];
while (lower - upper > 1 && !isRightTurn(hull[lower - 3], hull[lower - 2], hull[lower - 1])) {
hull[lower - 2] = hull[lower - 1];
lower--;
}
}
lower -= 1;
// Reduce array
Point[] convexHull = new Point[lower];
System.arraycopy(hull, 0, convexHull, 0, lower);
return convexHull;
}
/**
* Returns true, if the three given points make a right turn.
*
* @param p1 first point
* @param p2 second point
* @param p3 third point
* @return true if right turn.
*/
public static boolean isRightTurn(Point p1, Point p2, Point p3) {
if (p1.equals(p2)||p2.equals(p3)) {
// no right turn if points are at same location
return false;
}
double val = (p2.x * p3.y + p1.x * p2.y + p3.x * p1.y) - (p2.x * p1.y + p3.x * p2.y + p1.x * p3.y);
return val > 0;
}
/**
* Computes the convex hull from a set of points.
*
* @param points
* @return convex hull of the points
*/
public static Point2D.Double[] getConvexHull2D(Point2D.Double[] points) {
// Quickly return if no work is needed
if (points.length < 3) {
return points.clone();
}
// Sort points from left to right O(n log n)
Point2D.Double[] sorted = points.clone();
Arrays.sort(sorted, new Comparator() {
@Override
public int compare(Point2D.Double o1, Point2D.Double o2) {
double v = o1.x - o2.x;
if (v == 0) {
v = o1.y - o2.y;
}
return (v > 0) ? 1 : ((v < 0) ? -1 : 0);
}
});
Point2D.Double[] hull = new Point2D.Double[sorted.length + 2];
// Process upper part of convex hull O(n)
int upper = 0; // Number of points in upper part of convex hull
hull[upper++] = sorted[0];
hull[upper++] = sorted[1];
for (int i = 2; i < sorted.length; i++) {
hull[upper++] = sorted[i];
while (upper > 2 && !isRightTurn2D(hull[upper - 3], hull[upper - 2], hull[upper - 1])) {
hull[upper - 2] = hull[upper - 1];
upper--;
}
}
// Process lower part of convex hull O(n)
int lower = upper; // (lower - number + 1) = number of points in the lower part of the convex hull
hull[lower++] = sorted[sorted.length - 2];
for (int i = sorted.length - 3; i >= 0; i--) {
hull[lower++] = sorted[i];
while (lower - upper > 1 && !isRightTurn2D(hull[lower - 3], hull[lower - 2], hull[lower - 1])) {
hull[lower - 2] = hull[lower - 1];
lower--;
}
}
lower -= 1;
// Reduce array
Point2D.Double[] convexHull = new Point2D.Double[lower];
System.arraycopy(hull, 0, convexHull, 0, lower);
return convexHull;
}
/**
* Returns true, if the three given points make a right turn.
*
* @param p1 first point
* @param p2 second point
* @param p3 third point
* @return true if right turn.
*/
public static boolean isRightTurn2D(Point.Double p1, Point.Double p2, Point.Double p3) {
if (p1.equals(p2)||p2.equals(p3)) {
// no right turn if points are at same location
return false;
}
double val = (p2.x * p3.y + p1.x * p2.y + p3.x * p1.y) - (p2.x * p1.y + p3.x * p2.y + p1.x * p3.y);
return val > 0;
}
}