/******************************************************************************
* Bbox.c - computes bounding boxes for objects.				      *
*******************************************************************************
* (C) Gershon Elber, Technion, Israel Institute of Technology                 *
*******************************************************************************
* Written by Gershon Elber, June 1993.					      *
******************************************************************************/

#include "irit_sm.h"
#include "allocate.h"
#include "cagd_lib.h"
#include "geomat3d.h"
#include "bbox.h"

#define RESET_BBOX(Bbox) { Bbox.Min[0] = Bbox.Min[1] = Bbox.Min[2] = IRIT_INFNTY;\
			 Bbox.Max[0] = Bbox.Max[1] = Bbox.Max[2] = -IRIT_INFNTY; }

#define SET_IF_LESS_THAN(Val, NewVal)     { if (NewVal < Val) Val = NewVal; }
#define SET_IF_GREATER_THAN(Val, NewVal)  { if (NewVal > Val) Val = NewVal; }
#define SET_PT_IF_LESS_THAN(Pt, NewPt)    { SET_IF_LESS_THAN(Pt[0], NewPt[0]) \
					    SET_IF_LESS_THAN(Pt[1], NewPt[1]) \
					    SET_IF_LESS_THAN(Pt[2], NewPt[2]) }
#define SET_PT_IF_GREATER_THAN(Pt, NewPt) \
				       { SET_IF_GREATER_THAN(Pt[0], NewPt[0]) \
					 SET_IF_GREATER_THAN(Pt[1], NewPt[1]) \
					 SET_IF_GREATER_THAN(Pt[2], NewPt[2]) }

static IPObjectStruct
    *GlblObjList = NULL;

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes a bounding box of a given object of any type.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:      To compute a bounding box for.                                M
*                                                                            *
* RETURN VALUE:                                                              M
*   BBBboxStruct *:   A pointer to a statically allocated bounding box       M
*                     holding bounding box information on PObj.              M
*                                                                            *
* KEYWORDS:                                                                  M
*   BBComputeBboxObject, bounding box                                        M
*****************************************************************************/
BBBboxStruct *BBComputeBboxObject(IPObjectStruct *PObj)
{
    static BBBboxStruct Bbox;
    int i;
    IPObjectStruct *PObjTmp;
    BBBboxStruct *PBbox, *PBbox2, TmpBbox;
    CagdBBoxStruct CagdBbox;
    CagdRType *R;
    CagdPType Pt;

    switch (PObj -> ObjType) {
	case IP_OBJ_POLY:
	    return BBComputePolyListBbox(PObj -> U.Pl);
	case IP_OBJ_CTLPT:
	    R = PObj -> U.CtlPt.Coords;
	    CagdCoercePointTo(Pt, CAGD_PT_E3_TYPE,
			      &R, -1, PObj -> U.CtlPt.PtType);
	    return BBComputePointBbox(Pt);
	case IP_OBJ_POINT:
	    return BBComputePointBbox(PObj -> U.Pt);
	case IP_OBJ_VECTOR:
	    return BBComputePointBbox(PObj -> U.Vec);
	case IP_OBJ_LIST_OBJ:
	    RESET_BBOX(TmpBbox);
	    PBbox = &TmpBbox;
	    for (i = 0; (PObjTmp = ListObjectGet(PObj, i++)) != NULL; ) {
		PBbox2 = BBMergeBbox(PBbox, BBComputeBboxObject(PObjTmp));
		GEN_COPY(&TmpBbox, PBbox2, sizeof(BBBboxStruct));
	    }
	    GEN_COPY(&Bbox, &TmpBbox, sizeof(BBBboxStruct));
	    return &Bbox;
	case IP_OBJ_CURVE:
	    CagdCrvListBBox(PObj -> U.Crvs, &CagdBbox);
	    GEN_COPY(Bbox.Min, CagdBbox.Min, 3 * sizeof(RealType));
	    GEN_COPY(Bbox.Max, CagdBbox.Max, 3 * sizeof(RealType));
	    return &Bbox;
	case IP_OBJ_SURFACE:
	    CagdSrfListBBox(PObj -> U.Srfs, &CagdBbox);
	    GEN_COPY(Bbox.Min, CagdBbox.Min, 3 * sizeof(RealType));
	    GEN_COPY(Bbox.Max, CagdBbox.Max, 3 * sizeof(RealType));	
	    return &Bbox;
	case IP_OBJ_TRIMSRF:
	    CagdSrfListBBox(PObj -> U.TrimSrfs -> Srf, &CagdBbox);
	    GEN_COPY(Bbox.Min, CagdBbox.Min, 3 * sizeof(RealType));
	    GEN_COPY(Bbox.Max, CagdBbox.Max, 3 * sizeof(RealType));	
	    return &Bbox;
	case IP_OBJ_TRIVAR:
	    TrivTVListBBox(PObj -> U.Trivars, &CagdBbox);
	    GEN_COPY(Bbox.Min, CagdBbox.Min, 3 * sizeof(RealType));
	    GEN_COPY(Bbox.Max, CagdBbox.Max, 3 * sizeof(RealType));	
	    return &Bbox;
	case IP_OBJ_TRISRF:
	    TrngTriSrfListBBox(PObj -> U.TriSrfs, &CagdBbox);
	    GEN_COPY(Bbox.Min, CagdBbox.Min, 3 * sizeof(RealType));
	    GEN_COPY(Bbox.Max, CagdBbox.Max, 3 * sizeof(RealType));	
	    return &Bbox;
	case IP_OBJ_MODEL:
	    MdlModelListBBox(PObj -> U.Mdls, &CagdBbox);
	    GEN_COPY(Bbox.Min, CagdBbox.Min, 3 * sizeof(RealType));
	    GEN_COPY(Bbox.Max, CagdBbox.Max, 3 * sizeof(RealType));	
	    return &Bbox;
	case IP_OBJ_INSTANCE:
	    if (GlblObjList) {
		if ((PObjTmp = IPGetObjectByName(PObj -> U.Instance -> Name,
						GlblObjList, FALSE)) == NULL) {
		    fprintf(stderr,
			    "Failed to find instance's origin \"%s\"\n",
			    PObj -> U.Instance -> Name);
		    RESET_BBOX(Bbox);
		    return &Bbox;
		}
		else {
		    PObjTmp = GMTransformObject(PObjTmp,
						PObj -> U.Instance -> Mat);
		    PBbox = BBComputeBboxObject(PObjTmp);
		    IPFreeObject(PObjTmp);
		    return PBbox;
		}
	    }
	    RESET_BBOX(Bbox);
	    return &Bbox;
	default:
	    RESET_BBOX(Bbox);
	    return &Bbox;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes a bounding box of a list of objects of any type.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:      To compute a bounding box for.                                M
*                                                                            *
* RETURN VALUE:                                                              M
*   BBBboxStruct *:   A pointer to a statically allocated bounding box       M
*                     holding bounding box information on objects PObj.      M
*                                                                            *
* KEYWORDS:                                                                  M
*   BBComputeBboxObjectList, bounding box                                    M
*****************************************************************************/
BBBboxStruct *BBComputeBboxObjectList(IPObjectStruct *PObj)
{
    static BBBboxStruct Bbox;
    BBBboxStruct *PBbox;

    RESET_BBOX(Bbox);
    PBbox = &Bbox;

    GlblObjList = PObj;

    for ( ; PObj != NULL; PObj = PObj -> Pnext) {
	PBbox = BBMergeBbox(PBbox, BBComputeBboxObject(PObj));
	GEN_COPY(&Bbox, PBbox, sizeof(BBBboxStruct));
	PBbox = &Bbox;
    }

    GlblObjList = NULL;

    return PBbox;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes a bounding box of a polygon/polyline/pointlist object.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   PPoly:     To compute a bounding box for.                                M
*                                                                            *
* RETURN VALUE:                                                              M
*   BBBboxStruct *:   A pointer to a statically allocated bounding box       M
*                     holding bounding box information on PPoly.             M
*                                                                            *
* KEYWORDS:                                                                  M
*   BBComputeOnePolyBbox, bounding box                                       M
*****************************************************************************/
BBBboxStruct *BBComputeOnePolyBbox(IPPolygonStruct *PPoly)
{
    static BBBboxStruct Bbox;
    IPVertexStruct
	*V = PPoly -> PVertex;

    RESET_BBOX(Bbox);

    do {
	SET_PT_IF_LESS_THAN(Bbox.Min, V -> Coord);
	SET_PT_IF_GREATER_THAN(Bbox.Max, V -> Coord);
	V = V -> Pnext;
    }
    while (V != NULL && V != PPoly -> PVertex);

    return &Bbox;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes a bounding box for a list of polygon/polyline/pointlist objects.  M
*                                                                            *
* PARAMETERS:                                                                M
*   PPoly:     To compute a bounding box for.                                M
*                                                                            *
* RETURN VALUE:                                                              M
*   BBBboxStruct *:   A pointer to a statically allocated bounding box       M
*                     holding bounding box information on PPoly list.        M
*                                                                            *
* KEYWORDS:                                                                  M
*   BBComputeOnePolyListBbox, bounding box                                   M
*****************************************************************************/
BBBboxStruct *BBComputePolyListBbox(IPPolygonStruct *PPoly)
{
    static BBBboxStruct Bbox;

    RESET_BBOX(Bbox);

    for ( ; PPoly != NULL; PPoly = PPoly -> Pnext) {
	IPVertexStruct
	    *V = PPoly -> PVertex;

	do {
	    SET_PT_IF_LESS_THAN(Bbox.Min, V -> Coord);
	    SET_PT_IF_GREATER_THAN(Bbox.Max, V -> Coord);
	    V = V -> Pnext;
	}
	while (V != NULL && V != PPoly -> PVertex);
    }

    return &Bbox;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Computes a bounding box of a point object.                                 M
*                                                                            *
* PARAMETERS:                                                                M
*   Pt:     To compute a bounding box for.                                   M
*                                                                            *
* RETURN VALUE:                                                              M
*   BBBboxStruct *:   A pointer to a statically allocated bounding box       M
*                     holding bounding box information on Pt.                M
*                                                                            *
* KEYWORDS:                                                                  M
*   BBComputePointBbox, bounding box                                         M
*****************************************************************************/
BBBboxStruct *BBComputePointBbox(RealType *Pt)
{
    static BBBboxStruct Bbox;

    PT_COPY(Bbox.Min, Pt);
    PT_COPY(Bbox.Max, Pt);

    return &Bbox;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Merges (union) given two bounding boxes into one.			     M
* Either Bbox1 or Bbox2 can be pointing to the static area Bbox used herein. M
*                                                                            *
* PARAMETERS:                                                                M
*   Bbox1:      First bounding box to union up.                              M
*   Bbox2:      Second bounding box to union up.                             M
*                                                                            *
* RETURN VALUE:                                                              M
*   BBBboxStruct *:    A unioned bounding box the contains both BBox1 and    M
*                      BBox2.                                                M
*                                                                            *
* KEYWORDS:                                                                  M
*   BBMergeBbox, bounding box                                                M
*****************************************************************************/
BBBboxStruct *BBMergeBbox(BBBboxStruct *Bbox1, BBBboxStruct *Bbox2)
{
    static BBBboxStruct Bbox;
    int i;

    /* Make sure first Bbox is in Bbox and Bbox2 holds the other one. */
    if (Bbox1 == &Bbox) {
    }
    if (Bbox2 == &Bbox) {
	Bbox2 = Bbox1;
    }
    else {
	GEN_COPY(&Bbox, Bbox1, sizeof(BBBboxStruct));
    }

    /* Compare the two Bbox's and update. */
    for (i = 0; i < 3; i++) {
	if (Bbox.Min[i] > Bbox2 -> Min[i])
	    Bbox.Min[i] = Bbox2 -> Min[i];
	if (Bbox.Max[i] < Bbox2 -> Max[i])
	    Bbox.Max[i] = Bbox2 -> Max[i];
    }

    return &Bbox;
}
