//================================================================
// EQUINOX-3D plugin
//
// Create a tetrahedron, an icosahedron or a dodecahedron
//
// AUTHOR:  Gabor Nagy
// DATE:    2025-Mar-12 19:03:49
//
// Copyright (C) 1996-2026 by Gabor Nagy. All rights reserved.
//================================================================
#include <stdio.h>
#include <math.h>


#include <EMalloc.h>
#include <EPlugins.h>

#include <Xe/Scale.h>
#include <Xe/Separator.h>
#include <Xe/ToggleButton.h>

#include <E3D/UI/CreateMacros.h>
#include <E3D/UI/Panel.h>
#include <E3D/UI/Panels.h>

static EPlugin*  _Plugin = NULL;

static XeDWidget  _MenuButtons[3];static int  _MenuButtons_Count = 0;
static XeDWidget  _ToolBarButtons[3];static int  _ToolBarButtons_Count = 0;

EpIconDef3I(static, Tetra);

typedef struct
{
	XeWidget  CenterAtBottomW, EdgeLengthW, RadiusW;
} E3dTetraGUI;


EpIconDef3I(static, Icosa);

typedef struct
{
	XeWidget  VerticalW, EdgeLengthW, RadiusW;
} E3dIcosaGUI;


EpIconDef3I(static, Dodeca);

typedef struct
{
	XeWidget  CenterAtBottomW, EdgeLengthW, RadiusW;
} E3dDodecaGUI;


#define TETRA_EDGELENGTH_TO_RADIUS  0.61237243568762955874  // sqrt(0.375)
#define M3TETRAHEIGHTFACTOR         0.81649658092772603273  // sqrt(2/3)


#define E3dpICOSA_X  0.525731112119133606
#define E3dpICOSA_Z  0.850650808352039932

static const E3dCoordinate  _IcosaVertices[][3]=
{    
	{ -E3dpICOSA_X, 0.0, E3dpICOSA_Z }, {  E3dpICOSA_X, 0.0,  E3dpICOSA_Z }, { -E3dpICOSA_X, 0.0, -E3dpICOSA_Z }, { E3dpICOSA_X,  0.0, -E3dpICOSA_Z },    
	{  0.0, E3dpICOSA_Z, E3dpICOSA_X }, {  0.0, E3dpICOSA_Z, -E3dpICOSA_X }, {  0.0, -E3dpICOSA_Z, E3dpICOSA_X }, { 0.0, -E3dpICOSA_Z, -E3dpICOSA_X },    
	{  E3dpICOSA_Z, E3dpICOSA_X, 0.0 }, { -E3dpICOSA_Z,  E3dpICOSA_X, 0.0 }, {  E3dpICOSA_Z, -E3dpICOSA_X, 0.0 }, { -E3dpICOSA_Z, -E3dpICOSA_X, 0.0 } 
}; 


static const E3dCoordinate  _IcosaVerticalVertices[][3]=
{    
	{ -0.525731, -0.447209,  0.723610 },
	{  0.525731, -0.447209,  0.723610 },
	{ -0.525731,  0.447209, -0.723610 },
	{  0.525731,  0.447209, -0.723610 },
	{  0.000000,  0.447219,  0.894424 },
	{  0.000000,  1.000000, -0.000007 },
	{  0.000000, -1.000000,  0.000007 },
	{  0.000000, -0.447219, -0.894424 },
	{  0.850651,  0.447215,  0.276390 },
	{ -0.850651,  0.447215,  0.276390 },
	{  0.850651, -0.447215, -0.276390 },
	{ -0.850651, -0.447215, -0.276390 }
}; 

static const int  _IcosaPolyIndices[20][3]=
{
	{  1,4,0 }, {  4,9,0 }, {  4,5,9 }, {  8,5,4 }, { 1,8,4 }, { 1,10,8 }, { 10,3,8 }, {  8,3,5 }, { 3,2,5 }, { 3 ,7,2 },
	{ 3,10,7 }, { 10,6,7 }, { 6,11,7 }, { 6,0,11 }, { 6,1,0 }, { 10,1,6 }, { 11,0,9 }, { 2,11,9 }, { 5,2,9 }, { 11,2,7 }
};


typedef struct
{
	E3dObjectComponentCore;

// E3dTetraInfo part
//
	E3dCoordinate  EdgeLength;
	EBool          CenterAtBottom;
} E3dTetraInfo;

#define ROffset(field)  (void*)(EPtrOffset(E3dTetraInfo, field))
static EResource  _TetraResources[]=
{
	{ "EdgeLength",      E3dCOORDINATE_TYPE,  ROffset(EdgeLength) },
	{ "CenterAtBottom",  EResBOOLEAN,         ROffset(CenterAtBottom) },
};
#undef ROffset


static E3dObjectComponentClass*  _TetraComponentClass = NULL;


#define	ICOSA_EDGELENGTH_TO_RADIUSFACTOR  0.95105655

typedef struct
{
	E3dObjectComponentCore;

// E3dIcosaInfo part
//
	E3dCoordinate  Radius;
	EBool          Vertical;
} E3dIcosaInfo;


#define ROffset(field)  (void*)(EPtrOffset(E3dIcosaInfo, field))
static EResource  _IcosaResources[]=
{
	{ "Radius",    E3dCOORDINATE_TYPE,  ROffset(Radius) },
	{ "Vertical",  EResBOOLEAN,         ROffset(Vertical) },
};
#undef ROffset

static E3dObjectComponentClass*  _IcosaComponentClass = NULL;


#define	DODECA_EDGELENGTH_TO_RADIUSFACTOR  1.4012585


typedef struct
{
	E3dObjectComponentCore;

// E3dDodecaInfo part
//
	E3dCoordinate  Radius;
	EBool          CenterAtBottom;
} E3dDodecaInfo;

#define ROffset(field)  (void*)(EPtrOffset(E3dDodecaInfo, field))
static EResource  _DodecaResources[]=
{
	{ "Radius",          E3dCOORDINATE_TYPE,  ROffset(Radius) },
	{ "CenterAtBottom",  EResBOOLEAN,         ROffset(CenterAtBottom) },
};
#undef ROffset


static E3dObjectComponentClass*  _DodecaComponentClass = NULL;


//================================================
// Set Vertex coordinates of a tetrahedron
//================================================
static void _TetraSetVertices(E3dTetraInfo* PTetraInfo, E3dVertex* PVertices, const E3dVertexSize PVertexSize)
{
	const E3dCoordinate  LLen = PTetraInfo->EdgeLength;
	const E3dCoordinate  LLenH = LLen*0.5;
	const E3dCoordinate  LCo0 = 0.57735026918962576451;  // 1.0/sqrt(3)
	const E3dCoordinate  LCo1 = -0.288675134 * LLen;     // LLen * (sqrt(3) / 2 - 1 / sqrt(3))

	E3dCoordinate  LCo2;

	{
		E3dVertex*  LVertex = EStruct_ByIndex(PVertices, PVertexSize, 3);
		if(PTetraInfo->CenterAtBottom)
		{
			LCo2 = 0.0;
			LVertex->Position = E3dCoord3_s(0.0, LLen * M3TETRAHEIGHTFACTOR, 0.0);
		}
		else
		{
			LCo2 = LCo1 * 0.7071067811865;  // LCo1 * sqrt(2) / 2
			LVertex->Position = E3dCoord3_s(0.0, LLen * M3TETRAHEIGHTFACTOR + LCo2, 0.0);
		}
	}

	E3dVertex*  LVertex = PVertices;
	LVertex->Position = E3dCoord3_s(0.0, LCo2, LLen * LCo0);EStruct_Inc(LVertex, PVertexSize);
	LVertex->Position = E3dCoord3_s(LLenH, LCo2, LCo1);EStruct_Inc(LVertex, PVertexSize);
	LVertex->Position = E3dCoord3_s(-LLenH, LCo2, LCo1);
}



//================================================
// Copy editable fields
//================================================
static void _TetraCopyFields(const E3dTetraInfo* PSrc, E3dTetraInfo* PDst)
{
#define _CopyField(p)  PDst->p = PSrc->p
	_CopyField(CenterAtBottom);
	_CopyField(EdgeLength);
#undef _CopyField
}


//========================================================
// Update() (create/change) method for tetrahedron
//========================================================
static E3dObject* _TetraUpdate1(E3dObject* PObject, E3dObjectComponent* PComponentTemplate,  EUpdateFlags* PTriggeredUpdateFlags, EChangeFlags* PTriggeredSceneChangeFlags,  const EBool PCheckOnly)
{
	if(PCheckOnly) return(NULL);

	E3dTetraInfo*  LInfo;
	E3dMesh*       LMesh;
	E3dPolyGroup*  LPolyGroup;

	if(PObject == NULL)
	{
		E_New(E3dMesh, LMesh);
		PObject = (E3dObject*)LMesh;

		LInfo = (E3dTetraInfo*)E3dObjectComponent_Add((E3dObject*)LMesh, _TetraComponentClass);
		if(PComponentTemplate && PComponentTemplate->Class == _TetraComponentClass) _TetraCopyFields((E3dTetraInfo*)PComponentTemplate, LInfo);
		if((LPolyGroup = E3dMesh_PolyGroupAdd(LMesh)) == NULL)
		{
			E_Delete(LMesh);return(NULL);
		}
	}
	else
	{
		if(!EObject_IsSubClass(PObject, E3dMesh)) return(NULL);

		LMesh = (E3dMesh*)PObject;
		LInfo = (E3dTetraInfo*)E3dObjectComponent_ByClass((E3dObject*)LMesh, _TetraComponentClass, FALSE);

		if(!PCheckOnly && LInfo->AttributeChangeCount == 0 && LInfo->InputChangeCount == 0) return(NULL);    // If no attributes or inputs changed, bail

		LPolyGroup = LMesh->PolyGroups.Items[0];
	}

	if(LMesh)
	{
		EIndex                   LVNum = 4, LPNum = 4;
		const E3dVertexNodeSize  LVertexNodeSize =  LPolyGroup->VertexNodeSize;

		E3dMesh_FreeCaches(LMesh);
		E3dMesh_EdgesFree(LMesh);

// Allocate vertices for the Mesh
//
		if(LVNum != LMesh->Vertices.Count)
		{
			E3dMesh_VerticesFree(LMesh);
			E3dMesh_VerticesAdd(LMesh, LVNum);
		}

// Allocate Polygons for the Mesh
//
		E3dPolygon*  LPolygons;
		if(LPolyGroup->Polygons.CountAllocated >= LPNum)
		{
			LPolygons = LPolyGroup->Polygons.Items;
			LPolyGroup->Polygons.Count = LPNum;
		}
		else
		{
			if(LPolyGroup->Polygons.Items) E3dPolyGroup_FreePolygons(LPolyGroup);
			LPolygons = E3dPolygons_New(LPNum);if(LPolygons)
			{
				LPolyGroup->Polygons.Items = LPolygons;LPolyGroup->Polygons.Count = LPolyGroup->Polygons.CountAllocated = LPNum;
			}
		}

		_TetraSetVertices(LInfo,  LMesh->Vertices.Items, LMesh->VertexSize);

		E3dPolygon*  LPolygon = LPolygons;
		E3dPolygon_SetAsTriangle(LPolygon++, LVertexNodeSize, 2, 1, 0);
		E3dPolygon_SetAsTriangle(LPolygon++, LVertexNodeSize, 0, 1, 3);
		E3dPolygon_SetAsTriangle(LPolygon++, LVertexNodeSize, 1, 2, 3);
		E3dPolygon_SetAsTriangle(LPolygon, LVertexNodeSize, 0, 3, 2);

		if(PTriggeredUpdateFlags) *PTriggeredUpdateFlags = E3dUpdateALL;

		LInfo->AttributeChangeCount = LInfo->InputChangeCount = 0;
	}

	return(PObject);
}


//========================================
// Dialog-box callback handlers
//========================================
static void _DCB_Tetra(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dScene*                 LScene = E3d_Scene;
	E3dObjectPanel*           LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*                LObject = LPanel->Object;
	EChangeFlags              LChanged = E3dObject_GetChangeFlagsByClass(LObject);
	E3dMesh*                  LMesh = (E3dMesh*)LObject;
	E3dObjectComponentClass*  LClass = _TetraComponentClass;
	E3dTetraInfo*             LInfo = (E3dTetraInfo*)E3dObjectComponent_ByClass(LObject, LClass, FALSE);
	XeComponentClassDrawer*   LDrawer = XeInfoPanel_GetFolderByClass((XeInfoPanel*)LPanel, LClass);
	E3dTetraGUI*              LGUI = (E3dTetraGUI*)LDrawer->ResourcesGUI;

	if(PW == LGUI->CenterAtBottomW)
	{
		LInfo->CenterAtBottom = ((XeToggleButtonCallbackStruct*)PCallData)->Set;

		_TetraSetVertices(LInfo,  LMesh->Vertices.Items, LMesh->VertexSize);
		LChanged |= E3dObject_UpdateForDisplay(LObject,  E3dUpdateMeshSHAPE|E3dUpdateTextureMAPPING, 0,  LScene);
		E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
		E3dWindows_RedrawByChangeFlags(LScene, LChanged);
	}
	else if(PW == LGUI->EdgeLengthW)
	{
		LInfo->EdgeLength = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;

		_TetraSetVertices(LInfo,  LMesh->Vertices.Items, LMesh->VertexSize);
		LChanged |= E3dObject_UpdateForDisplay(LObject,  E3dUpdateMeshSHAPE|E3dUpdateTextureMAPPING, 0,  LScene);
		E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
		E3dWindows_RedrawByChangeFlags(LScene, LChanged);
	}
	else if(PW == LGUI->RadiusW)
	{
		LInfo->EdgeLength = (((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate) / TETRA_EDGELENGTH_TO_RADIUS;

		_TetraSetVertices(LInfo,  LMesh->Vertices.Items, LMesh->VertexSize);
		LChanged |= E3dObject_UpdateForDisplay(LObject,  E3dUpdateMeshSHAPE|E3dUpdateTextureMAPPING, 0,  LScene);
		E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
		E3dWindows_RedrawByChangeFlags(LScene, LChanged);
	}
}


//========================================
// Pop up the dialog
//========================================
static void _TetraGUI(E3dObject* PObject, E3dObjectComponent* PComponent, void* PPanel, void* PDrawer, const int PAction)
{
	E3dObjectPanel*          LPanel = (E3dObjectPanel*)PPanel;
	XeComponentClassDrawer*  LDrawer = (XeComponentClassDrawer*)PDrawer;
	E3dTetraInfo*            LInfo = (E3dTetraInfo*)PComponent;

	switch(PAction)
	{
		case E_UPDATE:
			if(LDrawer->ResourcesGUI)
			{
				E3dTetraGUI*  LGUI = (E3dTetraGUI*)(LDrawer->ResourcesGUI);

				XeToggleButton_SetState(LGUI->CenterAtBottomW, LInfo->CenterAtBottom, FALSE);
				XeScale_SetValue_Coord(LGUI->RadiusW, LInfo->EdgeLength * TETRA_EDGELENGTH_TO_RADIUS, FALSE);
				XeScale_SetValue_Coord(LGUI->EdgeLengthW, LInfo->EdgeLength, FALSE);
			}
			else
			{
				XeWidget  LTopMatrixW = LDrawer->Drawer.DrawerW, LW;
				XeWidget  LIndentWs[7];int  LIndentN;      // For indenting the ScaleWidgets
				XeWidget  LTraverseWs[7];int  LTraverseN;  // For TextField traversal
				EArg      Args[256];int  ArgCnt;

				E3dTetraGUI*  LGUI;ENew(LGUI);LDrawer->ResourcesGUI = LGUI;

				Xe_SetValuesVa(LTopMatrixW,  XeNSpacingLeft, XeDrawer_LeftIndent,  XeNSpacingRight, 4,  XeNSpacingBottom, 4,  NULL);

				EXeStart;
				EXeSetArg(XeNForegroundColorPtr, &Xe_ForegroundRGBAiColor);
				EXeSetArg(XeNBackgroundColorPtr, &Xe_BackgroundRGBAiColor);
				EXeSetArg(XeNSelectColorPtr, &Xe_HighlightRGBAiColor);
				EXeSetArg(XeNShadowThickness, 0);
				EXeSetArg(XeNHighlightThickness, 0);
				EXeSetArg(XeNFontList, Xe_LabelFontList);
				EXeSetArg(XeNSet, LInfo->CenterAtBottom);
				EXeSetArg(XeNResizableY, FALSE);
				LGUI->CenterAtBottomW = LW = XeWidget_ManagedNew("Center at bottom", XeToggleButton_class, LTopMatrixW,  Args, ArgCnt);
				XeAddCallback(LW, XeNValueChangedCallback, _DCB_Tetra, (EPointer)LPanel);

				LTraverseN = 0;
				LIndentN = 0;
				EXeStart;
				EXeSetArg(XeNScaleLengthInPixels, 160);
				EXeSetArg(XeNTextFieldDigits, 8);EXeSetArg(XeNDecimalPoints, 4);
				EXeSetArg(XeNIncrement, 10);
				EXeSetArg(XeNResizableY, FALSE);
				LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LGUI->RadiusW = LW = XeScale_New_Coord(LTopMatrixW, "Radius",  Args, ArgCnt);
				XeScale_SetValueAndRanges_Coord(LW,  LInfo->EdgeLength * TETRA_EDGELENGTH_TO_RADIUS,  0.0, TETRA_EDGELENGTH_TO_RADIUS * 10000.0,  0.0, TETRA_EDGELENGTH_TO_RADIUS * 100.0,  FALSE);
				XeAddCallback(LW, XeNValueChangedCallback, _DCB_Tetra, (EPointer)LPanel);

				EXeStart;
				EXeSetArg(XeNScaleLengthInPixels, 160);
				EXeSetArg(XeNTextFieldDigits, 8);EXeSetArg(XeNDecimalPoints, 4);
				EXeSetArg(XeNIncrement, 10);
				EXeSetArg(XeNResizableY, FALSE);
				LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LGUI->EdgeLengthW = LW = XeScale_New_Coord(LTopMatrixW, "Edge length",  Args, ArgCnt);
				XeScale_SetValueAndRanges_Coord(LW,  LInfo->EdgeLength,  0.0, 10000.0,  0.0, 100.0,  FALSE);
				XeAddCallback(LW, XeNValueChangedCallback, _DCB_Tetra, (EPointer)LPanel);

				XeWidgets_IndentFromLeft(LIndentWs, LIndentN, 4);


// Set up TextField traversal
//
				XeTraversal_SetRing(LTraverseWs, LTraverseN);
			}
		break;
	}
}


//================================================
// Menu callback (create new Tetrahedron)
//================================================
static void _MCB_Tetra(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dTpl_ObjectComponentActivateCB_Geometry(_TetraComponentClass);
}


//================================================
//================================================
static int _TetraInit(E3dObject* PObject, E3dObjectComponent* PThis)
{
	E3dTetraInfo*  LThis = (E3dTetraInfo*)PThis;

	LThis->CenterAtBottom = FALSE;
	LThis->EdgeLength = 1.0;

	return(E_SUCCESS);
}


//========================================
// Copy-constructor
//========================================
static void _TetraCopy(const E3dObject* PSrcObject, const E3dObjectComponent* PSrcInfo,  E3dObject* PDstObject, E3dObjectComponent* PDstInfo)
{
	const E3dTetraInfo*  LSInfo = (E3dTetraInfo*)PSrcInfo;
	E3dTetraInfo*        LDInfo = (E3dTetraInfo*)PDstInfo;

	_TetraCopyFields(LSInfo, LDInfo);
}


//================================================
// Set vertex coordinates of an icosahedron
//================================================
static void _IcosaSetVertices(E3dIcosaInfo* PInfo,  E3dVertex* PVertices, const E3dVertexSize PVertexSize)
{
	const E3dCoordinate  LRadius = PInfo->Radius;
	const E3dCoordinate  (*LPoints)[3];

	if(PInfo->Vertical) LPoints = _IcosaVerticalVertices;
	else LPoints = _IcosaVertices;

	int  LC = 0;E3dVertex*  LVertex = PVertices;for(;LC < 12;LC++, EStruct_Inc(LVertex, PVertexSize))
	{
		LVertex->Position.X = LPoints[LC][0] * LRadius;
		LVertex->Position.Y = LPoints[LC][1] * LRadius;
		LVertex->Position.Z = LPoints[LC][2] * LRadius;
	}
}



//================================================
// Copy editable fields
//================================================
static void _IcosaCopyFields(const E3dIcosaInfo* PSrc, E3dIcosaInfo* PDst)
{
#define _CopyField(p)  PDst->p = PSrc->p
	_CopyField(Vertical);
	_CopyField(Radius);
#undef _CopyField
}


//========================================================
// Update() (create/change) method for Icosahedron
//========================================================
static E3dObject* _IcosaUpdate1(E3dObject* PObject, E3dObjectComponent* PComponentTemplate,  EUpdateFlags* PTriggeredUpdateFlags, EChangeFlags* PTriggeredSceneChangeFlags,  const EBool PCheckOnly)
{
	if(PCheckOnly) return(NULL);

	E3dMesh*       LMesh;
	E3dPolyGroup*  LPolyGroup;
	E3dIcosaInfo*  LInfo;

	if(PObject == NULL)
	{
		E_New(E3dMesh, LMesh);
		PObject = (E3dObject*)LMesh;

		LInfo = (E3dIcosaInfo*)E3dObjectComponent_Add((E3dObject*)LMesh, _IcosaComponentClass);
		if(PComponentTemplate && PComponentTemplate->Class == _IcosaComponentClass) _IcosaCopyFields((E3dIcosaInfo*)PComponentTemplate, LInfo);
		if((LPolyGroup = E3dMesh_PolyGroupAdd(LMesh)) == NULL)
		{
			E_Delete(LMesh);return(NULL);
		}
	}
	else
	{
		if(!EObject_IsSubClass(PObject, E3dMesh)) return(NULL);

		LMesh = (E3dMesh*)PObject;
		LInfo = (E3dIcosaInfo*)E3dObjectComponent_ByClass((E3dObject*)LMesh, _IcosaComponentClass, FALSE);

		if(!PCheckOnly && LInfo->AttributeChangeCount == 0 && LInfo->InputChangeCount == 0) return(NULL);    // If no attributes or inputs changed, bail

		LPolyGroup = LMesh->PolyGroups.Items[0];
	}

	if(LMesh)
	{
		E3dPolygon*              LPolygons;
		E3dPolygon*              LPolygon;
		EIndex                   LVNum = 12, LPNum = 20;
		const E3dVertexNodeSize  LVertexNodeSize =  LPolyGroup->VertexNodeSize;

		E3dMesh_FreeCaches(LMesh);
		E3dMesh_EdgesFree(LMesh);

// Allocate vertices for the Mesh
//
		if(LVNum != LMesh->Vertices.Count)
		{
			E3dMesh_VerticesFree(LMesh);
			E3dMesh_VerticesAdd(LMesh, LVNum);
		}

// Allocate Polygons for the Mesh
//
		if(LPolyGroup->Polygons.CountAllocated >= LPNum)
		{
			LPolygons = LPolyGroup->Polygons.Items;
			LPolyGroup->Polygons.Count = LPNum;
		}
		else
		{
			if(LPolyGroup->Polygons.Items) E3dPolyGroup_FreePolygons(LPolyGroup);

			LPolygons = E3dPolygons_New(LPNum);if(LPolygons)
			{
				LPolyGroup->Polygons.Items = LPolygons;LPolyGroup->Polygons.Count = LPolyGroup->Polygons.CountAllocated = LPNum;
			}
		}

		_IcosaSetVertices(LInfo,  LMesh->Vertices.Items, LMesh->VertexSize);

		LPolygon = LPolygons;

		EIndex  LC = 0;for(;LC < 20;LC++)
		{
			E3dPolygon_SetAsTriangle(LPolygon++, LVertexNodeSize,  _IcosaPolyIndices[LC][0], _IcosaPolyIndices[LC][1], _IcosaPolyIndices[LC][2]);
		}

		if(PTriggeredUpdateFlags) *PTriggeredUpdateFlags = E3dUpdateALL;

		LInfo->AttributeChangeCount = LInfo->InputChangeCount = 0;
	}

	return(PObject);
}


//========================================
// Dialog box callback handlers
//========================================
static void _DCB_Icosa(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dScene*                 LScene = E3d_Scene;
	E3dObjectPanel*           LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*                LObject = LPanel->Object;
	E3dMesh*                  LMesh = (E3dMesh*)LObject;
	E3dObjectComponentClass*  LClass = _IcosaComponentClass;
	E3dIcosaInfo*             LInfo = (E3dIcosaInfo*)E3dObjectComponent_ByClass(LObject, LClass, FALSE);
	XeComponentClassDrawer*   LDrawer = XeInfoPanel_GetFolderByClass((XeInfoPanel*)LPanel, LClass);
	E3dIcosaGUI*              LGUI = (E3dIcosaGUI*)LDrawer->ResourcesGUI;
	EChangeFlags              LChanged = 0;

	if(PW == LGUI->VerticalW)
	{
		LInfo->Vertical = ((XeToggleButtonCallbackStruct*)PCallData)->Set;

		_IcosaSetVertices(LInfo,  LMesh->Vertices.Items, LMesh->VertexSize);
		LChanged |= E3dObject_GetChangeFlagsByClass(LObject);
		LChanged |= E3dObject_UpdateForDisplay(LObject,  E3dUpdateMeshSHAPE|E3dUpdateTextureMAPPING, 0,  LScene);
	}
	else if(PW == LGUI->RadiusW)
	{
		LInfo->Radius = (((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate);

		_IcosaSetVertices(LInfo,  LMesh->Vertices.Items, LMesh->VertexSize);
		LChanged |= E3dObject_GetChangeFlagsByClass(LObject);
		LChanged |= E3dObject_UpdateForDisplay(LObject,  E3dUpdateMeshSHAPE|E3dUpdateTextureMAPPING, 0,  LScene);
		E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
	}
	else if(PW == LGUI->EdgeLengthW)
	{
		LInfo->Radius = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate * ICOSA_EDGELENGTH_TO_RADIUSFACTOR;

		_IcosaSetVertices(LInfo,  LMesh->Vertices.Items, LMesh->VertexSize);
		LChanged |= E3dObject_GetChangeFlagsByClass(LObject);
		LChanged |= E3dObject_UpdateForDisplay(LObject,  E3dUpdateMeshSHAPE|E3dUpdateTextureMAPPING, 0,  LScene);
		E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
	}

	E3dWindows_RedrawByChangeFlags(LScene, LChanged);
}


//========================================
// GUI
//========================================
static void _IcosaGUI(E3dObject* PObject, E3dObjectComponent* PInfo, void* PPanel, void* PDrawer, const int PAction)
{
	E3dObjectPanel*          LPanel = (E3dObjectPanel*)PPanel;
	XeComponentClassDrawer*  LDrawer = (XeComponentClassDrawer*)PDrawer;
	E3dIcosaInfo*            LInfo = (E3dIcosaInfo*)PInfo;

	switch(PAction)
	{
		case E_UPDATE:
			if(LDrawer->ResourcesGUI)
			{
				E3dIcosaGUI*  LGUI = (E3dIcosaGUI*)LDrawer->ResourcesGUI;

				XeToggleButton_SetState(LGUI->VerticalW, LInfo->Vertical, FALSE);
				XeScale_SetValue_Coord(LGUI->RadiusW, LInfo->Radius, FALSE);
				XeScale_SetValue_Coord(LGUI->EdgeLengthW, LInfo->Radius / ICOSA_EDGELENGTH_TO_RADIUSFACTOR, FALSE);
			}
			else
			{
				XeWidget  LTopMatrixW = LDrawer->Drawer.DrawerW, LW;
				XeWidget  LIndentWs[7];int  LIndentN;      // For indenting the ScaleWidgets
				XeWidget  LTraverseWs[7];int  LTraverseN;  // For TextField traversal
				EArg      Args[256];int  ArgCnt;

				E3dIcosaGUI*  LGUI;ENew(LGUI);LDrawer->ResourcesGUI = LGUI;

				Xe_SetValuesVa(LTopMatrixW,  XeNSpacingLeft, XeDrawer_LeftIndent,  XeNSpacingRight, 4,  XeNSpacingBottom, 4,  NULL);

				EXeStart;
				EXeSetArg(XeNBackgroundColorPtr, &Xe_BackgroundRGBAiColor);
				EXeSetArg(XeNForegroundColorPtr, &Xe_ForegroundRGBAiColor);
				EXeSetArg(XeNSelectColorPtr, &Xe_HighlightRGBAiColor);
				EXeSetArg(XeNShadowThickness, 0);
				EXeSetArg(XeNHighlightThickness, 0);
				EXeSetArg(XeNMarginTop, 0);EXeSetArg(XeNMarginBottom, 4);
				EXeSetArg(XeNFontList, Xe_LabelFontList);
				EXeSetArg(XeNSet, LInfo->Vertical);
				EXeSetArg(XeNResizableX, FALSE);EXeSetArg(XeNAlignmentX, XeAlignBEGINNING);
				EXeSetArg(XeNResizableY, FALSE);
				LGUI->VerticalW = LW = XeWidget_ManagedNew("Vertical", XeToggleButton_class, LTopMatrixW,  Args, ArgCnt);
				XeAddCallback(LW, XeNValueChangedCallback, _DCB_Icosa, (EPointer)LPanel);

				EXeStart;
				EXeSetArg(XeNShadowThickness, 2);
				EXeSetArg(XeNShadowType, XeShadowETCHED_IN);
				EXeSetArg(XeNBackgroundColorPtr, &Xe_BackgroundRGBAiColor);
				EXeSetArg(XeNResizableY, FALSE);
				XeWidget_ManagedNew("", XeSeparatorWidget_class, LTopMatrixW,  Args, ArgCnt);

				LTraverseN = 0;
				LIndentN = 0;
				EXeStart;
				EXeSetArg(XeNScaleLengthInPixels, 160);
				EXeSetArg(XeNTextFieldDigits, 8);EXeSetArg(XeNDecimalPoints, 4);
				EXeSetArg(XeNMinimum, 1);EXeSetArg(XeNMaximum, 2000000000);EXeSetArg(XeNValue, 10000);
				EXeSetArg(XeNScaleMinimum, 10000);EXeSetArg(XeNScaleMaximum, 1000000);
				EXeSetArg(XeNIncrement, 10);
				EXeSetArg(XeNResizableY, FALSE);
				LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LGUI->RadiusW = LW = XeScale_New_Coord(LTopMatrixW, "Radius",  Args, ArgCnt);
				XeScale_SetValueAndRanges_Coord(LW,  LInfo->Radius,  0.0, 10000.0,  0.0, 100.0,  FALSE);
				XeAddCallback(LW, XeNValueChangedCallback, _DCB_Icosa, (EPointer)LPanel);

				EXeStart;
				EXeSetArg(XeNScaleLengthInPixels, 160);
				EXeSetArg(XeNTextFieldDigits, 8);EXeSetArg(XeNDecimalPoints, 4);
				EXeSetArg(XeNIncrement, 10);
				EXeSetArg(XeNResizableY, FALSE);
				LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LGUI->EdgeLengthW = LW = XeScale_New_Coord(LTopMatrixW, "Edge length",  Args, ArgCnt);
				XeScale_SetValueAndRanges_Coord(LW,  LInfo->Radius,  0.0, 10000.0 / ICOSA_EDGELENGTH_TO_RADIUSFACTOR,  0.0, 100.0 / ICOSA_EDGELENGTH_TO_RADIUSFACTOR,  FALSE);
				XeAddCallback(LW, XeNValueChangedCallback, _DCB_Icosa, (EPointer)LPanel);

				XeWidgets_IndentFromLeft(LIndentWs, LIndentN, 4);


// Set up TextField traversal
//
				XeTraversal_SetRing(LTraverseWs, LTraverseN);
			}
		break;
	}
}


//================================================
// Menu callback (create new Icosahedron)
//================================================
static void _MCB_Icosa(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dTpl_ObjectComponentActivateCB_Geometry(_IcosaComponentClass);
}


//================================================
//================================================
static int _IcosaInit(E3dObject* PObject, E3dObjectComponent* PInfo)
{
	E3dIcosaInfo*  LThis = (E3dIcosaInfo*)PInfo;

	LThis->Vertical = TRUE;
	LThis->Radius = 1.0;

	return(E_SUCCESS);
}


//========================================
// Copy-constructor
//========================================
static void _IcosaCopy(const E3dObject* PSrcObject, const E3dObjectComponent* PSrcInfo, E3dObject* PDstObject, E3dObjectComponent* PDstInfo)
{
	const E3dIcosaInfo*  LSInfo = (E3dIcosaInfo*)PSrcInfo;
	E3dIcosaInfo*        LDInfo = (E3dIcosaInfo*)PDstInfo;

	_IcosaCopyFields(LSInfo, LDInfo);
}



static const int  _DodecaPolys[12][5]=
{
	{ 2,1,0,4,3 },
	{ 3,7,8,18,2 },
	{ 2,18,17,16,1 },
	{ 3,4,5,6,7 },
	{ 18,8,9,19,17 },
	{ 7,6,10,9,8 },
	{ 1,16,13,14,0 },
	{ 4,0,14,15,5 },
	{ 17,19,12,13,16 },
	{ 5,15,11,10,6 },
	{ 14,13,12,11,15 },
	{ 9,10,11,12,19 }
};


static const int  _IcosaPolys[20][3]=
{
	{ 1,4,0 },
	{ 4,9,0 },
	{ 4,5,9 },
	{ 8,5,4 },
	{ 1,8,4 },
	{ 1,10,8 },
	{ 10,3,8 },
	{ 8,3,5 },
	{ 3,2,5 },
	{ 3,7,2 },
	{ 3,10,7 },
	{ 10,6,7 },
	{ 6,11,7 },
	{ 6,0,11 },
	{ 6,1,0 },
	{ 10,1,6 },
	{ 11,0,9 },
	{ 2,11,9 },
	{ 5,2,9 },
	{ 11,2,7 }
};


//================================================
// Set vertex coordinates of a dodecahedron
//================================================
static void _DodecaSetVertices(const E3dDodecaInfo* PInfo, E3dVertex* PVertices, const E3dVertexSize PVertexSize)
{
	const E3dCoordinate  LRadius = PInfo->Radius;

	int  LCn = 0;E3dVertex*  LVertex = PVertices;for(;LCn < 20;LCn++)
	{
		const int            LA = _IcosaPolys[LCn][0], LB = _IcosaPolys[LCn][1], LC = _IcosaPolys[LCn][2];
		const E3dCoordinate  LXA = _IcosaVertices[LA][0], LYA = _IcosaVertices[LA][1], LZA = _IcosaVertices[LA][2];
		const E3dCoordinate  LXB = _IcosaVertices[LB][0], LYB = _IcosaVertices[LB][1], LZB = _IcosaVertices[LB][2];
		const E3dCoordinate  LXC = _IcosaVertices[LC][0], LYC = _IcosaVertices[LC][1], LZC = _IcosaVertices[LC][2];

// The dodecahedron vertex is the centroid of the icosahedron face.
//
		const E3dCoordinate  LX = (LXA + LXB + LXC) * 0.33333333;
		const E3dCoordinate  LY = (LYA + LYB + LYC) * 0.33333333;
		const E3dCoordinate  LZ = (LZA + LZB + LZC) * 0.33333333;
		const E3dCoordinate  LL = 1.0 / E3d_Sqrt(LX * LX + LY * LY + LZ * LZ) * LRadius;

		LVertex->Position.X = LX * LL;
		LVertex->Position.Y = LY * LL;
		LVertex->Position.Z = LZ * LL;
		if(PInfo->CenterAtBottom) LVertex->Position.Y += 0.934172 * LRadius;
		EStruct_Inc(LVertex, PVertexSize);
	}
}


//================================================
// Copy editable fields
//================================================
static void _DodecaCopyFields(const E3dDodecaInfo* PSrc, E3dDodecaInfo* PDst)
{
#define _CopyField(p)  PDst->p = PSrc->p
	_CopyField(CenterAtBottom);
	_CopyField(Radius);
#undef _CopyField
}


//========================================================
// Update() (create/change) method for Dodecahedron
//========================================================
static E3dObject* _DodecaUpdate1(E3dObject* PObject, E3dObjectComponent* PComponentTemplate,  EUpdateFlags* PTriggeredUpdateFlags, EChangeFlags* PTriggeredSceneChangeFlags,  const EBool PCheckOnly)
{
	if(PCheckOnly) return(NULL);

	E3dMesh*        LMesh;
	E3dPolyGroup*   LPolyGroup;
	E3dDodecaInfo*  LInfo;

	if(PObject == NULL)
	{
		E_New(E3dMesh, LMesh);
		PObject = (E3dObject*)LMesh;

		LInfo = (E3dDodecaInfo*)E3dObjectComponent_Add((E3dObject*)LMesh, _DodecaComponentClass);
		if(PComponentTemplate && PComponentTemplate->Class == _DodecaComponentClass) _DodecaCopyFields((E3dDodecaInfo*)PComponentTemplate, LInfo);
		if((LPolyGroup = E3dMesh_PolyGroupAdd(LMesh)) == NULL)
		{
			E_Delete(LMesh);return(NULL);
		}
	}
	else
	{
		if(!EObject_IsSubClass(PObject, E3dMesh)) return(NULL);

		LMesh = (E3dMesh*)PObject;
		LInfo = (E3dDodecaInfo*)E3dObjectComponent_ByClass((E3dObject*)LMesh, _DodecaComponentClass, FALSE);

		if(!PCheckOnly && LInfo->AttributeChangeCount == 0 && LInfo->InputChangeCount == 0) return(NULL);    // If no attributes or inputs changed, bail

		LPolyGroup = LMesh->PolyGroups.Items[0];
	}

	if(LMesh)
	{
		E3dPolygon*              LPolygons;
		const EIndex             LVNum = 20, LPNum = 12;
		const E3dVertexNodeSize  LVertexNodeSize =  LPolyGroup->VertexNodeSize;

		E3dMesh_FreeCaches(LMesh);
		E3dMesh_EdgesFree(LMesh);

// Allocate vertices for the Mesh
//
		if(LVNum != LMesh->Vertices.Count)
		{
			E3dMesh_VerticesFree(LMesh);
			E3dMesh_VerticesAdd(LMesh, LVNum);
		}

// Allocate Polygons for the Mesh
//
		if(LPolyGroup->Polygons.CountAllocated >= LPNum)
		{
			LPolygons = LPolyGroup->Polygons.Items;
			LPolyGroup->Polygons.Count = LPNum;
		}
		else
		{
			if(LPolyGroup->Polygons.Items) E3dPolyGroup_FreePolygons(LPolyGroup);

			LPolygons = E3dPolygons_New(LPNum);if(LPolygons)
			{
				LPolyGroup->Polygons.Items = LPolygons;LPolyGroup->Polygons.Count = LPolyGroup->Polygons.CountAllocated = LPNum;
			}
		}

		_DodecaSetVertices(LInfo,  LMesh->Vertices.Items, LMesh->VertexSize);

		EIndex  LC = 0;E3dPolygon*  LPolygon = LPolygons;for(;LC < 12;LC++)
		{
			E3dPolygon_SetVa(LPolygon++, LVertexNodeSize, 5, _DodecaPolys[LC][0], _DodecaPolys[LC][1], _DodecaPolys[LC][2], _DodecaPolys[LC][3], _DodecaPolys[LC][4]);
		}

		if(PTriggeredUpdateFlags) *PTriggeredUpdateFlags = E3dUpdateALL;
		LInfo->AttributeChangeCount = LInfo->InputChangeCount = 0;
	}

	return(PObject);
}


static void _DCB_Dodeca(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dScene*                 LScene = E3d_Scene;
	E3dObjectPanel*           LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*                LObject = LPanel->Object;
	E3dMesh*                  LMesh = (E3dMesh*)LObject;
	E3dObjectComponentClass*  LClass = _DodecaComponentClass;
	E3dDodecaInfo*            LInfo = (E3dDodecaInfo*)E3dObjectComponent_ByClass(LObject, LClass, FALSE);
	XeComponentClassDrawer*   LDrawer = XeInfoPanel_GetFolderByClass((XeInfoPanel*)LPanel, LClass);
	E3dDodecaGUI*             LGUI = (E3dDodecaGUI*)LDrawer->ResourcesGUI;
	EChangeFlags              LChanged = 0;

	if(PW == LGUI->CenterAtBottomW)
	{
		LInfo->CenterAtBottom = ((XeToggleButtonCallbackStruct*)PCallData)->Set;

		_DodecaSetVertices(LInfo,  LMesh->Vertices.Items, LMesh->VertexSize);
		LChanged |= E3dObject_GetChangeFlagsByClass(LObject);
		LChanged |= E3dObject_UpdateForDisplay(LObject,  E3dUpdateALL, E3dCallALL,  LScene);
		E3dWindows_RedrawByChangeFlags(LScene, LChanged);
	}
	else if(PW == LGUI->EdgeLengthW)
	{
		LInfo->Radius = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate * DODECA_EDGELENGTH_TO_RADIUSFACTOR;

		_DodecaSetVertices(LInfo,  LMesh->Vertices.Items, LMesh->VertexSize);
		LChanged |= E3dObject_GetChangeFlagsByClass(LObject);
		LChanged |= E3dObject_UpdateForDisplay(LObject,  E3dUpdateMeshSHAPE|E3dUpdateTextureMAPPING, E3dCallALL,  LScene);
		E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
		E3dWindows_RedrawByChangeFlags(LScene, LChanged);
	}
	else if(PW == LGUI->RadiusW)
	{
		LInfo->Radius = (((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate);

		_DodecaSetVertices(LInfo,  LMesh->Vertices.Items, LMesh->VertexSize);
		LChanged |= E3dObject_GetChangeFlagsByClass(LObject);
		LChanged |= E3dObject_UpdateForDisplay(LObject,  E3dUpdateMeshSHAPE|E3dUpdateTextureMAPPING, E3dCallALL,  LScene);
		E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
		E3dWindows_RedrawByChangeFlags(LScene, LChanged);
	}
}


//========================================
// Pop up the dialog
//========================================
static void _DodecaGUI(E3dObject* PObject, E3dObjectComponent* PInfo, void* PPanel, void* PDrawer, const int PAction)
{
	E3dObjectPanel*          LPanel = (E3dObjectPanel*)PPanel;
	XeComponentClassDrawer*  LDrawer = (XeComponentClassDrawer*)PDrawer;
	E3dDodecaInfo*           LInfo = (E3dDodecaInfo*)PInfo;

	switch(PAction)
	{
		case E_UPDATE:
			if(LDrawer->ResourcesGUI)
			{
				E3dDodecaGUI*  LGUI = (E3dDodecaGUI*)(LDrawer->ResourcesGUI);

				XeToggleButton_SetState(LGUI->CenterAtBottomW, LInfo->CenterAtBottom, FALSE);

				XeScale_SetValue_Coord(LGUI->RadiusW, LInfo->Radius, FALSE);
				XeScale_SetValue_Coord(LGUI->EdgeLengthW, LInfo->Radius / DODECA_EDGELENGTH_TO_RADIUSFACTOR, FALSE);
			}
			else
			{
				XeWidget  LTopMatrixW = LDrawer->Drawer.DrawerW, LW;
				XeWidget  LIndentWs[7];int  LIndentN;      // For indenting the ScaleWidgets
				XeWidget  LTraverseWs[7];int  LTraverseN;  // For TextField traversal
				EArg      Args[256];int  ArgCnt;

				E3dDodecaGUI*  LGUI;ENew(LGUI);LDrawer->ResourcesGUI = LGUI;

				Xe_SetValuesVa(LTopMatrixW,  XeNSpacingLeft, XeDrawer_LeftIndent,  XeNSpacingRight, 4,  XeNSpacingBottom, 4,  NULL);

				EXeStart;
				EXeSetArg(XeNForegroundColorPtr, &Xe_ForegroundRGBAiColor);
				EXeSetArg(XeNBackgroundColorPtr, &Xe_BackgroundRGBAiColor);
				EXeSetArg(XeNSelectColorPtr, &Xe_HighlightRGBAiColor);
				EXeSetArg(XeNShadowThickness, 0);
				EXeSetArg(XeNHighlightThickness, 0);
				EXeSetArg(XeNMarginTop, 0);EXeSetArg(XeNMarginBottom, 4);
				EXeSetArg(XeNFontList, Xe_LabelFontList);
				EXeSetArg(XeNSet, LInfo->CenterAtBottom);
				EXeSetArg(XeNResizableY, FALSE);
				LGUI->CenterAtBottomW = LW = XeWidget_ManagedNew("Center at bottom", XeToggleButton_class, LTopMatrixW,  Args, ArgCnt);
				XeAddCallback(LW, XeNValueChangedCallback, _DCB_Dodeca, (EPointer)LPanel);

				EXeStart;
				EXeSetArg(XeNShadowThickness, 2);
				EXeSetArg(XeNShadowType, XeShadowETCHED_IN);
				EXeSetArg(XeNBackgroundColorPtr, &Xe_BackgroundRGBAiColor);
				EXeSetArg(XeNResizableY, FALSE);
				XeWidget_ManagedNew("", XeSeparatorWidget_class, LTopMatrixW,  Args, ArgCnt);

				LTraverseN = 0;
				LIndentN = 0;
				EXeStart;
				EXeSetArg(XeNScaleLengthInPixels, 160);
				EXeSetArg(XeNTextFieldDigits, 8);EXeSetArg(XeNDecimalPoints, 4);
				EXeSetArg(XeNIncrement, 10);
				EXeSetArg(XeNResizableY, FALSE);
				LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LGUI->RadiusW = LW = XeScale_New_Coord(LTopMatrixW, "Radius",  Args, ArgCnt);
				XeScale_SetValueAndRanges_Coord(LW,  LInfo->Radius,  0.0001, 1000000.0,  0.0, 100.0,  FALSE);
				XeAddCallback(LW, XeNValueChangedCallback, _DCB_Dodeca, (EPointer)LPanel);

				EXeStart;
				EXeSetArg(XeNScaleLengthInPixels, 160);
				EXeSetArg(XeNTextFieldDigits, 8);EXeSetArg(XeNDecimalPoints, 4);
				EXeSetArg(XeNIncrement, 10);
				EXeSetArg(XeNResizableY, FALSE);
				LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LGUI->EdgeLengthW = LW = XeScale_New_Coord(LTopMatrixW, "Edge length",  Args, ArgCnt);
				XeScale_SetValueAndRanges_Coord(LW,  LInfo->Radius / DODECA_EDGELENGTH_TO_RADIUSFACTOR,  0.0001, 1000000.0 / DODECA_EDGELENGTH_TO_RADIUSFACTOR,  0.0, 100.0 / DODECA_EDGELENGTH_TO_RADIUSFACTOR,  FALSE);
				XeAddCallback(LW, XeNValueChangedCallback, _DCB_Dodeca, (EPointer)LPanel);

				XeWidgets_IndentFromLeft(LIndentWs, LIndentN, 4);

// Set up TextField traversal
//
				XeTraversal_SetRing(LTraverseWs, LTraverseN);
			}
		break;
	}
}


//================================================
// Menu callback (create new Dodecahedron)
//================================================
static void _MCB_Dodeca(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dTpl_ObjectComponentActivateCB_Geometry(_DodecaComponentClass);
}


//========================================================
//========================================================
static int _DodecaInit(E3dObject* PObject, E3dObjectComponent* PInfo)
{
	E3dDodecaInfo*	LThis = (E3dDodecaInfo*)PInfo;

	LThis->CenterAtBottom = FALSE;
	LThis->Radius = 1.0;

	return(E_SUCCESS);
}


//========================================
// Copy-constructor
//========================================
static void _DodecaCopy(const E3dObject* PSrcObject, const E3dObjectComponent* PSrcInfo, E3dObject* PDstObject, E3dObjectComponent* PDstInfo)
{
	const E3dDodecaInfo*  LSInfo = (E3dDodecaInfo*)PSrcInfo;
	E3dDodecaInfo*        LDInfo = (E3dDodecaInfo*)PDstInfo;

	_DodecaCopyFields(LSInfo, LDInfo);
}


//========================================
// Entry point of the plugin
//========================================
int E_PluginInit(EPlugin* PPlugin)
{
	_Plugin = PPlugin;

	{
		E3dObjectComponentClass  LClass=
		{
			.Name=        "Tetrahedron",
			.StructSize=  sizeof(E3dTetraInfo),
			.Resources=   EListS_s(_TetraResources),

			.Init=        _TetraInit,
			.Copy=        _TetraCopy,
			.Update1=      _TetraUpdate1,
			.GUI=         _TetraGUI
		};
		if((_TetraComponentClass = E3dObjectComponent_ClassRegister(&LClass)) != NULL)
		{
			EpM_CreateIconStates3_sp(Tetra);E_Assign(_Image_Tetra, _TetraComponentClass->Icon);
			XePushButton_AddImg3(_ToolBarButtons[_ToolBarButtons_Count++], Tetra, "Tool->Create", "Tetrahedron", '\0', NULL, NULL, TRUE, "Create tetrahedron\nRight button:  dialog box for this tool", _MCB_Tetra, (EPointer)0);
			_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Create", "Tetrahedron", '\0', NULL, NULL, TRUE, NULL, _MCB_Tetra, (EPointer)0);
		}
	}

	{
		E3dObjectComponentClass  LClass=
		{
			.Name=        "Icosahedron",
			.StructSize=  sizeof(E3dIcosaInfo),
			.Resources=   EListS_s(_IcosaResources),

			.Init=        _IcosaInit,
			.Copy=        _IcosaCopy,
			.Update1=      _IcosaUpdate1,
			.GUI=         _IcosaGUI
		};
		if((_IcosaComponentClass = E3dObjectComponent_ClassRegister(&LClass)) != NULL)
		{
			EpM_CreateIconStates3_sp(Icosa);E_Assign(_Image_Icosa, _IcosaComponentClass->Icon);
			XePushButton_AddImg3(_ToolBarButtons[_ToolBarButtons_Count++], Icosa, "Tool->Create", "Icosahedron", '\0', NULL, NULL, TRUE, "Creae icosahedron\nRight button:  dialog box for this tool", _MCB_Icosa, (EPointer)0);
			_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Create", "Icosahedron", '\0', NULL, NULL, TRUE, NULL, _MCB_Icosa, (EPointer)0);
		}
	}

	{
		E3dObjectComponentClass  LClass=
		{
			.Name=        "Dodecahedron",
			.StructSize=  sizeof(E3dIcosaInfo),
			.Resources=   EListS_s(_DodecaResources),

			.Init=        _DodecaInit,
			.Copy=        _DodecaCopy,
			.Update1=      _DodecaUpdate1,
			.GUI=         _DodecaGUI
		};
		if((_DodecaComponentClass = E3dObjectComponent_ClassRegister(&LClass)) != NULL)
		{
			EpM_CreateIconStates3_sp(Dodeca);E_Assign(_Image_Dodeca, _DodecaComponentClass->Icon);
			XePushButton_AddImg3(_ToolBarButtons[_ToolBarButtons_Count++], Dodeca, "Tool->Create", "Dodecahedron", '\0', NULL, NULL, TRUE, "Create dodecahedron\nRight button:  dialog box for this tool", _MCB_Dodeca, (EPointer)0);
			_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Create", "Dodecahedron", '\0', NULL, NULL, TRUE, NULL, _MCB_Dodeca, (EPointer)0);
		}
	}

	return(E_SUCCESS);
}


//========================================
// Exit method of the plugin
//========================================
int E_PluginExit(const EBool PExiting)
{
	if(!PExiting || Xe_GUIFreeOnExit)
	{
		int	LC;

		for(LC = 0;LC < _MenuButtons_Count;LC++) { XeItemFree(_MenuButtons[LC]); } _MenuButtons_Count = 0;
		for(LC = 0;LC < _ToolBarButtons_Count;LC++) { XeItemFree(_ToolBarButtons[LC]); } _ToolBarButtons_Count = 0;
	}

	if(_TetraComponentClass)
	{
		E3dObjectComponent_ClassDeactivate(_TetraComponentClass);_TetraComponentClass = NULL;
	}
	if(_IcosaComponentClass)
	{
		E3dObjectComponent_ClassDeactivate(_IcosaComponentClass);_IcosaComponentClass = NULL;
	}
	if(_DodecaComponentClass)
	{
		E3dObjectComponent_ClassDeactivate(_DodecaComponentClass);_DodecaComponentClass = NULL;
	}

	EpIconFree3(Tetra);
	EpIconFree3(Icosa);
	EpIconFree3(Dodeca);

	return(E_SUCCESS);
}
