/*
* Copyright (C) 2011-2015, Peter Abeles. All Rights Reserved.
*
* This file is part of Geometric Regression Library (GeoRegression).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package georegression.geometry.algs;
import georegression.struct.point.Point2D_F32;
import georegression.struct.shapes.Polygon2D_F32;
import org.ddogleg.sorting.QuickSortComparator;
import org.ddogleg.struct.FastQueue;
import java.util.Comparator;
/**
* Computes the convex hull of a set of points using Andrew's monotone chain algorithm. O(n log n) for sort and
* O(N) for convex hull computation, where N is number of input points.
*
* @author Peter Abeles
*/
public class AndrewMonotoneConvexHull_F32 {
// Use this sorting routine to avoid declaring memory each time its called
QuickSortComparator<Point2D_F32> sorter;
FastQueue<Point2D_F32> work = new FastQueue<Point2D_F32>(Point2D_F32.class,false);
public AndrewMonotoneConvexHull_F32() {
// Sort the points based on their x value. If the same then use y value
sorter = new QuickSortComparator<Point2D_F32>(new Comparator<Point2D_F32>() {
@Override
public int compare(Point2D_F32 a, Point2D_F32 b) {
if( a.x < b.x )
return -1;
else if( a.x > b.x )
return 1;
else if( a.y < b.y )
return -1;
else if( a.y > b.y )
return 1;
return 0;
}
});
}
/**
* Computes the convex hull. The output will be in counter-clockwise order.
*
* @param input List of input points. The list will be modified by sorting
* @param length Number of valid elements in list
* @param hull (Output) Where the complex hull is written to
*/
public void process( Point2D_F32[] input , int length , Polygon2D_F32 hull )
{
// ahdnle special cases
if( length == 2 ) {
hull.vertexes.resize(length);
for (int i = 0; i < length; i++) {
hull.get(i).set(input[i]);
}
return;
}
sorter.sort(input,length);
work.reset();
// construct the lower hull
for (int i = 0; i < length; i++) {
Point2D_F32 p = input[i];
//Contains at least 2 points and the last two points and 'p' do not make a counter-clockwise turn
while( work.size() >= 2 && subtractThenCross(p,work.getTail(0),work.getTail(1)) >= 0) {
// remove the last points from the hull
work.removeTail();
}
// append p to the end
work.add(p);
}
work.removeTail();
int minSize = work.size+2;
// construct upper hull
for(int i = length-1 ; i >= 0 ; i --) // Finding top layer from hull
{
//Contains at least 2 points and the last two points and 'p' do not make a counter-clockwise turn
Point2D_F32 p = input[i];
while( work.size() >= minSize && subtractThenCross(p,work.getTail(0),work.getTail(1)) >= 0 ) {
work.removeTail();
}
// append p to the end
work.add(p);
}
work.removeTail();
// create a copy for the output
// the work buffer contains references to the input points, but to be safe the output should have its
// own instances
hull.vertexes.resize(work.size);
for (int i = 0; i < work.size(); i++) {
hull.vertexes.data[i].set(work.get(i));
}
}
/**
* Performs the following operation: output = z-component[ (a-b) cross (a-c) ]
*/
private static float subtractThenCross( Point2D_F32 a , Point2D_F32 b , Point2D_F32 c ) {
float x0 = b.x - a.x;
float y0 = b.y - a.y;
float x1 = c.x - a.x;
float y1 = c.y - a.y;
return x0 * y1 - y0 * x1;
}
}