/******************************************************************************
* Triv_Sub.c - Computes subdivision of tri-variates.			      *
*******************************************************************************
* Written by Gershon Elber, Sep. 94.					      *
******************************************************************************/

#include "triv_loc.h"

/*****************************************************************************
* DESCRIPTION:                                                               M
* Given a tri-variate, subdivides it at parameter value t in direction	     M
* Dir.									     M
*                                                                            *
* PARAMETERS:                                                                M
*   TV:       Trivariate to subdivide.                                       M
*   t:        Parameter to subdivide at.                                     M
*   Dir:      Direction of subdivision.                                      M
*                                                                            *
* RETURN VALUE:                                                              M
*   TrivTVStruct *:   A list of two trivariates, result of the subdivision.  M
*                                                                            *
* KEYWORDS:                                                                  M
*   TrivTVSubdivAtParam, trivariates                                         M
*****************************************************************************/
TrivTVStruct *TrivTVSubdivAtParam(TrivTVStruct *TV,
				  CagdRType t,
				  TrivTVDirType Dir)
{
    int i, j, KVLen, Index1, Index2, Mult,
	UOrder = TV -> UOrder,
	VOrder = TV -> VOrder,
	WOrder = TV -> WOrder,
	ULength = TV -> ULength,
	VLength = TV -> VLength,
	WLength = TV -> WLength;
    CagdRType *RefKV;
    CagdSrfStruct *Srf, *LSrf, *RSrf;
    TrivTVStruct *RTV, *LTV;

    if (TRIV_IS_BEZIER_TV(TV)) {
	RTV = TrivBzrTVNew(ULength, VLength, WLength, TV -> PType);
	LTV = TrivBzrTVNew(ULength, VLength, WLength, TV -> PType);
    }
    else if (TRIV_IS_BSPLINE_TV(TV)) {
	switch (Dir) {
	    case TRIV_CONST_U_DIR:
		RefKV = TV -> UKnotVector;
		KVLen = UOrder + ULength;
		Index1 = BspKnotLastIndexL(RefKV, KVLen, t);
		if (Index1 + 1 < UOrder)
		    Index1 = UOrder - 1;
		Index2 = BspKnotFirstIndexG(RefKV, KVLen, t);
		if (Index2 > ULength)
		    Index2 = ULength;
		LTV = TrivBspTVNew(Index1 + 1, VLength, WLength,
				   TV -> UOrder, TV -> VOrder, TV -> WOrder,
				   TV -> PType);
		RTV = TrivBspTVNew(ULength - Index2 + UOrder, VLength, WLength,
				   TV -> UOrder, TV -> VOrder, TV -> WOrder,
				   TV -> PType);
		Mult = UOrder - 1 - (Index2 - Index1 - 1);

		/* Update the new knot vectors. */
		CAGD_GEN_COPY(LTV -> UKnotVector,
			      TV -> UKnotVector,
			      sizeof(CagdRType) * (Index1 + 1));
		/* Close the knot vector with multiplicity Order: */
		for (j = Index1 + 1; j <= Index1 + UOrder; j++)
		    LTV -> UKnotVector[j] = t;
		CAGD_GEN_COPY(&RTV -> UKnotVector[UOrder],
			      &TV -> UKnotVector[Index2],
			      sizeof(CagdRType) *
				  (ULength + UOrder - Index2));
		/* Make sure knot vector starts with multiplicity Order: */
		for (j = 0; j < UOrder; j++)
		    RTV -> UKnotVector[j] = t;
	    
		/* And copy the other direction knot vectors. */
		CAGD_GEN_COPY(LTV -> VKnotVector,
			      TV -> VKnotVector,
			      sizeof(CagdRType) * (VOrder + VLength));
		CAGD_GEN_COPY(RTV -> VKnotVector,
			      TV -> VKnotVector,
			      sizeof(CagdRType) * (VOrder + VLength));
		CAGD_GEN_COPY(LTV -> WKnotVector,
			      TV -> WKnotVector,
			      sizeof(CagdRType) * (WOrder + WLength));
		CAGD_GEN_COPY(RTV -> WKnotVector,
			      TV -> WKnotVector,
			      sizeof(CagdRType) * (WOrder + WLength));
		break;
	    case TRIV_CONST_V_DIR:
		RefKV = TV -> VKnotVector;
		KVLen = VOrder + VLength;
		Index1 = BspKnotLastIndexL(RefKV, KVLen, t);
		if (Index1 + 1 < VOrder)
		    Index1 = VOrder - 1;
		Index2 = BspKnotFirstIndexG(RefKV, KVLen, t);
		if (Index2 > VLength)
		    Index2 = VLength;
		LTV = TrivBspTVNew(ULength, Index1 + 1, WLength,
				   TV -> UOrder, TV -> VOrder, TV -> WOrder,
				   TV -> PType);
		RTV = TrivBspTVNew(ULength, VLength - Index2 + VOrder, WLength,
				   TV -> UOrder, TV -> VOrder, TV -> WOrder,
				   TV -> PType);
		Mult = VOrder - 1 - (Index2 - Index1 - 1);

		/* Update the new knot vectors. */
		CAGD_GEN_COPY(LTV -> VKnotVector,
			      TV -> VKnotVector,
			      sizeof(CagdRType) * (Index1 + 1));
		/* Close the knot vector with multiplicity Order: */
		for (j = Index1 + 1; j <= Index1 + VOrder; j++)
		    LTV -> VKnotVector[j] = t;
		CAGD_GEN_COPY(&RTV -> VKnotVector[VOrder],
			      &TV -> VKnotVector[Index2],
			      sizeof(CagdRType) *
				  (VLength + VOrder - Index2));
		/* Make sure knot vector starts with multiplicity Order: */
		for (j = 0; j < VOrder; j++)
		    RTV -> VKnotVector[j] = t;
	    
		/* And copy the other direction knot vectors. */
		CAGD_GEN_COPY(LTV -> UKnotVector,
			      TV -> UKnotVector,
			      sizeof(CagdRType) * (UOrder + ULength));
		CAGD_GEN_COPY(RTV -> UKnotVector,
			      TV -> UKnotVector,
			      sizeof(CagdRType) * (UOrder + ULength));
		CAGD_GEN_COPY(LTV -> WKnotVector,
			      TV -> WKnotVector,
			      sizeof(CagdRType) * (WOrder + WLength));
		CAGD_GEN_COPY(RTV -> WKnotVector,
			      TV -> WKnotVector,
			      sizeof(CagdRType) * (WOrder + WLength));
		break;
	    case TRIV_CONST_W_DIR:
		RefKV = TV -> WKnotVector;
		KVLen = WOrder + WLength;
		Index1 = BspKnotLastIndexL(RefKV, KVLen, t);
		if (Index1 + 1 < WOrder)
		    Index1 = WOrder - 1;
		Index2 = BspKnotFirstIndexG(RefKV, KVLen, t);
		if (Index2 > WLength)
		    Index2 = WLength;
		LTV = TrivBspTVNew(ULength, VLength, Index1 + 1,
				   TV -> UOrder, TV -> VOrder, TV -> WOrder,
				   TV -> PType);
		RTV = TrivBspTVNew(ULength, VLength, WLength - Index2 + WOrder,
				   TV -> UOrder, TV -> VOrder, TV -> WOrder,
				   TV -> PType);
		Mult = WOrder - 1 - (Index2 - Index1 - 1);

		/* Update the new knot vectors. */
		CAGD_GEN_COPY(LTV -> WKnotVector,
			      TV -> WKnotVector,
			      sizeof(CagdRType) * (Index1 + 1));
		/* Close the knot vector with multiplicity Order: */
		for (j = Index1 + 1; j <= Index1 + WOrder; j++)
		    LTV -> WKnotVector[j] = t;
		CAGD_GEN_COPY(&RTV -> WKnotVector[WOrder],
			      &TV -> WKnotVector[Index2],
			      sizeof(CagdRType) *
				  (WLength + WOrder - Index2));
		/* Make sure knot vector starts with multiplicity Order: */
		for (j = 0; j < WOrder; j++)
		    RTV -> WKnotVector[j] = t;
	    
		/* And copy the other direction knot vectors. */
		CAGD_GEN_COPY(LTV -> UKnotVector,
			      TV -> UKnotVector,
			      sizeof(CagdRType) * (UOrder + ULength));
		CAGD_GEN_COPY(RTV -> UKnotVector,
			      TV -> UKnotVector,
			      sizeof(CagdRType) * (UOrder + ULength));
		CAGD_GEN_COPY(LTV -> VKnotVector,
			      TV -> VKnotVector,
			      sizeof(CagdRType) * (VOrder + VLength));
		CAGD_GEN_COPY(RTV -> VKnotVector,
			      TV -> VKnotVector,
			      sizeof(CagdRType) * (VOrder + VLength));
		break;
	    default:
		TRIV_FATAL_ERROR(TRIV_ERR_DIR_NOT_VALID);
		RTV = LTV = NULL;
		break;
	}
    }
    else {
	TRIV_FATAL_ERROR(TRIV_ERR_UNDEF_TRIVAR);
	return NULL;
    }

    switch (Dir) {
	case TRIV_CONST_U_DIR:
	    for (i = 0; i < WLength; i++) {
		Srf = TrivSrfFromMesh(TV, i, TRIV_CONST_W_DIR);
		LSrf = CagdSrfSubdivAtParam(Srf, t, CAGD_CONST_U_DIR);
		RSrf = LSrf -> Pnext;
		TrivSrfToMesh(LSrf, i, TRIV_CONST_W_DIR, LTV);
		TrivSrfToMesh(RSrf, i, TRIV_CONST_W_DIR, RTV);

		CagdSrfFree(Srf);
		CagdSrfFree(LSrf);
		CagdSrfFree(RSrf);
	    }
	    break;
	case TRIV_CONST_V_DIR:
	    for (i = 0; i < ULength; i++) {
		Srf = TrivSrfFromMesh(TV, i, TRIV_CONST_U_DIR);
		LSrf = CagdSrfSubdivAtParam(Srf, t, CAGD_CONST_U_DIR);
		RSrf = LSrf -> Pnext;
		TrivSrfToMesh(LSrf, i, TRIV_CONST_U_DIR, LTV);
		TrivSrfToMesh(RSrf, i, TRIV_CONST_U_DIR, RTV);

		CagdSrfFree(Srf);
		CagdSrfFree(LSrf);
		CagdSrfFree(RSrf);
	    }
	    break;
	case TRIV_CONST_W_DIR:
	    for (i = 0; i < ULength; i++) {
		Srf = TrivSrfFromMesh(TV, i, TRIV_CONST_U_DIR);
		LSrf = CagdSrfSubdivAtParam(Srf, t, CAGD_CONST_V_DIR);
		RSrf = LSrf -> Pnext;
		TrivSrfToMesh(LSrf, i, TRIV_CONST_U_DIR, LTV);
		TrivSrfToMesh(RSrf, i, TRIV_CONST_U_DIR, RTV);

		CagdSrfFree(Srf);
		CagdSrfFree(LSrf);
		CagdSrfFree(RSrf);
	    }
	    break;
	default:
	    TRIV_FATAL_ERROR(TRIV_ERR_DIR_NOT_CONST_UVW);
	    break;
    }

    LTV -> Pnext = RTV;
    return LTV;
}
