/***********************************************************************
* mt4j Copyright (c) 2008 - 2009 C.Ruff, Fraunhofer-Gesellschaft All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
***********************************************************************/
package org.mt4j.util.math;
import java.util.ArrayList;
/**
* The Class ConvexQuickHull2D.
*/
public class ConvexQuickHull2D{
/**
* Gets the convex hull2 d.
*
* @param Vector3Ds the vector3 ds
*
* @return the convex hull2 d
*/
@SuppressWarnings("unchecked")
public static ArrayList<Vector3D> getConvexHull2D(ArrayList<Vector3D> Vector3Ds) {
ArrayList<Vector3D> convexHull = new ArrayList<Vector3D>();
if (Vector3Ds.size() < 3) return (ArrayList<Vector3D>) Vector3Ds.clone();
// find extremals
int minVector3D = -1, maxVector3D = -1;
float minX = Float.POSITIVE_INFINITY;
float maxX = Float.NEGATIVE_INFINITY;
for (int i = 0; i < Vector3Ds.size(); i++) {
if (Vector3Ds.get(i).getX() < minX) {
minX = Vector3Ds.get(i).getX();
minVector3D = i;
}
if (Vector3Ds.get(i).getX() > maxX) {
maxX = Vector3Ds.get(i).getX();
maxVector3D = i;
}
}
Vector3D A = Vector3Ds.get(minVector3D);
Vector3D B = Vector3Ds.get(maxVector3D);
convexHull.add(A);
convexHull.add(B);
Vector3Ds.remove(A);
Vector3Ds.remove(B);
ArrayList<Vector3D> leftSet = new ArrayList<Vector3D>();
ArrayList<Vector3D> rightSet = new ArrayList<Vector3D>();
for (int i = 0; i < Vector3Ds.size(); i++) {
Vector3D p = Vector3Ds.get(i);
if (Vector3DLocation(A,B,p) == -1)
leftSet.add(p);
else
rightSet.add(p);
}
hullSet(A,B,rightSet,convexHull);
hullSet(B,A,leftSet,convexHull);
return convexHull;
}
/*
* Computes the square of the distance of Vector3D C to the segment defined by Vector3Ds AB
*/
/**
* Distance.
*
* @param A the a
* @param B the b
* @param C the c
*
* @return the float
*/
private static float distance(Vector3D A, Vector3D B, Vector3D C) {
float ABx = B.getX()-A.getX();
float ABy = B.getY()-A.getY();
float num = ABx*(A.getY()-C.getY())-ABy*(A.getX()-C.getX());
if (num < 0) num = -num;
return num;
}
/**
* Hull set.
*
* @param A the a
* @param B the b
* @param set the set
* @param hull the hull
*/
private static void hullSet(Vector3D A, Vector3D B, ArrayList<Vector3D> set, ArrayList<Vector3D> hull) {
int insertPosition = hull.indexOf(B);
if (set.size() == 0) return;
if (set.size() == 1) {
Vector3D p = set.get(0);
set.remove(p);
hull.add(insertPosition,p);
return;
}
float dist = Float.NEGATIVE_INFINITY;
int furthestVector3D = -1;
for (int i = 0; i < set.size(); i++) {
Vector3D p = set.get(i);
float distance = distance(A,B,p);
if (distance > dist) {
dist = distance;
furthestVector3D = i;
}
}
Vector3D P = set.get(furthestVector3D);
set.remove(furthestVector3D);
hull.add(insertPosition,P);
// Determine who's to the left of AP
ArrayList<Vector3D> leftSetAP = new ArrayList<Vector3D>();
for (int i = 0; i < set.size(); i++) {
Vector3D M = set.get(i);
if (Vector3DLocation(A,P,M)==1) {
leftSetAP.add(M);
}
}
// Determine who's to the left of PB
ArrayList<Vector3D> leftSetPB = new ArrayList<Vector3D>();
for (int i = 0; i < set.size(); i++) {
Vector3D M = set.get(i);
if (Vector3DLocation(P,B,M)==1) {
leftSetPB.add(M);
}
}
hullSet(A,P,leftSetAP,hull);
hullSet(P,B,leftSetPB,hull);
}
/**
* Vector3 d location.
*
* @param A the a
* @param B the b
* @param P the p
*
* @return the float
*/
private static float Vector3DLocation(Vector3D A, Vector3D B, Vector3D P) {
float cp1 = (B.getX()-A.getX())*(P.getY()-A.getY()) - (B.getY()-A.getY())*(P.getX()-A.getX());
return (cp1>0)?1:-1;
}
}