/******************************************************************************
* coerce.c - coerce an object into a different type.			      *
*******************************************************************************
* Written by Gershon Elber, January 1993.				      *
******************************************************************************/

#include <stdio.h>
#include "irit_sm.h"
#include "iritprsr.h"
#include "allocate.h"

/*****************************************************************************
* DESCRIPTION:                                                               M
*  Given a set of points, returns the list's common denominator that spans   M
* the space of all the points, taking into account type Type.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   PtObjList:  List of points.                                              M
*   Type:       Point type that we must span its space as well.              M
*                                                                            *
* RETURN VALUE:                                                              M
*   CagdPointType:   Point type that spans the space of point type Type as   M
*                    well as all points in PtObjList.                        M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrCoerceCommonSpace, coercion                                      M
*****************************************************************************/
CagdPointType IritPrsrCoerceCommonSpace(IPObjectStruct *PtObjList, int Type)
{
    int i,
	Dim = CAGD_NUM_OF_PT_COORD(Type),
	Proj = CAGD_IS_RATIONAL_PT(Type);
    IPObjectStruct *PtObj;

    if (!IP_IS_OLST_OBJ(PtObjList)) {
	IritPrsrFatalError("Coerce: Not a list object!");
	return CAGD_PT_NONE;
    }

    for (i = 0; (PtObj = ListObjectGet(PtObjList, i++)) != NULL; ) {
	if (IP_IS_CTLPT_OBJ(PtObj)) {
	    Dim = MAX(Dim, CAGD_NUM_OF_PT_COORD(PtObj -> U.CtlPt.PtType));
	    Proj |= CAGD_IS_RATIONAL_PT(PtObj -> U.CtlPt.PtType);
	}
	else if (IP_IS_POINT_OBJ(PtObj) || IP_IS_VEC_OBJ(PtObj)) {
	    Dim = MAX(Dim, 3);
	}
	else {
	    IritPrsrFatalError("Coerce: Not a point object in list!");
	    return CAGD_PT_NONE;
	}
    }

    return CAGD_MAKE_PT_TYPE(Proj, Dim) ;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Coerces a list of objects to Type.					     M
*                                                                            *
* PARAMETERS:                                                                M
*   PtObjList:   Coerce points/vectors/control points in this list to Type.  M
*   Type:        Minimum space type to coerce to in PtObjList.               M
*                                                                            *
* RETURN VALUE:                                                              M
*   CagdPointType:   The type coercion actualy took place with in PtObjList. M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrCoercePtsListTo, coercion                                        M
*****************************************************************************/
CagdPointType IritPrsrCoercePtsListTo(IPObjectStruct *PtObjList, int Type)
{
    int i;
    IPObjectStruct *TmpObj, *PtObj;
    CagdPointType
	PtType = IritPrsrCoerceCommonSpace(PtObjList, Type);

    if (PtType != CAGD_PT_NONE) {
	for (i = 0; (PtObj = ListObjectGet(PtObjList, i++)) != NULL; ) {
	    if (IP_IS_CTLPT_OBJ(PtObj)) {
		TmpObj = IritPrsrCoerceObjectTo(PtObj, PtType);
		PtObj -> U.CtlPt = TmpObj -> U.CtlPt;
		IPFreeObject(TmpObj);
	    }
	    else if (IP_IS_POINT_OBJ(PtObj) || IP_IS_VEC_OBJ(PtObj)) {
		TmpObj = IritPrsrCoerceObjectTo(PtObj, PtType);
		ReallocNewTypeObject(PtObj, IP_OBJ_CTLPT);
		PtObj -> U.CtlPt = TmpObj -> U.CtlPt;
		IPFreeObject(TmpObj);
	    }
	}
    }

    return PtType;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Coerces an object to a new object.					     M
*   Points, vectors, control points and planes can always be coerced between M
* themselves using this routine by specifying the new object type desired    M
* such as IP_OBJ_PLANE or control point type like CAGD_PT_E4_TYPE. 	     M
*   Control points of curves and surfaces may be coerced to a new type by    M
* prescribing the needed point type as NewType, such as CAGD_PT_P2_TYPE.     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:      Object to coerce.                                             M
*   NewType:   New type which can be object type like IP_OBJ_VECTOR or point M
*              type like E2.						     M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   Newly coerced object.                                M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrCoerceObjectTo, coercion                                         M
*****************************************************************************/
IPObjectStruct *IritPrsrCoerceObjectTo(IPObjectStruct *PObj, int NewType)
{
    int i;
    RealType Pt[CAGD_MAX_PT_SIZE], *R;
    CagdSrfStruct *NewSrf;
    IPObjectStruct
	*NewObj = NULL;

    switch (PObj -> ObjType) {
	case IP_OBJ_POINT:
	case IP_OBJ_VECTOR:
	case IP_OBJ_PLANE:
	case IP_OBJ_CTLPT:
	    Pt[0] = 1.0;
	    for (i = 1; i < CAGD_MAX_PT_SIZE; i++)
	        Pt[i] = 0.0;
	    switch (PObj -> ObjType) {
		case IP_OBJ_POINT:
		    PT_COPY(&Pt[1], PObj -> U.Pt);
		    break;
		case IP_OBJ_VECTOR:
		    PT_COPY(&Pt[1], PObj -> U.Vec);
		    break;
		case IP_OBJ_PLANE:
		    PLANE_COPY(&Pt[1], PObj -> U.Plane);
		    break;
		case IP_OBJ_CTLPT:
		    R = PObj -> U.CtlPt.Coords;
		    CagdCoercePointTo(&Pt[1], CAGD_PT_E5_TYPE,
				      &R, -1, PObj -> U.CtlPt.PtType);
		    break;
		default:
		    break;
	    }

	    switch (NewType) {
		case IP_OBJ_POINT:
		    NewObj = GenPTObject(&Pt[1], &Pt[2], &Pt[3]);
		    break;
		case IP_OBJ_VECTOR:
		    NewObj = GenVECObject(&Pt[1], &Pt[2], &Pt[3]);
		    break;
		case IP_OBJ_PLANE:
		    NewObj = GenPLANEObject(&Pt[1], &Pt[2], &Pt[3], &Pt[4]);
		    break;
		case IP_OBJ_CTLPT:
		    NewObj = GenCTLPTObject(CAGD_PT_E3_TYPE, NULL, Pt);
		    break;
		case CAGD_PT_P1_TYPE:
		case CAGD_PT_P2_TYPE:
		case CAGD_PT_P3_TYPE:
		case CAGD_PT_P4_TYPE:
		case CAGD_PT_P5_TYPE:
		    if (PObj -> ObjType == IP_OBJ_CTLPT)
		        CagdCoercePointTo(Pt, CAGD_PT_P5_TYPE,
					  &R, -1, PObj -> U.CtlPt.PtType);
		case CAGD_PT_E1_TYPE:
		case CAGD_PT_E2_TYPE:
		case CAGD_PT_E3_TYPE:
		case CAGD_PT_E4_TYPE:
		case CAGD_PT_E5_TYPE:
		    NewObj = GenCTLPTObject(NewType, NULL, Pt);
		    break;		    
		default:
		    break;		    
	    }
	    break;
	case IP_OBJ_CURVE:
	    switch (NewType) {
		case CAGD_PT_E1_TYPE:
		case CAGD_PT_E2_TYPE:
		case CAGD_PT_E3_TYPE:
		case CAGD_PT_E4_TYPE:
		case CAGD_PT_E5_TYPE:
		case CAGD_PT_P1_TYPE:
		case CAGD_PT_P2_TYPE:
		case CAGD_PT_P3_TYPE:
		case CAGD_PT_P4_TYPE:
		case CAGD_PT_P5_TYPE:
		    NewObj = GenCRVObject(CagdCoerceCrvTo(PObj -> U.Crvs,
							  NewType));
		    break;
		default:
		    break;
	    }
	    break;
	case IP_OBJ_SURFACE:
	    switch (NewType) {
		case CAGD_PT_E1_TYPE:
		case CAGD_PT_E2_TYPE:
		case CAGD_PT_E3_TYPE:
		case CAGD_PT_E4_TYPE:
		case CAGD_PT_E5_TYPE:
		case CAGD_PT_P1_TYPE:
		case CAGD_PT_P2_TYPE:
		case CAGD_PT_P3_TYPE:
		case CAGD_PT_P4_TYPE:
		case CAGD_PT_P5_TYPE:
		    NewObj = GenSRFObject(CagdCoerceSrfTo(PObj -> U.Srfs,
							  NewType));
		    break;
		default:
		    break;
	    }
	    break;
	case IP_OBJ_TRIVAR:
	    switch (NewType) {
		case CAGD_PT_E1_TYPE:
		case CAGD_PT_E2_TYPE:
		case CAGD_PT_E3_TYPE:
		case CAGD_PT_E4_TYPE:
		case CAGD_PT_E5_TYPE:
		case CAGD_PT_P1_TYPE:
		case CAGD_PT_P2_TYPE:
		case CAGD_PT_P3_TYPE:
		case CAGD_PT_P4_TYPE:
		case CAGD_PT_P5_TYPE:
		    NewObj = GenTRIVARObject(TrivTVCopyList(PObj -> U.Trivars));
		    NewSrf = CagdCoerceSrfTo(NewObj -> U.TrimSrfs -> Srf,
					     NewType);
		    CagdSrfFreeList(NewObj -> U.TrimSrfs -> Srf);
		    NewObj -> U.TrimSrfs -> Srf = NewSrf;
		    break;
		default:
		    break;
	    }
	    break;
	case IP_OBJ_TRIMSRF:
	    switch (NewType) {
		case CAGD_PT_E1_TYPE:
		case CAGD_PT_E2_TYPE:
		case CAGD_PT_E3_TYPE:
		case CAGD_PT_E4_TYPE:
		case CAGD_PT_E5_TYPE:
		case CAGD_PT_P1_TYPE:
		case CAGD_PT_P2_TYPE:
		case CAGD_PT_P3_TYPE:
		case CAGD_PT_P4_TYPE:
		case CAGD_PT_P5_TYPE:
		    NewObj = GenTRIMSRFObject(TrimSrfNew(
			CagdCoerceSrfTo(PObj -> U.TrimSrfs -> Srf, NewType),
			TrimCrvCopyList(PObj -> U.TrimSrfs -> TrimCrvList),
			TRUE));
		    break;
		default:
		    break;
	    }
	    break;
	default:
	    break;
    }

    return NewObj;
}
