![JAR search and dependency download from the Maven repository](/logo.png)
georegression.fitting.polygon.ConvexHullGrahamScan_F32 Maven / Gradle / Ivy
/*
* Copyright (C) 2020, 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.fitting.polygon;
import javax.annotation.Generated;
import georegression.struct.point.Point2D_F32;
import georegression.struct.shapes.Polygon2D_F32;
import org.ddogleg.sorting.QuickSortComparator;
import org.ddogleg.struct.FastAccess;
import org.ddogleg.struct.FastArray;
import java.util.Comparator;
/**
* Finds the convex hull using Graham Scan. Runtime complexity of O(n log n).
*
* @author Peter Abeles
*/
@Generated("georegression.fitting.polygon.ConvexHullGrahamScan_F64")
public class ConvexHullGrahamScan_F32 implements FitConvexHull_F32 {
// Point that all the other points are sorted based on their relative angle to it
Point2D_F32 pivot = new Point2D_F32();
// use a linked list to store the points because O(1) cost to removing an element
final FastArray stack = new FastArray<>(Point2D_F32.class);
// Sorts the array. Java's sort method is not used to avoid memory creation/destruction
final CompareAngle compareAngle = new CompareAngle();
final QuickSortComparator sorter = new QuickSortComparator<>(compareAngle);
/**
* Fits a convex hull to the provided set of points. The list is modified by changing the order of points inside
* of it. If the input is a degenerate case where there is no clear solution it will do the following: A single
* point will return a single point. If all points lie along a line (2 or more points) then a polygon that's
* composed of two points will be returned.
*
* @param points (Input, Output) Point that the convex hull is fit to. This list will be re-ordered.
* @param output (Output) The found convex hull.
*/
@Override
public void process(FastAccess points, Polygon2D_F32 output) {
output.vertexes.reset();
if (points.isEmpty())
return;
stack.clear();
int indexLowestX = findLowestX(points);
pivot = points.get(indexLowestX);
// Sort the points according to Graham Scan's approach below
sorter.sort(points.data,points.size);
if (points.get(0) != pivot)
throw new RuntimeException("BUG!");
for (int i = 0; i < points.size; i++) {
// Pop the last points from the stack until we turn counter-clockwise
while (stack.size >= 2 && isCW(stack.getTail(1), stack.getTail(), points.get(i)) >= 0) {
stack.removeTail();
}
stack.add(points.get(i));
}
// Copy the stack into the output polygon
output.vertexes.resize(stack.size());
for (int i = 0; i < stack.size; i++) {
output.vertexes.get(i).setTo(stack.get(i));
}
}
/**
* Finds the point with the lowest x-axis value. If two have the same value then the one with the lowest y value
* breaks the dead lock
*/
private int findLowestX(FastAccess points) {
int selectedIndex = 0;
Point2D_F32 pivot = points.get(selectedIndex);
for (int i = 1; i < points.size(); i++) {
Point2D_F32 p = points.get(i);
if (p.x <= pivot.x) {
if (p.x == pivot.x) {
if (p.y < pivot.y) {
pivot = p;
selectedIndex = i;
}
} else {
pivot = p;
selectedIndex = i;
}
}
}
return selectedIndex;
}
/**
* Returns 1 if (a-pivot) is cw of (b-pivot).
*/
static int isCW(Point2D_F32 pivot, Point2D_F32 a, Point2D_F32 b) {
float dxa = a.x - pivot.x;
float dya = a.y - pivot.y;
float dxb = b.x - pivot.x;
float dyb = b.y - pivot.y;
float cross = dxa*dyb - dya*dxb;
return Float.compare(0.0f, cross);
}
/**
* Compares two points. If a is counter-clockwise of b then it return 1. If they have the same angle then return -1
* if 'a' is closer to the pivot than 'b'.
*/
class CompareAngle implements Comparator {
@Override
public int compare(Point2D_F32 a, Point2D_F32 b) {
float dxa = a.x - pivot.x;
float dya = a.y - pivot.y;
float dxb = b.x - pivot.x;
float dyb = b.y - pivot.y;
float cross = dxa*dyb - dya*dxb;
if (cross < 0.0f)
return 1;
else if (cross > 0.0f)
return -1;
else {
float ra = dxa*dxa + dya*dya;
float rb = dxb*dxb + dyb*dyb;
return Float.compare(ra, rb);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy