/*======================================================================================================*/
/* Add a torus to the scene and change it interactively							*/
/*													*/
/* Plugin for EQUINOX-3D										*/
/*													*/
/* AUTHOR:	Gabor Nagy										*/
/* DATE:	1996-Dec-17 23:49:21									*/
/*													*/
/* Notes:												*/
/* Smooth U and smooth V separately!									*/
/*													*/
/* 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/Matrix.h>
#include <Xe/Scale.h>
#include <Xe/Separator.h>
#include <Xe/ToggleButton.h>

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

#include <E3D/E3D.h>
#include <E3D/3DWindow.h>


static EPlugin*		_Plugin=NULL;


enum
{
 E3dCR_TRIANGLES=EguiCUSTOM0,
 E3dCR_SMOOTH_U,
 E3dCR_SMOOTH_V,
 E3dCR_MAIN_RADIUS,
 E3dCR_CIRCLE_RADIUS,
 E3dCR_MAIN_SECTIONS,
 E3dCR_CIRCLE_SECTIONS
};

static EguiItem		_Dialog=NULL;

static Widget		E3d_SmoothUW, E3d_SmoothVW, E3d_TrianglesW=NULL,
			E3d_MainRadiusScaleW, E3d_CircleRadiusScaleW, E3d_MainSectionsScaleW, E3d_CircleSectionsScaleW;

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

static EguiItem		E3d_MenuButton=NULL, E3d_ToolPanelButton=NULL;

static Arg		Args[256];
static Cardinal		ArgCnt;

extern EpCImage		Image_Torus;
extern EpCImage		Image_TorusActive;
extern EpCImage		Image_TorusArm;
extern EpCImage		Image_TorusBig;


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

// E3dTorusInfo part
//
 float		MainRadius, CircleRadius;

 unsigned int	MainSections, CircleSections;
 EBool		SmoothU, SmoothV, Triangles;
} E3dTorusInfo;


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

static EResource _Resources[]=
{
 { "MainRadius",	EResFLOAT32,	ROffset(MainRadius), NULL },
 { "CircleRadius",	EResFLOAT32,	ROffset(CircleRadius), NULL },
 { "MainSections",	EResINT,	ROffset(MainSections), NULL },
 { "CircleSections",	EResINT,	ROffset(CircleSections), NULL },

 { "SmoothU",		EResBOOLEAN,	ROffset(SmoothU), NULL },
 { "SmoothV",		EResBOOLEAN,	ROffset(SmoothV), NULL },
 { "Triangles",		EResBOOLEAN,	ROffset(Triangles), NULL },
 { "",			EResNULL,	NULL, NULL }
};


static E3dTorusInfo	_Info;
static EBool		_CreateMode=FALSE;

static E3dGeometryPanel	_TorusGeometryPanel;


/*======================================*/
/* Get a torus				*/
/*======================================*/
static E3dModel* _Get(E3dModel* LModel, E3dGeometry* LGeometry, E3dTorusInfo* LInfoV, unsigned int LFlags)
{
 E3dMesh*		LMesh;
 E3dPolyGroup*		LPolyGroup=NULL;
 E3dPolygon*		LPolygon;
 E3dVertex*		LVertices;
 E3dVertex*		LVertex;
 E3dVertexNode*		LVertexNodes;
 E3dCoordinate		LTheta, LPhi, cx, cy, dx, dy, dz;
 E3dTorusInfo*		LInfo;
 E3dCoordinate		LMainRadius, LCircleRadius;
 int			LMainSections, LCircleSections;
 unsigned int		LN, LCV, LC, LH;
 EBool			LSmoothU, LSmoothV, LTriangles,
			LRefreshTopology=TRUE;


 if(LGeometry==NULL)
 {
  if((LModel=E3d_ModelAllocate("Torus"))==NULL) return(NULL);
  if((LMesh=(E3dMesh*)E3d_ModelAddGeometry(LModel, E3dGEO_MESH, "torus"))==NULL) { E3d_ModelHrcFree(LModel, TRUE);return(NULL); }

  LInfo=(E3dTorusInfo*)E3d_GeometryInfoAdd((E3dGeometry*)LMesh, LInfoV->Class);
  if(LInfo)
  {
   memcpy(LInfo, LInfoV, sizeof(E3dTorusInfo));
  }

  if(LMesh->PolyGroups==NULL) if((LPolyGroup=E3d_MeshAddPolyGroup(LMesh))==NULL) { E3d_ModelHrcFree(LModel, TRUE);return(NULL); }
  LPolyGroup->VertexNormalType=E3dNormalPROCESS;
 }
 else
 {
  LMesh=(E3dMesh*)LGeometry;
  LInfo=LInfoV;
  LPolyGroup=LMesh->PolyGroups[0];
 }

 LSmoothU=LInfo->SmoothU;
 LSmoothV=LInfo->SmoothV;
 LTriangles=LInfo->Triangles;
 LMainRadius=LInfo->MainRadius;
 LCircleRadius=LInfo->CircleRadius;
 LMainSections=LInfo->MainSections;
 LCircleSections=LInfo->CircleSections;

 if((LN=LCircleSections*LMainSections)==LMesh->NumOfVertices) LVertices=LMesh->Vertices;
 else
 {
  if(LMesh->Vertices) E3d_MeshFreeVertices(LMesh);
  LVertices=LMesh->Vertices=E3d_VerticesAllocate(LMesh->NumOfVertices=LN, TRUE);
 }
 if(LVertices)
 {
  LCV=0;
  for(LH=0;LH<LMainSections;LH++)
  {
   LTheta=3.14159265358979323846264*2.0*LH/LMainSections;
   cx=cos(LTheta)*LMainRadius;
   cy=sin(LTheta)*LMainRadius;
   for(LC=0;LC<LCircleSections;LC++)
   {
    LPhi=3.14159265358979323846264*2.0*LC/LCircleSections;
    dx=cos(LTheta)*cos(LPhi)*LCircleRadius;
    dy=sin(LTheta)*cos(LPhi)*LCircleRadius;
    dz=sin(LPhi)*LCircleRadius;
    LVertices[LCV].X=cx+dx;LVertices[LCV].Y=dz;LVertices[LCV++].Z=cy+dy;
   }
  }

  if(LRefreshTopology)
  {
   LN=LMainSections*LCircleSections;
   if(LInfo->Triangles) LN*=2;
   if(LN==LPolyGroup->NumOfPolygons) LPolygon=LPolyGroup->Polygons;
   else
   {
    if(LPolyGroup->Polygons!=NULL) E3d_PolyGroupFreePolygons(LPolyGroup);
    LPolygon=LPolyGroup->Polygons=E3d_PolygonsAllocate(LPolyGroup->NumOfPolygons=LN);
   }
   if(LPolygon!=NULL)
   {
#define left(row, col)	((row)*LCircleSections+(col))
#define right(row, col)	((row)*LCircleSections+((col)+1)%LCircleSections)
    if(LInfo->Triangles)
    {
     for(LH=0;LH<LMainSections;LH++)
     {
      for(LC=0;LC<LCircleSections;LC++)
      {
       E3d_PolygonSetAsTriangle(LPolygon, left(LH, LC), right(LH, LC), left((LH+1)%LMainSections, LC));LPolygon++;
       E3d_PolygonSetAsTriangle(LPolygon, right(LH, LC), right((LH+1)%LMainSections, LC), left((LH+1)%LMainSections, LC));LPolygon++;
      }
     }
    }
    else
    {
     for(LH=0;LH<LMainSections;LH++)
     {
      for(LC=0;LC<LCircleSections;LC++) { E3d_PolygonSet(LPolygon, 4, left(LH, LC), right(LH, LC), right((LH+1)%LMainSections, LC), left((LH+1)%LMainSections, LC));LPolygon++; }
     }
    }

    LPolygon=LPolyGroup->Polygons;
    for(LC=0;LC<LN;LC++, LPolygon++)
    {
     LVertexNodes=LPolygon->VertexNodes;
     for(LH=0;LH<LPolygon->NumOfVertexNodes;LH++)
     {
      if(LVertexNodes[LH].VertexID!=-1)
      {
       LVertex=LVertices+LVertexNodes[LH].VertexID;
       LVertexNodes[LH].S=LVertex->X/(LMainRadius+LCircleRadius)*0.5+0.5;
       LVertexNodes[LH].T=LVertex->Y/LCircleRadius*-0.5+0.5;
      }
     }
    }
   }
   E3d_MeshCreateEdgesFromPolyData(LMesh, NULL);
  }
  if(LSmoothU&&LSmoothV)
  {
   LPolyGroup->VertexNormalType=E3dNormalAVERAGED;
   LPolyGroup->Flags|=E3dUSE_VERTEX_NORMALS;
  }
  else
  {
   LPolyGroup->VertexNormalType=E3dNormalNONE;
   LPolyGroup->Flags&=E3dALL-E3dUSE_VERTEX_NORMALS;
  }

  E3d_MeshRefreshNormals(LMesh, TRUE);
  E3d_GeometryUpdateForDisplay((E3dGeometry*)LMesh, LFlags&(E3dGF_ALL-E3dGF_CALL_DESTROYPROCS));
  return(LModel);
 }
 return(NULL);
}


/*======================================*/
/* Update dialog			*/
/*======================================*/
static void _UpdateDialog(E3dTorusInfo* LInfo)
{
 E3d_GeometryPanelUpdate(&_TorusGeometryPanel, _MainGeometry);

 XeToggleButtonSetState(E3d_SmoothUW, LInfo->SmoothU, False);
 XeToggleButtonSetState(E3d_SmoothVW, LInfo->SmoothV, False);
 XeToggleButtonSetState(E3d_TrianglesW, LInfo->Triangles, False);

 XeScaleSetValueD(E3d_MainRadiusScaleW, LInfo->MainRadius, False);
 XeScaleSetValueD(E3d_CircleRadiusScaleW, LInfo->CircleRadius, False);

 XeScaleSetValue(E3d_MainSectionsScaleW, LInfo->MainSections, False);
 XeScaleSetValue(E3d_CircleSectionsScaleW, LInfo->CircleSections, False);
}


/*======================================*/
/* Torus dialog callback		*/
/*======================================*/
static void _DCB(EguiItem LI, EPointer LClientData, EPointer LCallData)
{
 switch((int)LClientData)
 {
  case EguiOK:
   E3dTpl_GeoOK(_);
  break;

  case EguiADD:
   E3dTpl_GeoADD(_);
  break;

  case EguiCANCEL:
   E3dTpl_GeoCANCEL(_);
  break;
 }
}


/*======================================*/
/* GetTorus dialog callback		*/
/*======================================*/
static void _XtDCB(Widget LW, XtPointer LClientData, XtPointer LCallData)
{
 E3dTorusInfo*	LInfo=(E3dTorusInfo*)E3d_GeometryInfoByClass(_Geometry, _Info.Class);


 switch((int)LClientData)
 {
  case E3dCR_MAIN_RADIUS:
   LInfo->MainRadius=((XeScaleCallbackStruct*)LCallData)->FloatValue;
   _Get(_Model, _Geometry, LInfo, E3dGF_SHAPE);
   E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
  break;

  case E3dCR_CIRCLE_RADIUS:
   LInfo->CircleRadius=((XeScaleCallbackStruct*)LCallData)->FloatValue;
   _Get(_Model, _Geometry, LInfo, E3dGF_SHAPE);
   E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
  break;

  case E3dCR_MAIN_SECTIONS:
   LInfo->MainSections=((XeScaleCallbackStruct*)LCallData)->IntValue;
   _Get(_Model, _Geometry, LInfo, E3dGF_ALL);
   E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
  break;

  case E3dCR_CIRCLE_SECTIONS:
   LInfo->CircleSections=((XeScaleCallbackStruct*)LCallData)->IntValue;
   _Get(_Model, _Geometry, LInfo, E3dGF_ALL);
   E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
  break;

  case E3dCR_SMOOTH_U:
   if(_Model!=NULL)
   {
    E3dMesh*		LMesh=(E3dMesh*)_Geometry;
    E3dPolyGroup*	LPolyGroup;

    LPolyGroup=LMesh->PolyGroups[0];
    LInfo->SmoothU=((XeToggleButtonCallbackStruct*)LCallData)->set;
    if(LInfo->SmoothU)
    {
     LPolyGroup->VertexNormalType=E3dNormalAVERAGED;
     LPolyGroup->Flags|=E3dUSE_VERTEX_NORMALS;
     E3d_MeshRefreshNormals(LMesh, FALSE);
    }
    else
    {
     LPolyGroup->VertexNormalType=E3dNormalNONE;
     LPolyGroup->Flags&=E3dALL-E3dUSE_VERTEX_NORMALS;
    }
    E3d_GeometryUpdateForDisplay((E3dGeometry*)LMesh, E3dGF_NORMALS);
    E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
   }
  break;

  case E3dCR_SMOOTH_V:
  break;

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


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


  _Dialog=LDialog;

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


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

  EXtStart;
  EXtSetArg(XeNbackground, EGUI_BackgroundColor);
  EXtSetArg(XeNselectColor, EGUI_HighlightColor);
  EXtSetArg(XeNshadowThickness, 0);
  EXtSetArg(XeNhighlightThickness, 0);
  EXtSetArg(XeNmarginLeft, 0);EXtSetArg(XeNmarginRight, 0);EXtSetArg(XeNmarginTop, 0);EXtSetArg(XeNmarginBottom, 0);
  EXtSetArg(XeNfontList, EGUI_LabelFontList);
  EXtSetArg(XeNset, LInfo->SmoothU);
  E3d_SmoothUW=XtCreateManagedWidget("Smooth U", xeToggleButtonWidgetClass, LMatrixW, Args, ArgCnt);
  XtAddCallback(E3d_SmoothUW, XeNvalueChangedCallback, _XtDCB, (XtPointer)E3dCR_SMOOTH_U);

  EXtStart;
  EXtSetArg(XeNbackground, EGUI_BackgroundColor);
  EXtSetArg(XeNselectColor, EGUI_HighlightColor);
  EXtSetArg(XeNshadowThickness, 0);
  EXtSetArg(XeNhighlightThickness, 0);
  EXtSetArg(XeNmarginLeft, 0);EXtSetArg(XeNmarginRight, 0);EXtSetArg(XeNmarginTop, 0);EXtSetArg(XeNmarginBottom, 0);
  EXtSetArg(XeNfontList, EGUI_LabelFontList);
  EXtSetArg(XeNset, LInfo->SmoothV);
  E3d_SmoothVW=XtCreateManagedWidget("Smooth V", xeToggleButtonWidgetClass, LMatrixW, Args, ArgCnt);
  XtAddCallback(E3d_SmoothVW, XeNvalueChangedCallback, _XtDCB, (XtPointer)E3dCR_SMOOTH_V);

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



  LTraverseN=0;
  LIndentN=0;
  EXtStart;
  EXtSetArg(XeNtextFieldDigits, 8);EXtSetArg(XeNdecimalPoints, 4);
  EXtSetArg(XeNminimum, 1);EXtSetArg(XeNmaximum, 1000000);EXtSetArg(XeNvalue, (int)(LInfo->MainRadius*10000.0));
  EXtSetArg(XeNincrement, 10);
  EXtSetArg(XeNorientation, XeHORIZONTAL);
  E3dp_SetScaleArgs(Args, &ArgCnt);
  LIndentWs[LIndentN++]=LTraverseWs[LTraverseN++]=E3d_MainRadiusScaleW=XtCreateManagedWidget("Main radius:", xeScaleWidgetClass, LTopMatrixW, Args, ArgCnt);
  XtAddCallback(E3d_MainRadiusScaleW, XeNvalueChangedCallback, _XtDCB, (XtPointer)E3dCR_MAIN_RADIUS);

  EXtStart;
  EXtSetArg(XeNtextFieldDigits, 8);EXtSetArg(XeNdecimalPoints, 4);
  EXtSetArg(XeNminimum, 1);EXtSetArg(XeNmaximum, 1000000);EXtSetArg(XeNvalue, (int)(LInfo->CircleRadius*10000.0));
  EXtSetArg(XeNincrement, 10);
  EXtSetArg(XeNorientation, XeHORIZONTAL);
  E3dp_SetScaleArgs(Args, &ArgCnt);
  LIndentWs[LIndentN++]=LTraverseWs[LTraverseN++]=E3d_CircleRadiusScaleW=XtCreateManagedWidget("Circle radius:", xeScaleWidgetClass, LTopMatrixW, Args, ArgCnt);
  XtAddCallback(E3d_CircleRadiusScaleW, XeNvalueChangedCallback, _XtDCB, (XtPointer)E3dCR_CIRCLE_RADIUS);

  XeIndentWidgetsFromLeft(LIndentWs, LIndentN, 4);


  LIndentN=0;
  EXtStart;
  EXtSetArg(XeNtextFieldDigits, 8);
  EXtSetArg(XeNminimum, 3);EXtSetArg(XeNmaximum, 100);EXtSetArg(XeNvalue, LInfo->MainSections);
  EXtSetArg(XeNincrement, 10);
  EXtSetArg(XeNorientation, XeHORIZONTAL);
  E3dp_SetScaleArgs(Args, &ArgCnt);
  LIndentWs[LIndentN++]=LTraverseWs[LTraverseN++]=E3d_MainSectionsScaleW=XtCreateManagedWidget("Main sections:", xeScaleWidgetClass, LTopMatrixW, Args, ArgCnt);
  XtAddCallback(E3d_MainSectionsScaleW, XeNvalueChangedCallback, _XtDCB, (XtPointer)E3dCR_MAIN_SECTIONS);


  EXtStart;
  EXtSetArg(XeNtextFieldDigits, 8);
  EXtSetArg(XeNminimum, 3);EXtSetArg(XeNmaximum, 100);EXtSetArg(XeNvalue, LInfo->CircleSections);
  EXtSetArg(XeNincrement, 1);
  EXtSetArg(XeNorientation, XeHORIZONTAL);
  E3dp_SetScaleArgs(Args, &ArgCnt);
  LIndentWs[LIndentN++]=LTraverseWs[LTraverseN++]=E3d_CircleSectionsScaleW=XtCreateManagedWidget("Circle sections:", xeScaleWidgetClass, LTopMatrixW, Args, ArgCnt);
  XtAddCallback(E3d_CircleSectionsScaleW, XeNvalueChangedCallback, _XtDCB, (XtPointer)E3dCR_CIRCLE_SECTIONS);

  XeIndentWidgetsFromLeft(LIndentWs, LIndentN, 4);


// Set up TextField traversal
//
  XeRingTraversal(LTraverseWs, LTraverseN);
 }
 else _UpdateDialog(LInfo);

 EGUI_RaiseShell(_Dialog);_Plugin->LockCount+=1;
}


/*======================================*/
/* Main callback (create new torus)	*/
/*======================================*/
static void _MCB(EguiItem LGUIItem, EPointer LClientData, EPointer LCallData)
{
 E3dTpl_ACB_CHist(E3dTorus, _);
}


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


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


 _Plugin=LPlugin;

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

  _Info.MainRadius=2.0;_Info.CircleRadius=0.7;
  _Info.MainSections=16;_Info.CircleSections=12;
  _Info.SmoothU=TRUE;_Info.SmoothV=TRUE;_Info.Triangles=TRUE;

  EpM_RGBA8ImageFromCStruct(LImage, Image_Torus);
  EpM_RGBA8ImageFromCStruct(LActiveImage, Image_TorusActive);
  EpM_RGBA8ImageFromCStruct(LArmImage, Image_TorusArm);

  E3d_ToolPanelButton=EGUI_AddPushButtonImg("Tool->Create", "Torus", '\0', NULL, NULL, TRUE, "Create torus\nRight button:  dialog box for this tool", _MCB, (EPointer)0, LImage, LActiveImage, LArmImage);

  E3d_MenuButton=EGUI_AddPushButton("Menu->Create", "Torus", '\0', NULL, NULL, TRUE, NULL, _MCB, (EPointer)0);
 }
 return(0);
}


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

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

 return(0);
}
