/*
 * pPlane.java
 *
 * (C) 2005, Alex S.
 */

/**
 * represents a plaine. 
 * Ax + By + Cz + D = 0
 */
public class pPlane {

    public final static int FRONT = 1;
    public final static int BACK = -1;
    public final static int SPAN = 0;
    public final static int INCID = 2;

    // Ax + By + Cz + D = 0
    public double[] p = new double[4];

    public double eval(double x,double y,double z){
        return x * p[0] + y * p[1] + z * p[2] + p[3];
    }

    public double eval(double[] a){
        return a[0] * p[0] + a[1] * p[1] + a[2] * p[2] + p[3];
    }


    /**
     * test brush
     * (ie: test every face)
     */
    public int eval(pBrush b){
        if (b.numFaces == 0)
            return 1;
        int max = -1,min = 1;
        for (int i=0;i<b.numFaces;i++) {
            int e = eval(b.faces[i]);
            if (e == SPAN)
                return SPAN;
            if (e == INCID)
                continue;
            if (max < e)
                max = e;
            if (min > e)
                min = e;
        }
        if (max != min)
            return SPAN;
        return max;        
    }

    /**
     * given a winding, evaluate it.
     */
    public int eval(pWinding w){
        double max,min;
        max = min = eval(w.x[0],w.y[0],w.z[0]);
        for (int i=1;i<w.size;i++) {
            double e = eval(w.x[i],w.y[i],w.z[i]);
            if (max < e)
                max = e;
            if (min > e)
                min = e;
        }
        if (max > 0 && min < 0) {
            if (max - min < 0.1)
                return INCID;
            return SPAN;
        }
        if (max > 0)
            return FRONT;
        return BACK;
    }

    /**
     * evaluate a face
     */
    public int eval(pFace f){
        return eval(f.winding);
    }

    /**
     * cut a brush
     */
    public void cut(pBrush in,pBrush front,pBrush back){
        if (in.numFaces == 0)
            return;
        for (int i=0;i<in.numFaces;i++) {
            int e = eval(in.faces[i]);
            if (e == FRONT) {
                front.faces[front.numFaces++] = in.faces[i];
            } else if (e == BACK) {
                back.faces[back.numFaces++] = in.faces[i];
            } else if (e == SPAN) {
                pFace f = new pFace();
                pFace b = new pFace();
                cut(in.faces[i],f,b);
                front.faces[front.numFaces++] = f;
                back.faces[back.numFaces++] = b;
            }
        }
    }

    /**
     * cut a face
     */
    public boolean cut(pFace in,pFace front,pFace back){
        cut(in.winding,front.winding,back.winding);
        front.parent = in.parent == null ? in:in.parent;
        back.parent = in.parent == null ? in:in.parent;
        front.color = front.parent.color;
        back.color = back.parent.color;
        front.plane = front.parent.plane;
        back.plane = back.parent.plane;
        return true;
    }

    /**
     * cut a winding
     */
    public boolean cut(pWinding in,pWinding front,pWinding back){

        double[] ix = in.x;
        double[] iy = in.y;
        double[] iz = in.z;

        double[] fx = front.x;
        double[] fy = front.y;
        double[] fz = front.z;

        double[] bx = back.x;
        double[] by = back.y;
        double[] bz = back.z;

        int n = in.size;

        int p,s,flen,blen;
        double k,l,d1,d2;
        double x,y,z;
        flen = blen = 0;
        s = n - 1;
        for (p=0;p<n;p++) {
            if ( (k = eval(ix[p],iy[p],iz[p])) >= 0 ) {    // p is front
                if ( (l = eval(ix[s],iy[s],iz[s])) >= 0 ) {  // s in font
                    fx[flen] = ix[p];
                    fy[flen] = iy[p];
                    fz[flen] = iz[p];
                    flen++;
                } else {
                    // find intersection
                    d1 = -l;
                    d2 = (d1 + k);     
                    x = ix[s] + d1/d2 * ( ix[p] - ix[s] );
                    y = iy[s] + d1/d2 * ( iy[p] - iy[s] );
                    z = iz[s] + d1/d2 * ( iz[p] - iz[s] );

                    bx[blen] = x;
                    by[blen] = y;
                    bz[blen] = z;
                    blen++;
                    // cut
                    fx[flen] = x;
                    fy[flen] = y;
                    fz[flen] = z;
                    flen++;
                    fx[flen] = ix[p];
                    fy[flen] = iy[p];
                    fz[flen] = iz[p];
                    flen++;
                }
            } else {
                if ( (l = eval(ix[s],iy[s],iz[s])) >= 0 ) {  // s in front
                    d1 = -k;
                    d2 = (d1 + l);
                    x = ix[p] + d1/d2 * ( ix[s] - ix[p] );
                    y = iy[p] + d1/d2 * ( iy[s] - iy[p] );
                    z = iz[p] + d1/d2 * ( iz[s] - iz[p] );

                    fx[flen] = x;
                    fy[flen] = y;
                    fz[flen] = z;
                    flen++;
                    // cut
                    bx[blen] = x;
                    by[blen] = y;
                    bz[blen] = z;
                    blen++;
                    bx[blen] = ix[p];
                    by[blen] = iy[p];
                    bz[blen] = iz[p];
                    blen++;
                } else {
                    // just save p.
                    bx[blen] = ix[p];
                    by[blen] = iy[p];
                    bz[blen] = iz[p];
                    blen++;
                }
            }
            s = p;
        }
        front.size = flen;
        back.size = blen;
        return true;
    }
}


