package Geometry;
import java.util.*;

/**
 * Class to represent a convex facet in 3 dimensions.
 * Written by Chris Studholme
 * Licenced for redistribution under the GNU General Public Licence
 */
public class Facet {

    /** Some edge on the outer boundary.
     */
    protected HalfEdge outer;

    /** Conflict list for convex hull algorithm.
     */
    protected Vector Pconflict;

    /** 
     * Create a triangular facet from a set of 3 vertices.
     * @param v1 first vertex
     * @param v2 second vertex
     * @param v3 third vertex
     */
    public Facet(NVector v1, NVector v2, NVector v3) {
	outer = new HalfEdge(v1,this);
	outer.next = new HalfEdge(v2,outer,this);
	outer.next.next = new HalfEdge(v3,outer.next,this);
	outer.prev = outer.next.next;
	outer.prev.next = outer;
    }

    /**
     * Create a triangular facet from one edge and one vertex.
     * @param twin the twin of the new edge this facet will have
     * @param v3 third vertex
     */
    public Facet(HalfEdge twin, NVector v3) {
	outer = new HalfEdge(twin,this);
	outer.next = new HalfEdge(twin.origin,outer,this);
	outer.next.next = new HalfEdge(v3,outer.next,this);
	outer.prev = outer.next.next;
	outer.prev.next = outer;
    }

    /**
     * Create of copy of some other facet.  The copy is left unconnected
     * to any other facet.
     * @param other some other facet object to copy
     */
    protected Facet(Facet other) {
	HalfEdge e=other.outer;
	outer = new HalfEdge(e.origin,this);
	HalfEdge prev = outer;
	e=e.next;
	do {
	    prev = new HalfEdge(e.origin,prev,this);
	    e=e.next;
	} while (e!=other.outer);
	outer.prev=prev;
	prev.next=outer;
    }

    /**
     * Enumerate the outer vertices of this facet and return as an array.
     * @return array of vertices
     */
    public NVector[] getVertices() {
	Vector v = new Vector();
	HalfEdge e=outer;
	do {
	    v.add(e.origin);
	    e=e.next;
	} while (e!=outer);
	NVector[] result = new NVector[v.size()];
	return (NVector[])v.toArray(result);
    }

    /**
     * Enumerate the outer half edges of this facet and return as an array.
     * @return array of edges
     */
    public HalfEdge[] getEdges() {
	Vector v = new Vector();
	HalfEdge e=outer;
	do {
	    v.add(e);
	    e=e.next;
	} while (e!=outer);
	HalfEdge[] result = new HalfEdge[v.size()];
	return (HalfEdge[])v.toArray(result);
    }


    /**
     * Find a particular outer edge of this facet.
     * @param v1 first vertex
     * @param v2 second vertex
     * @return an edge (or null if not found)
     */
    public HalfEdge findEdge(NVector v1, NVector v2) {
	HalfEdge e=outer;
	do {
	    if ((v1.equals(e.origin))&&
 		(v2.equals(e.next.origin)))
		return e;
	    e=e.next;
	} while (e!=outer);
	return null;
    }

    /**
     * Attempt to connect this facet to the given one.
     * @param f facet to connect to
     * @return true if connection made
     */
    public boolean connectTo(Facet f) {
	HalfEdge e=outer;
	do {
	    HalfEdge twin = f.findEdge(e.next.origin,e.origin);
	    if (twin!=null) {
		if ((twin.twin!=null)&&(twin.twin!=e))
		    System.err.println("Facet.connectTo() already connected elsewhere!");
		e.twin = twin;
		twin.twin = e;
		return true;
	    }
	    e=e.next;
	} while (e!=outer);
	return false;
    }
    /**
     * Disconnect this facet from all neighbours.
     */
    public void disconnect() {
	HalfEdge e=outer;
	do {
	    if (e.twin!=null) {
		e.twin.twin=null;
		e.twin=null;
	    }
	    e=e.next;
	} while (e!=outer);
    }

    /**
     * Determine if the outside surface of this facet is visible from
     * the specified vertex.
     * @param v a vertex
     * @return true if surface is visible
     */
    public boolean isOutside(NVector v) {
	NVector cross = outer.next.origin.subtract(outer.origin).
	    crossProduct(outer.origin.subtract(outer.prev.origin));
	double dot = v.subtract(outer.origin).innerProduct(cross);
	return (dot<0);
    }

    /**
     * Return a unit vector representing the normal of this facet.
     * @return unit length normal vector
     */
    public NVector getNormal() {
	NVector cross = outer.next.origin.subtract(outer.origin).
	    crossProduct(outer.origin.subtract(outer.prev.origin));
	return cross.normalizeDirect();
    }
}
