/*****************************************************************************
*   Dynamic allocation module of "Irit" - the 3d polygonal solid modeller.   *
*									     *
* Written by:  Gershon Elber				Ver 0.2, Mar. 1990   *
*****************************************************************************/

#include <stdio.h>
#include <string.h>
#include "irit_sm.h"
#include "iritprsr.h"
#include "allocate.h"
#include "attribut.h"
#include "imalloc.h"

/* #define DEBUG1		    Print more messages in free/allocating. */

#define ALLOCATE_NUM	100	   /* Number of objects to allocate at once. */

typedef enum {
    ALLOC_OTHER,
    ALLOC_VERTEX,
    ALLOC_POLYGON,
    ALLOC_OBJECT
} AllocateStructType;

/* Used for fast reallocation of most common object types: */
static IPVertexStruct
    *VertexFreedList = NULL;
static IPPolygonStruct
    *PolygonFreedList = NULL;
static IPObjectStruct
    *ObjectFreedList = NULL;

static void IPFreeObjectSlots(IPObjectStruct *PObj);
static void ListObjectRealloc(IPObjectStruct *PObj);
static void IPMallocObjectSlots(IPObjectStruct *PObj);

/*****************************************************************************
* Routine to free the slots of a given object.				     *
*****************************************************************************/
static void IPFreeObjectSlots(IPObjectStruct *PObj)
{
    int Index;
    char Line[LINE_LEN];
    IPObjectStruct *PObjTmp;

    if (PObj == NULL)
	return;

    switch (PObj -> ObjType) {
	case IP_OBJ_UNDEF:
	    break;
	case IP_OBJ_POLY:		   /* Free the polygon list. */
	    IPFreePolygonList(PObj -> U.Pl);
	    break;
	case IP_OBJ_NUMERIC:
	case IP_OBJ_POINT:
	case IP_OBJ_VECTOR:
	case IP_OBJ_PLANE:
	case IP_OBJ_CTLPT:
	    break;
	case IP_OBJ_MATRIX:
	    IritFree((VoidPtr) PObj -> U.Mat);
	    break;
	case IP_OBJ_STRING:
	    IritFree((VoidPtr) PObj -> U.Str);
	    break;
	case IP_OBJ_LIST_OBJ: /* Need to dereference list elements. */
	    for (Index = 0;
		 (PObjTmp = ListObjectGet(PObj, Index)) != NULL;
		 Index++) {
		if (PObjTmp -> Count-- == 1)
		    IPFreeObject(PObjTmp);
	    }
	    IritFree((VoidPtr) PObj -> U.Lst.PObjList);
	    break;
	case IP_OBJ_CURVE:
	    CagdCrvFreeList(PObj -> U.Crvs);
	    break;
	case IP_OBJ_SURFACE:
	    CagdSrfFreeList(PObj -> U.Srfs);
	    break;
	default:  	   /* Kill the program - something is WRONG! */
	    sprintf(Line,
		    "IPFree: Attempt to free undefined Object type %d",
						PObj -> ObjType);
	    IritPrsrFatalError(Line);
	    break;
    }
}

/*****************************************************************************
* Allocate one Vertex Structure:					     *
*****************************************************************************/
IPVertexStruct *IPAllocVertex(ByteType Count, ByteType Tags,
			      IPPolygonStruct *PAdj, IPVertexStruct *Pnext)
{
    IPVertexStruct *p;

    if (VertexFreedList != NULL) {
	p = VertexFreedList;
	VertexFreedList = VertexFreedList -> Pnext;
    }
    else {
	int i;
	IPVertexStruct *V;

	/* Allocate ALLOCATE_NUM objects, returns first one as new   */
	/* and chain together the rest of them into the free list.   */
	if ((V = (IPVertexStruct *) IritMalloc(sizeof(IPVertexStruct)
					       * ALLOCATE_NUM)) != NULL) {
	    for (i = 1; i < ALLOCATE_NUM - 1; i++)
		V[i].Pnext = &V[i+1];
	    V[ALLOCATE_NUM-1].Pnext = NULL;
	    VertexFreedList = &V[1];
	}
	p = V;
    }

    ZAP_MEM(p, sizeof(IPVertexStruct));
    p -> Count = Count;
    p -> Tags = Tags;
    p -> PAdj = PAdj;
    p -> Pnext = Pnext;

    AttrResetAttributes(&p -> Attrs);		   /* Initialize attributes. */

    return p;
}

/*****************************************************************************
* Allocate one Polygon Structure:					     *
*****************************************************************************/
IPPolygonStruct *IPAllocPolygon(ByteType Count, ByteType Tags,
				IPVertexStruct *V, IPPolygonStruct *Pnext)
{
    IPPolygonStruct *p;

    if (PolygonFreedList != NULL) {
	p = PolygonFreedList;
	PolygonFreedList = PolygonFreedList -> Pnext;
    }
    else {
	int i;
	IPPolygonStruct *P;

	/* Allocate ALLOCATE_NUM objects, returns first one as new   */
	/* and chain together the rest of them into the free list.   */
	if ((P = (IPPolygonStruct *) IritMalloc(sizeof(IPPolygonStruct)
					        * ALLOCATE_NUM)) != NULL) {
	    for (i = 1; i < ALLOCATE_NUM - 1; i++)
		P[i].Pnext = &P[i+1];
	    P[ALLOCATE_NUM-1].Pnext = NULL;
	    PolygonFreedList = &P[1];
	}
	p = P;
    }

    ZAP_MEM(p, sizeof(IPPolygonStruct));
    p -> Count = Count;
    p -> Tags = Tags;
    p -> PVertex = V;
    p -> Pnext = Pnext;

    AttrResetAttributes(&p -> Attrs);		   /* Initialize attributes. */

    return p;
}

/*****************************************************************************
* Allocate one Object Structure:					     *
*****************************************************************************/
IPObjectStruct *IPAllocObject(char *Name, IPObjStructType ObjType,
						 	IPObjectStruct *Pnext)
{
    IPObjectStruct *p;

    if (ObjectFreedList != NULL) {
	p = ObjectFreedList;
	ObjectFreedList = ObjectFreedList -> Pnext;
    }
    else {
	int i;
	IPObjectStruct *O;

	/* Allocate ALLOCATE_NUM objects, returns first one as new   */
	/* and chain together the rest of them into the free list.   */
	if ((O = (IPObjectStruct *) IritMalloc(sizeof(IPObjectStruct)
					       * ALLOCATE_NUM)) != NULL) {
	    for (i = 1; i < ALLOCATE_NUM - 1; i++)
		O[i].Pnext = &O[i+1];
	    O[ALLOCATE_NUM - 1].Pnext = NULL;
	    ObjectFreedList = &O[1];
	}
	p = O;
    }

    ZAP_MEM(p, sizeof(IPObjectStruct));
    strcpy(p -> Name, Name);
    p -> ObjType = ObjType;
    p -> Count = 1;
    p -> Pnext = Pnext;

    IPMallocObjectSlots(p);
    AttrResetAttributes(&p -> Attrs);		   /* Initialize attributes. */

    return p;
}

/*****************************************************************************
* Free one Vertex Structure:						     *
*****************************************************************************/
void IPFreeVertex(IPVertexStruct *V)
{
    if (V != NULL) {
	V ->Pnext = NULL;
	IPFreeVertexList(V);
    }
}

/*****************************************************************************
* Free one Polygon Structure:						     *
*****************************************************************************/
void IPFreePolygon(IPPolygonStruct *P)
{
    if (P != NULL) {
	P -> Pnext = NULL;
	IPFreePolygonList(P);
    }
}

/*****************************************************************************
* Free one Object Structure:						     *
*****************************************************************************/
void IPFreeObject(IPObjectStruct *O)
{
    if (O != NULL) {
	if (O -> Count > 1) {
	    /* Do not free object - just decrease its reference count. */
	    O -> Count--;
	}
	else {
	    AttrFreeAttributes(&O -> Attrs);
	    IPFreeObjectSlots(O);

	    /* Add it to global freed object list: */
	    O -> Pnext = ObjectFreedList;
	    ObjectFreedList = O;
	}
    }
}

/*****************************************************************************
* Free a (circular) Vertex Structure list.				     *
*****************************************************************************/
void IPFreeVertexList(IPVertexStruct *VFirst)
{
    if (VFirst != NULL) {
	IPVertexStruct *Vtemp,
	    *V = VFirst;

	/* Handle both circular or NULL terminated. */
	do {
	    Vtemp = V;
	    V = V -> Pnext;
	}
	while (V != NULL && V != VFirst);

	/* Now chain this new list to the global freed vertex list: */
	Vtemp -> Pnext = VertexFreedList;
	VertexFreedList = VFirst;
    }
}

/*****************************************************************************
* Free a Polygon Structure list.					     *
*****************************************************************************/
void IPFreePolygonList(IPPolygonStruct *PFirst)
{
    if (PFirst != NULL) {
	IPPolygonStruct
	    *Ptemp = NULL,
	    *P = PFirst;

	while (P != NULL) {
	    IPFreeVertexList(P -> PVertex);

	    Ptemp = P;
	    P = P -> Pnext;
	}

	/* Now chain this new list to the global freed polygon list: */
	Ptemp -> Pnext = PolygonFreedList;
	PolygonFreedList = PFirst;
    }
}

/*****************************************************************************
* Free an Object Structure list.					     *
*****************************************************************************/
void IPFreeObjectList(IPObjectStruct *OFirst)
{
    while (OFirst != NULL) {
	IPObjectStruct
	    *NextO = OFirst -> Pnext;

	IPFreeObject(OFirst);
	OFirst = NextO;
    }
}

/*****************************************************************************
* Returns the length of a list, given a list object.			     *
*****************************************************************************/
int ListObjectLength(IPObjectStruct *PObj)
{
    int i;
    IPObjectStruct **PObjList;

    if (!IP_IS_OLST_OBJ(PObj))
	IritPrsrFatalError("List object expected");

    for (i = 0, PObjList = PObj -> U.Lst.PObjList; *PObjList++ != NULL; i++)
	if (i >= PObj -> U.Lst.ListMaxLen)
	    break;

    return i;
}

/*****************************************************************************
* Insert an object at index Index into list object.			     *
*****************************************************************************/
void ListObjectInsert(IPObjectStruct *PObj, int Index,
						     IPObjectStruct *PObjItem)
{
    if (!IP_IS_OLST_OBJ(PObj))
	IritPrsrFatalError("List object expected");

    if (PObj -> U.Lst.ListMaxLen <= Index)
	ListObjectRealloc(PObj);

    PObj -> U.Lst.PObjList[Index] = PObjItem;
}

/*****************************************************************************
* Returns the Index object in list of PObjList object.			     *
*****************************************************************************/
IPObjectStruct *ListObjectGet(IPObjectStruct *PObj, int Index)
{
    if (!IP_IS_OLST_OBJ(PObj))
	IritPrsrFatalError("List object expected");

    return PObj -> U.Lst.ListMaxLen > Index ? PObj -> U.Lst.PObjList[Index]
					    : NULL;
}

/*****************************************************************************
* Reallocate a list Object to twice the list size:			     *
*****************************************************************************/
static void ListObjectRealloc(IPObjectStruct *PObj)
{
    IPObjectStruct
	**PObjList = IritMalloc(sizeof(IPObjectStruct *) *
				PObj -> U.Lst.ListMaxLen * 2);

    GEN_COPY(PObjList, PObj -> U.Lst.PObjList,
	     PObj -> U.Lst.ListMaxLen * sizeof(IPObjectStruct *));
    PObj -> U.Lst.ListMaxLen *= 2;
    IritFree((VoidPtr) PObj -> U.Lst.PObjList);
    PObj -> U.Lst.PObjList = PObjList;
}

/*****************************************************************************
* Routine to (m)allocate the slots of a given object.			     *
*****************************************************************************/
static void IPMallocObjectSlots(IPObjectStruct *PObj)
{
    switch (PObj -> ObjType) {
	case IP_OBJ_MATRIX:
	    PObj -> U.Mat = (MatrixType *) IritMalloc(sizeof(MatrixType));
	    break;
	case IP_OBJ_STRING:
	    PObj -> U.Str = (char *) IritMalloc(LINE_LEN);
	    PObj -> U.Str[0] = 0;
	    break;
	case IP_OBJ_LIST_OBJ:
	    PObj -> U.Lst.PObjList = (IPObjectStruct **)
		IritMalloc(sizeof(IPObjectStruct *) * MAX_OBJ_LIST);
	    PObj -> U.Lst.PObjList[0] = NULL;
	    PObj -> U.Lst.ListMaxLen = MAX_OBJ_LIST;
	    break;
	default:
	    PObj -> U.VPtr = NULL;
	    break;
    }
}

/*****************************************************************************
*   Generate one polygonal object:					     *
*****************************************************************************/
IPObjectStruct *GenPolyObject(char *Name, IPPolygonStruct *Pl,
							IPObjectStruct *Pnext)
{
    IPObjectStruct *PObj;

    PObj = IPAllocObject(Name, IP_OBJ_POLY, Pnext);
    IP_SET_POLYGON_OBJ(PObj);		   /* Default - not polyline object. */

    PObj -> U.Pl = Pl;			     /* Link the union part of it... */

    return PObj;
}

/*****************************************************************************
*   Generate one polygonal object (wrapper):				     *
*****************************************************************************/
IPObjectStruct *GenPOLYObject(IPPolygonStruct *Pl)
{
    return GenPolyObject("", Pl, NULL);
}

/*****************************************************************************
*   Generate one curve object:						     *
*****************************************************************************/
IPObjectStruct *GenCrvObject(char *Name, CagdCrvStruct *Crv,
							IPObjectStruct *Pnext)
{
    IPObjectStruct *PObj;

    PObj = IPAllocObject(Name, IP_OBJ_CURVE, Pnext);

    PObj -> U.Crvs = Crv;		     /* Link the union part of it... */

    return PObj;
}

/*****************************************************************************
*   Generate one curve object (wrapper):				     *
*****************************************************************************/
IPObjectStruct *GenCRVObject(CagdCrvStruct *Crv)
{
    return GenCrvObject("", Crv, NULL);
}

/*****************************************************************************
*   Generate one surface object:					     *
*****************************************************************************/
IPObjectStruct *GenSrfObject(char *Name, CagdSrfStruct *Srf,
							IPObjectStruct *Pnext)
{
    IPObjectStruct *PObj;

    PObj = IPAllocObject(Name, IP_OBJ_SURFACE, Pnext);

    PObj -> U.Srfs = Srf;		     /* Link the union part of it... */

    return PObj;
}

/*****************************************************************************
*   Generate one surface object (wrapper):				     *
*****************************************************************************/
IPObjectStruct *GenSRFObject(CagdSrfStruct *Srf)
{
    return GenSrfObject("", Srf, NULL);
}

/*****************************************************************************
*   Generate one control point object:					     *
*   Only one of CagdCoords/Coords should be specified.			     *
*****************************************************************************/
IPObjectStruct *GenCtlPtObject(char *Name, CagdPointType PtType,
		CagdRType *CagdCoords, RealType *Coords, IPObjectStruct *Pnext)
{
    int i;
    CagdBType
	IsNotRational = !CAGD_IS_RATIONAL_PT(PtType);
    IPObjectStruct *PObj;
    RealType *t;

    PObj = IPAllocObject(Name, IP_OBJ_CTLPT, Pnext);

    PObj -> U.CtlPt.PtType = PtType;
    t = PObj -> U.CtlPt.Coords;

    if (CagdCoords != NULL)
	for (i = IsNotRational; i <= CAGD_NUM_OF_PT_COORD(PtType); i++)
	    t[i] = CagdCoords[i];
    else
	for (i = IsNotRational; i <= CAGD_NUM_OF_PT_COORD(PtType); i++)
	    t[i] = Coords[i];

    return PObj;
}

/*****************************************************************************
*   Generate one control point object (wrapper):			     *
*****************************************************************************/
IPObjectStruct *GenCTLPTObject(CagdPointType PtType, CagdRType *CagdCoords,
							RealType *Coords)
{
    return GenCtlPtObject("", PtType, CagdCoords, Coords, NULL);
}

/*****************************************************************************
*   Generate one numeric object:					     *
*****************************************************************************/
IPObjectStruct *GenNumObject(char *Name, RealType *R, IPObjectStruct *Pnext)
{
    IPObjectStruct *PObj;

    PObj = IPAllocObject(Name, IP_OBJ_NUMERIC, Pnext);

    PObj -> U.R = *R;			     /* Link the union part of it... */

    return PObj;
}

/*****************************************************************************
*   Generate one numeric object (wrapper):				     *
*****************************************************************************/
IPObjectStruct *GenNUMObject(RealType *R)
{
    return GenNumObject("", R, NULL);
}

/*****************************************************************************
*   Generate one numeric object (wrapper):				     *
*****************************************************************************/
IPObjectStruct *GenNUMValObject(RealType R)
{
    return GenNumObject("", &R, NULL);
}

/*****************************************************************************
*   Generate one point object:						     *
*****************************************************************************/
IPObjectStruct *GenPtObject(char *Name, RealType *Pt0, RealType *Pt1,
					RealType *Pt2, IPObjectStruct *Pnext)
{
    IPObjectStruct *PObj;

    PObj = IPAllocObject(Name, IP_OBJ_POINT, Pnext);

    PObj -> U.Pt[0] = *Pt0;		     /* Link the union part of it... */
    PObj -> U.Pt[1] = *Pt1;
    PObj -> U.Pt[2] = *Pt2;

    return PObj;
}

/*****************************************************************************
*   Generate one point object (wrapper):				     *
*****************************************************************************/
IPObjectStruct *GenPTObject(RealType *Pt0, RealType *Pt1, RealType *Pt2)
{
    return GenPtObject("", Pt0, Pt1, Pt2, NULL);
}

/*****************************************************************************
*   Generate one vector object:						     *
*****************************************************************************/
IPObjectStruct *GenVecObject(char *Name, RealType *Vec0, RealType *Vec1,
					RealType *Vec2, IPObjectStruct *Pnext)
{
    IPObjectStruct *PObj;

    PObj = IPAllocObject(Name, IP_OBJ_VECTOR, Pnext);

    PObj -> U.Vec[0] = *Vec0;		     /* Link the union part of it... */
    PObj -> U.Vec[1] = *Vec1;
    PObj -> U.Vec[2] = *Vec2;

    return PObj;
}

/*****************************************************************************
*   Generate one vector object (wrapper):				     *
*****************************************************************************/
IPObjectStruct *GenVECObject(RealType *Vec0, RealType *Vec1, RealType *Vec2)
{
    return GenVecObject("", Vec0, Vec1, Vec2, NULL);
}

/*****************************************************************************
*   Generate one plane object:						     *
*****************************************************************************/
IPObjectStruct *GenPlaneObject(char *Name, RealType *Plane0, RealType *Plane1,
		      RealType *Plane2, RealType *Plane3, IPObjectStruct *Pnext)
{
    IPObjectStruct *PObj;

    PObj = IPAllocObject(Name, IP_OBJ_PLANE, Pnext);

    PObj -> U.Plane[0] = *Plane0;	    /* Link the union part of it... */
    PObj -> U.Plane[1] = *Plane1;
    PObj -> U.Plane[2] = *Plane2;
    PObj -> U.Plane[3] = *Plane3;

    return PObj;
}

/*****************************************************************************
*   Generate one plane object (wrapper):				     *
*****************************************************************************/
IPObjectStruct *GenPLANEObject(RealType *Plane0, RealType *Plane1,
					     RealType *Plane2, RealType *Plane3)
{
    return GenPlaneObject("", Plane0, Plane1, Plane2, Plane3, NULL);
}

/*****************************************************************************
*   Generate one matrix object:						     *
*****************************************************************************/
IPObjectStruct *GenMatObject(char *Name, MatrixType Mat, IPObjectStruct *Pnext)
{
    int i, j;
    IPObjectStruct *PObj;

    PObj = IPAllocObject(Name, IP_OBJ_MATRIX, Pnext);

    for (i = 0; i < 4; i++)		     /* Link the union part of it... */
	for (j = 0; j < 4; j++)
	    (*PObj -> U.Mat)[i][j] = Mat[i][j];

    return PObj;
}

/*****************************************************************************
*   Generate one matrix object (wrapper):				     *
*****************************************************************************/
IPObjectStruct *GenMATObject(MatrixType Mat)
{
    return GenMatObject("", Mat, NULL);
}

/*****************************************************************************
*   Routine to reallocate as necessary and object to a new object type.      *
*****************************************************************************/
void ReallocNewTypeObject(IPObjectStruct *PObj, IPObjStructType ObjType)
{
    if (PObj -> ObjType == ObjType)
        return;
    IPFreeObjectSlots(PObj);
    PObj -> ObjType = ObjType;
    IPMallocObjectSlots(PObj);
}

/*****************************************************************************
*   Routine to create a whole new copy of an object Src into Dest.	     *
* if Dest is NULL, new object is allocated, otherwise Dest itself is updated *
* If CopyAll then all the record is copied, otherwise, only its variant      *
* element (i.e. no Name/Pnext coping) is been copied.			     *
*****************************************************************************/
IPObjectStruct *CopyObject(IPObjectStruct *Dest, IPObjectStruct *Src,
								int CopyAll)
{
    int Index;
    char Line[LINE_LEN];
    IPObjectStruct *PObjTmp;

    if (Dest == Src)
	return Dest;			/* Called with same object - ignore. */
    else if (Dest == NULL)
	Dest = IPAllocObject("", Src -> ObjType, NULL);
    else {
	IPFreeObjectSlots(Dest);
	Dest -> ObjType = Src -> ObjType;
	IPMallocObjectSlots(Dest);
	AttrFreeAttributes(&Dest -> Attrs);
    }

    if (CopyAll) {
	strcpy(Dest -> Name, Src -> Name);
	Dest -> Pnext = Src -> Pnext;	 /* Maybe assigning NULL is better!? */
    }

    Dest -> Attrs = AttrCopyAttributes(Src -> Attrs);

    switch (Src -> ObjType) {
	case IP_OBJ_UNDEF:
	    break;
	case IP_OBJ_POLY:
	    Dest -> U.Pl = CopyPolygonList(Src -> U.Pl);
	    if (IP_IS_POLYGON_OBJ(Src))
	        IP_SET_POLYGON_OBJ(Dest);
	    else if (IP_IS_POLYLINE_OBJ(Src))
	        IP_SET_POLYLINE_OBJ(Dest);
	    else if (IP_IS_POINTLIST_OBJ(Src))
	        IP_SET_POINTLIST_OBJ(Dest);
	    break;
	case IP_OBJ_NUMERIC:
	    Dest -> U.R = Src -> U.R;
	    break;
	case IP_OBJ_POINT:
	    PT_COPY(Dest -> U.Pt, Src -> U.Pt);
	    break;
	case IP_OBJ_VECTOR:
	    PT_COPY(Dest -> U.Vec, Src -> U.Vec);
	    break;
	case IP_OBJ_PLANE:
	    PLANE_COPY(Dest -> U.Plane, Src -> U.Plane);
	    break;
	case IP_OBJ_CTLPT:
	    GEN_COPY(&Dest -> U.CtlPt, &Src -> U.CtlPt,
		     sizeof(CagdCtlPtStruct));
	    break;
	case IP_OBJ_MATRIX:
	    if (Dest -> U.Mat == NULL)
		Dest -> U.Mat = (MatrixType *) IritMalloc(sizeof(MatrixType));
	    MAT_COPY(*Dest -> U.Mat, *Src -> U.Mat);
	    break;
	case IP_OBJ_STRING:
	    if (Dest -> U.Str == NULL)
		Dest -> U.Str = (char *) IritMalloc(LINE_LEN);
	    strcpy(Dest -> U.Str, Src -> U.Str);
	    break;
	case IP_OBJ_LIST_OBJ:
	    if (Dest -> U.Lst.PObjList != NULL)
		IritFree((VoidPtr) Dest -> U.Lst.PObjList);
	    Dest -> U.Lst.PObjList = (IPObjectStruct **)
		IritMalloc(sizeof(IPObjectStruct *) * Src -> U.Lst.ListMaxLen);
	    Dest -> U.Lst.ListMaxLen = Src -> U.Lst.ListMaxLen;

	    GEN_COPY(Dest -> U.Lst.PObjList, Src -> U.Lst.PObjList,
		     Dest -> U.Lst.ListMaxLen * sizeof(IPObjectStruct *));
	    for (Index = 0;
		 (PObjTmp = ListObjectGet(Dest, Index)) != NULL;
		 Index++)
		PObjTmp -> Count++;			   /* Inc. # of ref. */
	    break;
	case IP_OBJ_CURVE:
	    Dest -> U.Crvs = CagdCrvCopyList(Src -> U.Crvs);
	    break;
	case IP_OBJ_SURFACE:
	    Dest -> U.Srfs = CagdSrfCopyList(Src -> U.Srfs);
	    break;
	default:
	    sprintf(Line,
		"CopyObject Attemp to copy undefined object %s type %d",
		Src -> Name, Src -> ObjType);
	    IritPrsrFatalError(Line);
    }
    return Dest;
}

/*****************************************************************************
*   Routine to generate a new copy of an object polygon list.		     *
*****************************************************************************/
IPObjectStruct *CopyObjectList(IPObjectStruct *PObjs, int CopyAll)
{
    IPObjectStruct *PObj,
	*NewPObjs = NULL,
	*TailPObj =NULL;

    for (PObj = PObjs; PObj != NULL; PObj = PObj -> Pnext) {
	if (NewPObjs == NULL)
	    NewPObjs = TailPObj = CopyObject(NULL, PObj, CopyAll);
	else {
	    TailPObj -> Pnext = CopyObject(NULL, PObj, CopyAll);
	    TailPObj = TailPObj -> Pnext;
	}
    }

    return NewPObjs;
}

/*****************************************************************************
*   Routine to generate a new copy of an object polygon list.		     *
*****************************************************************************/
IPPolygonStruct *CopyPolygonList(IPPolygonStruct *Src)
{
    IPPolygonStruct *Phead, *Ptail;

    if (Src == NULL)
	return NULL;

    /* Prepare the header of the new polygon list: */
    Phead = Ptail = IPAllocPolygon(1, Src -> Tags,
				   CopyVertexList(Src -> PVertex), NULL);
    PLANE_COPY(Ptail -> Plane, Src -> Plane);
    Ptail -> Attrs = AttrCopyAttributes(Src -> Attrs);
    IP_RST_BBOX_POLY(Ptail);
    Src = Src -> Pnext;

    while (Src != NULL) {
	Ptail -> Pnext = IPAllocPolygon(Src -> Count, Src -> Tags,
					CopyVertexList(Src -> PVertex), NULL);
	Ptail = Ptail -> Pnext;
	PLANE_COPY(Ptail -> Plane, Src -> Plane);
	Ptail -> Attrs = AttrCopyAttributes(Src -> Attrs);
	IP_RST_BBOX_POLY(Ptail);
	Src = Src -> Pnext;
    }

    return Phead;
}

/*****************************************************************************
*   Routine to generate a new copy of a polygon vertices list.		     *
*****************************************************************************/
IPVertexStruct *CopyVertexList(IPVertexStruct *Src)
{
    IPVertexStruct *Phead, *Ptail,
	*SrcFirst = Src;

    if (Src == NULL)
	return NULL;

    /* Prepare the header of the new vertex list: */
    Phead = Ptail = IPAllocVertex(Src -> Count, Src -> Tags, NULL, NULL);
    PT_COPY(Phead -> Coord, Src -> Coord);
    PT_COPY(Phead -> Normal, Src -> Normal);
    Phead -> Attrs = AttrCopyAttributes(Src -> Attrs);
    Src = Src -> Pnext;

    while (Src != SrcFirst && Src != NULL) {
	Ptail -> Pnext = IPAllocVertex(Src -> Count, Src -> Tags, NULL, NULL);
	Ptail = Ptail -> Pnext;
	PT_COPY(Ptail -> Coord, Src -> Coord);
	PT_COPY(Ptail -> Normal, Src -> Normal);
	Ptail -> Attrs = AttrCopyAttributes(Src -> Attrs);

	Src = Src -> Pnext;
    }

    if (Src == SrcFirst)
	Ptail -> Pnext = Phead;		       /* Make vertex list circular. */

    return Phead;
}
