/******************************************************************************
* CagdCoer.c - Handle point coercesions/conversions.			      *
*******************************************************************************
* Written by Gershon Elber, Aug. 90.					      *
******************************************************************************/

#include "cagd_loc.h"

/*****************************************************************************
* DESCRIPTION:                                                               M
* Coerce Srf/Crv Point from index Index of Points array of Type PType to a   M
* point type E2.                                                             M
*   If however Index < 0 Points is considered single point.                  M
*                                                                            *
* PARAMETERS:                                                                M
*   E2Point:   Where the coerced information is to besaved.                  M
*   Points:    Array of vectors if Index >= 0, a single point if Index < 0.  M
*   Index:     Index into the vectors of Points.                             M
*   PType:     Point type to be expected from Points.                        M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   CagdCoerceToE2, coercion                                                 M
*****************************************************************************/
void CagdCoerceToE2(CagdRType *E2Point,
		    CagdRType *Points[CAGD_MAX_PT_SIZE],
		    int Index,
		    CagdPointType PType)
{
    CagdBType
	IsRational = CAGD_IS_RATIONAL_PT(PType);
    int i,
        MaxCoord = CAGD_NUM_OF_PT_COORD(PType);
    CagdRType *Point;

    if (MaxCoord > 2)
	MaxCoord = 2;

    if (Index < 0) {			      /* Points is one single point. */
        Point = *Points;
	for (i = 1; i <= MaxCoord; i++)
	    *E2Point++ = IsRational ? Point[i] / Point[W] : Point[i];
    }
    else                         /* Points is a full arrays from Srf or Crv. */
	for (i = 1; i <= MaxCoord; i++)
	    *E2Point++ = IsRational ? Points[i][Index] / Points[W][Index] :
				      Points[i][Index];

    for (i = MaxCoord + 1; i <= 2; i++)
	*E2Point++ = 0.0;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Coerce Srf/Crv Point from index Index of Points array of Type PType to a   M
* point type E3.                                                             M
*   If however Index < 0 Points is considered single point.                  M
*                                                                            *
* PARAMETERS:                                                                M
*   E3Point:   Where the coerced information is to besaved.                  M
*   Points:    Array of vectors if Index >= 0, a single point if Index < 0.  M
*   Index:     Index into the vectors of Points.                             M
*   PType:     Point type to be expected from Points.                        M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   CagdCoerceToE3 coercion                                                  M
*****************************************************************************/
void CagdCoerceToE3(CagdRType *E3Point,
		    CagdRType *Points[CAGD_MAX_PT_SIZE],
		    int Index,
		    CagdPointType PType)
{
    CagdBType
	IsRational = CAGD_IS_RATIONAL_PT(PType);
    int i,
	MaxCoord = CAGD_NUM_OF_PT_COORD(PType);
    CagdRType *Point;

    if (MaxCoord > 3)
	MaxCoord = 3;

    if (Index < 0) {			      /* Points is one single point. */
	Point = *Points;
	for (i = 1; i <= MaxCoord; i++)
	    *E3Point++ = IsRational ? Point[i] / Point[W] : Point[i];
    }
    else                         /* Points is a full arrays from Srf or Crv. */
	for (i = 1; i <= MaxCoord; i++)
	    *E3Point++ = IsRational ? Points[i][Index] / Points[W][Index] :
				      Points[i][Index];

    for (i = MaxCoord + 1; i <= 3; i++)
	*E3Point++ = 0.0;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Coerce Srf/Crv Point from index Index of Points array of Type PType to a   M
* point type P2.                                                             M
*   If however Index < 0 Points is considered single point.                  M
*                                                                            *
* PARAMETERS:                                                                M
*   P2Point:   Where the coerced information is to besaved.                  M
*   Points:    Array of vectors if Index >= 0, a single point if Index < 0.  M
*   Index:     Index into the vectors of Points.                             M
*   PType:     Point type to be expected from Points.                        M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   CagdCoerceToP2, coercion                                                 M
*****************************************************************************/
void CagdCoerceToP2(CagdRType *P2Point,
		    CagdRType *Points[CAGD_MAX_PT_SIZE],
		    int Index,
		    CagdPointType PType)
{
    CagdBType
	IsRational = CAGD_IS_RATIONAL_PT(PType);
    int i,
        MaxCoord = CAGD_NUM_OF_PT_COORD(PType);
    CagdRType *Point;

    if (MaxCoord > 2)
	MaxCoord = 2;

    if (Index < 0) {			      /* Points is one single point. */
        Point = *Points;
	*P2Point++ = IsRational ? Point[W] : 1.0;
	for (i = 1; i <= MaxCoord; i++)
	    *P2Point++ = Point[i];
    }
    else {                       /* Points is a full arrays from Srf or Crv. */
	*P2Point++ = IsRational ? Points[W][Index] : 1.0;
	for (i = 1; i <= MaxCoord; i++)
	    *P2Point++ = Points[i][Index];
    }

    for (i = MaxCoord + 1; i <= 2; i++)
	*P2Point++ = 0.0;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Coerce Srf/Crv Point from index Index of Points array of Type PType to a   M
* point type P3.                                                             M
*   If however Index < 0 Points is considered single point.                  M
*                                                                            *
* PARAMETERS:                                                                M
*   P3Point:   Where the coerced information is to besaved.                  M
*   Points:    Array of vectors if Index >= 0, a single point if Index < 0.  M
*   Index:     Index into the vectors of Points.                             M
*   PType:     Point type to be expected from Points.                        M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   CagdCoerceToP3, coercion                                                 M
*****************************************************************************/
void CagdCoerceToP3(CagdRType *P3Point,
		    CagdRType *Points[CAGD_MAX_PT_SIZE],
		    int Index,
		    CagdPointType PType)
{
    CagdBType
	IsRational = CAGD_IS_RATIONAL_PT(PType);
    int i,
	MaxCoord = CAGD_NUM_OF_PT_COORD(PType);
    CagdRType *Point;

    if (MaxCoord > 3)
	MaxCoord = 3;

    if (Index < 0) {			      /* Points is one single point. */
	Point = *Points;
	*P3Point++ = IsRational ? Point[W] : 1.0;
	for (i = 1; i <= MaxCoord; i++)
	    *P3Point++ = Point[i];
    }
    else {                       /* Points is a full arrays from Srf or Crv. */
	*P3Point++ = IsRational ? Points[W][Index] : 1.0;
	for (i = 1; i <= MaxCoord; i++)
	    *P3Point++ = Points[i][Index];
    }

    for (i = MaxCoord + 1; i <= 3; i++)
	*P3Point++ = 0.0;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Coerces Srf/Crv Point from index Index of Points array of Type PType to a  M
* new type NewPType. If however Index < 0 Points is considered single point. M
*                                                                            *
* PARAMETERS:                                                                M
*   NewPoint:  Where the coerced information is to besaved.                  M
*   NewPType:  Point type of the coerced new point.                          M
*   Points:    Array of vectors if Index >= 0, a single point if Index < 0.  M
*   Index:     Index into the vectors of Points.                             M
*   OldPType:  Point type to be expected from Points.                        M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   CagdCoercePointTo, coercion                                              M
*****************************************************************************/
void CagdCoercePointTo(CagdRType *NewPoint,
		       CagdPointType NewPType,
		       CagdRType *Points[CAGD_MAX_PT_SIZE],
		       int Index,
		       CagdPointType OldPType)
{
    CagdBType
	IsRational = CAGD_IS_RATIONAL_PT(OldPType),
	NewIsRational = CAGD_IS_RATIONAL_PT(NewPType);
    int i,
	MaxCoord = CAGD_NUM_OF_PT_COORD(OldPType),
	NewMaxCoord = CAGD_NUM_OF_PT_COORD(NewPType);
    CagdRType *Point, Weight;

    if (MaxCoord > NewMaxCoord)
	MaxCoord = NewMaxCoord;

    if (Index < 0) {			      /* Points is one single point. */
        Point = *Points;
	Weight = IsRational ? Point[W] : 1.0;
	if (NewIsRational) {
	    *NewPoint++ = Weight;
	    Weight = 1.0;
	}
	for (i = 1; i <= MaxCoord; i++)
	    *NewPoint++ = Point[i] / Weight;
    }
    else {                       /* Points is a full arrays from Srf or Crv. */
	Weight = IsRational ? Points[W][Index] : 1.0;
	if (NewIsRational) {
	    *NewPoint++ = Weight;
	    Weight = 1.0;
	}
	for (i = 1; i <= MaxCoord; i++)
	    *NewPoint++ = Points[i][Index] / Weight;
    }

    for (i = MaxCoord + 1; i <= NewMaxCoord; i++)
	*NewPoint++ = 0.0;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Coerces an array of vectors, Points. of point type OldPType to point type  M
* NewPType, in place.							     M
*                                                                            *
* PARAMETERS:                                                                M
*   Points:    Where the old and new points are placed.                      M
*   Len:       Length of vectors in the array of vectors, Points.            M
*   OldPType:  Point type to be expected from Points.                        M
*   NewPType:  Point type of the coerced new point.                          M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            M
* KEYWORDS:                                                                  M
*   CagdCoercePointsTo, coercion                                             M
*****************************************************************************/
void CagdCoercePointsTo(CagdRType *Points[],
			int Len,
			CagdPointType OldPType,
			CagdPointType NewPType)
{
    int i, j,
	OldIsRational = CAGD_IS_RATIONAL_PT(OldPType),
	OldNumOfCoords = CAGD_NUM_OF_PT_COORD(OldPType),
	NewIsRational = CAGD_IS_RATIONAL_PT(NewPType),
	NewNumOfCoords = CAGD_NUM_OF_PT_COORD(NewPType);
    CagdRType *NewPoints[CAGD_MAX_PT_SIZE], Pt[CAGD_MAX_PT_SIZE];

    for (i = !NewIsRational; i <= NewNumOfCoords; i++)
	NewPoints[i] = (CagdRType *) IritMalloc(sizeof(CagdRType) * Len);

    for (i = 0; i < Len; i++) {
	CagdCoercePointTo(Pt, NewPType, Points, i, OldPType);

	if (NewIsRational)
	    for (j = 0; j <= NewNumOfCoords; j++)
		NewPoints[j][i] = Pt[j];
	else
	    for (j = 1; j <= NewNumOfCoords; j++)
		NewPoints[j][i] = Pt[j - 1];
    }

    /* Replace old rep. with new. */
    for (i = !OldIsRational; i <= OldNumOfCoords; i++)
	IritFree((VoidPtr) Points[i]);
    Points[0] = NULL;
    for (i = !NewIsRational; i <= NewNumOfCoords; i++)
	Points[i] = NewPoints[i];
    for (; i <= CAGD_MAX_PT_COORD; i++)
	Points[i] = NULL;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Coerces a curve to a new point type PType. If given curve is E1 or P1      M
* and requested new type is E2 or P2 the Y coefficients are updated to       M
* hold the parametric domain of the curve.	                             M
*                                                                            *
* PARAMETERS:                                                                M
*   Crv:       To be coerced to a new point type PType.                      M
*   PType:     New point type for Crv.                                       M
*                                                                            *
* RETURN VALUE:                                                              M
*   CagdCrvStruct *:  The new, coerced to PType, curve.                      M
*                                                                            *
* KEYWORDS:                                                                  M
*   CagdCoerceCrvTo, coercion                                                M
*****************************************************************************/
CagdCrvStruct *CagdCoerceCrvTo(CagdCrvStruct *Crv, CagdPointType PType)
{
    Crv = CagdCrvCopy(Crv);
    CagdCoercePointsTo(Crv -> Points, Crv -> Length, Crv -> PType, PType);

    if (CAGD_NUM_OF_PT_COORD(Crv -> PType) == 1 &&
	CAGD_NUM_OF_PT_COORD(PType) == 2) {
	/* Update the parameter space to be the second axis. */
	CagdRType
	    *WPts = Crv -> Points[0],
	    *Pts = Crv -> Points[2],
	    *Nodes = CagdCrvNodes(Crv);

	CAGD_GEN_COPY(Pts, Nodes, sizeof(CagdRType) * Crv -> Length);
	if (WPts != NULL) {
	    int i;

	    for (i = 0; i < Crv -> Length; i++)
	        *Pts++ *= *WPts++;
	}

	IritFree((VoidPtr) Nodes);
    }

    Crv -> PType = PType;

    return Crv;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Coerces a surface to a new point type PType. If given surface is E1 or P1  M
* and requested new type is E3 or P3 the Y and Z coefficients are updated to M
* hold the parametric domain of the surface.	                             M
*                                                                            *
* PARAMETERS:                                                                M
*   Srf:       To be coerced to a new point type PType.                      M
*   PType:     New point type for Srf.                                       M
*                                                                            *
* RETURN VALUE:                                                              M
*   CagdSrfStruct *:  The new, coerced to PType, surface.                    M
*                                                                            *
* KEYWORDS:                                                                  M
*   CagdCoerceSrfTo, coercion                                                M
*****************************************************************************/
CagdSrfStruct *CagdCoerceSrfTo(CagdSrfStruct *Srf, CagdPointType PType)
{
    Srf = CagdSrfCopy(Srf);
    CagdCoercePointsTo(Srf -> Points, Srf -> ULength * Srf -> VLength,
		       Srf -> PType, PType);

    if (CAGD_NUM_OF_PT_COORD(Srf -> PType) == 1 &&
	CAGD_NUM_OF_PT_COORD(PType) == 3) {
	/* Update the parameter space to be the second and third axis. */
	int i;
	CagdRType *PtsY, *PtsZ,
	    *WPts = Srf -> Points[0],
	    *UNodes = CagdSrfNodes(Srf, CAGD_CONST_U_DIR),
	    *VNodes = CagdSrfNodes(Srf, CAGD_CONST_V_DIR);

	for (i = 0, PtsY = Srf -> Points[2];
	     i < Srf -> VLength;
	     i++, PtsY += Srf -> ULength)
	    CAGD_GEN_COPY(PtsY, UNodes, sizeof(CagdRType) * Srf -> ULength);

	for (i = 0, PtsZ = Srf -> Points[3]; i < Srf -> VLength; i++) {
	    int j;

	    for (j = 0; j < Srf -> ULength; j++)
	        *PtsZ++ = VNodes[i];
	}

	if (WPts != NULL) {
	    PtsY = Srf -> Points[2];
	    PtsZ = Srf -> Points[3];
	    for (i = 0; i < Srf -> ULength * Srf -> VLength; i++) {
	        *PtsY++ *= *WPts;
	        *PtsZ++ *= *WPts++;

	    }
	}

	IritFree((VoidPtr) UNodes);
	IritFree((VoidPtr) VNodes);
    }

    Srf -> PType = PType;
    return Srf;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Returns a point type which spans the spaces of both two given point types. M
*                                                                            *
* PARAMETERS:                                                                M
*   PType1, PType2: To point types to find the point type of their union.    M
*                                                                            *
* RETURN VALUE:                                                              M
*   CagdPointType:  A point type of the union of the spaces of PType1 and    M
*                   PType2.                                                  M
*                                                                            *
* KEYWORDS:                                                                  M
*   CagdMergePointType, coercion                                             M
*****************************************************************************/
CagdPointType CagdMergePointType(CagdPointType PType1, CagdPointType PType2)
{
    CagdBType
	IsRational = CAGD_IS_RATIONAL_PT(PType1) || CAGD_IS_RATIONAL_PT(PType2);
    int NumCoords = MAX(CAGD_NUM_OF_PT_COORD(PType1),
		        CAGD_NUM_OF_PT_COORD(PType2));

    return CAGD_MAKE_PT_TYPE(IsRational, NumCoords);
}
