/******************************************************************************
* SrfCntr.c - contour a surface with a plane.				      *
*******************************************************************************
* (C) Gershon Elber, Technion, Israel Institute of Technology                 *
*******************************************************************************
* Written by Gershon Elber, June. 95.					      *
******************************************************************************/

#include "irit_sm.h"
#include "cagd_lib.h"
#include "symb_lib.h"
#include "iritprsr.h"
#include "ip_cnvrt.h"
#include "bool_lib.h"
#include "allocate.h"
#include "primitiv.h"
#include "geomat3d.h"
#include "user_loc.h"

#define MAX_PLANE_SIZE	100.0

static PlaneType
    ContourPlane = { 0, 0, 1, 0 };

static CagdRType SrfOnContour(CagdSrfStruct *Srf);
static CagdRType TriangleOnContour(CagdPType P1, CagdPType P2, CagdPType P3);

/*****************************************************************************
* DESCRIPTION:                                                               *
*   A call back function of the subdivision process for contouring.          *
* This function tests if given surface intersects or on with the contouring  *
* plane.  If no intersection occurs, this function returns a value that will *
* terminate the subdivision process.                                         *
*                                                                            *
* PARAMETERS:                                                                *
*   Srf:          Current sub-surface in the subdivision tree.               *
*                                                                            *
* RETURN VALUE:                                                              *
*   CagdRType:    Negative value if given surface does no intersect the	     *
*		contouring plane, positive otherwise.			     *
*                                                                            *
* SEE ALSO:                                                                  *
*   UserCntrSrfWithPlane                                                     *
*                                                                            *
* KEYWORDS:                                                                  *
*   SrfOnContour                                                             *
*****************************************************************************/
static CagdRType SrfOnContour(CagdSrfStruct *Srf)
{
    int i,
	IsRational = CAGD_IS_RATIONAL_SRF(Srf),
	Above = FALSE,
	Below = FALSE,
	Len = Srf -> ULength * Srf -> VLength;
    CagdRType
	**Points = Srf -> Points;

    /* Tests for surface control mesh being above and below the plane. */
    for (i = 0; i < Len; i++) {
	CagdRType R;

	if (IsRational)
	    R = Points[1][i] * ContourPlane[0] +
	        Points[2][i] * ContourPlane[1] + 
	        Points[3][i] * ContourPlane[2] + Points[0][i] * ContourPlane[3];
	else
	    R = Points[1][i] * ContourPlane[0] +
	        Points[2][i] * ContourPlane[1] + 
	        Points[3][i] * ContourPlane[2] + ContourPlane[3];

	if (R < 0.0)
	    Below = TRUE;
	if (R > 0.0)
	    Above = TRUE;

	if (Above && Below)
	    break;
    }

    /* Surface does not intersect plane or at lowest subdivision level. */
    return !Above || !Below ? -1.0 : 0.0;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   A call back function of the subdivision process for contouring.          *
* This function tests if given triangle intersects or on with the contouring *
* plane.  If no intersection occurs, this function returns a value that will *
* terminate the subdivision process.                                         *
*                                                                            *
* PARAMETERS:                                                                *
*   P1, P2, P3:	The three points of the triangle.                            *
*                                                                            *
* RETURN VALUE:                                                              *
*   CagdRType:    Negative value if given triangle does no intersect the     *
*		contouring plane, positive otherwise.			     *
*                                                                            *
* SEE ALSO:                                                                  *
*   UserCntrSrfWithPlane                                                     *
*                                                                            *
* KEYWORDS:                                                                  *
*   TriangleOnContour                                                        *
*****************************************************************************/
static CagdRType TriangleOnContour(CagdPType P1, CagdPType P2, CagdPType P3)
{
    int i,
	Above = FALSE,
	Below = FALSE;
    CagdRType R[3];

    R[0] = DOT_PROD(P1, ContourPlane) + ContourPlane[3];
    R[1] = DOT_PROD(P2, ContourPlane) + ContourPlane[3];
    R[2] = DOT_PROD(P3, ContourPlane) + ContourPlane[3];

    for (i = 0; i < 3; i++) {
	if (R[i] < 0.0)
	    Below = TRUE;
	if (R[i] > 0.0)
	    Above = TRUE;
    }

    return !Above || !Below ? -1.0 : 0.0;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Computes the intersection of a freeform surface and a plane by	     M
* approximating the surface by polygons and calling Boolean tools.	     M
*   If PSrfObj is a scalar surface then it is promoted first into a          M
* 3D Euclidean surface with UV as YZ coordinates (and X has scalar field).   M
*                                                                            *
* PARAMETERS:                                                                M
*   Srf:     To approximate its intersection with the given plane.           M
*   Plane:   To intersect with the given surface. If NULL the XY plane is    M
*	     used.							     M
*   Fineness:   Control of polygonal approximation of surface. See	     M
*	     IritSurface2Polygons function.				     M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPPolygonStruct *:   A list of polylines approximating the contour.      M
*                                                                            *
* SEE ALSO:                                                                  M
*   UserCntrScalarFieldAndEval						     M
*                                                                            *
* KEYWORDS:                                                                  M
*   UserCntrSrfWithPlane, contouring                                         M
*****************************************************************************/
IPPolygonStruct *UserCntrSrfWithPlane(CagdSrfStruct *Srf,
				      PlaneType Plane,
				      RealType FineNess)
{
    int OldInterCurve,
      	OldCirc = IritPrsrSetPolyListCirc(TRUE);
    CagdRType Width, t;
    CagdPType SrfCenter, PlaneCenter, Size;
    CagdBBoxStruct BBox;
    CagdSrfStruct *Srf3Space;
    IPObjectStruct *PlaneObj, *SrfObj, *CntrObj;
    IPPolygonStruct *CntrPoly, *SrfPolys, *Poly, *PrevPoly;
    CagdSrfErrorFuncType OldSrfFunc;
    CagdPlgErrorFuncType OldPlgFunc;

    PLANE_COPY(ContourPlane, Plane);

    switch (CAGD_NUM_OF_PT_COORD(Srf -> PType)) {
	case 1:
	case 2:
	    if (CAGD_IS_RATIONAL_SRF(Srf))
	        Srf3Space = CagdCoerceSrfTo(Srf, CAGD_PT_P3_TYPE);
	    else
	        Srf3Space = CagdCoerceSrfTo(Srf, CAGD_PT_E3_TYPE);
	    break;
	default:
	    Srf3Space = Srf;
	    break;
    }

    OldSrfFunc = BspSrf2PolygonSetErrFunc(SrfOnContour);
    OldPlgFunc = CagdPolygonSetErrFunc(TriangleOnContour);
    SrfPolys = IritSurface2Polygons(Srf3Space, TRUE, FineNess,
				    FALSE, FALSE, 0);
    CagdPolygonSetErrFunc(OldPlgFunc);
    BspSrf2PolygonSetErrFunc(OldSrfFunc);

    /* Filters out polygons that do not intersect the plane. */
    PrevPoly = NULL;
    for (Poly = SrfPolys; Poly != NULL; ) {
	IPVertexStruct
	    *V = Poly -> PVertex;
	int Above = FALSE,
	    Below = FALSE;

	do {
	    CagdRType
		R = DOT_PROD(V -> Coord, ContourPlane) + ContourPlane[3];

	    if (R < 0.0)
		Below = TRUE;
	    if (R > 0.0)
		Above = TRUE;

	    if (Above && Below)
		break;

	    V = V -> Pnext;
	}
	while (V != NULL && V != Poly -> PVertex);
 
	if (!Above || !Below) {
	    /* This polygons does not intersect the plane - purge it. */
	    if (PrevPoly != NULL)
		PrevPoly -> Pnext = Poly -> Pnext;
	    else
		SrfPolys = SrfPolys -> Pnext;
	    Poly -> Pnext = NULL;
	    IPFreePolygon(Poly);
	    Poly = PrevPoly != NULL ? PrevPoly -> Pnext : SrfPolys;
	}
	else {
	    PrevPoly = Poly;
	    Poly = Poly -> Pnext;
	}
    }

#ifdef DUMP_SRF_AND_POLYS
    {
	int Handle = IritPrsrOpenDataFile("srf_cntr.dat", FALSE, TRUE);

	IritPrsrPutObjectToHandler(Handle, GenSRFObject(Srf3Space));
	IritPrsrPutObjectToHandler(Handle, GenPOLYObject(SrfPolys));
	IritPrsrCloseStream(Handle, TRUE);
    }
#endif /* DUMP_SRF_AND_POLYS */

    /* Find location of surface so we can position the plane properly. */
    CagdSrfBBox(Srf3Space, &BBox);
    PT_CLEAR(SrfCenter);
    PT_SUB(Size, BBox.Max, BBox.Min);
    Width = MAX(MAX(Size[0], Size[1]), Size[2]);
    if (Width > MAX_PLANE_SIZE)
        Width = MAX_PLANE_SIZE;

    CGPointFromLinePlane(SrfCenter, Plane, Plane, PlaneCenter, &t);
    PlaneObj = PrimGenPOLYDISKObject(Plane, PlaneCenter, Width * 10.0);

    if ((SrfObj = GenPOLYObject(SrfPolys)) != NULL && SrfObj -> U.Pl != NULL) {
	OldInterCurve = BoolSetOutputInterCurve(TRUE);
	CntrObj = BooleanAND(SrfObj, PlaneObj);
	BoolSetOutputInterCurve(OldInterCurve);

	CntrPoly = CntrObj -> U.Pl;
	CntrObj -> U.Pl = NULL;
	IPFreeObject(CntrObj);
    }
    else {
	CntrPoly = NULL;
    }

    if (SrfObj != NULL)
        IPFreeObject(SrfObj);

    IPFreeObject(PlaneObj);

    if (Srf != Srf3Space)
        CagdSrfFree(Srf3Space);

    IritPrsrSetPolyListCirc(OldCirc);

    return IritPrsrMergePolylines(CntrPoly);
}
