/*======================================================================================================*/
/* Interactive revolve tool										*/
/*													*/
/* AUTHOR:	Gabor Nagy										*/
/* DATE:	1996-Dec-17 23:49:08									*/
/*													*/
/* EQUINOX-3D(TM), 3DPanel(TM) and 3DLib(TM) Copyright (C) 1995 By Gabor Nagy. All rights reserved.	*/
/*======================================================================================================*/
#include <math.h>
#include <stdio.h>

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


#include <Xe/Label.h>
#include <Xe/Matrix.h>
#include <Xe/Scale.h>
#include <Xe/ToggleButton.h>

#include <EGUI/PushButton.h>
#include <EGUI/Dialog.h>

#include <E3D/3DWindow.h>

#include <E3D/StatusPanel.h>


static EPlugin*		_Plugin=NULL;

static EguiItem		_Dialog = NULL;
static Widget		_TrianglesW=NULL, _SmoothingAngleW=NULL,
			_StartAngleScaleW, _EndAngleScaleW,
			_SidesScaleW;


static E3dModel*	_Model=NULL;
static E3dGeometry*	_MainGeometry=NULL;
static E3dGeometry*	_Geometry=NULL;

static EguiItem		_MenuButton=NULL, _ToolPanelButton=NULL;


static Arg		Args[256];
static Cardinal		ArgCnt;


extern EpCImage		Image_Revolve;
extern EpCImage		Image_RevolveActive;
extern EpCImage		Image_RevolveArm;
extern EpCImage		Image_RevolveBig;


enum
{
 E3dCR_TRIANGLES=EguiCUSTOM0,
 E3dCR_CHANGE_SMOOTHING_ANGLE,
 E3dCR_SET_START_ANGLE,
 E3dCR_SET_END_ANGLE,
 E3dCR_SET_SIDES,
 E3dCR_SET_CLOSE
};


typedef struct
{
// E3dGeometryInfo part
//
 E3dGeometryClass*	Class;

// E3dRevolveInfo part
//
// Resources
//
 E3dModel*	Model;				// This points back to the Model we created
 E3dGeometry*	Geometry;			// This points back to the resulting Geometry
 E3dGeometry*	ProfileGeometry;
 E3dAngle	StartAngle, EndAngle;
 float		SmoothingAngle;
 int		Sides;
 EBool		AroundWorldAxis, Close,
		Triangles;

// Internal variables
//
 E3dInterface	InputNode;		// This is added to the cross-section Geometries as an output node
} E3dRevolveInfo;





/*======================================*/
/* ProfileGeo resource translator	*/
/*======================================*/
static int _RS_ProfileGeometry(void* LDStructAddress, void* LSPtr, void* LDPtr, int LAction)
{
 E3dGeometry**	LSGeometryPtr=(E3dGeometry**)LSPtr;

 switch(LAction)
 {
  case EResCOPY:
   {
    E3dGeometry**	LDGeometryPtr=(E3dGeometry**)LDPtr;
    E3dGeometry*	LGeometry=*LSGeometryPtr;
    E3dRevolveInfo*	LInfo=(E3dRevolveInfo*)LDStructAddress;

    *LDGeometryPtr=LGeometry;
    LGeometry->RefCnt+=1;

    E3d_GeometryAddOutput(LGeometry, &(LInfo->InputNode));
   }
  break;

  case EResPACK:
  break;

  case EResUNPACK:
  break;
 }

 return(0);
}



#define ROffset(field) (void*)(EPtrOffset(E3dRevolveInfo, field))

EResource _Resources[]=
{
 { "Sides",		EResINT,	ROffset(Sides), NULL },

 { "ProfGeo",		EResCUSTOM,	ROffset(ProfileGeometry), _RS_ProfileGeometry },

 { "SAngle",		EResFLOAT32,	ROffset(StartAngle), NULL },
 { "EAngle",		EResFLOAT32,	ROffset(EndAngle), NULL },
 { "Tris",		EResBOOLEAN,	ROffset(Triangles), NULL },
 { "SmoothingAngle",	EResFLOAT32,	ROffset(SmoothingAngle), NULL },
 { "",			EResNULL,	NULL, NULL }
};




static E3dRevolveInfo	_Info;
static E3dGeometryPanel	_GeometryPanel;
static EBool		_CreateMode=FALSE;






/*======================================*/
/* Set vertex coordinates		*/
/*======================================*/
static void _SetVertices(E3dVertex* LVertices, E3dRevolveInfo* LInfo)
{
 register E3dVertex*	LVertex=LVertices;
 E3dSpline*		LProfileSpline;
 E3dAngle		LStartAngle, LEndAngle, LAngle, LAngleStep;
 double			LA;
 E3dGLType*		LGLLinSegments;
 E3dGLType*		LGLLinSegment;
 int			LSides, LUPoints, LVPoints;
 unsigned int		LUCnt, LVCnt;
 EBool			LFull;


 LStartAngle=LInfo->StartAngle;
 LEndAngle=LInfo->EndAngle;
 if((LEndAngle-LStartAngle)==360.0) LFull=TRUE;
 else LFull=FALSE;


 LProfileSpline=(E3dSpline*)(LInfo->ProfileGeometry);
 LGLLinSegments=LProfileSpline->GLLinSegments;

 LVPoints=LProfileSpline->NumOfLinSegments;

 LSides=LInfo->Sides;

 if(LFull) LUPoints=LSides;
 else LUPoints=LSides+1;

 LAngle=LStartAngle;LAngleStep=(LEndAngle-LStartAngle)/((E3dAngle)LSides);
 for(LUCnt=0;LUCnt<LUPoints;LUCnt++, LAngle+=LAngleStep)
 {
  LGLLinSegment=LGLLinSegments;
  for(LVCnt=0;LVCnt<LVPoints;LVCnt++, LGLLinSegment+=3)
  {
   LA=LAngle*E3dDEGREES_TO_RADIANS;
   LVertex->X=cos(LA)*LGLLinSegment[E3dX];
   LVertex->Y=LGLLinSegment[E3dY];
   LVertex->Z=-sin(LA)*LGLLinSegment[E3dX];
   LVertex++;
  }
 }

 {
  E3dMesh*	LMesh=(E3dMesh*)(LInfo->Geometry);
  E3dPolyGroup*	LPolyGroup=LMesh->PolyGroups[0];

  if(LInfo->SmoothingAngle>0.0)
  {
   LPolyGroup->Flags|=E3dUSE_VERTEX_NORMALS;

   LPolyGroup->VertexNormalType=E3dNormalFROM_DISCONTINUITY_ANGLE;
   E3d_MeshRefreshNormals(LMesh, TRUE);
  }
  else
  {
   LPolyGroup->VertexNormalType=E3dNormalNONE;
   LPolyGroup->Flags&=(0xFFFFFFFF-E3dUSE_VERTEX_NORMALS);
   E3d_MeshRefreshPolygonNormals(LMesh);
  }
 }
}


/*======================================*/
/* Get a revolved object		*/
/*======================================*/
static E3dModel* _Get(E3dModel* LModel, E3dGeometry* LGeometry, E3dRevolveInfo* LInfoV, unsigned int LFlags)
{
 E3dMesh*		LMesh;
 E3dPolyGroup*		LPolyGroup;
 register E3dVertex*	LVertices;
 E3dPolygon*		LPolygons;
 E3dPolygon*		LPolygon;
 int			LSides, LSegments, LUPoints, LVPoints,
			LNumOfVertices, LNumOfPolygons;
 unsigned int		LUCnt, LVCnt, LColumnA, LNextColumnA;
 E3dRevolveInfo*	LInfo;
 E3dSpline*		LProfileSpline;
 E3dAngle		LStartAngle, LEndAngle;
 EBool			LFull, LNewModel;


//Printf("Get %p %p\n", LModel, LGeometry);fflush(stdout);

 if(LGeometry==NULL)
 {
  if((LModel=E3d_ModelAllocate("Revolve"))==NULL) return(NULL);

  LNewModel=TRUE;

  if((LMesh=(E3dMesh*)E3d_ModelAddGeometry(LModel, E3dGEO_MESH, "revolve"))==NULL) { E3d_ModelHrcFree(LModel, TRUE);return(NULL); }
  LMesh->LockCount=1;

  LInfo=(E3dRevolveInfo*)E3d_GeometryInfoAdd((E3dGeometry*)LMesh, LInfoV->Class);
  if(LInfo)
  {
   memcpy(LInfo, LInfoV, sizeof(E3dRevolveInfo));
  }
  if((LPolyGroup=E3d_MeshAddPolyGroup(LMesh))==NULL) { E3d_ModelHrcFree(LModel, TRUE);return(NULL); }
  LInfo->Model=LModel;
 }
 else
 {
  LNewModel=FALSE;
  LMesh=(E3dMesh*)LGeometry;
  LInfo=LInfoV;
  LPolyGroup=LMesh->PolyGroups[0];
 }

 LInfo->Geometry=(E3dGeometry*)LMesh;

 LStartAngle=LInfo->StartAngle;
 LEndAngle=LInfo->EndAngle;
 LProfileSpline=(E3dSpline*)(LInfo->ProfileGeometry);


 LVPoints=LSegments=LProfileSpline->NumOfLinSegments;
 if(!LProfileSpline->Closed) LSegments--;

 LSides=LInfo->Sides;

 if((LEndAngle-LStartAngle)==360.0) LFull=TRUE;
 else LFull=FALSE;

 if(LFull) LUPoints=LSides;
 else LUPoints=LSides+1;

 LNumOfVertices=LUPoints*LVPoints;
 LNumOfPolygons=LSides*LSegments;

 if(LInfo->Triangles) LNumOfPolygons*=2;

//printf("LUPoints %d, LVPoints %d, LNumOfVertices %d, LNumOfPolygons %d\n", LUPoints, LVPoints, LNumOfVertices, LNumOfPolygons);fflush(stdout);

 if(LMesh)
 {
  unsigned int	LUN, LVN;

// Allocate Vertices for the Mesh
//
  if(LNumOfVertices==LMesh->NumOfVertices) LVertices=LMesh->Vertices;
  else
  {
   if(LMesh->Vertices) E3d_MeshFreeVertices(LMesh);
   if((LVertices=E3d_VerticesAllocate(LNumOfVertices, TRUE))!=NULL)
   {
    LMesh->Vertices=LVertices;LMesh->NumOfVertices=LNumOfVertices;
   }
   else LMesh->NumOfVertices=0;
  }

// Allocate Polygons for the Mesh
//
  if(LNumOfPolygons==LPolyGroup->NumOfPolygons) LPolygons=LPolyGroup->Polygons;
  else
  {
   if(LPolyGroup->Polygons) E3d_PolyGroupFreePolygons(LPolyGroup);
   if((LPolygons=E3d_PolygonsAllocate(LNumOfPolygons))!=NULL)
   {
    LPolyGroup->Polygons=LPolygons;LPolyGroup->NumOfPolygons=LNumOfPolygons;
   }
  }

  LPolygon=LPolygons;

// Build the Polygons
//
  LPolygon=LPolyGroup->Polygons;

  LColumnA=0;LNextColumnA=LVPoints;
  if(LInfo->Triangles)
  {
   if(LFull)
   {
    LUN=LSides-1;
    for(LUCnt=0;LUCnt<LUN;LUCnt++, LColumnA+=LVPoints, LNextColumnA+=LVPoints)
    {
     if(LProfileSpline->Closed)
     {
      LVN=LSegments-1;
      for(LVCnt=0;LVCnt<LVN;LVCnt++)
      {
       E3d_PolygonSetAsTriangle(LPolygon++, LColumnA+LVCnt, LNextColumnA+LVCnt, LNextColumnA+LVCnt+1);
       E3d_PolygonSetAsTriangle(LPolygon++, LNextColumnA+LVCnt+1, LColumnA+LVCnt+1, LColumnA+LVCnt);
      }
      E3d_PolygonSetAsTriangle(LPolygon++, LColumnA+LVCnt, LNextColumnA+LVCnt, LNextColumnA);
      E3d_PolygonSetAsTriangle(LPolygon++, LNextColumnA, LColumnA, LColumnA+LVCnt);
     }
     else
     {
      for(LVCnt=0;LVCnt<LSegments;LVCnt++)
      {
       E3d_PolygonSetAsTriangle(LPolygon++, LColumnA+LVCnt, LNextColumnA+LVCnt, LNextColumnA+LVCnt+1);
       E3d_PolygonSetAsTriangle(LPolygon++, LNextColumnA+LVCnt+1, LColumnA+LVCnt+1, LColumnA+LVCnt);
      }
     }
    }
    LNextColumnA=0;
    if(LProfileSpline->Closed)
    {
     LVN=LSegments-1;
     for(LVCnt=0;LVCnt<LVN;LVCnt++)
     {
      E3d_PolygonSetAsTriangle(LPolygon++, LColumnA+LVCnt, LNextColumnA+LVCnt, LNextColumnA+LVCnt+1);
      E3d_PolygonSetAsTriangle(LPolygon++, LNextColumnA+LVCnt+1, LColumnA+LVCnt+1, LColumnA+LVCnt);
     }
     E3d_PolygonSetAsTriangle(LPolygon++, LColumnA+LVCnt, LNextColumnA+LVCnt, LNextColumnA);
     E3d_PolygonSetAsTriangle(LPolygon++, LNextColumnA, LColumnA, LColumnA+LVCnt);
    }
    else
    {
     for(LVCnt=0;LVCnt<LSegments;LVCnt++)
     {
      E3d_PolygonSetAsTriangle(LPolygon++, LColumnA+LVCnt, LNextColumnA+LVCnt, LNextColumnA+LVCnt+1);
      E3d_PolygonSetAsTriangle(LPolygon++, LNextColumnA+LVCnt+1, LColumnA+LVCnt+1, LColumnA+LVCnt);
//printf(">%d-%d\n", LNextColumnA+LVCnt+1, LColumnA+LVCnt+1);fflush(stdout);
     }
    }
   }
   else
   {
    for(LUCnt=0;LUCnt<LSides;LUCnt++, LColumnA+=LVPoints, LNextColumnA+=LVPoints)
    {
     if(LProfileSpline->Closed)
     {
      LVN=LSegments-1;
      for(LVCnt=0;LVCnt<LVN;LVCnt++)
      {
       E3d_PolygonSetAsTriangle(LPolygon++, LColumnA+LVCnt, LNextColumnA+LVCnt, LNextColumnA+LVCnt+1);
       E3d_PolygonSetAsTriangle(LPolygon++, LNextColumnA+LVCnt+1, LColumnA+LVCnt+1, LColumnA+LVCnt);
      }
      E3d_PolygonSetAsTriangle(LPolygon++, LColumnA+LVCnt, LNextColumnA+LVCnt, LNextColumnA);
      E3d_PolygonSetAsTriangle(LPolygon++, LNextColumnA, LColumnA, LColumnA+LVCnt);
     }
     else
     {
      for(LVCnt=0;LVCnt<LSegments;LVCnt++)
      {
       E3d_PolygonSetAsTriangle(LPolygon++, LColumnA+LVCnt, LNextColumnA+LVCnt, LNextColumnA+LVCnt+1);
       E3d_PolygonSetAsTriangle(LPolygon++, LNextColumnA+LVCnt+1, LColumnA+LVCnt+1, LColumnA+LVCnt);
      }
     }
    }
   }
  }
  else
  {
   if(LFull)
   {
    LUN=LSides-1;
    for(LUCnt=0;LUCnt<LUN;LUCnt++, LColumnA+=LVPoints, LNextColumnA+=LVPoints)
    {
     if(LProfileSpline->Closed)
     {
      LVN=LSegments-1;
      for(LVCnt=0;LVCnt<LVN;LVCnt++)
      {
       E3d_PolygonSet(LPolygon++, 4, LColumnA+LVCnt, LNextColumnA+LVCnt, LNextColumnA+LVCnt+1, LColumnA+LVCnt+1);
      }
      E3d_PolygonSet(LPolygon++, 4, LColumnA+LVCnt, LNextColumnA+LVCnt, LNextColumnA, LColumnA);
     }
     else
     {
      for(LVCnt=0;LVCnt<LSegments;LVCnt++)
      {
       E3d_PolygonSet(LPolygon++, 4, LColumnA+LVCnt, LNextColumnA+LVCnt, LNextColumnA+LVCnt+1, LColumnA+LVCnt+1);
//printf("%d-%d\n", LNextColumnA+LVCnt+1, LColumnA+LVCnt+1);fflush(stdout);
      }
     }
    }
    LNextColumnA=0;
    if(LProfileSpline->Closed)
    {
     LVN=LSegments-1;
     for(LVCnt=0;LVCnt<LVN;LVCnt++)
     {
      E3d_PolygonSet(LPolygon++, 4, LColumnA+LVCnt, LNextColumnA+LVCnt, LNextColumnA+LVCnt+1, LColumnA+LVCnt+1);
     }
     E3d_PolygonSet(LPolygon++, 4, LColumnA+LVCnt, LNextColumnA+LVCnt, LNextColumnA, LColumnA);
    }
    else
    {
     for(LVCnt=0;LVCnt<LSegments;LVCnt++)
     {
      E3d_PolygonSet(LPolygon++, 4, LColumnA+LVCnt, LNextColumnA+LVCnt, LNextColumnA+LVCnt+1, LColumnA+LVCnt+1);
//printf(">%d-%d\n", LNextColumnA+LVCnt+1, LColumnA+LVCnt+1);fflush(stdout);
     }
    }
   }
   else
   {
    for(LUCnt=0;LUCnt<LSides;LUCnt++, LColumnA+=LVPoints, LNextColumnA+=LVPoints)
    {
     if(LProfileSpline->Closed)
     {
      LVN=LSegments-1;
      for(LVCnt=0;LVCnt<LVN;LVCnt++)
      {
       E3d_PolygonSet(LPolygon++, 4, LColumnA+LVCnt, LNextColumnA+LVCnt, LNextColumnA+LVCnt+1, LColumnA+LVCnt+1);
      }
      E3d_PolygonSet(LPolygon++, 4, LColumnA+LVCnt, LNextColumnA+LVCnt, LNextColumnA, LColumnA);
     }
     else
     {
      for(LVCnt=0;LVCnt<LSegments;LVCnt++)
      {
       E3d_PolygonSet(LPolygon++, 4, LColumnA+LVCnt, LNextColumnA+LVCnt, LNextColumnA+LVCnt+1, LColumnA+LVCnt+1);
      }
     }
    }
   }
  }

// Set up normals
//
  LPolyGroup->DiscontinuityAngle=LInfo->SmoothingAngle;

  _SetVertices(LMesh->Vertices, LInfo);

  E3d_GeometryUpdateForDisplay((E3dGeometry*)LMesh, E3dGF_ALL-E3dGF_CALL_DESTROYPROCS);
  if(LNewModel) E3d_ModelHrcUpdateMaterialsForDisplay(LModel);
  return(LModel);
 }
 return(NULL);
}


/*======================================*/
/* Update the dialog			*/
/*======================================*/
static void _UpdateDialog(E3dRevolveInfo* LInfo)
{
 E3d_GeometryPanelUpdate(&_GeometryPanel, _MainGeometry);

 XeToggleButtonSetState(_TrianglesW, LInfo->Triangles, False);

 XeScaleSetValueD(_SmoothingAngleW, LInfo->SmoothingAngle, False);

 XeScaleSetValueD(_StartAngleScaleW, LInfo->StartAngle, False);
 XeScaleSetValueD(_EndAngleScaleW, LInfo->EndAngle, False);

 XeScaleSetValue(_SidesScaleW, LInfo->Sides, False);
}


/*======================================*/
/* Revolve dialog callback		*/
/*======================================*/
static void _DCB(EguiItem LI, EPointer LClientData, EPointer LCallData)
{
 E3dMesh*		LMesh=(E3dMesh*)_Geometry;
 E3dRevolveInfo*	LInfo=(E3dRevolveInfo*)E3d_GeometryInfoByClass(_Geometry, _Info.Class);

 switch((int)LClientData)
 {
  case EguiOK:
   E3dTpl_GeoOK(_);
  break;

  case EguiADD:
   E3dTpl_GeoADD(_);
  break;

  case EguiCANCEL:
   E3dTpl_GeoCANCEL(_);
  break;

  case E3dCR_TRIANGLES:
   LInfo->Triangles=((XeToggleButtonCallbackStruct*)LCallData)->set;
   _Get(_Model, _Geometry, LInfo, E3dGF_ALL-E3dGF_CALL_DESTROYPROCS);
   E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
  break;

  case E3dCR_CHANGE_SMOOTHING_ANGLE:
   {
    E3dPolyGroup*	LPolyGroup;

    LPolyGroup=LMesh->PolyGroups[0];

    LPolyGroup->DiscontinuityAngle=LInfo->SmoothingAngle=((XeScaleCallbackStruct*)LCallData)->FloatValue;

    E3d_MeshRefreshNormals(LMesh, FALSE);
    E3d_GeometryUpdateForDisplay((E3dGeometry*)LMesh, E3dGF_NORMALS);
    E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
   }
  break;

  case E3dCR_SET_START_ANGLE:
   {
    EBool		LWasFull, LFull;


    if((LInfo->EndAngle-LInfo->StartAngle)==360.0) LWasFull=TRUE;
    else LWasFull=FALSE;

    LInfo->StartAngle=((XeScaleCallbackStruct*)LCallData)->FloatValue;

    if(LInfo->StartAngle>LInfo->EndAngle)
    {
     LInfo->EndAngle=LInfo->StartAngle;
     XeScaleSetValueD(_EndAngleScaleW, (double)(LInfo->EndAngle), False);
    }
    if(LInfo->StartAngle<(LInfo->EndAngle-360.0))
    {
     LInfo->EndAngle=LInfo->StartAngle+360.0;
     XeScaleSetValueD(_EndAngleScaleW, (double)(LInfo->EndAngle), False);
    }

    if((LInfo->EndAngle-LInfo->StartAngle)==360.0) LFull=TRUE;
    else LFull=FALSE;

    if(LFull==LWasFull)
    {
     _SetVertices(LMesh->Vertices, LInfo);
     E3d_GeometryUpdateForDisplay(_Geometry, E3dGF_SHAPE);
    }
    else _Get(_Model, _Geometry, LInfo, E3dGF_ALL-E3dGF_CALL_DESTROYPROCS);

    E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_NORMAL3D);
   }
  break;

  case E3dCR_SET_END_ANGLE:
   {
    EBool		LWasFull, LFull;


    if((LInfo->EndAngle-LInfo->StartAngle)==360.0) LWasFull=TRUE;
    else LWasFull=FALSE;

    LInfo->EndAngle=((XeScaleCallbackStruct*)LCallData)->FloatValue;

    if(LInfo->StartAngle>LInfo->EndAngle)
    {
     LInfo->StartAngle=LInfo->EndAngle;
     XeScaleSetValueD(_StartAngleScaleW, (double)(LInfo->StartAngle), False);
    }
    if(LInfo->StartAngle<(LInfo->EndAngle-360.0))
    {
     LInfo->StartAngle=LInfo->EndAngle-360.0;
     XeScaleSetValueD(_StartAngleScaleW, (double)(LInfo->StartAngle), False);
    }


    if((LInfo->EndAngle-LInfo->StartAngle)==360.0) LFull=TRUE;
    else LFull=FALSE;

    if(LFull==LWasFull)
    {
     _SetVertices(LMesh->Vertices, LInfo);
     E3d_GeometryUpdateForDisplay(_Geometry, E3dGF_SHAPE);
    }
    else _Get(_Model, _Geometry, LInfo, E3dGF_ALL-E3dGF_CALL_DESTROYPROCS);

    E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_NORMAL3D);
   }
  break;

  case E3dCR_SET_SIDES:
   LInfo->Sides=((XeScaleCallbackStruct*)LCallData)->IntValue;

   _Get(_Model, _Geometry, LInfo, E3dGF_ALL-E3dGF_CALL_DESTROYPROCS);

   E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
  break;
 }
}


/*======================================*/
/* Pop up the dialog			*/
/*======================================*/
static void _PopupDialog(E3dRevolveInfo* LInfo)
{
 if(_Dialog==NULL)
 {
  Widget	LTopMatrixW, LMatrixW;
  Widget	LIndentWs[7];			// For indenting the ScaleWidgets
  Widget	LTraverseWs[7];			// For TextField traversal
  EguiItem	LDialog=E3d_GeometryDialogCreate("Revolve", _MainGeometry, &_GeometryPanel, _DCB, &Image_RevolveBig);
  int		LIndentN, LTraverseN;


  _Dialog=LDialog;

  LTopMatrixW=EGUI_DialogGetChild(LDialog, EguiDIALOG_WORK_AREA);
  EXtStart;
  EXtSetArg(XeNySpacing, 4);
  XtSetValues(LTopMatrixW, Args, ArgCnt);

  EXtStart;
  EXtSetArg(XeNmarginHeight, 0);
  EXtSetArg(XeNbackground, EGUI_BackgroundColor);
  EXtSetArg(XeNorientation, XeHORIZONTAL);
  EXtSetArg(XeNxSpacing, 12);
  EXtSetArg(XeNwidthAdjustable, False);EXtSetArg(XeNxAlignment, XeALIGN_BEGINNING);
  LMatrixW=XtCreateManagedWidget("MatrixW", xeMatrixWidgetClass, LTopMatrixW, Args, ArgCnt);


  EXtStart;
  EXtSetArg(XeNbackground, EGUI_BackgroundColor);
  EXtSetArg(XeNselectColor, EGUI_HighlightColor);
  EXtSetArg(XeNshadowThickness, 0);
  EXtSetArg(XeNhighlightThickness, 0);
  EXtSetArg(XeNfontList, EGUI_LabelFontList);
  EXtSetArg(XeNset, LInfo->Triangles);
  _TrianglesW=XtCreateManagedWidget("Triangles", xeToggleButtonWidgetClass, LMatrixW, Args, ArgCnt);
  XtAddCallback(_TrianglesW, XeNvalueChangedCallback, (XtCallbackProc)_DCB, (XtPointer)E3dCR_TRIANGLES);


  LTraverseN=0;
  LIndentN=0;
  EXtStart;
  EXtSetArg(XeNtextFieldDigits, 6);EXtSetArg(XeNdecimalPoints, 2);
  EXtSetArg(XeNminimum, 0);EXtSetArg(XeNmaximum, 18000);
  EXtSetArg(XeNvalue, (int)(LInfo->SmoothingAngle*100.0));
  EXtSetArg(XeNorientation, XeHORIZONTAL);
  E3dp_SetScaleArgs(Args, &ArgCnt);
  LIndentWs[LIndentN++]=LTraverseWs[LTraverseN++]=_SmoothingAngleW=XtCreateManagedWidget("Smoothing angle:", xeScaleWidgetClass, LTopMatrixW, Args, ArgCnt);
  XtAddCallback(_SmoothingAngleW, XeNvalueChangedCallback, (XtCallbackProc)_DCB, (XtPointer)E3dCR_CHANGE_SMOOTHING_ANGLE);

  EXtStart;
  EXtSetArg(XeNtextFieldDigits, 6);EXtSetArg(XeNdecimalPoints, 2);
  EXtSetArg(XeNminimum, -36000);EXtSetArg(XeNmaximum, 36000);EXtSetArg(XeNvalue, (int)(LInfo->StartAngle*100.0));
  EXtSetArg(XeNscaleMinimum, -36000);EXtSetArg(XeNscaleMaximum, 36000);
  EXtSetArg(XeNincrement, 10);
  EXtSetArg(XeNorientation, XeHORIZONTAL);
  E3dp_SetScaleArgs(Args, &ArgCnt);
  LIndentWs[LIndentN++]=LTraverseWs[LTraverseN++]=_StartAngleScaleW=XtCreateManagedWidget("Start angle", xeScaleWidgetClass, LTopMatrixW, Args, ArgCnt);
  XtAddCallback(_StartAngleScaleW, XeNvalueChangedCallback, (XtCallbackProc)_DCB, (XtPointer)E3dCR_SET_START_ANGLE);

  EXtStart;
  EXtSetArg(XeNtextFieldDigits, 6);EXtSetArg(XeNdecimalPoints, 2);
  EXtSetArg(XeNminimum, -36000);EXtSetArg(XeNmaximum, 36000);EXtSetArg(XeNvalue, (int)(LInfo->EndAngle*100.0));
  EXtSetArg(XeNscaleMinimum, -36000);EXtSetArg(XeNscaleMaximum, 36000);
  EXtSetArg(XeNincrement, 10);
  EXtSetArg(XeNorientation, XeHORIZONTAL);
  E3dp_SetScaleArgs(Args, &ArgCnt);
  LIndentWs[LIndentN++]=LTraverseWs[LTraverseN++]=_EndAngleScaleW=XtCreateManagedWidget("End angle", xeScaleWidgetClass, LTopMatrixW, Args, ArgCnt);
  XtAddCallback(_EndAngleScaleW, XeNvalueChangedCallback, (XtCallbackProc)_DCB, (XtPointer)E3dCR_SET_END_ANGLE);


  EXtStart;
  EXtSetArg(XeNtextFieldDigits, 6);
  EXtSetArg(XeNminimum, 3);EXtSetArg(XeNmaximum, 100);
  EXtSetArg(XeNscaleMaximum, 50);
  EXtSetArg(XeNvalue, LInfo->Sides);
  EXtSetArg(XeNorientation, XeHORIZONTAL);
  E3dp_SetScaleArgs(Args, &ArgCnt);
  LIndentWs[LIndentN++]=LTraverseWs[LTraverseN++]=_SidesScaleW=XtCreateManagedWidget("Sides:", xeScaleWidgetClass, LTopMatrixW, Args, ArgCnt);
  XtAddCallback(_SidesScaleW, XeNvalueChangedCallback, (XtCallbackProc)_DCB, (XtPointer)E3dCR_SET_SIDES);

  XeIndentWidgetsFromLeft(LIndentWs, LIndentN, 2);


// Set up TextField traversal
//
  XeRingTraversal(LTraverseWs, LTraverseN);

  EGUI_RaiseShell(_Dialog);
  EGUI_CenterShellToItem(_Dialog, E3dp_TopShell);
 }
 else { _UpdateDialog(LInfo);EGUI_RaiseShell(_Dialog); }

 _Plugin->LockCount+=1;
}


/*==============================================================*/
/* Callback for Profile-Spline changes				*/
/* This function should Not call E3dp_Refresh3DWindows(), just	*/
/* return with TRUE if a redraw is needed!			*/
/*==============================================================*/
static EBool _InputChanged(void* LGeo, EPointer LClientData, EPointer LCallData)
{
 E3dRevolveInfo*	LInfo=(E3dRevolveInfo*)LClientData;

 _Get(LInfo->Model, LInfo->Geometry, LInfo, E3dGF_ALL-E3dGF_CALL_DESTROYPROCS);
 return(TRUE);
}


/*==============================================*/
/* Menu/ToolPanel callback			*/
/*==============================================*/
static void _MCB(EguiItem LGUIItem, EPointer LClientData, EPointer LCallData)
{
 E3dItem	LItem;


 if(_Geometry) return;

 if(E3d_SceneGetSingleSelection(E3d_Scene, &LItem, E3dITEM_SPLINE)!=E3dITEM_NONE)
 {
  E3dSplineItem*	LSplineItem=(E3dSplineItem*)(&LItem);
  E3dSpline*		LSpline=LSplineItem->Spline;
  EBool			LSuccess=FALSE;


  _Info.ProfileGeometry=(E3dGeometry*)LSpline;LSpline->RefCnt+=1;

  if((_Model=_Get(NULL, NULL, &_Info, E3dGF_ALL-E3dGF_CALL_DESTROYPROCS))!=NULL)
  {
   E3dRevolveInfo*	LInfo;

   _MainGeometry=_Geometry=_Model->Geometries[0];_Geometry->RefCnt+=1;
   _Geometry->AutoLOD=FALSE;

   LInfo=(E3dRevolveInfo*)E3d_GeometryInfoByClass(_Geometry, _Info.Class);

   LInfo->InputNode.ClientData=(EPointer)LInfo;

   E3d_GeometryAddOutput((E3dGeometry*)LSpline, &(LInfo->InputNode));

   LSuccess=E3d_SceneAddModelHrc(E3d_Scene, _Model);
   E3dp_SceneLayoutOnSchematics(E3d_Scene);
  }
  _CreateMode=TRUE;

  if(LSuccess)
  {
   if(((EguiPushButtonCallbackStruct*)LCallData)->Reason==ECR_OPTIONS) _PopupDialog(&_Info);
   else
   {
    E3dTpl_GeoDoneEditing(_);
   }
   E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
  }
 }
 else E3dp_PrintMessage(0, 5000, "Select a Spline to revolve");
}


/*==============================================*/
/* EditProc for the "Revolve" Geometry type	*/
/*==============================================*/
static int _EditProc(E3dModel* LModel, E3dGeometry* LGeometry, E3dGeometryInfo* LInfo, int LLODIndex)
{
 E3dTplEditProc_LODCHistInput(E3dRevolve, _);
}


/*==============================================*/
/* DestroyProc for the "Revolve" Geometry type	*/
/*==============================================*/
static int _DestroyProc(E3dModel* LModel, E3dGeometry* LGeometry, E3dGeometryInfo* LInfoIn)
{
 E3dRevolveInfo*	LInfo=(E3dRevolveInfo*)LInfoIn;

 E3d_GeometryRemoveOutput(LInfo->ProfileGeometry, &(LInfo->InputNode));
 if(LInfo->ProfileGeometry) E3d_GeometryFree(LInfo->ProfileGeometry);

 return(0);
}


/*======================================*/
/* Entry point of the plugin		*/
/*======================================*/
int Plugin_Init(EPlugin* LPlugin)
{
 E3dGeometryClass	LGeoClass=
   {
    "Revolve",			// Name
    sizeof(E3dRevolveInfo),	// StructSize
    _EditProc,			// EditProc
    _DestroyProc,		// DestroyProc
    _Resources			// Resources
   };


 _Plugin=LPlugin;

 if((_Info.Class=E3d_GeometryClassRegister(&LGeoClass))!=NULL)
 {
  EpImage*	LImage;
  EpImage*	LActiveImage;
  EpImage*	LArmImage;

  EpM_RGBA8ImageFromCStruct(LImage, Image_Revolve);
  EpM_RGBA8ImageFromCStruct(LActiveImage, Image_RevolveActive);
  EpM_RGBA8ImageFromCStruct(LArmImage, Image_RevolveArm);
  _ToolPanelButton=EGUI_AddPushButtonImg("Tool->Surface", "Revolve", '\0', NULL, NULL, TRUE, "Revolve spline to create a surface\nRight button: dialog box for this tool", _MCB, (EPointer)0, LImage, LActiveImage, LArmImage);



  _Info.ProfileGeometry=NULL;
  _Info.StartAngle=0.0;_Info.EndAngle=360.0;
  _Info.Sides=24;
  _Info.AroundWorldAxis=TRUE;
  _Info.Close=FALSE;
  _Info.Triangles=FALSE;
  _Info.SmoothingAngle=60.0;

  _Info.InputNode.Proc=_InputChanged;

  _MenuButton=EGUI_AddPushButton("Menu->Surface", "Revolve", '\0', NULL, NULL, TRUE, NULL, _MCB, (EPointer)0);
 }
 return(0);
}


/*======================================*/
/* Exit method of the plugin		*/
/*======================================*/
int Plugin_Exit()
{
 if(_MenuButton!=NULL) EGUI_DestroyItem(_MenuButton);
 if(_ToolPanelButton!=NULL) EGUI_DestroyItem(_ToolPanelButton);

 if(_Info.Class!=NULL) E3d_GeometryClassDeactivate(_Info.Class);

 return(0);
}
