/******************************************************************************
* CagdBSum.c - Boolean sum surface constructor out of given four curves.      *
*******************************************************************************
* Written by Gershon Elber, Sep. 91.					      *
******************************************************************************/

#include "cagd_loc.h"

/*****************************************************************************
* DESCRIPTION:                                                               M
* Constructs a Boolean sum surface using the four provided boundary curves.  M
*   Curve's end points must meet at the four surface corners if surface      M
* boundary are to be identical to the four given curves.	             M
*                                                                            *
* PARAMETERS:                                                                M
*   CrvLeft:    Left boundary curve of Boolean sum surface to be created.    M
*   CrvRight:   Right boundary curve of Boolean sum surface to be created.   M
*   CrvTop:     Top boundary curve of Boolean sum surface to be created.     M
*   CrvBottom:  Bottom boundary curve of Boolean sum surface to be created.  M
*                                                                            *
* RETURN VALUE:                                                              M
*   CagdSrfStruct *:  A Boolean sum surface constructed using given four     M
*                     curves.                                                M
*                                                                            *
* KEYWORDS:                                                                  M
*   CagdBoolSumSrf, Boolean sum, surface constructors                        M
*****************************************************************************/
CagdSrfStruct *CagdBoolSumSrf(CagdCrvStruct *CrvLeft,
			      CagdCrvStruct *CrvRight,
			      CagdCrvStruct *CrvTop,
			      CagdCrvStruct *CrvBottom)
{
    int i, j;
    CagdCrvStruct *Crv1, *Crv2;
    CagdSrfStruct *Ruled1, *Ruled2, *Ruled3, *Srf;
    CagdPtStruct Pt1, Pt2;
    CagdRType **SrfPoints, **Ruled1Pts, **Ruled2Pts, **Ruled3Pts;

    if (CAGD_IS_PERIODIC_CRV(CrvLeft) ||
	CAGD_IS_PERIODIC_CRV(CrvRight) ||
	CAGD_IS_PERIODIC_CRV(CrvTop) ||
	CAGD_IS_PERIODIC_CRV(CrvBottom)) {
	CAGD_FATAL_ERROR(CAGD_ERR_PERIODIC_NO_SUPPORT);
	return NULL;
    }

    CrvLeft = CagdCrvCopy(CrvLeft);
    CrvRight = CagdCrvCopy(CrvRight);
    CrvTop = CagdCrvCopy(CrvTop);
    CrvBottom = CagdCrvCopy(CrvBottom);

    /* The Left-Right and Top-Bottom curves should share same point/curve    */
    /* type as well as same order and knot vector (if Bspline representation)*/
    CagdMakeCrvsCompatible(&CrvLeft, &CrvRight, TRUE, TRUE);
    CagdMakeCrvsCompatible(&CrvTop, &CrvBottom, TRUE, TRUE);

    /* The Left-Right and Top-Bottom pairs must share same point/curve type. */
    CagdMakeCrvsCompatible(&CrvLeft, &CrvTop, FALSE, FALSE);
    CagdMakeCrvsCompatible(&CrvLeft, &CrvBottom, FALSE, FALSE);
    CagdMakeCrvsCompatible(&CrvRight, &CrvTop, FALSE, FALSE);
    CagdMakeCrvsCompatible(&CrvRight, &CrvBottom, FALSE, FALSE);

    /* Now that the curves are in the right representation, form surface(s). */
    /* The two ruled surface between the respective curves, in right orders: */
    Ruled1 = CagdRuledSrf(CrvLeft, CrvRight,
			  CrvTop -> Order, CrvTop -> Length);
    Ruled2 = CagdRuledSrf(CrvTop, CrvBottom,
			  CrvRight -> Order, CrvRight -> Length);
    Srf = CagdSrfReverse2(Ruled2);
    CagdSrfFree(Ruled2);
    Ruled2 = Srf;

    /* The ruled surface between the four corner points in the right orders. */
    CagdCoerceToE3(Pt1.Pt, CrvLeft -> Points, 0, CrvLeft -> PType);
    CagdCoerceToE3(Pt2.Pt, CrvLeft -> Points, CrvLeft -> Length - 1,
							CrvLeft -> PType);
    Crv1 = CagdMergePtPt(&Pt1, &Pt2);

    CagdCoerceToE3(Pt1.Pt, CrvRight -> Points, 0, CrvRight -> PType);
    CagdCoerceToE3(Pt2.Pt, CrvRight -> Points, CrvRight -> Length - 1,
							CrvRight -> PType);
    Crv2 = CagdMergePtPt(&Pt1, &Pt2);

    /* Should not change CrvLeft/Right only Crv1/2: */
    CagdMakeCrvsCompatible(&Crv1, &CrvLeft, TRUE, TRUE);
    CagdMakeCrvsCompatible(&Crv2, &CrvRight, TRUE, TRUE);

    Ruled3 = CagdRuledSrf(Crv1, Crv2, CrvTop -> Order, CrvTop -> Length);
    CagdCrvFree(Crv1);
    CagdCrvFree(Crv2);

    /* The boolean sum is equal to Ruled1 + Ruled2 - Ruled3. This boolean    */
    /* sum as computed is not exactly as defined in the literature for non   */
    /* uniform Bsplines since the ruled surfaces are computed with uniform   */
    /* distribution along the other axis even if it is non uniform.	     */
    if (CrvRight -> GType == CAGD_CBSPLINE_TYPE) {
	Srf = BspSrfNew(Ruled1 -> ULength, Ruled1 -> VLength,
			Ruled1 -> UOrder, Ruled1 -> VOrder, Ruled1 -> PType);
	IritFree((VoidPtr) Srf -> UKnotVector);
	Srf -> UKnotVector = BspKnotCopy(CrvLeft -> KnotVector,
					 CrvLeft -> Length + CrvLeft -> Order);
	IritFree((VoidPtr) Srf -> VKnotVector);
	Srf -> VKnotVector = BspKnotCopy(CrvTop -> KnotVector,
					 CrvTop -> Length + CrvTop -> Order);
    }
    else if (CrvRight -> GType == CAGD_CBEZIER_TYPE)
	Srf = BzrSrfNew(Ruled1 -> ULength, Ruled1 -> VLength, Ruled1 -> PType);
    else
	CAGD_FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
    SrfPoints = Srf -> Points;

    Ruled1Pts = Ruled1 -> Points;
    Ruled2Pts = Ruled2 -> Points;
    Ruled3Pts = Ruled3 -> Points;

    for (i = !CAGD_IS_RATIONAL_SRF(Srf);
	 i <= CAGD_NUM_OF_PT_COORD(Srf -> PType);
	 i++) {
	CagdRType
	    *Ruled1PtsPtr = Ruled1Pts[i],
	    *Ruled2PtsPtr = Ruled2Pts[i],
	    *Ruled3PtsPtr = Ruled3Pts[i],
	    *SrfPointsPtr = SrfPoints[i];

	 for (j = Srf -> ULength * Srf -> VLength; j > 0; j--)
	     *SrfPointsPtr++ =
		 *Ruled1PtsPtr++ + *Ruled2PtsPtr++ - *Ruled3PtsPtr++;
    }

    CagdSrfFree(Ruled1);
    CagdSrfFree(Ruled2);
    CagdSrfFree(Ruled3);

    return Srf;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Constructs a Boolean sum surface using the single boundary curve.	     M
*   The curve is subdivided into four, equally spaced in parameter space,    M
* sub-regions which are used as the four curves to the Boolean sum           M
* constructor. See CagdBoolSumSrf.                                           M
*                                                                            *
* PARAMETERS:                                                                M
*   BndryCrv:   To be subdivided into four curves for a Boolean sum          M
*               construction.                                                M
*                                                                            *
* RETURN VALUE:                                                              M
*   CagdSrfStruct *:  A Boolean sum surface constructed using given curve    M
*                                                                            *
* KEYWORDS:                                                                  M
*   CagdBoolSumSrf, Boolean sum, surface constructors                        M
*****************************************************************************/
CagdSrfStruct *CagdOneBoolSumSrf(CagdCrvStruct *BndryCrv)
{
    CagdRType TMin, TMax;
    CagdCrvStruct *Crvs, *TCrv, *CrvLeft, *CrvRight, *CrvTop, *CrvBottom;
    CagdSrfStruct *BoolSumSrf;

    BspCrvDomain(BndryCrv, &TMin, &TMax);

    Crvs = BspCrvSubdivAtParam(BndryCrv, TMin * 0.5 + TMax * 0.5);
    CrvLeft = BspCrvSubdivAtParam(Crvs, TMin * 0.75 + TMax * 0.25);
    CrvRight = BspCrvSubdivAtParam(Crvs -> Pnext, TMin * 0.25 + TMax * 0.75);

    CagdCrvFreeList(Crvs);

    CrvBottom = CrvLeft -> Pnext;
    CrvLeft -> Pnext = NULL;
    CrvTop = CrvRight -> Pnext;
    CrvRight -> Pnext = NULL;

    /* Reverse CrvBottom and CrvLeft: */
    TCrv = CagdCrvReverse(CrvTop);
    CagdCrvFree(CrvTop);
    CrvTop = TCrv;

    TCrv = CagdCrvReverse(CrvRight);
    CagdCrvFree(CrvRight);
    CrvRight = TCrv;

    BoolSumSrf = CagdBoolSumSrf(CrvLeft, CrvRight, CrvTop, CrvBottom);

    CagdCrvFree(CrvTop);
    CagdCrvFree(CrvRight);
    CagdCrvFree(CrvBottom);
    CagdCrvFree(CrvLeft);

    return BoolSumSrf;
}
