/******************************************************************************
* CagdCMrg.c - Surface/Surface merging routine.				      *
*******************************************************************************
* (C) Gershon Elber, Technion, Israel Institute of Technology                 *
*******************************************************************************
* Written by Gershon Elber, Sep 92.					      *
******************************************************************************/

#include "cagd_loc.h"

static void InterpolateLinearSeg(CagdRType *V1,
				 CagdRType *V2,
				 int Len,
				 int Step);

/*****************************************************************************
* DESCRIPTION:                                                               M
* Merges two surfaces in the requested direction Dir.			     M
*  If SameEdge, it is assumed last edge of Srf1 is identical to first edge   M
* of Srf2 and one row is dropped from new mesh. Otherwise a ruled surface is M
* fit between the two edges.                                                 M
*                                                                            *
* PARAMETERS:                                                                M
*   Srf1:                 To connect to Srf1's starting boundary at its end. M
*   Srf2:                 To connect to Srf2's end boundary at its start.    M
*   Dir:                  Direction the merge should take place. Either U    M
*                         or V.						     M
*   SameEdge:             If the two surfaces sharea common edge.            M
*   InterpolateDiscont:   If TRUE, linearly interpolate discontinuity.       M
*                                                                            *
* RETURN VALUE:                                                              M
*   CagdSrfStruct *:     The merged surface.                                 M
*                                                                            M
* KEYWORDS:                                                                  M
*   CagdMergeSrfSrf, merge                                                   M
*****************************************************************************/
CagdSrfStruct *CagdMergeSrfSrf(CagdSrfStruct *Srf1,
			       CagdSrfStruct *Srf2,
			       CagdSrfDirType Dir,
			       CagdBType SameEdge,
			       int InterpolateDiscont)
{
    CagdBType IsNotRational;
    int i, j, UOrder, VOrder, ULen1, VLen1, ULen2, VLen2, MaxCoord, Length;
    CagdRType **Points1, **Points2, **Points;
    CagdPointType SrfPType;
    CagdSrfStruct *Srf;

    if (CAGD_IS_PERIODIC_SRF(Srf1) || CAGD_IS_PERIODIC_SRF(Srf2)) {
	Srf1 = CnvrtPeriodic2FloatSrf(Srf1);
	Srf2 = CnvrtPeriodic2FloatSrf(Srf2);
    }
    else {
	/* To make surfaces compatible: */
	Srf1 = CagdSrfCopy(Srf1);
	Srf2 = CagdSrfCopy(Srf2);
    }

    switch (Dir) {
	case CAGD_CONST_U_DIR:
	case CAGD_CONST_V_DIR:
	    if (!CagdMakeSrfsCompatible(&Srf1, &Srf2, TRUE, TRUE,
					Dir == CAGD_CONST_V_DIR,
					Dir == CAGD_CONST_U_DIR))
		CAGD_FATAL_ERROR(CAGD_ERR_SRF_FAIL_CMPT);
	    break;
	default:
	    CAGD_FATAL_ERROR(CAGD_ERR_DIR_NOT_CONST_UV);
	    break;
    }

    /* Verify surface geometric types. */
    switch (Srf1 -> GType) {
	case CAGD_SBEZIER_TYPE:
	    Srf = CnvrtBezier2BsplineSrf(Srf1);
	    CagdSrfFree(Srf1);
	    Srf1 = Srf;
	    Srf = CnvrtBezier2BsplineSrf(Srf2);
	    CagdSrfFree(Srf2);
	    Srf2 = Srf;
	    break;
	case CAGD_SBSPLINE_TYPE:
	    break;
	case CAGD_SPOWER_TYPE:
	    CAGD_FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
	    break;
	default:
	    CAGD_FATAL_ERROR(CAGD_ERR_UNDEF_SRF);
	    break;
    }

    UOrder = Srf1 -> UOrder;
    VOrder = Srf1 -> VOrder;
    ULen1 = Srf1 -> ULength;
    VLen1 = Srf1 -> VLength;
    ULen2 = Srf2 -> ULength;
    VLen2 = Srf2 -> VLength;
    Points1 = Srf1 -> Points;
    Points2 = Srf2 -> Points;
    IsNotRational = !CAGD_IS_RATIONAL_SRF(Srf1);
    MaxCoord = CAGD_NUM_OF_PT_COORD(Srf1 -> PType),
    SrfPType = CAGD_MAKE_PT_TYPE(!IsNotRational, MaxCoord);

    switch (Dir) {
	case CAGD_CONST_U_DIR:
	    Length = SameEdge ? ULen1 + ULen2 - 1
			      : InterpolateDiscont ? ULen1 + ULen2 + UOrder - 2
						   : ULen1 + ULen2;

	    Srf = BspSrfNew(Length, VLen1, UOrder, VOrder, SrfPType);

	    /* Update knot vectors. We assume open end condition here... */
	    CAGD_GEN_COPY(Srf -> UKnotVector, Srf1 -> UKnotVector,
			  (ULen1 + UOrder - 1) * sizeof(CagdRType));
	    CAGD_GEN_COPY(Srf -> VKnotVector, Srf1 -> VKnotVector,
			  (VLen1 + VOrder) * sizeof(CagdRType));

	    if (SameEdge) {
		/* Copy kv of second surface immediately after. */
		CAGD_GEN_COPY(&Srf -> UKnotVector[ULen1 + UOrder - 1],
			      &Srf2 -> UKnotVector[UOrder],
			      ULen2 * sizeof(CagdRType));
		BspKnotAffineTrans(&Srf -> UKnotVector[ULen1 + UOrder - 1],
				   ULen2,
				   Srf -> UKnotVector[ULen1 + UOrder - 2] -
				   Srf2 -> UKnotVector[0],
				   1.0);
	    }
	    else if (InterpolateDiscont) {
		/* Copy kv of second surface order after. */
		CAGD_GEN_COPY(&Srf -> UKnotVector[ULen1 + UOrder - 1],
			      &Srf2 -> UKnotVector[1],
			      (ULen2 + UOrder - 1) * sizeof(CagdRType));
		BspKnotAffineTrans(&Srf -> UKnotVector[ULen1 + UOrder - 1],
				   ULen2 + UOrder - 1,
				   Srf -> UKnotVector[ULen1 + UOrder - 2] -
				   Srf -> UKnotVector[ULen1 + UOrder - 1] + 1.0,
				   1.0);
	    }
	    else {
		CAGD_GEN_COPY(&Srf -> UKnotVector[ULen1 + UOrder - 1],
			      &Srf2 -> UKnotVector[UOrder - 1],
			      (ULen2 + 1) * sizeof(CagdRType));
		BspKnotAffineTrans(&Srf -> UKnotVector[ULen1 + UOrder - 1],
				   ULen2 + 1,
				   Srf1 -> UKnotVector[ULen1 + UOrder - 1] -
				   Srf -> UKnotVector[ULen1 + UOrder - 1],
				   1.0);
	    }

	    Points = Srf -> Points;

	    for (i = 0; i < VLen1; i++) {
		for (j = IsNotRational; j <= MaxCoord; j++) {
		    CAGD_GEN_COPY(&Points[j][CAGD_MESH_UV(Srf, 0, i)],
				  &Points1[j][CAGD_MESH_UV(Srf1, 0, i)],
				  ULen1 * sizeof(CagdRType));
		    if (SameEdge) {
			/* Copy row of second surface immediately after. */
			CAGD_GEN_COPY(
			    &Points[j][CAGD_MESH_UV(Srf, ULen1 - 1, i)],
			    &Points2[j][CAGD_MESH_UV(Srf2, 0, i)],
			    ULen2 * sizeof(CagdRType));
		    }
		    else if (InterpolateDiscont) {
			/* Copy row of 2nd srf order after and lin. interp. */
			CAGD_GEN_COPY(
			    &Points[j][CAGD_MESH_UV(Srf, ULen1 + UOrder - 2, i)],
			    &Points2[j][CAGD_MESH_UV(Srf2, 0, i)],
			    ULen2 * sizeof(CagdRType));
			InterpolateLinearSeg(
			    &Points[j][CAGD_MESH_UV(Srf, ULen1 - 1, i)],
			    &Points[j][CAGD_MESH_UV(Srf, ULen1 + UOrder - 2, i)],
			    UOrder, 1);
		    }
		    else {
			/* Copy row of 2nd srf order after and lin. interp. */
			CAGD_GEN_COPY(
			    &Points[j][CAGD_MESH_UV(Srf, ULen1, i)],
			    &Points2[j][CAGD_MESH_UV(Srf2, 0, i)],
			    ULen2 * sizeof(CagdRType));
		    }
		}
	    }
	    break;
	case CAGD_CONST_V_DIR:
	    Length = SameEdge ? VLen1 + VLen2 - 1
			      : InterpolateDiscont ? VLen1 + VLen2 + VOrder - 2
						   : VLen1 + VLen2;

	    Srf = BspSrfNew(ULen1, Length, UOrder, VOrder, SrfPType);

	    /* Update knot vectors. We assume open end condition here... */
	    CAGD_GEN_COPY(Srf -> UKnotVector, Srf1 -> UKnotVector,
			  (ULen1 + UOrder) * sizeof(CagdRType));
	    CAGD_GEN_COPY(Srf -> VKnotVector, Srf1 -> VKnotVector,
			  (VLen1 + VOrder - 1) * sizeof(CagdRType));

	    if (SameEdge) {
		/* Copy kv of second surface immediately after. */
		CAGD_GEN_COPY(&Srf -> VKnotVector[VLen1 + VOrder - 1],
			      &Srf2 -> VKnotVector[VOrder],
			      VLen2 * sizeof(CagdRType));
		BspKnotAffineTrans(&Srf -> VKnotVector[VLen1 + VOrder - 1],
				   VLen2,
				   Srf -> VKnotVector[VLen1 + VOrder - 2] -
				   Srf2 -> VKnotVector[0],
				   1.0);
	    }
	    else if (InterpolateDiscont) {
		/* Copy kv of second surface order after. */
		CAGD_GEN_COPY(&Srf -> VKnotVector[VLen1 + VOrder - 1],
			      &Srf2 -> VKnotVector[1],
			      (VLen2 + VOrder - 1) * sizeof(CagdRType));
		BspKnotAffineTrans(&Srf -> VKnotVector[VLen1 + VOrder - 1],
				   VLen2 + VOrder - 1,
				   Srf -> VKnotVector[VLen1 + VOrder - 2] -
				   Srf -> VKnotVector[VLen1 + VOrder - 1] + 1.0,
				   1.0);
	    }
            else {
		CAGD_GEN_COPY(&Srf -> VKnotVector[VLen1 + VOrder - 1],
			      &Srf2 -> VKnotVector[VOrder - 1],
			      (VLen2 + 1) * sizeof(CagdRType));
		BspKnotAffineTrans(&Srf -> VKnotVector[VLen1 + VOrder - 1],
				   VLen2 + 1,
				   Srf1 -> VKnotVector[VLen1 + VOrder - 1] -
				   Srf -> VKnotVector[VLen1 + VOrder - 1],
				   1.0);
	    }

	    Points = Srf -> Points;

	    for (i = 0; i < ULen1; i++) {
		for (j = IsNotRational; j <= MaxCoord; j++) {
		    CAGD_GEN_COPY_STEP(&Points[j][CAGD_MESH_UV(Srf, i, 0)],
				       &Points1[j][CAGD_MESH_UV(Srf1, i, 0)],
				       VLen1, ULen1, ULen1, CagdRType);
		    if (SameEdge) {
			/* Copy col of second surface immediately after. */
			CAGD_GEN_COPY_STEP(
			    &Points[j][CAGD_MESH_UV(Srf, i, VLen1 - 1)],
			    &Points2[j][CAGD_MESH_UV(Srf2, i, 0)],
			    VLen2, ULen1, ULen1, CagdRType);
		    }
		    else if (InterpolateDiscont) {
			/* Copy col of 2nd srf order after and lin. interp. */
			CAGD_GEN_COPY_STEP(
			    &Points[j][CAGD_MESH_UV(Srf, i, VLen1 + VOrder - 2)],
			    &Points2[j][CAGD_MESH_UV(Srf2, i, 0)],
			    VLen2, ULen1, ULen1, CagdRType);
			InterpolateLinearSeg(
			    &Points[j][CAGD_MESH_UV(Srf, i, VLen1 - 1)],
			    &Points[j][CAGD_MESH_UV(Srf, i, VLen1 + VOrder - 2)],
			    VOrder, ULen1);
		    }
		    else {
			/* Copy col of 2nd srf order after and lin. interp. */
			CAGD_GEN_COPY_STEP(
			    &Points[j][CAGD_MESH_UV(Srf, i, VLen1)],
			    &Points2[j][CAGD_MESH_UV(Srf2, i, 0)],
			    VLen2, ULen1, ULen1, CagdRType);
		    }
		}
	    }
	    break;
	default:
	    Srf = NULL;
	    CAGD_FATAL_ERROR(CAGD_ERR_DIR_NOT_CONST_UV);
	    break;
    }

    CagdSrfFree(Srf1);
    CagdSrfFree(Srf2);

    return Srf;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Merges a list of surfaces by connecting the end of one surface to the      M
* begining of the next. See also CagdMergeSrfSrf.                            M
*                                                                            *
* PARAMETERS:                                                                M
*   SrfList:              To connect into one surface.                       M
*   Dir:                  Direction the merge should take place. Either U    M
*                         or V.						     M
*   SameEdge:             If the two surfaces sharea common edge.            M
*   InterpolateDiscont:   If TRUE, linearly interpolate discontinuity.       M
*                                                                            *
* RETURN VALUE:                                                              M
*   CagdSrfStruct *:     The merged surface.                                 M
*                                                                            *
* KEYWORDS:                                                                  M
*   CagdMergeSrfList, merge                                                  M
*****************************************************************************/
CagdSrfStruct *CagdMergeSrfList(CagdSrfStruct *SrfList,
				CagdSrfDirType Dir,
				CagdBType SameEdge,
				int InterpolateDiscont)
{
    if (SrfList != NULL && SrfList -> Pnext != NULL) {
	CagdSrfStruct
	    *MergedSrf = CagdSrfCopy(SrfList);

	for (SrfList = SrfList -> Pnext;
	     SrfList != NULL;
	     SrfList = SrfList -> Pnext) {
	    CagdSrfStruct
		*TmpSrf = CagdMergeSrfSrf(MergedSrf, SrfList, Dir, SameEdge,
					  InterpolateDiscont);

	    CagdSrfFree(MergedSrf);
	    MergedSrf = TmpSrf;
	}
	return MergedSrf;
    }
    else
	return SrfList ? CagdSrfCopy(SrfList) : NULL;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Linearly interpolates between V1 and V2 values Len times (Len includes V1  *
* and V2) and step the array using Step.				     *
*                                                                            *
*                                                                            *
*                                                                            *
* PARAMETERS:                                                                *
*   V1:       First coefficient to linearly interpolate.                     *
*   V2:       Seoncd coefficient to linearly interpolate.                    *
*   Len:      Of coefficients to linearly interpolate from V1 to V2.         *
*   Step:     To take while interpolating.                                   *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void InterpolateLinearSeg(CagdRType *V1,
				 CagdRType *V2,
				 int Len,
				 int Step)
{
    int i;
    CagdRType
	*V = V1 + Step;

    if (Len-- <= 2)
	return;				     /* No middle points to interp. */

    for (i = 1; i < Len; i++) {
	*V = (i * (*V2) + (Len - i) * (*V1)) / Len;

	V += Step;
    }
}
