/******************************************************************************
* CagdBbox.c - Handle freeform cuves and surfaces bounding boxes.	      *
*******************************************************************************
* Written by Gershon Elber, Jan. 92.					      *
******************************************************************************/

#include "cagd_loc.h"

#define RESET_BBOX(BBox) { \
    BBox -> Min[0] = BBox -> Min[1] = BBox -> Min[2] = INFINITY; \
    BBox -> Max[0] = BBox -> Max[1] = BBox -> Max[2] = -INFINITY; }

static void CagdPointsBBox(CagdRType **Points, int Length, CagdBBoxStruct *BBox);

/******************************************************************************
* Computes a bounding box around a freeform curve.			      *
******************************************************************************/
void CagdCrvBBox(CagdCrvStruct *Crv, CagdBBoxStruct *BBox)
{
    CagdCrvStruct
	*E3Crv = CagdCoerceCrvTo(Crv, CAGD_PT_E3_TYPE);
    int Length = E3Crv -> Length;
    CagdRType
	**Points = E3Crv -> Points;

    CagdPointsBBox(Points, Length, BBox);

    CagdCrvFree(E3Crv);
}

/******************************************************************************
* Computes a bounding box around a list of freeform curves.		      *
******************************************************************************/
void CagdCrvListBBox(CagdCrvStruct *Crvs, CagdBBoxStruct *BBox)
{
    RESET_BBOX(BBox);

    for ( ; Crvs != NULL; Crvs = Crvs -> Pnext) {
	CagdBBoxStruct TmpBBox;

	CagdCrvBBox(Crvs, &TmpBBox);
	CagdMergeBBox(BBox, &TmpBBox);
    }
}

/******************************************************************************
* Computes a bounding box around a freeform surface.			      *
******************************************************************************/
void CagdSrfBBox(CagdSrfStruct *Srf, CagdBBoxStruct *BBox)
{
    CagdSrfStruct
	*E3Srf = CagdCoerceSrfTo(Srf, CAGD_PT_E3_TYPE);
    int Length = E3Srf -> ULength * E3Srf -> VLength;
    CagdRType
	**Points = E3Srf -> Points;

    CagdPointsBBox(Points, Length, BBox);

    CagdSrfFree(E3Srf);
}

/******************************************************************************
* Computes a bounding box around a list of freeform surfaces.		      *
******************************************************************************/
void CagdSrfListBBox(CagdSrfStruct *Srfs, CagdBBoxStruct *BBox)
{
    RESET_BBOX(BBox);

    for ( ; Srfs != NULL; Srfs = Srfs -> Pnext) {
	CagdBBoxStruct TmpBBox;

	CagdSrfBBox(Srfs, &TmpBBox);
	CagdMergeBBox(BBox, &TmpBBox);
    }
}

/******************************************************************************
* Computes a bounding box of a set of points.				      *
******************************************************************************/
static void CagdPointsBBox(CagdRType **Points, int Length, CagdBBoxStruct *BBox)
{
    int i;

    RESET_BBOX(BBox);

    for (i = 0; i < Length; i++) {
	if (BBox -> Min[0] > Points[X][i])
	    BBox -> Min[0] = Points[X][i];
	if (BBox -> Min[1] > Points[Y][i])
	    BBox -> Min[1] = Points[Y][i];
	if (BBox -> Min[2] > Points[Z][i])
	    BBox -> Min[2] = Points[Z][i];

	if (BBox -> Max[0] < Points[X][i])
	    BBox -> Max[0] = Points[X][i];
	if (BBox -> Max[1] < Points[Y][i])
	    BBox -> Max[1] = Points[Y][i];
	if (BBox -> Max[2] < Points[Z][i])
	    BBox -> Max[2] = Points[Z][i];
    }
}

/******************************************************************************
* Merge (union) two bounding boxes into one.				      *
******************************************************************************/
void CagdMergeBBox(CagdBBoxStruct *DestBBox, CagdBBoxStruct *SrcBBox)
{
    int i;

    for (i = 0; i < 3; i++) {
	if (DestBBox -> Min[i] > SrcBBox -> Min[i])
	    DestBBox -> Min[i] = SrcBBox -> Min[i];
	if (DestBBox -> Max[i] < SrcBBox -> Max[i])
	    DestBBox -> Max[i] = SrcBBox -> Max[i];
    }
}

/******************************************************************************
* Computes a min max bound on a curve in a given axis.			      *
* The curve is not coerced to anything and the given axis is tested directly  *
* where 0 is the W axis and 1, 2, 3 are the X, Y, Z etc.		      *
******************************************************************************/
void CagdCrvMinMax(CagdCrvStruct *Crv, int Axis,
		   CagdRType *Min, CagdRType *Max)
{
    CagdBType
	IsNotRational = !CAGD_IS_RATIONAL_CRV(Crv);
    int i,
	Length = Crv -> Length,
	MaxCoord = CAGD_NUM_OF_PT_COORD(Crv -> PType);
    CagdRType
	*Pts = Crv -> Points[Axis],
	*WPts = IsNotRational ? NULL : Crv -> Points[0];

    if ((Axis == 0 && IsNotRational) ||	(Axis > MaxCoord))
	FATAL_ERROR(CAGD_ERR_WRONG_CRV);

    for (i = 0, *Min = INFINITY, *Max = -INFINITY; i < Length; i++) {
	CagdRType
	    V = WPts ? Pts[i] / WPts[i] : Pts[i];

	if (*Max < V)
	    *Max = V;
	if (*Min > V)
	    *Min = V;
    }
}

/******************************************************************************
* Computes a min max bound on a curve in a given axis (1 for X, 2 for Y etc.).*
******************************************************************************/
void CagdSrfMinMax(CagdSrfStruct *Srf, int Axis,
		   CagdRType *Min, CagdRType *Max)
{
    CagdBType
	IsNotRational = !CAGD_IS_RATIONAL_SRF(Srf);
    int i,
	Length = Srf -> ULength * Srf -> VLength,
	MaxCoord = CAGD_NUM_OF_PT_COORD(Srf -> PType);
    CagdRType
	*Pts = Srf -> Points[Axis],
	*WPts = IsNotRational ? NULL : Srf -> Points[0];

    if ((Axis == 0 && IsNotRational) ||	(Axis > MaxCoord))
	FATAL_ERROR(CAGD_ERR_WRONG_SRF);

    for (i = 0, *Min = INFINITY, *Max = -INFINITY; i < Length; i++) {
	CagdRType
	    V = WPts ? Pts[i] / WPts[i] : Pts[i];

	if (*Max < V)
	    *Max = V;
	if (*Min > V)
	    *Min = V;
    }
}
