/******************************************************************************
* TrivSTrv.c - Construct a trivariate using a set of surfaces.                *
*******************************************************************************
* (C) Gershon Elber, Technion, Israel Institute of Technology                 *
*******************************************************************************
* Written by Gershon Elber, Sep. 91.					      *
******************************************************************************/

#include "triv_loc.h"

/*****************************************************************************
* DESCRIPTION:                                                               M
* Constructs a trivariate using a set of surfaces. Surfaces are made to be   M
* compatible and then each is substituted into the new trivariate's mesh as  M
* a row.								     M
*   If the OtherOrder is less than the number of curves, number of curves is M
* used.									     M
*   A knot vector is formed with uniform open end for the other direction,   M
* so it interpolates the first and last surfaces.		             M
*   Note, however, that only the first and the last surfaces are	     M
* interpolated if OtherOrder is greater than 2.                              M
*                                                                            *
* PARAMETERS:                                                                M
*   SrfList:      List of surfaces to consturct a trivariate with.           M
*   OtherOrder:   Other, third, order of trivariate. 			     M
*                                                                            *
* RETURN VALUE:                                                              M
*   TrivTVStruct *:  Constructed trivariate from surfaces.                   M
*                                                                            *
* KEYWORDS:                                                                  M
*   TrivTVFromSrfs, trivar constructors                                      M
*****************************************************************************/
TrivTVStruct *TrivTVFromSrfs(CagdSrfStruct *SrfList, int OtherOrder)
{
    CagdBType IsNotRational;
    int i, j, NumSrfs, UOrder, VOrder, WOrder, MaxCoord, Length;
    CagdRType **TVPoints;
    CagdSrfStruct *Srf, **SrfVec;
    TrivTVStruct *TV;

    /* Find out how many curves we have and put them in a linear vector.     */
    /* Note the vector have a COPY of the curves so we can modify them.      */
    for (NumSrfs = 0, Srf = SrfList;
	 Srf != NULL;
	 NumSrfs++, Srf = Srf -> Pnext);
    SrfVec = (CagdSrfStruct **) IritMalloc(sizeof(CagdSrfStruct *) * NumSrfs);
    for (i = 0, Srf = SrfList;
	 i < NumSrfs;
	 i++, Srf = Srf -> Pnext)
	SrfVec[i] = CagdSrfCopy(Srf);

    /* Traverse vector in a O(n^2) fashion and make all curves compatible.   */
    for (i = 0; i < NumSrfs - 1; i++)
	for (j = i + 1; j < NumSrfs; j++)
	    CagdMakeSrfsCompatible(&SrfVec[i], &SrfVec[j],
				   TRUE, TRUE, TRUE, TRUE);

    /* Construct the surface. All required information is now available.     */
    UOrder = SrfVec[0] -> UOrder;
    VOrder = SrfVec[0] -> VOrder;
    WOrder = MIN(NumSrfs, OtherOrder);
    if (NumSrfs == WOrder && SrfVec[0] -> GType == CAGD_SBEZIER_TYPE) {
    	/* Allocate a bezier surface. */
	TV = TrivBzrTVNew(SrfVec[0] -> ULength, SrfVec[0] -> VLength,
			  NumSrfs, SrfVec[0] -> PType);
    }
    else {
	/* Allocate a bspline surface. */
	TV = TrivBspTVNew(SrfVec[0] -> ULength, SrfVec[0] -> VLength,
			  NumSrfs, UOrder, VOrder, WOrder,
			  SrfVec[0] -> PType);
	if (SrfVec[0] -> GType == CAGD_SBEZIER_TYPE) {
	    BspKnotUniformOpen(UOrder, UOrder, TV -> UKnotVector);
	    BspKnotUniformOpen(VOrder, VOrder, TV -> VKnotVector);
	}
	else {
	    IritFree((VoidPtr) TV -> UKnotVector);
	    TV -> UKnotVector = BspKnotCopy(SrfVec[0] -> UKnotVector,
					    CAGD_SRF_UPT_LST_LEN(SrfVec[0]) +
					    UOrder);
	    IritFree((VoidPtr) TV -> VKnotVector);
	    TV -> VKnotVector = BspKnotCopy(SrfVec[0] -> VKnotVector,
					    CAGD_SRF_VPT_LST_LEN(SrfVec[0]) +
					    VOrder);
	}
	BspKnotUniformOpen(NumSrfs, WOrder, TV -> WKnotVector);
    }

    /* Substitute each surface as a plane into the trivar's mesh and delete. */
    TVPoints = TV -> Points;
    i = 0;
    MaxCoord = CAGD_NUM_OF_PT_COORD(SrfVec[0] -> PType),
    IsNotRational = !CAGD_IS_RATIONAL_SRF(SrfVec[0]);
    Length = SrfVec[0] -> ULength * SrfVec[0] -> VLength;

    for (j = 0; j < NumSrfs; j++) {
	int k;
	CagdRType
	    **SrfPoints = SrfVec[j] -> Points;

    	for (k = IsNotRational; k <= MaxCoord; k++)
	    CAGD_GEN_COPY(&TVPoints[k][i], SrfPoints[k],
			  sizeof(CagdRType) * Length);

	CagdSrfFree(SrfVec[j]);
	i += Length;
    }

    IritFree((VoidPtr) SrfVec);

    return TV;
}
