/*****************************************************************************
*   "Irit" - the 3d (not only polygonal) solid modeller.		     *
*									     *
* Written by:  Gershon Elber				Ver 0.2, Mar. 1990   *
******************************************************************************
*   Module to handle the high level Boolean operations. The other modules    *
* should only call this module to perform Boolean operations. All the        *
* operations are none-destructives, meaning the given data is not modified.  *
*   Note all the polygons of the two given objects must be convex, and the   *
* returned object will also have only convex polygons!			     *
*****************************************************************************/

/* #define DEBUG2	         If defined, defines some printing routines. */

#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <string.h>
#include <signal.h>
#include "irit_sm.h"
#include "allocate.h"
#include "attribut.h"
#include "bool_loc.h"
#include "convex.h"
#include "poly_cln.h"
#include "geomat3d.h"

int BoolHandleCoplanarPoly = FALSE,     /* Whether to handle coplanar polys. */
    BoolOutputInterCurve = FALSE;	/* Kind of output from Boolean oper. */

static jmp_buf LclLongJumpBuffer;	     /* Used in fatal Boolean error. */
static int FatalErrorType;		     /* Type of fatal Boolean error. */
static BoolOperType BooleanOperation;		/* One of BOOL_OPER_OR, etc. */

static IPObjectStruct *BooleanCombineThreeObjs(IPObjectStruct *PObj1,
					       IPObjectStruct *PObj2,
					       IPObjectStruct *PObj3);
static IPObjectStruct *BooleanCoplanar(IPObjectStruct *PObj1,
				       IPObjectStruct *PObj2,
				       BoolOperType BoolOper);
static IPObjectStruct *VerifyBooleanInput(IPObjectStruct *PObj1,
					  IPObjectStruct *PObj2,
					  BoolOperType Oper);
static IPPolygonStruct *ComputeRotatedPolys(IPPolygonStruct *Pl,
					    int CopyPl,
					    MatrixType RotMat);

static void BooleanFPE(int Type);

#ifdef DEBUG2
static void PrintVrtxList(IPVertexStruct *V);
#endif /* DEBUG2 */

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Verify input for Booleans. Ret. NULL if OK, otherwise an obj to return.  *
*                                                                            *
* PARAMETERS:                                                                *
*   PObj1:     First object for to be performed Boolean.                     *
*   PObj2:     Second object for to be performed Boolean.                    *
*   Oper:      Type of operation (and, or, etc.).                            *
*                                                                            *
* RETURN VALUE:                                                              *
*   IPObjectStruct:      NULL if Boolean operation can/should be performed.  *
*                        Otherwise, an object to return as Boolean result.   *
*****************************************************************************/
static IPObjectStruct *VerifyBooleanInput(IPObjectStruct *PObj1,
					  IPObjectStruct *PObj2,
					  BoolOperType Oper)
{
    IPObjectStruct *PObj;
    IPPolygonStruct *Pl;

    BooleanOperation = Oper;

    if (!IP_IS_POLY_OBJ(PObj1) || (PObj2 != NULL && !IP_IS_POLY_OBJ(PObj2)))
	IritFatalError("Boolean: operation on non polygonal object(s).");

    signal(SIGFPE, BooleanFPE);		 /* Will trap floating point errors. */

    switch (Oper) {
	case BOOL_OPER_OR:
	    if (IP_IS_POLYLINE_OBJ(PObj1) && IP_IS_POLYLINE_OBJ(PObj2)) {
		if (PObj1 -> U.Pl == NULL)
		    PObj = CopyObject(NULL, PObj2, FALSE);
		else {
		    PObj = CopyObject(NULL, PObj1, FALSE);
		    Pl = IritPrsrGetLastPoly(PObj -> U.Pl);
		    Pl -> Pnext = CopyPolygonList(PObj2 -> U.Pl);
		}
	    	return PObj;
	    }
	case BOOL_OPER_AND:
	case BOOL_OPER_SUB:
	case BOOL_OPER_CUT:
	case BOOL_OPER_MERGE:
	case BOOL_OPER_NEG:
    	    if (IP_IS_POLYLINE_OBJ(PObj1) ||
		(PObj2 != NULL && IP_IS_POLYLINE_OBJ(PObj2))) {
    	    	IritWarningError(
		"Boolean: illegal operation on mixed polygon/line geometric object(s).");
		PObj = GenPolyObject("", NULL, NULL);
		return PObj;
    	    }

	    if (Oper != BOOL_OPER_NEG) {
	    	ConvexPolyObject(PObj1);/* Make sure all polygons are convex.*/
	    	ConvexPolyObject(PObj2);
	    }

	    return NULL;
	default:
    	    IritFatalError("Boolean: undefined Boolean operation.");
    	    return NULL;			     /* Make warning silent. */
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Performs a Boolean OR between two objects.				     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj1:    First object to perform the Boolean operation on.              M
*   PObj2:    Second object to perform the Boolean operation on.             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct: The result of the Boolean operation.                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   BooleanOR, Booleans                                                      M
*****************************************************************************/
IPObjectStruct *BooleanOR(IPObjectStruct *PObj1, IPObjectStruct *PObj2)
{
    IPObjectStruct *PObj;
    IPPolygonStruct *Pl;

    if ((PObj = VerifyBooleanInput(PObj1, PObj2, BOOL_OPER_OR)) != NULL)
	return PObj;
    else {
	if (setjmp(LclLongJumpBuffer) == 0) { /* Its the setjmp itself call! */
	    signal(SIGFPE, BooleanFPE);	 /* Will trap floating point errors. */
	    if (BoolOutputInterCurve)
		PObj = BooleanLow1Out2(PObj1, PObj2);/* Ret intersection crv.*/
	    else
		PObj = BooleanCombineThreeObjs(BooleanLow1Out2(PObj1, PObj2),
					       BooleanLow1Out2(PObj2, PObj1),
					       BooleanCoplanar(PObj1, PObj2,
							       BOOL_OPER_OR));

	}
	else {
	    /* We gain control from fatal error long jump - usually we should*/
	    /* return empty object, but if error is no intersection between  */
	    /* the two objects, we assume they have no common volume and     */
	    /* return a new object consists of the concat. of all polygons!  */
	    if (FatalErrorType != FTL_BOOL_NO_INTER) {
		PObj = GenPolyObject("", NULL, NULL);/* Return empty object. */
	    }
	    else {
		if (PObj1 -> U.Pl == NULL)
		    PObj = CopyObject(NULL, PObj2, FALSE);
		else {
		    PObj = CopyObject(NULL, PObj1, FALSE);/* Copy Obj1 polys.*/
		    Pl = PObj -> U.Pl;
		    while (Pl -> Pnext) Pl = Pl -> Pnext;
		    Pl -> Pnext = CopyPolygonList(PObj2 -> U.Pl);/*Obj2 poly.*/
		}
	    }
	}
    }

    return PObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Performs a Boolean AND between two objects.				     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj1:    First object to perform the Boolean operation on.              M
*   PObj2:    Second object to perform the Boolean operation on.             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct: The result of the Boolean operation.                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   BooleanAND, Booleans                                                     M
*****************************************************************************/
IPObjectStruct *BooleanAND(IPObjectStruct *PObj1, IPObjectStruct *PObj2)
{
    IPObjectStruct *PObj;

    if ((PObj = VerifyBooleanInput(PObj1, PObj2, BOOL_OPER_AND)) != NULL)
	return PObj;
    else {
	if (setjmp(LclLongJumpBuffer) == 0) { /* Its the setjmp itself call! */
	    signal(SIGFPE, BooleanFPE);	 /* Will trap floating point errors. */
	    if (BoolOutputInterCurve)
		PObj = BooleanLow1In2(PObj1, PObj2);/* Ret intersection crv. */
	    else
		PObj = BooleanCombineThreeObjs(BooleanLow1In2(PObj1, PObj2),
					       BooleanLow1In2(PObj2, PObj1),
					       BooleanCoplanar(PObj1, PObj2,
							       BOOL_OPER_AND));

	}
	else {/* We gain control from fatal error long jump - ret empty obj. */
	    PObj = GenPolyObject("", NULL, NULL);
	}
    }

    return PObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Performs a Boolean SUBtracion between two objects.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj1:    First object to perform the Boolean operation on.              M
*   PObj2:    Second object to perform the Boolean operation on.             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct: The result of the Boolean operation.                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   BooleanSUB, Booleans                                                     M
*****************************************************************************/
IPObjectStruct *BooleanSUB(IPObjectStruct *PObj1, IPObjectStruct *PObj2)
{
    IPObjectStruct *PObj, *PTemp, *PTempRev;

    if ((PObj = VerifyBooleanInput(PObj1, PObj2, BOOL_OPER_SUB)) != NULL)
	return PObj;
    else {
	if (setjmp(LclLongJumpBuffer) == 0) { /* Its the setjmp itself call! */
	    signal(SIGFPE, BooleanFPE);	 /* Will trap floating point errors. */
	    /* The 1 in 2 must be reversed (the inside/outside orientation): */
	    if (BoolOutputInterCurve) {
		PObj = BooleanLow1In2(PObj2, PObj1);/* Ret intersection crv. */
	    }
	    else {
		PTemp = BooleanLow1In2(PObj2, PObj1);
		PTempRev = BooleanNEG(PTemp);
		IPFreeObject(PTemp);

		PObj = BooleanCombineThreeObjs(BooleanLow1Out2(PObj1, PObj2),
					       PTempRev,
					       BooleanCoplanar(PObj1, PObj2,
							       BOOL_OPER_SUB));
	    }
	}
	else {/* We gain control from fatal error long jump - ret empty obj. */
	    PObj = GenPolyObject("", NULL, NULL);
	}
    }

    return PObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Performs a Boolean CUT between two objects.				     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj1:    First object to perform the Boolean operation on.              M
*   PObj2:    Second object to perform the Boolean operation on.             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct: The result of the Boolean operation.                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   BooleanCUT, Booleans                                                     M
*****************************************************************************/
IPObjectStruct *BooleanCUT(IPObjectStruct *PObj1, IPObjectStruct *PObj2)
{
    IPObjectStruct *PObj;

    if ((PObj = VerifyBooleanInput(PObj1, PObj2, BOOL_OPER_CUT)) != NULL)
	return PObj;
    else {
	if (setjmp(LclLongJumpBuffer) == 0) { /* Its the setjmp itself call! */
	    signal(SIGFPE, BooleanFPE);	 /* Will trap floating point errors. */
	    /* The 1 in 2 must be reversed (the inside/outside orientation): */
	    if (BoolOutputInterCurve) {
		PObj = BooleanLow1In2(PObj2, PObj1);/* Ret intersection crv. */
	    }
	    else {
		PObj = BooleanLow1Out2(PObj1, PObj2);
	    }
	}
	else {/* We gain control from fatal error long jump - ret empty obj. */
	    PObj = GenPolyObject("", NULL, NULL);
	}
    }

    return PObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Performs a Boolean Inside CUT between two objects.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj1:    First object to perform the Boolean operation on.              M
*   PObj2:    Second object to perform the Boolean operation on.             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct: The result of the Boolean operation.                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   BooleanICUT, Booleans                                                    M
*****************************************************************************/
IPObjectStruct *BooleanICUT(IPObjectStruct *PObj1, IPObjectStruct *PObj2)
{
    IPObjectStruct *PObj;

    if ((PObj = VerifyBooleanInput(PObj1, PObj2, BOOL_OPER_CUT)) != NULL)
	return PObj;
    else {
	if (setjmp(LclLongJumpBuffer) == 0) { /* Its the setjmp itself call! */
	    signal(SIGFPE, BooleanFPE);	 /* Will trap floating point errors. */
	    /* The 1 in 2 must be reversed (the inside/outside orientation): */
	    if (BoolOutputInterCurve) {
		PObj = BooleanLow1In2(PObj2, PObj1);/* Ret intersection crv. */
	    }
	    else {
		PObj = BooleanLow1In2(PObj1, PObj2);
	    }
	}
	else {/* We gain control from fatal error long jump - ret empty obj. */
	    PObj = GenPolyObject("", NULL, NULL);
	}
    }

    return PObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Performs a Boolean MERGE between two objects.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj1:    First object to perform the Boolean operation on.              M
*   PObj2:    Second object to perform the Boolean operation on.             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct: The result of the Boolean operation.                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   BooleanMERGE, Booleans                                                   M
*****************************************************************************/
IPObjectStruct *BooleanMERGE(IPObjectStruct *PObj1, IPObjectStruct *PObj2)
{
    IPObjectStruct *PObj;
    IPPolygonStruct *Pl;

    if ((PObj = VerifyBooleanInput(PObj1, PObj2, BOOL_OPER_MERGE)) != NULL)
	return PObj;
    else {
	if (PObj1 -> U.Pl == NULL)
    	    PObj = CopyObject(NULL, PObj2, FALSE);
    	else {
    	    PObj = CopyObject(NULL, PObj1, FALSE);       /* Copy Obj1 polys. */
	    Pl = PObj -> U.Pl;
	    while (Pl -> Pnext) Pl = Pl -> Pnext;
	    Pl -> Pnext = CopyPolygonList(PObj2 -> U.Pl);     /* Obj2 polys. */
	}
    }

    return PObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Performs a Boolean NEG between two objects.				     M
*   Negation is simply reversing the direction of the plane equation of each M
* polygon - the simplest Boolean operation...				     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:    Object to negate.						     M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct: The result of the Boolean operation.                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   BooleanNEG, Booleans                                                     M
*****************************************************************************/
IPObjectStruct *BooleanNEG(IPObjectStruct *PObj)
{
    int i;
    IPObjectStruct *PTemp;
    IPPolygonStruct *Pl;
    IPVertexStruct *V;

    if ((PTemp = VerifyBooleanInput(PObj, NULL, BOOL_OPER_NEG)) != NULL)
	return PTemp;
    else {
	PTemp = CopyObject(NULL, PObj, FALSE); /* Make fresh copy of object. */

	/* Scans all polygons and reverse plane equation and their vetrex    */
	/* list (cross prod. of consecutive edges must be in normal dir.).   */
	Pl = PTemp -> U.Pl;
	while (Pl != NULL) {
	    for (i = 0; i < 4; i++)
	        Pl -> Plane[i] = (-Pl -> Plane[i]);
	    IP_RST_CONVEX_POLY(Pl);

	    /* Invert vertices normals as well. */
	    V = Pl -> PVertex;
	    do {
		PT_SCALE(V -> Normal, -1.0);
		V = V -> Pnext;
	    }
	    while (V != NULL && V != Pl -> PVertex);

	    /* And reverse the order of the vertices. */
	    IritPrsrReverseVrtxList(Pl);

	    Pl = Pl -> Pnext;
	}
    }

    return PTemp;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Combining three geometric objects, by simply concat. their polygon.      *
* lists. Any object may be NULL in which only the other two are merged.	     *
*                                                                            *
* PARAMETERS:                                                                *
*   PObj1, PObj2, PObj3: The three objects to concatenate.                   *
*                                                                            *
* RETURN VALUE:                                                              *
*   IPObjectStruct: Concatenated object.                                     *
*****************************************************************************/
static IPObjectStruct *BooleanCombineThreeObjs(IPObjectStruct *PObj1,
					       IPObjectStruct *PObj2,
					       IPObjectStruct *PObj3)
{
    IPPolygonStruct *Pl;

    if (PObj1 != NULL) {
	CleanUpPolygonList(&PObj1 -> U.Pl);
	if (PObj1 -> U.Pl == NULL) {
	    IPFreeObject(PObj1);
	    PObj1 = NULL;
	}
    }
    if (PObj2 != NULL) {
	CleanUpPolygonList(&PObj2 -> U.Pl);
	if (PObj2 -> U.Pl == NULL) {
	    IPFreeObject(PObj2);
	    PObj2 = NULL;
	}
    }
    if (PObj3 != NULL) {
	CleanUpPolygonList(&PObj3 -> U.Pl);
	if (PObj3 -> U.Pl == NULL) {
	    IPFreeObject(PObj3);
	    PObj3 = NULL;
	}
    }

    if (PObj1 == NULL) {
	PObj1 = PObj2;
	PObj2 = PObj3;
	PObj3 = NULL;
    }
    if (PObj2 == NULL) {
	PObj2 = PObj3;
	PObj3 = NULL;
    }

    if (PObj2 != NULL) {
	/* Concat the polygons of PObj2 after the polygons of PObj1. */
	Pl = PObj1 -> U.Pl;
	while (Pl -> Pnext != NULL)
	    Pl = Pl -> Pnext;
	Pl -> Pnext = PObj2 -> U.Pl;  /* Concat. the polygons into one list. */
	PObj2 -> U.Pl = NULL;	    /* And release the second object header. */
	IPFreeObject(PObj2);

        if (PObj3 != NULL) {
	    /* Concat the polygons of PObj3 after the polygons of PObj1/2. */
	    while (Pl -> Pnext != NULL)
		Pl = Pl -> Pnext;
	    Pl -> Pnext = PObj3 -> U.Pl;  /* Concat. polygons into one list. */
	    PObj3 -> U.Pl = NULL;   /* And release the second object header. */
	    IPFreeObject(PObj3);
        }
    }

    return PObj1;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   If required (I.e. Object "COPLANAR" is set to TRUE) search all coplanar  *
* polygons in PObj1/2 and invoke the necessary two dimenstional Boolean.     *
*                                                                            *
* PARAMETERS:                                                                *
*   PObj1, PObj2: Two objects to search for coplanar polygons.               *
*   BoolOper:     Type of Boolean operation to be performed between PObj1/2. *
*                                                                            *
* RETURN VALUE:                                                              *
*   IPObjectStruct:   The resulting Boolean operation.                       *
*****************************************************************************/
static IPObjectStruct *BooleanCoplanar(IPObjectStruct *PObj1,
				       IPObjectStruct *PObj2,
				       BoolOperType BoolOper)
{
    MatrixType RotMat;
    IPObjectStruct *PObj;
    IPPolygonStruct *Pl, *PlTmp, *Pl1, *Pl2, *Pl1XY, *Pl2XY, *Pl1XYR, *Pl2XYR,
	*PlOut = NULL;

    if (!BoolHandleCoplanarPoly)
	return NULL;

    for ( Pl1 = PObj1 -> U.Pl; Pl1 != NULL; Pl1 = Pl1 -> Pnext) {
        for ( Pl2 = PObj2 -> U.Pl; Pl2 != NULL; Pl2 = Pl2 -> Pnext) {
	    RealType
		*Plane1 = Pl1 -> Plane,
		*Plane2 = Pl2 -> Plane;
	    int Shared = BOOL_APX_EQ(Plane1[0], Plane2[0]) &&
		         BOOL_APX_EQ(Plane1[1], Plane2[1]) &&
		         BOOL_APX_EQ(Plane1[2], Plane2[2]) &&
		         BOOL_APX_EQ(Plane1[3], Plane2[3]),
		AntiShared = BOOL_APX_EQ(Plane1[0], -Plane2[0]) &&
			     BOOL_APX_EQ(Plane1[1], -Plane2[1]) &&
			     BOOL_APX_EQ(Plane1[2], -Plane2[2]) &&
			     BOOL_APX_EQ(Plane1[3], -Plane2[3]);

	    if (!Shared && !AntiShared) {
		/* The two polygons are not coplanar, do not intersect. */
		continue;
	    }

	    if (Shared) {
		switch (BoolOper) {
		    case BOOL_OPER_AND:
		    case BOOL_OPER_OR:
		    case BOOL_OPER_SUB:
			GenRotateMatrix(RotMat, Pl1 -> Plane);
			Pl1XY = ComputeRotatedPolys(Pl1, TRUE, RotMat);
			Pl2XY = ComputeRotatedPolys(Pl2, TRUE, RotMat);
			/* Find the inverse matrix. */
			if (!MatInverseMatrix(RotMat, RotMat))
			    IritFatalError("BooleanCoplanar: Inverse matrix does not exists");

			if ((Pl = Boolean2D(Pl1XY, Pl2XY, BoolOper)) != NULL) {
			    Pl = ComputeRotatedPolys(Pl, FALSE, RotMat);
			    for (PlTmp = Pl;
				 PlTmp -> Pnext != NULL;
				 PlTmp = PlTmp -> Pnext);
			    PlTmp -> Pnext = PlOut;
			    PlOut = Pl;
			}

			IPFreePolygonList(Pl1XY);
			IPFreePolygonList(Pl2XY);
			break;

		    default:
		        IritFatalError("BooleanCoplanar: Unsupported Boolean operation BooleanCoplanar");
		        return NULL;
		}
	    }
	    else if (AntiShared) {
		switch (BoolOper) {
		    case BOOL_OPER_AND:
		    case BOOL_OPER_SUB:
			IritWarningError("Antishared coplanar polygons are ignored.");
			break;

		    case BOOL_OPER_OR:
			GenRotateMatrix(RotMat, Pl1 -> Plane);
			Pl1XY = ComputeRotatedPolys(Pl1, TRUE, RotMat);
			Pl1XYR = CopyPolygonList(Pl1XY);
			IritPrsrReverseVrtxList(Pl1XYR);
			Pl2XY = ComputeRotatedPolys(Pl2, TRUE, RotMat);
			Pl2XYR = CopyPolygonList(Pl2XY);
			IritPrsrReverseVrtxList(Pl2XYR);

			if (!MatInverseMatrix(RotMat, RotMat))
			    IritFatalError("BooleanCoplanar: Inverse matrix does not exists");

			if ((Pl = Boolean2D(Pl1XY, Pl2XYR, BOOL_OPER_SUB)) != NULL) {
			    Pl = ComputeRotatedPolys(Pl, FALSE, RotMat);
			    for (PlTmp = Pl;
				 PlTmp -> Pnext != NULL;
				 PlTmp = PlTmp -> Pnext);
			    PlTmp -> Pnext = PlOut;
			    PlOut = Pl;
			}
			if ((Pl = Boolean2D(Pl2XY, Pl1XYR, BOOL_OPER_SUB)) != NULL) {
			    Pl = ComputeRotatedPolys(Pl, FALSE, RotMat);
			    for (PlTmp = Pl;
				 PlTmp -> Pnext != NULL;
				 PlTmp = PlTmp -> Pnext);
			    PlTmp -> Pnext = PlOut;
			    PlOut = Pl;
			}

			IPFreePolygonList(Pl1XY);
			IPFreePolygonList(Pl2XY);
			IPFreePolygonList(Pl1XYR);
			IPFreePolygonList(Pl2XYR);
			break;

		    default:
		        IritFatalError("BooleanCoplanar: Unsupported Boolean operation BooleanCoplanar");
		        return NULL;
		}
	    }
	}
    }

    PObj = IPAllocObject("", IP_OBJ_POLY, NULL);
    PObj -> U.Pl = PlOut;
    return PObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Routine to optionally copy (if CpolyOnePl) a single polygon and rotate   *
* according to the rotation matrix provided. If, however, CopyOnePl is False *
* all polygons in list are converted.					     *
*                                                                            *
* PARAMETERS:                                                                *
*   Pl:             Polygon(s) to transform.                                 *
*   CopyOnePl:      Should we copy?                                          *
*   RotMat:         Transformation matrix.                                   *
*                                                                            *
* RETURN VALUE:                                                              *
*   IPPolygonStruct: Transformed polygon(s).                                 *
*****************************************************************************/
static IPPolygonStruct *ComputeRotatedPolys(IPPolygonStruct *Pl,
					    int CopyOnePl,
					    MatrixType RotMat)
{
    IPVertexStruct *V, *VHead;
    IPPolygonStruct *PlNext, *PlTemp,
	*PlOut = NULL;

    while (Pl != NULL) {
	PlNext = Pl -> Pnext;

	if (CopyOnePl) {
	    PlTemp = IPAllocPolygon(Pl -> Count, Pl -> Tags,
				    CopyVertexList(Pl -> PVertex), NULL);
	    PLANE_COPY(PlTemp ->Plane, Pl -> Plane);
	    Pl = PlTemp;
	}

	V = VHead = Pl -> PVertex;
	do {				    /* Transform the polygon itself. */
	    PointType PTemp;

	    PT_ADD(PTemp, V -> Coord, V -> Normal);

	    MatMultVecby4by4(V -> Coord, V -> Coord, RotMat);

	    MatMultVecby4by4(PTemp, PTemp, RotMat);   /* Update normal. */
	    PT_SUB(V -> Normal, PTemp, V -> Coord);
	    PT_NORMALIZE(V -> Normal);

	    V = V -> Pnext;
	}
	while (V != NULL && V != VHead);

	Pl -> Pnext = PlOut;
	PlOut = Pl;

	if (CopyOnePl)
	    break;
	else
	    Pl = PlNext;
    }

    return PlOut;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Routine that is called from the floating point package in case of fatal  *
* floating point error. Print error message, and quit the Boolean module.    *
*                                                                            *
* PARAMETERS:                                                                *
*   Type:       of exception.                                                *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void BooleanFPE(int Type)
{
    char Line[LINE_LEN];

    sprintf(Line, "Floating point error %d.", Type);
    IritWarningError(Line);

    FatalErrorType = FTL_BOOL_FPE;

    longjmp(LclLongJumpBuffer, 1);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Controls if intersection curves or full Boolean operation is to be       M
* performed.								     M
*                                                                            *
* PARAMETERS:                                                                M
*   OutputInterCurve:  If TRUE only intersection curves are computed, If     M
*                      false, full blown Boolean is applied.                 M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   BoolSetOutputInterCurve, Booleans                                        M
*****************************************************************************/
void BoolSetOutputInterCurve(int OutputInterCurve)
{
    BoolOutputInterCurve = OutputInterCurve;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Controls if coplanar polygons should be handled or not.	             M
*                                                                            *
* PARAMETERS:                                                                M
*   HandleCoplanarPoly:  If TRUE, coplanar polygons are handled.             M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   BoolSetHandleCoplanarPoly, Booleans                                      M
*****************************************************************************/
void BoolSetHandleCoplanarPoly(int HandleCoplanarPoly)
{
    BoolHandleCoplanarPoly = HandleCoplanarPoly;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Routine that is called by the bool-low module in fatal error cases.      *
* Will print error message and long jump using LclLongJumpBuffer.	     *
*                                                                            *
* PARAMETERS:                                                                *
*   ErrorType:   Type of error.                                              *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
void FatalBooleanError(int ErrorType)
{
    char s[LINE_LEN_LONG];

    FatalErrorType = ErrorType;

    switch (ErrorType) {
	case FTL_BOOL_NO_INTER:
	    /* If operation is union (OR), then if no intersection we assume */
	    /* the objects have no common volume and simply combine them!    */
	    if (BooleanOperation != BOOL_OPER_OR) {
		sprintf(s, "Boolean: %s",
			"Objects do not intersect - Empty object result");
		IritWarningError(s);
	    }
	    break;
	default:
	    sprintf(s, "Boolean: Undefined Fatal Error (%d !?)", ErrorType);
	    IritWarningError(s);
	    break;
    }

    longjmp(LclLongJumpBuffer, 1);
}

#ifdef DEBUG2

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Prints the content of the given vertex list, to standard output.	     *
*                                                                            *
* PARAMETERS:                                                                *
*   V:      Vertex list to print.                                            *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void PrintVrtxList(IPVertexStruct *V)
{
    IPVertexStruct
	*VHead = V;

    do {
	printf("[NORMAL %8lf %8lf %8lf] %12lf %12lf %12lf",
	       V -> Normal[0], V -> Normal[1], V -> Normal[2],
	       V -> Coord[0], V -> Coord[1], V -> Coord[2]);
	if (IP_IS_INTERNAL_EDGE(V))
	    printf(" (Internal)\n");
	else
	    printf("\n");
	V = V -> Pnext;
    }
    while (V!= NULL && V != VHead);
}

#endif /* DEBUG2 */
