package Geometry;
import java.util.*;

/**
 * Class to represent a vertex (an NVector) and a facet conflict list for
 * use in the convex hull algorithm.
 * Written by Chris Studholme
 * Licenced for redistribution under the GNU General Public Licence
 */
class Vertex {

    /** Location of vertex.
     */
    public NVector vertex;

    /** Conflict list for convex hull algorithm.
     */
    public Vector Fconflict;

    /**
     * Construct new vertex.
     * @param location location of vertex
     */
    public Vertex(NVector vertex) {
	this.vertex = vertex;
    }
}


/**
 * Class to compute the 3-dimensional convex hull of a set of points.
 */
public class ConvexHull extends Hull {

    /** Random number generator used in convex hull algorithm.
     */
    private static Random random = new Random();

    /**
     * Construct convex hull.  
     * Precondition: there are at least 4 points and at least 4 of the points
     * are non-coplanar.
     * @param points array of points in 3D
     * @param npoints number of points to use in computation
     */
    public ConvexHull(NVector[] points, int npoints) {
	Vertex[] vertices = new Vertex[npoints];
	for (int i=0; i<npoints; ++i)
	    vertices[i] = new Vertex(points[i]);
	// find first four non-coplaner vertices
	int v1 = 0;
	// find v2 different from v1
	int v2 = 1;
	while (vertices[v2].vertex.equals(vertices[v1].vertex))
	    ++v2;
	// find v3 not colinear with v1 and v2
	int v3 = v2;
	NVector line = vertices[v2].vertex.subtract(vertices[v1].vertex);
	NVector cross;
	do {
	    cross = line.crossProduct(vertices[++v3].vertex.
				      subtract(vertices[v1].vertex));
	} while (cross.lengthSquare()==0);
	// find v4 not coplaner with v1, v2, and v3
	int v4 = v3;
	double dot;
	do {
	    dot = cross.innerProduct(vertices[++v4].vertex.
				     subtract(vertices[v1].vertex));
	} while (dot==0);
	// make v1, v2, v3, v4 be 0, 1, 2, 3
	if (v2>1) 
	    swap(vertices,v2,1);
	if (v3>2)
	    swap(vertices,v3,2);
	if (v4>3)
	    swap(vertices,v4,3);
	// create inital hull
	Facet[] facets = new Facet[4];
	facets[0] = dot<0 ?
	    new Facet(vertices[0].vertex,
		      vertices[1].vertex,
		      vertices[2].vertex) :
	    new Facet(vertices[2].vertex,
		      vertices[1].vertex,
		      vertices[0].vertex);
	facets[1]=new Facet(facets[0].outer,vertices[3].vertex);
	facets[2]=new Facet(facets[0].outer.next,vertices[3].vertex);
	facets[3]=new Facet(facets[0].outer.prev,vertices[3].vertex);
	// connect facets
	for (int i=1; i<4; ++i)
	    for (int j=i+1; j<4; ++j)
		facets[i].connectTo(facets[j]);
	// randomize vertex order
	for (int i=4; i<vertices.length-1; ++i) {
	    int j = i+random.nextInt(vertices.length-i);
	    if (j>i) 
		swap(vertices,i,j);
	}
	// inital conflict graph
	for (int i=4; i<vertices.length; ++i) {
	    for (int j=0; j<4; ++j) {
		if (facets[j].isOutside(vertices[i].vertex)) {
		    if (vertices[i].Fconflict==null)
			vertices[i].Fconflict=new Vector();
		    if (facets[j].Pconflict==null)
			facets[j].Pconflict=new Vector();
		    vertices[i].Fconflict.add(facets[j]);
		    facets[j].Pconflict.add(vertices[i]);
		}
	    }
	}	
	// main loop
	face = facets[0];
	int nfacets=4;
	for (int i=4; i<vertices.length; ++i) {
	    if ((vertices[i].Fconflict!=null)&&
		(vertices[i].Fconflict.size()>0)) {

		HalfEdge[] edgelist = 
		    new HalfEdge[1+vertices[i].Fconflict.size()*2];
		int nedges = 0;
		// remove conflicting facets
		while (vertices[i].Fconflict.size()>0) {
		    Facet f = (Facet)vertices[i].Fconflict.lastElement();
		    // check edge list for this facet's edges and remove
		    for (int k=nedges-1; k>=0; --k)
			if (edgelist[k].facet==f)
			    edgelist[k]=edgelist[--nedges];
		    // add twins to edge list
		    HalfEdge e=f.outer;
		    do {
			if (e.twin!=null)
			    edgelist[nedges++]=e.twin;
			e=e.next;
		    } while (e!=f.outer);
		    // remove facet
		    f.disconnect();
		    --nfacets;
		    // remove from all Fconflict lists
		    for (Enumeration k=f.Pconflict.elements(); 
			 k.hasMoreElements();)
			((Vertex)k.nextElement()).Fconflict.remove(f);
		}

		// create new facets
		facets = new Facet[nedges];
		for (int j=0; j<nedges; ++j) {
		    facets[j] = new Facet(edgelist[j],vertices[i].vertex);
		    ++nfacets;
		    for (int k=0; k<j; ++k)
			facets[j].connectTo(facets[k]);
		}
		// update conflict graph (not the best algorithm)
		for (int j=0; j<nedges; ++j)
		    for (int k=i+1; k<vertices.length; ++k) 
			if (facets[j].isOutside(vertices[k].vertex)) {
			    if (vertices[k].Fconflict==null)
				vertices[k].Fconflict=new Vector();
			    if (facets[j].Pconflict==null)
				facets[j].Pconflict=new Vector();
			    vertices[k].Fconflict.add(facets[j]);
			    facets[j].Pconflict.add(vertices[k]);
			}
		// update starting point
		face = facets[0];
	    }
	}
    }

}
