/******************************************************************************
* CagdCmpt.c - Make objects compatible.					      *
*******************************************************************************
* Written by Gershon Elber, Sep. 91.					      *
******************************************************************************/

#include "cagd_loc.h"

/******************************************************************************
* Given two curves, make the compatible by:				      *
* 1. Coercing their point type to be the same.				      *
* 2. Make them have the same curve type.				      *
* 3. Raising the degree of the lower one to be the same.		      *
* 4. Refine them to a common knot vector (If Bspline).			      *
* Note 3 is preformed if SameOrder TRUE, 4 if SameKV TRUE.		      *
* Returns TRUE if succesfull. Both Curves are modified IN PLACE.	      *
******************************************************************************/
CagdBType CagdMakeCrvsCompatible(CagdCrvStruct **Crv1, CagdCrvStruct **Crv2,
				 CagdBType SameOrder, CagdBType SameKV)
{
    int KV1Len, KV2Len, RefLen;
    CagdRType *KV1, *KV2, *RefKV;
    CagdCrvStruct *TCrv;
    CagdPointType CommonPType;

    if ((*Crv1 == NULL) || (*Crv2 == NULL))
	return TRUE;

    CommonPType = CagdMergePointType((*Crv1) -> PType, (*Crv2) -> PType);

    /* Make the point types compatible. */
    if (CommonPType != (*Crv1) -> PType) {
	TCrv = CagdCoerceCrvTo(*Crv1, CommonPType);
	CagdCrvFree(*Crv1);
	*Crv1 = TCrv;
    }
    if (CommonPType != (*Crv2) -> PType) {
	TCrv = CagdCoerceCrvTo(*Crv2, CommonPType);
	CagdCrvFree(*Crv2);
	*Crv2 = TCrv;
    }

    if (SameOrder) {
	/* Raise the degree of the lower one. */
	if ((*Crv1) -> Order < (*Crv2) -> Order) {
	    TCrv = CagdCrvDegreeRaiseN(*Crv1, (*Crv2) -> Order);
	    CagdCrvFree(*Crv1);
	    *Crv1 = TCrv;
	}
	else if ((*Crv2) -> Order < (*Crv1) -> Order) {
	    TCrv = CagdCrvDegreeRaiseN(*Crv2, (*Crv1) -> Order);
	    CagdCrvFree(*Crv2);
	    *Crv2 = TCrv;
	}
    }

    /* If incompatible curve type - make it the same as well. */
    if ((*Crv1) -> GType != (*Crv2) -> GType) {
	/* If power basis - promote to bezier: */
	if ((*Crv1) -> GType == CAGD_CPOWER_TYPE) {
	    TCrv = CnvrtPower2BezierCrv(*Crv1);
	    CagdCrvFree(*Crv1);
	    *Crv1 = TCrv;
	}
	if ((*Crv2) -> GType == CAGD_CPOWER_TYPE) {
	    TCrv = CnvrtPower2BezierCrv(*Crv2);
	    CagdCrvFree(*Crv2);
	    *Crv2 = TCrv;
	}

	/* Now both curves may be either bezier or bspline curves. */
	if ((*Crv1) -> GType != (*Crv2) -> GType) {
	    /* If bezier basis - promote to bspline: */
	    if ((*Crv1) -> GType == CAGD_CBEZIER_TYPE) {
		TCrv = CnvrtBezier2BsplineCrv(*Crv1);
		CagdCrvFree(*Crv1);
		*Crv1 = TCrv;
	    }
	    if ((*Crv2) -> GType == CAGD_CBEZIER_TYPE) {
		TCrv = CnvrtBezier2BsplineCrv(*Crv2);
		CagdCrvFree(*Crv2);
		*Crv2 = TCrv;
	    }
	}
    }

    if (SameKV) {
	/* If bspline curve - make sure knot vectors are the same. */
	if ((*Crv1) -> GType == CAGD_CBSPLINE_TYPE) {
	    KV1 = (*Crv1) -> KnotVector;
	    KV2 = (*Crv2) -> KnotVector;
	    KV1Len = (*Crv1) -> Length + (*Crv1) -> Order;
	    KV2Len = (*Crv2) -> Length + (*Crv2) -> Order;

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

	    /* Find knots in KV2 which are not in KV1 and refine Crv1 there. */
	    RefKV  = BspKnotSubtrTwo(KV2, KV2Len, KV1, KV1Len, &RefLen);
	    if (RefLen > 0) {
		TCrv = CagdCrvRefineAtParams(*Crv1, FALSE, RefKV, RefLen);
		CagdCrvFree(*Crv1);
		*Crv1 = TCrv;
		KV1 = (*Crv1) -> KnotVector;
		KV1Len = (*Crv1) -> Length + (*Crv1) -> Order;
	    }
	    IritFree((VoidPtr) RefKV);

	    /* Find knots in KV1 which are not in KV2 and refine Crv2 there. */
	    RefKV  = BspKnotSubtrTwo(KV1, KV1Len, KV2, KV2Len, &RefLen);
	    if (RefLen > 0) {
		TCrv = CagdCrvRefineAtParams(*Crv2, FALSE, RefKV, RefLen);
		CagdCrvFree(*Crv2);
		*Crv2 = TCrv;
	    }
	    IritFree((VoidPtr) RefKV);
	}
    }

    return TRUE;
}

/******************************************************************************
* Given two surfaces, make the compatible by:				      *
* 1. Coercing their point type to be the same.				      *
* 2. Make them have the same surface type.				      *
* 3. Raising the degree of the lower one to be the same.		      *
* 4. Refine them to common knot vectors (If Bspline).			      *
* Note 3 is preformed if SameOrder TRUE, 4 if SameKV TRUE.		      *
* Returns TRUE if succesfull. Both Surfaces are modified IN PLACE.	      *
******************************************************************************/
CagdBType CagdMakeSrfsCompatible(CagdSrfStruct **Srf1, CagdSrfStruct **Srf2,
				 CagdBType SameUOrder, CagdBType SameVOrder,
				 CagdBType SameUKV, CagdBType SameVKV)
{
    int i, KV1Len, KV2Len, RefLen;
    CagdRType *KV1, *KV2, *RefKV;
    CagdSrfStruct *TSrf;
    CagdPointType CommonPType;

    if ((*Srf1 == NULL) || (*Srf2 == NULL))
	return TRUE;

    CommonPType = CagdMergePointType((*Srf1) -> PType, (*Srf2) -> PType);

    /* Make the point types compatible. */
    if (CommonPType != (*Srf1) -> PType) {
	TSrf = CagdCoerceSrfTo(*Srf1, CommonPType);
	CagdSrfFree(*Srf1);
	*Srf1 = TSrf;
    }
    if (CommonPType != (*Srf2) -> PType) {
	TSrf = CagdCoerceSrfTo(*Srf2, CommonPType);
	CagdSrfFree(*Srf2);
	*Srf2 = TSrf;
    }

    if (SameUOrder) {
	/* Raise the degree of the lower one. */
	for (i = (*Srf1) -> UOrder; i < (*Srf2) -> UOrder; i++) {
	    TSrf = CagdSrfDegreeRaise(*Srf1, CAGD_CONST_V_DIR);
	    CagdSrfFree(*Srf1);
	    *Srf1 = TSrf;
	}
	for (i = (*Srf2) -> UOrder; i < (*Srf1) -> UOrder; i++) {
	    TSrf = CagdSrfDegreeRaise(*Srf2, CAGD_CONST_V_DIR);
	    CagdSrfFree(*Srf2);
	    *Srf2 = TSrf;
	}
    }
    if (SameVOrder) {
	for (i = (*Srf1) -> VOrder; i < (*Srf2) -> VOrder; i++) {
	    TSrf = CagdSrfDegreeRaise(*Srf1, CAGD_CONST_U_DIR);
	    CagdSrfFree(*Srf1);
	    *Srf1 = TSrf;
	}
	for (i = (*Srf2) -> VOrder; i < (*Srf1) -> VOrder; i++) {
	    TSrf = CagdSrfDegreeRaise(*Srf2, CAGD_CONST_U_DIR);
	    CagdSrfFree(*Srf2);
	    *Srf2 = TSrf;
	}
    }

    /* If incompatible surface type - make it the same as well. */
    if ((*Srf1) -> GType != (*Srf2) -> GType) {
	/* If power basis - promote to bezier: */
	if ((*Srf1) -> GType == CAGD_SPOWER_TYPE) {
	    TSrf = CnvrtPower2BezierSrf(*Srf1);
	    CagdSrfFree(*Srf1);
	    *Srf1 = TSrf;
	}
	if ((*Srf2) -> GType == CAGD_SPOWER_TYPE) {
	    TSrf = CnvrtPower2BezierSrf(*Srf2);
	    CagdSrfFree(*Srf2);
	    *Srf2 = TSrf;
	}

	/* Now both surfaces may be either bezier or bspline surfaces. */
	if ((*Srf1) -> GType != (*Srf2) -> GType) {
	    /* If bezier basis - promote to bspline: */
	    if ((*Srf1) -> GType == CAGD_SBEZIER_TYPE) {
		TSrf = CnvrtBezier2BsplineSrf(*Srf1);
		CagdSrfFree(*Srf1);
		*Srf1 = TSrf;
	    }
	    if ((*Srf2) -> GType == CAGD_SBEZIER_TYPE) {
		TSrf = CnvrtBezier2BsplineSrf(*Srf2);
		CagdSrfFree(*Srf2);
		*Srf2 = TSrf;
	    }
	}
    }

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

	if (SameUKV) {
	    /* Handle the U Direction. */
	    KV1 = (*Srf1) -> UKnotVector;
	    KV2 = (*Srf2) -> UKnotVector;
	    KV1Len = (*Srf1) -> ULength + (*Srf1) -> UOrder;
	    KV2Len = (*Srf2) -> ULength + (*Srf2) -> UOrder;

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

	    /* Find knots in KV2 which are not in KV1 and refine Srf1 there. */
	    RefKV  = BspKnotSubtrTwo(KV2, KV2Len, KV1, KV1Len, &RefLen);
	    if (RefLen > 0) {
		TSrf = CagdSrfRefineAtParams(*Srf1, CAGD_CONST_U_DIR,
					     FALSE, RefKV, RefLen);
		CagdSrfFree(*Srf1);
		*Srf1 = TSrf;
		KV1 = (*Srf1) -> UKnotVector;
		KV1Len = (*Srf1) -> ULength + (*Srf1) -> UOrder;
	    }
	    IritFree((VoidPtr) RefKV);

	    /* Find knots in KV1 which are not in KV2 and refine Srf2 there. */
	    RefKV  = BspKnotSubtrTwo(KV1, KV1Len, KV2, KV2Len, &RefLen);
	    if (RefLen > 0) {
		TSrf = CagdSrfRefineAtParams(*Srf2, CAGD_CONST_U_DIR,
					     FALSE, RefKV, RefLen);
		CagdSrfFree(*Srf2);
		*Srf2 = TSrf;
	    }
	    IritFree((VoidPtr) RefKV);
	}

	if (SameVKV) {
	    /* Handle the V Direction. */
	    KV1 = (*Srf1) -> VKnotVector;
	    KV2 = (*Srf2) -> VKnotVector;
	    KV1Len = (*Srf1) -> VLength + (*Srf1) -> VOrder;
	    KV2Len = (*Srf2) -> VLength + (*Srf2) -> VOrder;

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

	    /* Find knots in KV2 which are not in KV1 and refine Srf1 there. */
	    RefKV  = BspKnotSubtrTwo(KV2, KV2Len, KV1, KV1Len, &RefLen);
	    if (RefLen > 0) {
		TSrf = CagdSrfRefineAtParams(*Srf1, CAGD_CONST_V_DIR,
					     FALSE, RefKV, RefLen);
		CagdSrfFree(*Srf1);
		*Srf1 = TSrf;
		KV1 = (*Srf1) -> VKnotVector;
		KV1Len = (*Srf1) -> VLength + (*Srf1) -> VOrder;
	    }
	    IritFree((VoidPtr) RefKV);

	    /* Find knots in KV1 which are not in KV2 and refine Srf2 there. */
	    RefKV  = BspKnotSubtrTwo(KV1, KV1Len, KV2, KV2Len, &RefLen);
	    if (RefLen > 0) {
		TSrf = CagdSrfRefineAtParams(*Srf2, CAGD_CONST_V_DIR,
					     FALSE, RefKV, RefLen);
		CagdSrfFree(*Srf2);
		*Srf2 = TSrf;
	    }
	    IritFree((VoidPtr) RefKV);
	}
    }

    return TRUE;
}

