/*****************************************************************************
*   Animation module - machine independent part. 			     *
******************************************************************************
* (C) Gershon Elber, Technion, Israel Institute of Technology                *
******************************************************************************
*							Ver 0.1, Feb. 1995.  *
*****************************************************************************/

#ifdef USE_VARARGS
#include <varargs.h>
#else
#include <stdarg.h>
#endif /* USE_VARARGS */
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <string.h>
#include "irit_sm.h"
#include "iritprsr.h"
#include "attribut.h"
#include "allocate.h"
#include "geomat3d.h"
#include "iritgrap.h"
#include "animate.h"

static int
    FileSaveHandler = 0;

static CagdRType *EvalCurveObject(IPObjectStruct *CrvObj, RealType t);
static int IsAnimation(IPObjectStruct *PObjs, int Depth);
static void AnimFindAnimationTimeAux(AnimationStruct *Anim,
				     IPObjectStruct *PObjs);
static void ExecuteAnimation(AnimationStruct *Anim, IPObjectStruct *PObjs);
static void ExecuteAnimationAux(AnimationStruct *Anim, 
				IPObjectStruct *PObjs,
				MatrixType ObjsMat);
static void ExecuteAnimationAux2(AnimationStruct *Anim, 
				 IPObjectStruct *PObjs,
				 MatrixType ObjMat);
static void DumpOneTraversedObject(IPObjectStruct *PObj, MatrixType Mat);

/*****************************************************************************
* DESCRIPTION:								     M
*   Resets the slots of an animation structure.                              M
*									     *
* PARAMETERS:								     M
*   Anim:      The animation state to reset.				     M
*									     *
* RETURN VALUE:								     M
*   void								     M
*                                                                            *
* KEYWORDS:                                                                  M
*   AnimResetAnimStruct, animation                                           M
*****************************************************************************/
void AnimResetAnimStruct(AnimationStruct *Anim)
{
    Anim -> StartT = 0.0;
    Anim -> FinalT = 1.0;
    Anim -> Dt = 0.01;
    Anim -> RunTime = 0.0;
    Anim -> TwoWaysAnimation = FALSE;
    Anim -> SaveAnimation = FALSE;
    Anim -> BackToOrigin = FALSE;
    Anim -> NumOfRepeat = 1;
    Anim -> StopAnim = FALSE;
    Anim -> SingleStep = FALSE;
    Anim -> TextInterface = TRUE;
    Anim -> MiliSecSleep = 30;
    Anim -> ExecEachStep = NULL;
    strcpy(Anim -> BaseFileName, ANIM_DEFAULT_ANIM_FILE_NAME);
}

/*****************************************************************************
* DESCRIPTION:								     M
*   Getting input parameters of animation from user using textual user       M
* interface.								     M
*									     *
* PARAMETERS:								     M
*   Anim:      The animation state to update.				     M
*									     *
* RETURN VALUE:								     M
*   void								     M
*                                                                            *
* KEYWORDS:                                                                  M
*   AnimGenAnimInfoText, animation                                           M
*****************************************************************************/
void AnimGetAnimInfoText(AnimationStruct *Anim)
{
    char Line[LINE_LEN];

#ifdef IRIT_DOUBLE
#    define REAL_FRMT "%lf"
#else
#    define REAL_FRMT "%f"
#endif /* IRIT_DOUBLE */
    do {
	printf("Start time [%f] : ", Anim -> StartT);
	gets(Line);
    }
    while (strlen(Line) > 0 &&
	   sscanf(Line, REAL_FRMT, &Anim -> StartT) != 1);

    do {
	printf("Final time [%f] : ", Anim -> FinalT);
	gets(Line);
    }
    while (strlen(Line) > 0 &&
	   sscanf(Line, REAL_FRMT, &Anim -> FinalT) != 1);

    do {
	printf("Interval of time [%f] : ", Anim -> Dt);
	gets(Line);
    }
    while (strlen(Line) > 0 &&
	   sscanf(Line, REAL_FRMT, &Anim -> Dt) != 1);

    printf("\nSpecial Commands (y/n) [n] : ");
    gets(Line);
    if (Line[0] != 'y' && Line[0] != 'Y') {
    	Anim -> TwoWaysAnimation = FALSE;
    	Anim -> SaveAnimation = FALSE;
	Anim -> BackToOrigin = FALSE;
    	Anim -> NumOfRepeat = 1;
    	Anim -> MiliSecSleep = 30;
    	return;
    }

    printf("Bounce Animation (y/n) [n] : ");
    gets(Line);
    if (Line[0] == 'y' || Line[0] == 'Y') {
      	Anim -> TwoWaysAnimation = TRUE;
	Anim -> BackToOrigin = FALSE;
    }
    else {
      	Anim -> TwoWaysAnimation = FALSE;
	printf("Back to origin (y/n) [n] : ");
	gets(Line);
	if (Line[0] == 'y' || Line[0] == 'Y')
	    Anim -> BackToOrigin = TRUE;
	else
	    Anim -> BackToOrigin = FALSE;
    }

    do {
	printf("Number of Repeatitions [%d] : ", Anim -> NumOfRepeat);
	gets(Line);
    }
    while (strlen(Line) > 0 && sscanf(Line, "%d", &Anim -> NumOfRepeat) != 1);
    Anim -> NumOfRepeat = MAX(Anim -> NumOfRepeat, 1);

    do {
	printf("Mili Seconds' Sleep [%d] : ", Anim -> MiliSecSleep);
	gets(Line);
    }
    while (strlen(Line) > 0 && sscanf(Line, "%d", &Anim -> MiliSecSleep) != 1);
    Anim -> MiliSecSleep = BOUND(Anim -> MiliSecSleep, 0, 10000);

    printf("Save iterations into data files (y/n) [n] : ");
    gets(Line);
    if (Line[0] == 'y' || Line[0] == 'Y') {
    	Anim -> SaveAnimation = TRUE; 
	do {
	    printf("Base name of data files : ");
	    gets(Line);
	}
	while (strlen(Line) > 0 &&
	       sscanf(Line, "%s", Anim -> BaseFileName) != 1 &&
	       strlen(Anim -> BaseFileName) <= 0);
    } 
    else
	Anim -> SaveAnimation = FALSE;
}

/*****************************************************************************
* DESCRIPTION:							     	     *
*   Evaluate curve in specified parameter value and project it to Euclidean  *
* space, if necessary.							     *
*									     *
* PARAMETERS:								     *
*   CrvObj:	a pointer to the curve's object.			     *
*   t:		the parameter of time to calculate the value of the curve.   * 
*									     *
* RETURN VALUE:								     *
*   CagdRType *: The value(s) of the curve at t.			     *
*****************************************************************************/
static CagdRType *EvalCurveObject(IPObjectStruct *CrvObj, RealType t)
{
    int i;
    CagdRType
	*Pt = CagdCrvEval(CrvObj -> U.Crvs, t);

    switch (CrvObj -> U.Crvs -> PType)  {
        case CAGD_PT_E1_TYPE:
        case CAGD_PT_E3_TYPE:
             break;
        case CAGD_PT_P1_TYPE:
	     Pt[1] /= Pt[0];
	     break;
        case CAGD_PT_P3_TYPE:
             for (i = 1; i <= 3; i++)
	          Pt[i] /= Pt[0];
	     break;
         default:
	     break;
    }
    return Pt;
}

/*****************************************************************************
* DESCRIPTION:								     *
*   Scan the given geometry for possible animation attributes.		     *
*									     *
* PARAMETERS:								     *
*   PObjs:      Objects to scan for animation attributes.		     *
*   Depth:	Of object hierarchy.					     *
*									     *
* RETURN VALUE:								     *
*   int:	TRUE if there are animation attributes. FALSE otherwise.     *
*****************************************************************************/
static int IsAnimation(IPObjectStruct *PObjs, int Depth)
{
    IPObjectStruct *PObj;

    for (PObj = PObjs; PObj != NULL; PObj = PObj -> Pnext) {
	if (IP_IS_OLST_OBJ(PObj)) {
	    int i = 0;
	    IPObjectStruct *PTmp;

	    while ((PTmp = ListObjectGet(PObj, i++)) != NULL) {
		if (IsAnimation(PTmp, Depth + 1))
		    return TRUE;
	    }
 
	}
	else if (AttrGetObjectObjAttrib(PObj, "animation") != NULL)
	    return TRUE;

	/* No linked list in inner levels. */
	if (Depth > 0)
	    break;
    }

    return FALSE;   
}

/*****************************************************************************
* DESCRIPTION:								     M
*   Computes the time span for which the animation executes.		     M
*									     *
* PARAMETERS:								     M
*   Anim:	Animation structure to update.				     M
*   PObjs:      Objects to scan for animation attributes.		     M
*									     *
* RETURN VALUE:								     M
*   void								     M
*                                                                            *
* KEYWORDS:                                                                  M
*   AnimFindAnimationTime, animation                                         M
*****************************************************************************/
void AnimFindAnimationTime(AnimationStruct *Anim, IPObjectStruct *PObjs)
{
    IPObjectStruct *PObj;

    if (!IsAnimation(PObjs, 0))
	return;

    Anim -> RunTime = Anim -> StartT = IRIT_INFNTY;
    Anim -> FinalT = -IRIT_INFNTY;

    for (PObj = PObjs; PObj != NULL; PObj = PObj -> Pnext)
	AnimFindAnimationTimeAux(Anim, PObj);
}

/*****************************************************************************
* DESCRIPTION:								     *
*   Auxiliary function of AnimFindAnimationTime				     *
*****************************************************************************/
static void AnimFindAnimationTimeAux(AnimationStruct *Anim,
				     IPObjectStruct *PObj)
{
    IPObjectStruct *ObjPtr, *AnimationP;
    RealType T1, T2, StartT, FinalT;

    StartT = IRIT_INFNTY;
    FinalT = -IRIT_INFNTY;

    if (IP_IS_OLST_OBJ(PObj)) {
	int i = 0;
	IPObjectStruct *PTmp;

	while ((PTmp = ListObjectGet(PObj, i++)) != NULL)
	    AnimFindAnimationTimeAux(Anim, PTmp);
    }
    else if ((AnimationP = AttrGetObjectObjAttrib(PObj, "animation")) != NULL) {
	int i = 0;

	if (IP_IS_OLST_OBJ(AnimationP)) {
	    while ((ObjPtr = ListObjectGet(AnimationP, i++)) != NULL) {
		if (IP_IS_CRV_OBJ(ObjPtr)) {
		    CagdCrvDomain(ObjPtr -> U.Crvs, &T1, &T2);
		    StartT = MIN(StartT, T1);
		    FinalT = MAX(FinalT, T2);
		}
	    }
	}
	else if (IP_IS_CRV_OBJ(AnimationP)) {
	    CagdCrvDomain(AnimationP -> U.Crvs, &T1, &T2);
	    StartT = MIN(StartT, T1);
	    FinalT = MAX(FinalT, T2);
	}
    }

    if (StartT < Anim -> StartT)
	Anim -> RunTime = Anim -> StartT = StartT;
    if (FinalT > Anim -> FinalT)
	Anim -> FinalT = FinalT;
}

/*****************************************************************************
* DESCRIPTION:								     *
*   Executes one time step of the animation. 				     *
*									     *
* PARAMETERS:								     *
*   Anim:	Animation structure.					     *
*   PObjs:	Objects to render.					     *
*									     *
* RETURN VALUE:								     *
*   void 								     *
*****************************************************************************/
static void ExecuteAnimation(AnimationStruct *Anim, IPObjectStruct *PObjs)
{
    IPObjectStruct *PObj;
    MatrixType ObjsMat, ObjMat;

    if (Anim -> TextInterface) {
	printf("\b\b\b\b\b\b\b%7.3f", Anim -> RunTime);
	fflush(stdout);
    }
    MatGenUnitMat(ObjsMat);
    
    for (PObj = PObjs ; PObj != NULL && !Anim -> StopAnim ; 
	 PObj = PObj -> Pnext) { 
	GEN_COPY(ObjMat, ObjsMat, sizeof(MatrixType));
	ExecuteAnimationAux(Anim, PObj,ObjMat);
	if (AnimCheckInterrupt(Anim))
	    break;      
    }
    
    IGRedrawViewWindow();
    IritSleep(Anim -> MiliSecSleep);
}

/*****************************************************************************
* DESCRIPTION:								     *
*   Auxiliary function of ExecuteAnimation. Traverses the hierarchical       *
* object structure of the data.						     *
*****************************************************************************/
static void ExecuteAnimationAux(AnimationStruct *Anim, 
				IPObjectStruct *PObj,
				MatrixType ObjsMat)
{
    IPObjectStruct *ObjPtr;
    
    ExecuteAnimationAux2(Anim, PObj, ObjsMat);
    
    if (IP_IS_OLST_OBJ(PObj)) {
	int i = 0;
	IPObjectStruct *PTmp;
	MatrixType ObjMat;

	while ((PTmp = ListObjectGet(PObj, i++)) != NULL) {
	    GEN_COPY(ObjMat, ObjsMat, sizeof(MatrixType));
	    ExecuteAnimationAux(Anim, PTmp, ObjMat);
	}
    }
    else {
	ObjPtr = GenMatObject("transform", ObjsMat, NULL);
	AttrSetObjAttrib(&PObj -> Attrs, "_animation_mat", ObjPtr, FALSE);
    }
}

/*****************************************************************************
* DESCRIPTION:								     *
*   Auxiliary function of ExecuteAnimation. Processes a linked list of       *
* objects.								     *
*****************************************************************************/
static void ExecuteAnimationAux2(AnimationStruct *Anim, 
				 IPObjectStruct *PObjs,
				 MatrixType ObjMat)
{
    IPObjectStruct *AnimationP, *PObj;
    MatrixType Mat;
    int i, NeedToMult;

    if ((AnimationP = AttrGetObjAttrib(PObjs -> Attrs, "animation"))
	                                                    != NULL) {
	AttrSetObjectIntAttrib(PObjs, "_isvisible", TRUE);
	
	i = 0;
	while (IP_IS_OLST_OBJ(AnimationP) ?
	       (PObj = ListObjectGet(AnimationP, i++)) != NULL :
	       (PObj = (i++ == 0 ? AnimationP : NULL)) != NULL) {
	    CagdRType *CurveResult, CurveRes, TMin, TMax, t;
	    char
	        *Name = PObj -> Name; 
	    
	    NeedToMult = TRUE;
	    switch (PObj -> ObjType) {
		case IP_OBJ_MATRIX:
		    GEN_COPY(Mat, *PObj -> U.Mat, sizeof(MatrixType)); 
		    break;
		case IP_OBJ_CURVE:
		    CagdCrvDomain(PObj -> U.Crvs, &TMin, &TMax);

		    MatGenUnitMat(Mat);

		    t = Anim -> RunTime;
		    if (t < TMin)
		        t = TMin;
		    else if (t > TMax)
		        t = TMax;
		    
		    CurveResult = EvalCurveObject(PObj, t); 
		    CurveRes = CurveResult[1];
		    if (strnicmp(Name, "scl", 3) == 0) {
			if (strnicmp(Name, "scl_x", 5) == 0)
			    MatGenMatScale(CurveRes, 1, 1, Mat);
			else if (strnicmp(Name, "scl_y", 5) == 0)
			    MatGenMatScale(1, CurveRes, 1, Mat);
			else if (strnicmp(Name, "scl_z", 5) == 0)
			    MatGenMatScale(1, 1, CurveRes, Mat);
			else
			    MatGenMatUnifScale(CurveRes, Mat);
		    }
		    else if (strnicmp(Name, "rot", 3) == 0) {
			if (strnicmp(Name, "rot_x", 5) == 0)
			    MatGenMatRotX1(-DEG2RAD(CurveRes), Mat );
			else if (strnicmp(Name, "rot_y", 5) == 0)
			    MatGenMatRotY1(-DEG2RAD(CurveRes), Mat);
			else if (strnicmp(Name, "rot_z", 5) == 0)
			    MatGenMatRotZ1(-DEG2RAD(CurveRes), Mat);
		    }
		    else if (strnicmp(Name, "mov", 3) == 0) {
			if (strnicmp(Name, "mov_xyz", 7) == 0)
			    MatGenMatTrans(CurveResult[1],
					   CurveResult[2],
					   CurveResult[3],
					   Mat);
			else if (strnicmp(Name, "mov_x", 5) == 0)
			    MatGenMatTrans(CurveRes, 0, 0, Mat);
			else if (strnicmp(Name, "mov_y", 5) == 0)
			    MatGenMatTrans(0, CurveRes, 0, Mat);
			else if (strnicmp(Name, "mov_z", 5) == 0)
			    MatGenMatTrans(0, 0, CurveRes, Mat);
		    }
		    else if (strnicmp(Name, "visible", 7) == 0) { 
			NeedToMult = FALSE;
			if (CurveRes < 0)
			    AttrSetObjectIntAttrib(PObjs, "_isvisible", FALSE);
		    }
		    else {
			fprintf(stderr,
				"Animation curve named \"%s\" is unknown and ignored\n",
				Name);
		    }
		break;
                    default:
		    NeedToMult = FALSE;
		    fprintf(stderr,
			    "Only matrices and curves are supported in animation, name = \"%s\"\n",
			    Name);
		    break;
	    }

	    if (NeedToMult)
	        MatMultTwo4by4(ObjMat, ObjMat, Mat);
	}
    }
    
#ifdef SAVE_MATRIX_IN_INTERNAL_NODES
    ObjPtr = GenMatObject("transform", ObjMat, NULL);
    AttrSetObjAttrib(&PObjs -> Attrs, "_animation_mat", ObjPtr, FALSE);
#endif /* SAVE_MATRIX_IN_INTERNAL_NODES */
}

/*****************************************************************************
* DESCRIPTION:								     M
*   Routine to run a sequence of objects through an animation according to   M
* animation attributes of matrices and curves that are attached to them.     M
* 									     *
* PARAMETERS:								     M
*   Anim:	Animation structure.					     M
*   PObjs:	Objects to render.					     M
*									     *
* RETURN VALUE:								     M
*   void.								     M
*                                                                            *
* SEE ALSO:                                                                  M
*   AnimEvalAnimation                                                        M
*									     *
* KEYWORDS:								     M
*   AnimDoAnimation, animation						     M
*****************************************************************************/
void AnimDoAnimation(AnimationStruct *Anim, IPObjectStruct *PObjs)
{ 
    int Loops;

    Anim -> StopAnim = FALSE;

    if (!IsAnimation(PObjs, 0)) {     /* Checking if there is any animation */ 
        fprintf(stderr, "No animation attributes were found.\n");
        return;
    }

    if (Anim -> TextInterface) {
	printf("Animate from %f to %f step %f\n",
	       Anim -> StartT, Anim -> FinalT, Anim -> Dt);
	printf("\nAnimation time:        ");
    }

    Anim -> _Count = 1;
    for (Loops = 1; Loops <= Anim -> NumOfRepeat; Loops++) {
        for (Anim -> RunTime = Anim -> StartT;
	     Anim -> RunTime <= Anim -> FinalT + IRIT_EPS &&
	     !Anim -> StopAnim;
	     Anim -> RunTime += Anim -> Dt) {
    	    ExecuteAnimation(Anim, PObjs);
	    if (Loops == 1) {
		if (Anim -> SaveAnimation)
		    AnimSaveIterationsToFiles(Anim, PObjs);
		if (Anim -> ExecEachStep != NULL) {
		    char Line[LINE_LEN];

		    sprintf(Line, "%s %d",
			    Anim -> ExecEachStep, Anim -> _Count++);
		    system(Line);
		}
	    }
	}

    	if (Anim -> TwoWaysAnimation) 
	    for (Anim -> RunTime = Anim -> FinalT;
		 Anim -> RunTime >= Anim -> StartT - IRIT_EPS &&
		 !Anim -> StopAnim;
		 Anim -> RunTime -= Anim -> Dt)
 	    	ExecuteAnimation(Anim, PObjs);
    }

    if (Anim -> BackToOrigin && !APX_EQ(Anim -> RunTime, Anim -> StartT)) {
	Anim -> RunTime = Anim -> StartT;
	ExecuteAnimation(Anim, PObjs);
    }

    if (Anim -> TextInterface) {
	printf("\n\nAnimation is done.\n");
	fflush(stdout);
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Evaluate the animation curves at the given time, setting the proper      M
* animation attributes ("_animation_mat" and "_isvisible"), in place.        M
*                                                                            *
* PARAMETERS:                                                                M
*   t:         Time to evaluate the animation at.                            M
*   PObjs:     To evaluate their animation curves.                           M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* SEE ALSO:                                                                  M
*   AnimDoAnimation                                                          M
*                                                                            *
* KEYWORDS:                                                                  M
*   AnimEvalAnimation, animation                                             M
*****************************************************************************/
void AnimEvalAnimation(RealType t, IPObjectStruct *PObjs)
{
    AnimationStruct Anim;

    AnimResetAnimStruct(&Anim);

    Anim.StartT = t;
    Anim.FinalT = t + IRIT_EPS;
    Anim.Dt = t + 1.0;
    Anim.RunTime = t;
    Anim.TextInterface = FALSE;

    ExecuteAnimation(&Anim, PObjs);
}

/*****************************************************************************
* DESCRIPTION:								     M
*   Routine to exectue a single step the animation, at current time.         M
* 									     *
* PARAMETERS:								     M
*   Anim:	Animation structure.					     M
*   PObjs:	Objects to render.					     M
*									     *
* RETURN VALUE:								     M
*   void.								     M
*									     *
* KEYWORDS:								     M
*   AnimDoSingleStep, animation						     M
*****************************************************************************/
void AnimDoSingleStep(AnimationStruct *Anim, IPObjectStruct *PObjs)
{ 
    Anim -> StopAnim = FALSE;

    if (!IsAnimation(PObjs, 0)) {     /* Checking if there is any animation */ 
        fprintf(stderr, "No animation attributes were found.\n");
        return;
    }

    ExecuteAnimation(Anim, PObjs);

    if (Anim -> SaveAnimation)
        AnimSaveIterationsToFiles(Anim, PObjs);
    if (Anim -> ExecEachStep != NULL) {
	char Line[LINE_LEN];

	sprintf(Line, "%s %d",
		Anim -> ExecEachStep, Anim -> _Count++);
	system(Line);
    }
}

/*****************************************************************************
* DESCRIPTION:								     M
*   Saves one iteration of the animation sequence as IRIT data (*.dat).      M
*   The objects that are saved are those that are visibled on the current    M
* time frame as set via current animation_mat attribute.	             M
*									     *
* PARAMETERS:								     M
*   Anim:	Animation structure.					     M
*   PObjs:	Objects to render.					     M
*									     *
* RETURN VALUE:								     M
*   void								     M
*									     *
* KEYWORDS:								     M
*   AnimSaveIterationsToFiles, animation				     M
*****************************************************************************/
void AnimSaveIterationsToFiles(AnimationStruct *Anim, IPObjectStruct *PObjs)
{
    IPObjectStruct *PObj;
    char FileName[LINE_LEN];
    MatrixType ViewMat;

    sprintf(FileName, "%s%03d.dat", Anim -> BaseFileName, Anim -> _Count++);
    FileSaveHandler = IritPrsrOpenDataFile(FileName, FALSE, TRUE);

    MatGenUnitMat(ViewMat);
    IPTraverseObjListHierarchy(PObjs, ViewMat, DumpOneTraversedObject);

    PObj = GenMatObject("view_mat", IritPrsrViewMat, NULL);
    IritPrsrPutObjectToHandler(FileSaveHandler, PObj);
    IPFreeObject(PObj);
    if (IGGlblViewMode == IG_VIEW_PERSPECTIVE) {
	PObj = GenMatObject("prsp_mat", IritPrsrPrspMat, NULL);
	IritPrsrPutObjectToHandler(FileSaveHandler, PObj);
    	IPFreeObject(PObj);
    }

    IritPrsrCloseStream(FileSaveHandler, TRUE); 
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Call back function of IPTraverseObjListHierarchy. Called on every non    *
* list object found in hierarchy.                                            *
*                                                                            *
* PARAMETERS:                                                                *
*   PObj:       Non list object to handle.                                   *
*   Mat:        Transformation matrix to apply to this object.               *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void DumpOneTraversedObject(IPObjectStruct *PObj, MatrixType Mat)
{
    IPObjectStruct *CopyObj, *CopyTObj;

    CopyObj = CopyObject(NULL, PObj, TRUE);
    AttrFreeOneAttribute(&CopyObj -> Attrs, "animation");
    CopyObj -> Pnext = NULL;
    CopyTObj = GMTransformObject(CopyObj, Mat);

    IritPrsrPutObjectToHandler(FileSaveHandler, CopyTObj);

    IPFreeObject(CopyObj);
    IPFreeObject(CopyTObj);
}
