/******************************************************************************
* TrivCmpt.c - Make objects compatible.					      *
*******************************************************************************
* (C) Gershon Elber, Technion, Israel Institute of Technology                 *
*******************************************************************************
* Written by Gershon Elber, Sep. 91.					      *
******************************************************************************/

#include "triv_loc.h"

/*****************************************************************************
* DESCRIPTION:                                                               M
* Given two trivariates, makes them compatible by:			     M
* 1. Coercing their point type to be the same.				     M
* 2. Making them have the same curve type.				     M
* 3. Raising the degree of the lower one to be the same as the higher.	     M
* 4. Refining them to a common knot vector (If Bspline and SameOrder).	     M
*                                                                            M
* Note 3 is performed if SameOrder TRUE, 4 if SameKV TRUE.		     M
* Both trivariates are modified IN PLACE.				     M
*                                                                            *
* PARAMETERS:                                                                M
*   TV1, TV2:  Two surfaces to be made compatible, in place.                 M
*   SameUOrder:  If TRUE, this routine make sure they share the same U       M
*                order.							     M
*   SameVOrder:  If TRUE, this routine make sure they share the same V       M
*                order.							     M
*   SameWOrder:  If TRUE, this routine make sure they share the same W       M
*                order.							     M
*   SameUKV:     If TRUE, this routine make sure they share the same U       M
*                knot vector and hence continuity.                           *
*   SameVKV:     If TRUE, this routine make sure they share the same V       M
*                knot vector and hence continuity.                           M
*   SameWKV:     If TRUE, this routine make sure they share the same W       M
*                knot vector and hence continuity.                           M
*                                                                            *
* RETURN VALUE:                                                              M
*   CagdBType:   TRUE if successful, FALSE otherwise.                        M
*                                                                            *
* KEYWORDS:                                                                  M
*   TrivMakeTVsCompatible, compatibility                                     M
*****************************************************************************/
CagdBType TrivMakeTVsCompatible(TrivTVStruct **TV1,
				TrivTVStruct **TV2,
				CagdBType SameUOrder,
				CagdBType SameVOrder,
				CagdBType SameWOrder,
				CagdBType SameUKV,
				CagdBType SameVKV,
				CagdBType SameWKV)
{
    int i, KV1Len, KV2Len, RefLen;
    CagdRType *KV1, *KV2, *RefKV;
    TrivTVStruct *TmpTV;
    CagdPointType CommonPType;

    if ((*TV1 == NULL) || (*TV2 == NULL))
	return TRUE;

    CommonPType = CagdMergePointType((*TV1) -> PType, (*TV2) -> PType);

    /* Make the point types compatible. */
    if (CommonPType != (*TV1) -> PType) {
	TmpTV = TrivCoerceTVTo(*TV1, CommonPType);
	TrivTVFree(*TV1);
	*TV1 = TmpTV;
    }
    if (CommonPType != (*TV2) -> PType) {
	TmpTV = TrivCoerceTVTo(*TV2, CommonPType);
	TrivTVFree(*TV2);
	*TV2 = TmpTV;
    }

    if (SameUOrder) {
	/* Raise the degree of the lower one. */
	for (i = (*TV1) -> UOrder; i < (*TV2) -> UOrder; i++) {
	    TmpTV = TrivTVDegreeRaise(*TV1, TRIV_CONST_U_DIR);
	    TrivTVFree(*TV1);
	    *TV1 = TmpTV;
	}
	for (i = (*TV2) -> UOrder; i < (*TV1) -> UOrder; i++) {
	    TmpTV = TrivTVDegreeRaise(*TV2, TRIV_CONST_U_DIR);
	    TrivTVFree(*TV2);
	    *TV2 = TmpTV;
	}
    }
    if (SameVOrder) {
	for (i = (*TV1) -> VOrder; i < (*TV2) -> VOrder; i++) {
	    TmpTV = TrivTVDegreeRaise(*TV1, TRIV_CONST_V_DIR);
	    TrivTVFree(*TV1);
	    *TV1 = TmpTV;
	}
	for (i = (*TV2) -> VOrder; i < (*TV1) -> VOrder; i++) {
	    TmpTV = TrivTVDegreeRaise(*TV2, TRIV_CONST_V_DIR);
	    TrivTVFree(*TV2);
	    *TV2 = TmpTV;
	}
    }
    if (SameWOrder) {
	for (i = (*TV1) -> WOrder; i < (*TV2) -> WOrder; i++) {
	    TmpTV = TrivTVDegreeRaise(*TV1, TRIV_CONST_W_DIR);
	    TrivTVFree(*TV1);
	    *TV1 = TmpTV;
	}
	for (i = (*TV2) -> WOrder; i < (*TV1) -> WOrder; i++) {
	    TmpTV = TrivTVDegreeRaise(*TV2, TRIV_CONST_W_DIR);
	    TrivTVFree(*TV2);
	    *TV2 = TmpTV;
	}
    }

    /* If incompatible trivar type - make it the same as well. */
    if ((*TV1) -> GType != (*TV2) -> GType) {
	/* Assume both trivars are either Bezier or Bspline surfaces. */
	if ((*TV1) -> GType != (*TV2) -> GType) {
	    /* If Bezier basis - promote to bspline: */
	    if ((*TV1) -> GType == TRIV_TVBEZIER_TYPE) {
		TmpTV = TrivCnvrtBezier2BsplineTV(*TV1);
		TrivTVFree(*TV1);
		*TV1 = TmpTV;
	    }
	    if ((*TV2) -> GType == TRIV_TVBEZIER_TYPE) {
		TmpTV = TrivCnvrtBezier2BsplineTV(*TV2);
		TrivTVFree(*TV2);
		*TV2 = TmpTV;
	    }
	}
    }

    if ((*TV1) -> GType == TRIV_TVBSPLINE_TYPE) {
	/* If bspline surface - make sure knot vectors are the same. */

	if (SameUKV && SameUOrder) {
	    /* Handle the U Direction. */
	    int Order = (*TV1) -> UOrder;

	    KV1 = (*TV1) -> UKnotVector;
	    KV2 = (*TV2) -> UKnotVector;
	    KV1Len = (*TV1) -> ULength + Order;
	    KV2Len = (*TV2) -> ULength + Order;

	    /* Affine map second knot vector to span same parametric domain. */
	    BspKnotAffineTrans(KV2, KV2Len, KV1[Order - 1] - KV2[Order - 1],
			       (KV1[KV1Len - Order] - KV1[Order - 1]) /
			       (KV2[KV2Len - Order] - KV2[Order - 1]));

	    /* Find knots in KV2 which are not in KV1 and refine TV1 there. */
	    RefKV  = BspKnotSubtrTwo(&KV2[Order - 1], KV2Len - Order * 2 + 2,
				     &KV1[Order - 1], KV1Len - Order * 2 + 2,
				     &RefLen);
	    if (RefLen > 0) {
		TmpTV = TrivTVRefineAtParams(*TV1, TRIV_CONST_U_DIR,
					     FALSE, RefKV, RefLen);
		TrivTVFree(*TV1);
		*TV1 = TmpTV;
		KV1 = (*TV1) -> UKnotVector;
		KV1Len = (*TV1) -> ULength + Order;
	    }
	    IritFree((VoidPtr) RefKV);

	    /* Find knots in KV1 which are not in KV2 and refine TV2 there. */
	    RefKV  = BspKnotSubtrTwo(&KV1[Order - 1], KV1Len - Order * 2 + 2,
				     &KV2[Order - 1], KV2Len - Order * 2 + 2,
				     &RefLen);
	    if (RefLen > 0) {
		TmpTV = TrivTVRefineAtParams(*TV2, TRIV_CONST_U_DIR,
					     FALSE, RefKV, RefLen);
		TrivTVFree(*TV2);
		*TV2 = TmpTV;
	    }
	    IritFree((VoidPtr) RefKV);
	}

	if (SameVKV && SameVOrder) {
	    /* Handle the V Direction. */
	    int Order = (*TV1) -> VOrder;

	    KV1 = (*TV1) -> VKnotVector;
	    KV2 = (*TV2) -> VKnotVector;
	    KV1Len = (*TV1) -> VLength + Order;
	    KV2Len = (*TV2) -> VLength + Order;

	    /* Affine map second knot vector to span same parametric domain. */
	    BspKnotAffineTrans(KV2, KV2Len, KV1[Order - 1] - KV2[Order - 1],
			       (KV1[KV1Len - Order] - KV1[Order - 1]) /
			       (KV2[KV2Len - Order] - KV2[Order - 1]));

	    /* Find knots in KV2 which are not in KV1 and refine TV1 there. */
	    RefKV  = BspKnotSubtrTwo(&KV2[Order - 1], KV2Len - Order * 2 + 2,
				     &KV1[Order - 1], KV1Len - Order * 2 + 2,
				     &RefLen);
	    if (RefLen > 0) {
		TmpTV = TrivTVRefineAtParams(*TV1, TRIV_CONST_V_DIR,
					     FALSE, RefKV, RefLen);
		TrivTVFree(*TV1);
		*TV1 = TmpTV;
		KV1 = (*TV1) -> VKnotVector;
		KV1Len = (*TV1) -> VLength + Order;
	    }
	    IritFree((VoidPtr) RefKV);

	    /* Find knots in KV1 which are not in KV2 and refine TV2 there. */
	    RefKV = BspKnotSubtrTwo(&KV1[Order - 1], KV1Len - Order * 2 + 2,
				    &KV2[Order - 1], KV2Len - Order * 2 + 2,
				    &RefLen);
	    if (RefLen > 0) {
		TmpTV = TrivTVRefineAtParams(*TV2, TRIV_CONST_V_DIR,
					     FALSE, RefKV, RefLen);
		TrivTVFree(*TV2);
		*TV2 = TmpTV;
	    }
	    IritFree((VoidPtr) RefKV);
	}

	if (SameWKV && SameWOrder) {
	    /* Handle the V Direction. */
	    int Order = (*TV1) -> WOrder;

	    KV1 = (*TV1) -> WKnotVector;
	    KV2 = (*TV2) -> WKnotVector;
	    KV1Len = (*TV1) -> WLength + Order;
	    KV2Len = (*TV2) -> WLength + Order;

	    /* Affine map second knot vector to span same parametric domain. */
	    BspKnotAffineTrans(KV2, KV2Len, KV1[Order - 1] - KV2[Order - 1],
			       (KV1[KV1Len - Order] - KV1[Order - 1]) /
			       (KV2[KV2Len - Order] - KV2[Order - 1]));

	    /* Find knots in KV2 which are not in KV1 and refine TV1 there. */
	    RefKV = BspKnotSubtrTwo(&KV2[Order - 1], KV2Len - Order * 2 + 2,
				    &KV1[Order - 1], KV1Len - Order * 2 + 2,
				    &RefLen);
	    if (RefLen > 0) {
		TmpTV = TrivTVRefineAtParams(*TV1, TRIV_CONST_W_DIR,
					     FALSE, RefKV, RefLen);
		TrivTVFree(*TV1);
		*TV1 = TmpTV;
		KV1 = (*TV1) -> WKnotVector;
		KV1Len = (*TV1) -> WLength + Order;
	    }
	    IritFree((VoidPtr) RefKV);

	    /* Find knots in KV1 which are not in KV2 and refine TV2 there. */
	    RefKV  = BspKnotSubtrTwo(&KV1[Order - 1], KV1Len - Order * 2 + 2,
				     &KV2[Order - 1], KV2Len - Order * 2 + 2,
				     &RefLen);
	    if (RefLen > 0) {
		TmpTV = TrivTVRefineAtParams(*TV2, TRIV_CONST_W_DIR,
					     FALSE, RefKV, RefLen);
		TrivTVFree(*TV2);
		*TV2 = TmpTV;
	    }
	    IritFree((VoidPtr) RefKV);
	}
    }

    return TRUE;
}
