/*======================================================================================================*/
/* Create a tube and change it interactively								*/
/*													*/
/* AUTHOR:	Gabor Nagy										*/
/* DATE:	1996-Dec-17 22:52:56									*/
/*													*/
/* EQUINOX-3D(TM), 3DPanel(TM) and 3DLib(TM) Copyright (C) 1995 By Gabor Nagy. All rights reserved.	*/
/*======================================================================================================*/
#include <math.h>
#include <stdio.h>
#include <assert.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/Dialog.h>
#include <EGUI/Menu.h>

#include <E3D/3DWindow.h>


static EPlugin*		_Plugin=NULL;


enum
{
 E3dTubePOLYGONS=0,
 E3dTubeBEZIER,
 E3dTubeNURBS
};

#define E3dRPI	(1.0/180.0*E3dPI)

/*
static EguiOptionMenuRec	_TubeTypes[]=
{
 { "Polygons",	E3dTubePOLYGONS,		NULL, (XtPointer)E3dTubePOLYGONS,	NULL }
// { "Bezier",	E3dTubeBEZIER,		NULL, (XtPointer)E3dTubeBEZIER,	NULL },
// { "NURBS",	E3dTubeNURBS,		NULL, (XtPointer)E3dTubeNURBS,	NULL }
};

static Widget		_TypesOptionMenuW=NULL;
*/


static EguiItem		_Dialog=NULL;
static Widget		_SmoothW,
			_TopW, E3d_BottomW, _OriginAtBottomW,
			_InnerRadiusScaleW, _OuterRadiusScaleW, _HeightScaleW,
			_StartAngleScaleW, _EndAngleScaleW, _SidesScaleW, _RadialSectionsScaleW, _HeightSectionsScaleW;

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

static E3d3DPosition*	_UnitCirclePoints=NULL;

static EguiItem		_MenuButton=NULL, _ToolPanelButton=NULL;

static Arg		Args[256];
static Cardinal		ArgCnt;


extern EpCImage		Image_Tube;
extern EpCImage		Image_TubeActive;
extern EpCImage		Image_TubeArm;
extern EpCImage		Image_TubeBig;


enum
{
 E3dCR_SMOOTH=EguiCUSTOM0+20,
 E3dCR_ORIGIN_AT_BOTTOM,
 E3dCR_TOPCAP,
 E3dCR_BOTTOMCAP,
 E3dCR_SET_OUTER_RADIUS,
 E3dCR_SET_INNER_RADIUS,
 E3dCR_SET_HEIGHT,
 E3dCR_SET_START_ANGLE,
 E3dCR_SET_END_ANGLE,
 E3dCR_SET_SIDES,
 E3dCR_SET_RADIAL_SECTIONS,
 E3dCR_SET_HEIGHT_SECTIONS
};


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

// E3dTubeInfo part
//
 float		InnerRadius, OuterRadius, Height;
 float		StartAngle, EndAngle;

 unsigned int	Sides, RadialSections, HeightSections;
 unsigned int	Type;

 EBool		Smooth, TopCap, BottomCap, AllowHoledPolys, OriginAtBottom;
} E3dTubeInfo;

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

static EResource _Resources[]=
{
 { "InnerRadius",	EResFLOAT32,	ROffset(InnerRadius), NULL },
 { "OuterRadius",	EResFLOAT32,	ROffset(OuterRadius), NULL },
 { "Height",		EResFLOAT32,	ROffset(Height), NULL },
 { "StartAngle",	EResFLOAT32,	ROffset(StartAngle), NULL },
 { "EndAngle",		EResFLOAT32,	ROffset(EndAngle), NULL },

 { "Sides",		EResINT,	ROffset(Sides), NULL },
 { "RadialSections",	EResINT,	ROffset(RadialSections), NULL },
 { "HeightSections",	EResINT,	ROffset(HeightSections), NULL },

 { "Type",		EResINT,	ROffset(Type), NULL },

 { "Smooth",		EResBOOLEAN,	ROffset(Smooth), NULL },
 { "TopCap",		EResBOOLEAN,	ROffset(TopCap), NULL },
 { "BottomCap",		EResBOOLEAN,	ROffset(BottomCap), NULL },
 { "AllowHoledPolys",	EResBOOLEAN,	ROffset(AllowHoledPolys), NULL },
 { "OriginAtBottom",	EResBOOLEAN,	ROffset(OriginAtBottom), NULL },
 { "",			EResNULL,	NULL, NULL }
};


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


static int	_VNum;

/*==============================================================*/
/* Set up the vertices of the unit-radius circle or arc		*/
/*==============================================================*/
static EBool _CreateUnitCircle(int LSides, float LStartAngle, float LEndAngle)
{
 E3d3DPosition*	LPoints;
 float		LAngle, LDeg;
 unsigned int	LC, LSidesN;
 EBool		LFull;

 if(_UnitCirclePoints) { EFree(_UnitCirclePoints);_UnitCirclePoints=NULL; }
 if((_UnitCirclePoints=(E3d3DPosition*)EMalloc(sizeof(E3d3DPosition)*(LSides+1)))==NULL) return(FALSE);

 LDeg=(LEndAngle-LStartAngle)/(double)LSides;

 if((LEndAngle-LStartAngle)==360.0) { LFull=TRUE;LSidesN=LSides; }
 else { LFull=FALSE;LSidesN=LSides+1; }


 for(LC=0, LPoints=_UnitCirclePoints;LC<LSidesN;LC++)
 {
  LAngle=(LDeg*LC+LStartAngle)*E3dRPI;
  LPoints[LC].X=cos(LAngle);LPoints[LC].Y=0.0;LPoints[LC].Z=sin(LAngle);
 }

 return(TRUE);
}


/*==============================================*/
/* Compute vertex coordinates of the tube	*/
/*==============================================*/
static void _SetVertices(register E3dVertex* LVertex, E3dTubeInfo* LInfo)
{
 unsigned int			LC, LC1;
 register E3dCoordinate		LY, LHSize;
 register E3d3DPosition*	LPoint;
 E3dCoordinate			LOuterRadius, LInnerRadius, LHeight, LRad, LRadStep, LTopY, LBottomY;
 unsigned int			LSides, LRadialSections, LHeightSections;
 EBool				LFull;

//E3dVertex* LVertexB=LVertex;

 LOuterRadius=LInfo->OuterRadius;
 LInnerRadius=LInfo->InnerRadius;
 LHeight=LInfo->Height;
 LSides=LInfo->Sides;
 LRadialSections=LInfo->RadialSections;
 LHeightSections=LInfo->HeightSections;

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

 if(LInfo->OriginAtBottom) { LTopY=LHeight;LBottomY=0.0; }
 else { LTopY=LHeight*0.5;LBottomY=-LHeight*0.5; }

 switch(LInfo->Type)
 {
  case E3dTubePOLYGONS:
   _CreateUnitCircle(LInfo->Sides, LInfo->StartAngle, LInfo->EndAngle);

   if(!LFull) LSides++;

   LHSize=LHeight/(double)LHeightSections;

   LRadStep=(LOuterRadius-LInnerRadius)/(E3dCoordinate)LRadialSections;


//printf("#A: %d\n", LVertex-LVertexB);fflush(stdout);

// Sides
//
   LY=LTopY;
   for(LC=0;LC<=LHeightSections;LC++)
   {
    LPoint=_UnitCirclePoints;
    for(LC1=0;LC1<LSides;LC1++)
    {
     LVertex->X=LInnerRadius*LPoint->X;LVertex->Y=LY;LVertex->Z=LInnerRadius*LPoint->Z;
     LVertex++;LPoint++;
    }
    LY-=LHSize;
   }


   LY=LTopY;
   for(LC=0;LC<=LHeightSections;LC++)
   {
    LPoint=_UnitCirclePoints;
    for(LC1=0;LC1<LSides;LC1++)
    {
     LVertex->X=LOuterRadius*LPoint->X;LVertex->Y=LY;LVertex->Z=LOuterRadius*LPoint->Z;
     LVertex++;LPoint++;
    }
    LY-=LHSize;
   }


   if(LRadialSections>1)
   {
// Radial section dividers in top cap
//
    if(LInfo->TopCap)
    {
     unsigned int	LRN=LRadialSections-1;

     LY=LTopY;
     LRad=LInnerRadius+LRadStep;
     for(LC=0;LC<LRN;LC++)
     {
      LPoint=_UnitCirclePoints;
      for(LC1=0;LC1<LSides;LC1++)
      {
       LVertex->X=LRad*LPoint->X;LVertex->Y=LY;LVertex->Z=LRad*LPoint->Z;
       LVertex++;LPoint++;
      }
      LRad+=LRadStep;
     }
    }
   }

// Radial section dividers in bottom cap
//
   if(LRadialSections>1)
   {
    if(LInfo->BottomCap)
    {
     unsigned int	LRN=LRadialSections-1;

     LY=LBottomY;
     LRad=LInnerRadius+LRadStep;
     for(LC=0;LC<LRN;LC++)
     {
      LPoint=_UnitCirclePoints;
      for(LC1=0;LC1<LSides;LC1++)
      {
       LVertex->X=LRad*LPoint->X;LVertex->Y=LY;LVertex->Z=LRad*LPoint->Z;
       LVertex++;LPoint++;
      }
      LRad+=LRadStep;
     }
    }
   }

//printf("#D: %d\n", LVertex-LVertexB);fflush(stdout);
//if((LVertex-LVertexB)!=_VNum) assert(0);

  break;
 }
}




static void _CreatePolygonsForFullTube(E3dTubeInfo* LInfo, E3dMesh* LMesh, E3dPolygon* LPolygon, unsigned int LSides)
{
 E3dVertex*	LVertex=LMesh->Vertices;
 E3d3DPosition*	LPoints;
 E3dVertexNode*	LVertexNode;
 unsigned int	LC, LN, LH, LV, LVC, LRC,
		LRadialSections=LInfo->RadialSections,
		LHeightSections=LInfo->HeightSections;


// Inner sides
//
 LV=0;
 LPoints=_UnitCirclePoints;
 for(LC=0;LC<LHeightSections;LC++, LV+=LSides)
 {
// Use last vtx on this section
//
  E3d_PolygonSet(LPolygon, 4, LV+LSides-1+LSides, LV+LSides, LV, LV+LSides-1);
  LVertexNode=LPolygon->VertexNodes;
  E3d_VertexNormal(&(LPolygon->Normal), LVertex+LVertexNode[0].VertexID, LVertex+LVertexNode[1].VertexID, LVertex+LVertexNode[2].VertexID);
  LN=LSides;

  LVertexNode->Normal.X=-LPoints[LSides-1].X;LVertexNode->Normal.Y=0.0;LVertexNode->Normal.Z=-LPoints[LSides-1].Z;
  LVertexNode++;
  LVertexNode->Normal.X=-LPoints[0].X;LVertexNode->Normal.Y=0.0;LVertexNode->Normal.Z=-LPoints[0].Z;
  LVertexNode++;
  LVertexNode->Normal.X=-LPoints[0].X;LVertexNode->Normal.Y=0.0;LVertexNode->Normal.Z=-LPoints[0].Z;
  LVertexNode++;
  LVertexNode->Normal.X=-LPoints[LSides-1].X;LVertexNode->Normal.Y=0.0;LVertexNode->Normal.Z=-LPoints[LSides-1].Z;
  LPolygon++;

  for(LH=1;LH<LN;LH++) 
  {
   E3d_PolygonSet(LPolygon, 4, LV+LH-1+LSides, LV+LH+LSides, LV+LH, LV+LH-1);
   LVertexNode=LPolygon->VertexNodes;
   LVertexNode->Normal.X=-LPoints[LH-1].X;LVertexNode->Normal.Y=0.0;LVertexNode->Normal.Z=-LPoints[LH-1].Z;
   LVertexNode++;
   LVertexNode->Normal.X=-LPoints[LH].X;LVertexNode->Normal.Y=0.0;LVertexNode->Normal.Z=-LPoints[LH].Z;
   LVertexNode++;
   LVertexNode->Normal.X=-LPoints[LH].X;LVertexNode->Normal.Y=0.0;LVertexNode->Normal.Z=-LPoints[LH].Z;
   LVertexNode++;
   LVertexNode->Normal.X=-LPoints[LH-1].X;LVertexNode->Normal.Y=0.0;LVertexNode->Normal.Z=-LPoints[LH-1].Z;
   LVertexNode=LPolygon->VertexNodes;
   E3d_VertexNormal(&(LPolygon->Normal), LVertex+LVertexNode[0].VertexID, LVertex+LVertexNode[1].VertexID, LVertex+LVertexNode[2].VertexID);
   LPolygon++;
  }

 }

// Outer sides
//
 LV=LSides*(LHeightSections+1);
 LPoints=_UnitCirclePoints;
 for(LC=0;LC<LHeightSections;LC++, LV+=LSides)
 {
// Use last vtx on this section
//
  E3d_PolygonSet(LPolygon, 4, LV+LSides-1, LV, LV+LSides, LV+LSides-1+LSides);
  LVertexNode=LPolygon->VertexNodes;
  E3d_VertexNormal(&(LPolygon->Normal), LVertex+LVertexNode[0].VertexID, LVertex+LVertexNode[1].VertexID, LVertex+LVertexNode[2].VertexID);
  LN=LSides;

  LVertexNode->Normal.X=LPoints[LSides-1].X;LVertexNode->Normal.Y=0.0;LVertexNode->Normal.Z=LPoints[LSides-1].Z;
  LVertexNode++;
  LVertexNode->Normal.X=LPoints[0].X;LVertexNode->Normal.Y=0.0;LVertexNode->Normal.Z=LPoints[0].Z;
  LVertexNode++;
  LVertexNode->Normal.X=LPoints[0].X;LVertexNode->Normal.Y=0.0;LVertexNode->Normal.Z=LPoints[0].Z;
  LVertexNode++;
  LVertexNode->Normal.X=LPoints[LSides-1].X;LVertexNode->Normal.Y=0.0;LVertexNode->Normal.Z=LPoints[LSides-1].Z;
  LPolygon++;

  for(LH=1;LH<LN;LH++) 
  {
   E3d_PolygonSet(LPolygon, 4, LV+LH-1, LV+LH, LV+LH+LSides, LV+LH-1+LSides);
   LVertexNode=LPolygon->VertexNodes;
   LVertexNode->Normal.X=LPoints[LH-1].X;LVertexNode->Normal.Y=0.0;LVertexNode->Normal.Z=LPoints[LH-1].Z;
   LVertexNode++;
   LVertexNode->Normal.X=LPoints[LH].X;LVertexNode->Normal.Y=0.0;LVertexNode->Normal.Z=LPoints[LH].Z;
   LVertexNode++;
   LVertexNode->Normal.X=LPoints[LH].X;LVertexNode->Normal.Y=0.0;LVertexNode->Normal.Z=LPoints[LH].Z;
   LVertexNode++;
   LVertexNode->Normal.X=LPoints[LH-1].X;LVertexNode->Normal.Y=0.0;LVertexNode->Normal.Z=LPoints[LH-1].Z;
   LVertexNode=LPolygon->VertexNodes;
   E3d_VertexNormal(&(LPolygon->Normal), LVertex+LVertexNode[0].VertexID, LVertex+LVertexNode[1].VertexID, LVertex+LVertexNode[2].VertexID);
   LPolygon++;
  }
 }


 if(LInfo->TopCap)
 {
  LV=0;
  if((LRadialSections<2)&&(LInfo->AllowHoledPolys))
  {
   if((LVertexNode=E3d_PolygonVertexNodesAllocate(LPolygon, LSides*2+1))!=NULL)
   {
    for(LC=0, LH=LSides-1;LC<LSides;LH--, LC++, LVertexNode++)
    {
     LVertexNode->VertexID=LH;
     LVertexNode->Normal.X=0.0;LVertexNode->Normal.Y=1.0;LVertexNode->Normal.Z=0.0;
    }
    LPolygon->NumOfVertices=LPolygon->NumOfExteriorVertices=LSides;
    LPolygon->Normal.X=0.0;LPolygon->Normal.Y=1.0;LPolygon->Normal.Z=0.0;
    LPolygon++;
   }
  }
  else
  {
   unsigned int	LNextCircleV;

   for(LRC=0;LRC<LRadialSections;LRC++)
   {
    if(LRC<(LRadialSections-1))
    {
     if((LRC==0)&&(LRadialSections>1)) LNextCircleV=LSides*(LHeightSections+1)*2;
     else LNextCircleV=LV+LSides;
    }
    else LNextCircleV=LSides*(LHeightSections+1);

    for(LVC=0;LVC<LSides;LVC++, LV++)
    {
     if(LVC<(LSides-1)) E3d_PolygonSet(LPolygon, 4, LV+1, LVC+LNextCircleV+1, LVC+LNextCircleV, LV);
     else E3d_PolygonSet(LPolygon, 4, LV-LSides+1, LVC+LNextCircleV-LSides+1, LVC+LNextCircleV, LV);
     LPolygon->Normal.X=0.0;LPolygon->Normal.Y=1.0;LPolygon->Normal.Z=0.0;

     LVertexNode=LPolygon->VertexNodes;
     LVertexNode->Normal.X=0.0;LVertexNode->Normal.Y=1.0;LVertexNode->Normal.Z=0.0;LVertexNode++;
     LVertexNode->Normal.X=0.0;LVertexNode->Normal.Y=1.0;LVertexNode->Normal.Z=0.0;LVertexNode++;
     LVertexNode->Normal.X=0.0;LVertexNode->Normal.Y=1.0;LVertexNode->Normal.Z=0.0;LVertexNode++;
     LVertexNode->Normal.X=0.0;LVertexNode->Normal.Y=1.0;LVertexNode->Normal.Z=0.0;
     LPolygon++;
    }
    LV=LNextCircleV;
   }

  }
 }

 if(LInfo->BottomCap)
 {
  LV=LSides*LHeightSections;
  if((LRadialSections<2)&&(LInfo->AllowHoledPolys))
  {
   if((LVertexNode=E3d_PolygonVertexNodesAllocate(LPolygon, LSides*2+1))!=NULL)
   {
    for(LC=0, LH=LSides-1;LC<LSides;LH--, LC++, LVertexNode++)
    {
     LVertexNode->VertexID=LH;
     LVertexNode->Normal.X=0.0;LVertexNode->Normal.Y=1.0;LVertexNode->Normal.Z=0.0;
    }
    LPolygon->NumOfVertices=LPolygon->NumOfExteriorVertices=LSides;
    LPolygon->Normal.X=0.0;LPolygon->Normal.Y=1.0;LPolygon->Normal.Z=0.0;
    LPolygon++;
   }
  }
  else
  {
   unsigned int	LNextCircleV;

   for(LRC=0;LRC<LRadialSections;LRC++)
   {
    if(LRC<(LRadialSections-1))
    {
     if((LRC==0)&&(LRadialSections>1)) LNextCircleV=LSides*((LHeightSections+1)*2+LRadialSections-1);
     else LNextCircleV=LV+LSides;
    }
    else LNextCircleV=LSides*(LHeightSections*2+1);

    for(LVC=0;LVC<LSides;LVC++, LV++)
    {
     if(LVC<(LSides-1)) E3d_PolygonSet(LPolygon, 4, LV, LVC+LNextCircleV, LVC+LNextCircleV+1, LV+1);
     else E3d_PolygonSet(LPolygon, 4, LV, LVC+LNextCircleV, LVC+LNextCircleV-LSides+1, LV-LSides+1);
     LPolygon->Normal.X=0.0;LPolygon->Normal.Y=-1.0;LPolygon->Normal.Z=0.0;

     LVertexNode=LPolygon->VertexNodes;
     LVertexNode->Normal.X=0.0;LVertexNode->Normal.Y=-1.0;LVertexNode->Normal.Z=0.0;LVertexNode++;
     LVertexNode->Normal.X=0.0;LVertexNode->Normal.Y=-1.0;LVertexNode->Normal.Z=0.0;LVertexNode++;
     LVertexNode->Normal.X=0.0;LVertexNode->Normal.Y=-1.0;LVertexNode->Normal.Z=0.0;LVertexNode++;
     LVertexNode->Normal.X=0.0;LVertexNode->Normal.Y=-1.0;LVertexNode->Normal.Z=0.0;
     LPolygon++;
    }
    LV=LNextCircleV;
   }

  }
 }
}


static void _CreatePolygonsForPie(E3dTubeInfo* LInfo, E3dMesh* LMesh, E3dPolygon* LPolygon, unsigned int LSides, unsigned int LNSideVertices)
{
 E3dVertex*	LVertex=LMesh->Vertices;
 E3d3DPosition*	LPoints;
 E3dVertexNode*	LVertexNode;
 unsigned int	LC, LH, LV, LVC, LRC,
		LRadialSections=LInfo->RadialSections,
		LHeightSections=LInfo->HeightSections;


// Inner sides
//
 LV=0;
 LPoints=_UnitCirclePoints;
 for(LC=0;LC<LHeightSections;LC++, LV+=LNSideVertices)
 {
  for(LH=0;LH<LSides;LH++)
  {
   E3d_PolygonSet(LPolygon, 4, LV+LH+LNSideVertices, LV+LH+LNSideVertices+1, LV+LH+1, LV+LH);
   LVertexNode=LPolygon->VertexNodes;
   LVertexNode->Normal.X=-LPoints[LH].X;LVertexNode->Normal.Y=0.0;LVertexNode->Normal.Z=-LPoints[LH].Z;
   LVertexNode++;
   LVertexNode->Normal.X=-LPoints[LH+1].X;LVertexNode->Normal.Y=0.0;LVertexNode->Normal.Z=-LPoints[LH+1].Z;
   LVertexNode++;
   LVertexNode->Normal.X=-LPoints[LH+1].X;LVertexNode->Normal.Y=0.0;LVertexNode->Normal.Z=-LPoints[LH+1].Z;
   LVertexNode++;
   LVertexNode->Normal.X=-LPoints[LH].X;LVertexNode->Normal.Y=0.0;LVertexNode->Normal.Z=-LPoints[LH].Z;
   LVertexNode=LPolygon->VertexNodes;
   E3d_VertexNormal(&(LPolygon->Normal), LVertex+LVertexNode[0].VertexID, LVertex+LVertexNode[1].VertexID, LVertex+LVertexNode[2].VertexID);
   LPolygon++;
  }
 }

// Outer sides
//
 LV=LNSideVertices*(LHeightSections+1);
 LPoints=_UnitCirclePoints;
 for(LC=0;LC<LHeightSections;LC++, LV+=LNSideVertices)
 {
  for(LH=0;LH<LSides;LH++)
  {
   E3d_PolygonSet(LPolygon, 4, LV+LH, LV+LH+1, LV+LH+LNSideVertices+1, LV+LH+LNSideVertices);
   LVertexNode=LPolygon->VertexNodes;
   LVertexNode->Normal.X=LPoints[LH].X;LVertexNode->Normal.Y=0.0;LVertexNode->Normal.Z=LPoints[LH].Z;
   LVertexNode++;
   LVertexNode->Normal.X=LPoints[LH+1].X;LVertexNode->Normal.Y=0.0;LVertexNode->Normal.Z=LPoints[LH+1].Z;
   LVertexNode++;
   LVertexNode->Normal.X=LPoints[LH+1].X;LVertexNode->Normal.Y=0.0;LVertexNode->Normal.Z=LPoints[LH+1].Z;
   LVertexNode++;
   LVertexNode->Normal.X=LPoints[LH].X;LVertexNode->Normal.Y=0.0;LVertexNode->Normal.Z=LPoints[LH].Z;
   LVertexNode=LPolygon->VertexNodes;
   E3d_VertexNormal(&(LPolygon->Normal), LVertex+LVertexNode[0].VertexID, LVertex+LVertexNode[1].VertexID, LVertex+LVertexNode[2].VertexID);
   LPolygon++;
  }
 }


 if(LInfo->TopCap)
 {
  LV=0;
  if((LRadialSections<2)&&(LInfo->AllowHoledPolys))
  {
   if((LVertexNode=E3d_PolygonVertexNodesAllocate(LPolygon, LSides*2+1))!=NULL)
   {
    for(LC=0, LH=LSides-1;LC<LSides;LH--, LC++, LVertexNode++)
    {
     LVertexNode->VertexID=LH;
     LVertexNode->Normal.X=0.0;LVertexNode->Normal.Y=1.0;LVertexNode->Normal.Z=0.0;
    }
    LPolygon->NumOfVertices=LPolygon->NumOfExteriorVertices=LSides;
    LPolygon->Normal.X=0.0;LPolygon->Normal.Y=1.0;LPolygon->Normal.Z=0.0;
    LPolygon++;
   }
  }
  else
  {
   unsigned int	LNextCircleV;

   for(LRC=0;LRC<LRadialSections;LRC++)
   {
    if(LRC<(LRadialSections-1))
    {
     if((LRC==0)&&(LRadialSections>1)) LNextCircleV=LNSideVertices*(LHeightSections+1)*2;
     else LNextCircleV=LV+LNSideVertices;
    }
    else LNextCircleV=LNSideVertices*(LHeightSections+1);

    for(LVC=0;LVC<LSides;LVC++, LV++)
    {
     E3d_PolygonSet(LPolygon, 4, LV+1, LVC+LNextCircleV+1, LVC+LNextCircleV, LV);
     LPolygon->Normal.X=0.0;LPolygon->Normal.Y=1.0;LPolygon->Normal.Z=0.0;

     LVertexNode=LPolygon->VertexNodes;
     LVertexNode->Normal.X=0.0;LVertexNode->Normal.Y=1.0;LVertexNode->Normal.Z=0.0;LVertexNode++;
     LVertexNode->Normal.X=0.0;LVertexNode->Normal.Y=1.0;LVertexNode->Normal.Z=0.0;LVertexNode++;
     LVertexNode->Normal.X=0.0;LVertexNode->Normal.Y=1.0;LVertexNode->Normal.Z=0.0;LVertexNode++;
     LVertexNode->Normal.X=0.0;LVertexNode->Normal.Y=1.0;LVertexNode->Normal.Z=0.0;
     LPolygon++;
    }
    LV=LNextCircleV;
   }

  }
 }


 if(LInfo->BottomCap)
 {
  LV=LNSideVertices*LHeightSections;
  if((LRadialSections<2)&&(LInfo->AllowHoledPolys))
  {
   if((LVertexNode=E3d_PolygonVertexNodesAllocate(LPolygon, LSides*2+1))!=NULL)
   {
    for(LC=0, LH=LSides-1;LC<LSides;LH--, LC++, LVertexNode++)
    {
     LVertexNode->VertexID=LH;
     LVertexNode->Normal.X=0.0;LVertexNode->Normal.Y=1.0;LVertexNode->Normal.Z=0.0;
    }
    LPolygon->NumOfVertices=LPolygon->NumOfExteriorVertices=LSides;
    LPolygon->Normal.X=0.0;LPolygon->Normal.Y=1.0;LPolygon->Normal.Z=0.0;
    LPolygon++;
   }
  }
  else
  {
   unsigned int	LNextCircleV;

   for(LRC=0;LRC<LRadialSections;LRC++)
   {
    if(LRC<(LRadialSections-1))
    {
     if((LRC==0)&&(LRadialSections>1)) LNextCircleV=LNSideVertices*((LHeightSections+1)*2+LRadialSections-1);
     else LNextCircleV=LV+LNSideVertices;
    }
    else LNextCircleV=LNSideVertices*(LHeightSections*2+1);

    for(LVC=0;LVC<LSides;LVC++, LV++)
    {
     E3d_PolygonSet(LPolygon, 4, LV, LVC+LNextCircleV, LVC+LNextCircleV+1, LV+1);
     LPolygon->Normal.X=0.0;LPolygon->Normal.Y=-1.0;LPolygon->Normal.Z=0.0;

     LVertexNode=LPolygon->VertexNodes;
     LVertexNode->Normal.X=0.0;LVertexNode->Normal.Y=-1.0;LVertexNode->Normal.Z=0.0;LVertexNode++;
     LVertexNode->Normal.X=0.0;LVertexNode->Normal.Y=-1.0;LVertexNode->Normal.Z=0.0;LVertexNode++;
     LVertexNode->Normal.X=0.0;LVertexNode->Normal.Y=-1.0;LVertexNode->Normal.Z=0.0;LVertexNode++;
     LVertexNode->Normal.X=0.0;LVertexNode->Normal.Y=-1.0;LVertexNode->Normal.Z=0.0;
     LPolygon++;
    }
    LV=LNextCircleV;
   }

  }
 }
}


/*======================================*/
/* Get a tube				*/
/*======================================*/
static E3dModel* _Get(E3dModel* LModel, E3dGeometry* LGeometry, E3dTubeInfo* LInfoV, unsigned int LFlags)
{
 E3dMesh*		LMesh;
 E3dVertex*		LVertex;
 E3dPolyGroup*		LPolyGroup;
 E3dPolygon*		LPolygon;
 unsigned int		LVNum=0, LPNum=0;
 int			LSidesN=0;
 E3dCoordinate		LRadius, LHeight;
 E3dAngle		LStartAngle, LEndAngle;
 int			LSides, LRadialSections, LHeightSections;
 E3dTubeInfo*		LInfo;
 EBool			LFull;


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

  LInfo=(E3dTubeInfo*)E3d_GeometryInfoAdd((E3dGeometry*)LMesh, LInfoV->Class);
  if(LInfo)
  {
   memcpy(LInfo, LInfoV, sizeof(E3dTubeInfo));
  }
  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];
 }

 LRadius=LInfo->OuterRadius;
 LHeight=LInfo->Height;
 LSides=LInfo->Sides;
 LRadialSections=LInfo->RadialSections;
 LHeightSections=LInfo->HeightSections;
 LStartAngle=LInfo->StartAngle;
 LEndAngle=LInfo->EndAngle;

 if((LEndAngle-LStartAngle)==360.0) LFull=TRUE;
 else LFull=FALSE;
 switch(LInfo->Type)
 {
  case E3dTubePOLYGONS:
   _CreateUnitCircle(LInfo->Sides, LInfo->StartAngle, LInfo->EndAngle);

   LVNum=LPNum=0;

// Get number of Vertices and Polygons and initialize the unit cross-section
//
   if(LFull)
   {
    LSidesN=LSides;

    LVNum+=LSides*(LHeightSections+1)*2;
    LPNum+=LSides*LHeightSections*2;

    if((LRadialSections<2)&&(LInfo->AllowHoledPolys))
    {
     if(LInfo->TopCap) LPNum++;
     if(LInfo->BottomCap) LPNum++;
    }
    else
    {
     if(LInfo->TopCap)
     {
      LVNum+=((LRadialSections-1)*LSides);
      LPNum+=LRadialSections*LSides;
     }

     if(LInfo->BottomCap)
     {
      LVNum+=((LRadialSections-1)*LSides);
      LPNum+=LRadialSections*LSides;
     }
    }
   }
   else
   {
    LSidesN=LSides+1;

    LVNum+=LSidesN*(LHeightSections+1)*2+(LRadialSections-1)*(LHeightSections-1)*2;
//    LPNum+=LSides*LHeightSections*2+LRadialSections*LHeightSections*2;
    LPNum+=LSides*LHeightSections*2;

    if((LRadialSections<2)&&(LInfo->AllowHoledPolys))
    {
     if(LInfo->TopCap) LPNum++;
     if(LInfo->BottomCap) LPNum++;
    }
    else
    {
     if(LInfo->TopCap)
     {
      LVNum+=((LRadialSections-1)*LSidesN);
      LPNum+=LRadialSections*LSides;
     }

     if(LInfo->BottomCap)
     {
      LVNum+=((LRadialSections-1)*LSidesN);
      LPNum+=LRadialSections*LSides;
     }
    }
   }


_VNum=LVNum;
printf("%d %d LVNum %d  LPNum %d\n", LSides, LRadialSections, LVNum, LPNum);fflush(stdout);
  break;
 }

 if(LVNum==LMesh->NumOfVertices) LVertex=LMesh->Vertices;
 else
 {
  if(LMesh->Vertices) E3d_MeshFreeVertices(LMesh);
  LMesh->Vertices=E3d_VerticesAllocate(LMesh->NumOfVertices=LVNum, TRUE);
 }

 if(LMesh->Vertices)
 {
  switch(LInfo->Type)
  {
   case E3dTubePOLYGONS:
    LVertex=LMesh->Vertices;
    if(LPNum==LPolyGroup->NumOfPolygons) LPolygon=LPolyGroup->Polygons;
    else
    {
     if(LPolyGroup->Polygons) E3d_PolyGroupFreePolygons(LPolyGroup);
     LPolygon=LPolyGroup->Polygons=E3d_PolygonsAllocate(LPolyGroup->NumOfPolygons=LPNum);
    }

    _SetVertices(LVertex, LInfo);

    if(LPolygon)
    {
     if(LFlags&E3dGF_TOPOLOGY)
     {
      if(LFull) _CreatePolygonsForFullTube(LInfo, LMesh, LPolygon, LSides);
      else _CreatePolygonsForPie(LInfo, LMesh, LPolygon, LSides, LSidesN);
     }

     if(LInfo->Smooth) LPolyGroup->Flags|=E3dUSE_VERTEX_NORMALS;
     else LPolyGroup->Flags&=E3dALL-E3dUSE_VERTEX_NORMALS;

     LPolyGroup->DiscontinuityAngle=60.0;

     E3d_GeometryUpdateForDisplay((E3dGeometry*)LMesh, LFlags&(E3dGF_ALL-E3dGF_CALL_DESTROYPROCS));

     return(LModel);
    }
   break;
  }
 }
 return(NULL);
}


/*======================================*/
/* Set GUI for tube type		*/
/*======================================*/
static void _SetGUIToType(E3dTubeInfo* LInfo)
{
 EWidthAlignRec	LWAs[6];
 int		LXSize, LC, LN;

 switch(LInfo->Type)
 {
  case E3dTubeNURBS:
  case E3dTubePOLYGONS:
   XeScaleSetRangeAndValue(_SidesScaleW, 3, 360, LInfo->Sides, False);

   LN=0;
   XtVaGetValues(LWAs[LN].W=_OuterRadiusScaleW, XeNlabelWidth, &(LWAs[LN].XSize), NULL);LN++;
   XtVaGetValues(LWAs[LN].W=_InnerRadiusScaleW, XeNlabelWidth, &(LWAs[LN].XSize), NULL);LN++;
   XtVaGetValues(LWAs[LN].W=_HeightScaleW, XeNlabelWidth, &(LWAs[LN].XSize), NULL);LN++;
   XtVaGetValues(LWAs[LN].W=_StartAngleScaleW, XeNlabelWidth, &(LWAs[LN].XSize), NULL);LN++;
   XtVaGetValues(LWAs[LN].W=_EndAngleScaleW, XeNlabelWidth, &(LWAs[LN].XSize), NULL);LN++;
   for(LC=1, LXSize=LWAs[0].XSize;LC<LN;LC++) { if(LWAs[LC].XSize>LXSize) LXSize=LWAs[LC].XSize; }
   for(LC=0;LC<LN;LC++) { XtVaSetValues(LWAs[LC].W, XeNleftOffset, LXSize-LWAs[LC].XSize+2, NULL); }

   LN=0;
   XtVaGetValues(LWAs[LN].W=_SidesScaleW, XeNlabelWidth, &(LWAs[LN].XSize), NULL);LN++;
   XtVaGetValues(LWAs[LN].W=_RadialSectionsScaleW, XeNlabelWidth, &(LWAs[LN].XSize), NULL);LN++;
   XtVaGetValues(LWAs[LN].W=_HeightSectionsScaleW, XeNlabelWidth, &(LWAs[LN].XSize), NULL);LN++;
   for(LC=1, LXSize=LWAs[0].XSize;LC<LN;LC++) { if(LWAs[LC].XSize>LXSize) LXSize=LWAs[LC].XSize; }
   for(LC=0;LC<LN;LC++) { XtVaSetValues(LWAs[LC].W, XeNleftOffset, LXSize-LWAs[LC].XSize+2, NULL); }
  break;
 }
}


/*======================================*/
/* Set Tube type			*/
/*======================================*/
static void _DCB_SetType(Widget LW, XtPointer LClientData, XtPointer LCallData)
{
 E3dTubeInfo*	LInfo=(E3dTubeInfo*)E3d_GeometryInfoByClass(_Geometry, _Info.Class);


 if(LInfo->Type!=(int)LClientData)
 {
  if(_UnitCirclePoints) { EFree(_UnitCirclePoints);_UnitCirclePoints=NULL; }
  LInfo->Type=(int)LClientData;
  _SetGUIToType(LInfo);

  _Get(_Model, _Geometry, LInfo, E3dGF_ALL);

  E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
 }
}


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

 XeToggleButtonSetState(_SmoothW, LInfo->Smooth, False);
 XeToggleButtonSetState(_OriginAtBottomW, LInfo->OriginAtBottom, False);

// EGUI_OptionMenuSetOptionByNumber(_TypesOptionMenuW, LInfo->Type);

 XeToggleButtonSetState(_TopW, LInfo->TopCap, False);
 XeToggleButtonSetState(E3d_BottomW, LInfo->BottomCap, False);

 XeScaleSetValueD(_OuterRadiusScaleW, LInfo->OuterRadius, False);
 XeScaleSetValueD(_InnerRadiusScaleW, LInfo->InnerRadius, False);
 XeScaleSetValueD(_HeightScaleW, LInfo->Height, False);
 XeScaleSetValueD(_StartAngleScaleW, LInfo->StartAngle, False);
 XeScaleSetValueD(_EndAngleScaleW, LInfo->EndAngle, False);
 XeScaleSetValue(_SidesScaleW, LInfo->Sides, False);
 XeScaleSetValue(_RadialSectionsScaleW, LInfo->RadialSections, False);
 XeScaleSetValue(_HeightSectionsScaleW, LInfo->HeightSections, False);

 _SetGUIToType(LInfo);
}


/*======================================*/
/* Dialog callback			*/
/*======================================*/
static void _DCB(EguiItem LW, EPointer LClientData, EPointer LCallData)
{
 switch((int)LClientData)
 {
  case EguiOK:
   E3dTpl_GeoOK(_);
   if(_UnitCirclePoints) { EFree(_UnitCirclePoints);_UnitCirclePoints=NULL; }
  break;

  case EguiADD:
   if(_UnitCirclePoints) { EFree(_UnitCirclePoints);_UnitCirclePoints=NULL; }
   E3dTpl_GeoADD(_);
  break;

  case EguiCANCEL:
   E3dTpl_GeoCANCEL(_);
   if(_UnitCirclePoints) { EFree(_UnitCirclePoints);_UnitCirclePoints=NULL; }
  break;
 }
}


/*======================================*/
/* Dialog Xt callback			*/
/*======================================*/
static void _XtDCB(Widget LW, XtPointer LClientData, XtPointer LCallData)
{
 E3dMesh*	LMesh=(E3dMesh*)_Geometry;
 E3dTubeInfo*	LInfo=(E3dTubeInfo*)E3d_GeometryInfoByClass(_Geometry, _Info.Class);


 switch((int)LClientData)
 {
  case E3dCR_SMOOTH:
   LInfo->Smooth=((XeToggleButtonCallbackStruct*)LCallData)->set;
   if(LInfo->Smooth) LMesh->PolyGroups[0]->Flags|=E3dUSE_VERTEX_NORMALS;
   else LMesh->PolyGroups[0]->Flags&=E3dALL-E3dUSE_VERTEX_NORMALS;
   E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
  break;

  case E3dCR_ORIGIN_AT_BOTTOM:
   LInfo->OriginAtBottom=(((XeToggleButtonCallbackStruct*)LCallData)->set);
   _Get(_Model, _Geometry, LInfo, E3dGF_SHAPE);
   E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
  break;

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

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

  case E3dCR_SET_OUTER_RADIUS:
   LInfo->OuterRadius=((XeScaleCallbackStruct*)LCallData)->FloatValue;

   if(LInfo->OuterRadius<LInfo->InnerRadius)
   {
    LInfo->InnerRadius=LInfo->OuterRadius-0.01;
    XeScaleSetValueD(_InnerRadiusScaleW, LInfo->InnerRadius, False);
   }

   _SetVertices(LMesh->Vertices, LInfo);

   E3d_GeometryUpdateForDisplay((E3dGeometry*)LMesh, E3dGF_SHAPE);

   E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
  break;

  case E3dCR_SET_INNER_RADIUS:
   LInfo->InnerRadius=((XeScaleCallbackStruct*)LCallData)->FloatValue;

   if(LInfo->InnerRadius>LInfo->OuterRadius)
   {
    LInfo->OuterRadius=LInfo->InnerRadius+0.01;
    XeScaleSetValueD(_OuterRadiusScaleW, LInfo->OuterRadius, False);
   }

   _SetVertices(LMesh->Vertices, LInfo);
   E3d_GeometryUpdateForDisplay((E3dGeometry*)LMesh, E3dGF_SHAPE);

   E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
  break;

  case E3dCR_SET_HEIGHT:
   LInfo->Height=((XeScaleCallbackStruct*)LCallData)->FloatValue;
   _SetVertices(LMesh->Vertices, LInfo);
   E3d_GeometryUpdateForDisplay((E3dGeometry*)LMesh, E3dGF_SHAPE);

   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) { _Get(_Model, _Geometry, LInfo, E3dGF_SHAPE);E3d_GeometryUpdateForDisplay(_Geometry, E3dGF_SHAPE); }
    else { _Get(_Model, _Geometry, LInfo, E3dGF_ALL);E3d_GeometryUpdateForDisplay(_Geometry, E3dGF_ALL); }


    E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
   }
  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) { _Get(_Model, _Geometry, LInfo, E3dGF_SHAPE);E3d_GeometryUpdateForDisplay(_Geometry, E3dGF_SHAPE); }
    else { _Get(_Model, _Geometry, LInfo, E3dGF_ALL);E3d_GeometryUpdateForDisplay(_Geometry, E3dGF_ALL); }

    E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
   }
  break;

  case E3dCR_SET_SIDES:
   if(_UnitCirclePoints) { EFree(_UnitCirclePoints);_UnitCirclePoints=NULL; }
   LInfo->Sides=((XeScaleCallbackStruct*)LCallData)->IntValue;

   _Get(_Model, _Geometry, LInfo, E3dGF_ALL);

   E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
  break;

  case E3dCR_SET_RADIAL_SECTIONS:
   if(_UnitCirclePoints) { EFree(_UnitCirclePoints);_UnitCirclePoints=NULL; }
   LInfo->RadialSections=((XeScaleCallbackStruct*)LCallData)->IntValue;

   _Get(_Model, _Geometry, LInfo, E3dGF_ALL);

   E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
  break;

  case E3dCR_SET_HEIGHT_SECTIONS:
   if(_UnitCirclePoints) { EFree(_UnitCirclePoints);_UnitCirclePoints=NULL; }
   LInfo->HeightSections=((XeScaleCallbackStruct*)LCallData)->IntValue;

   _Get(_Model, _Geometry, LInfo, E3dGF_ALL);

   E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
  break;
 }
}


/*======================================*/
/* Pop up the dialog			*/
/*======================================*/
static void _PopupDialog(E3dTubeInfo* LInfo)
{
 if(_Dialog==NULL)
 {
  Widget	LTopMatrixW, LMatrixW;
  Widget	LIndentWs[7];			// For indenting the ScaleWidgets
  Widget	LTraverseWs[7];			// For TextField traversal
  EguiItem	LDialog=E3d_GeometryDialogCreate("Tube", _MainGeometry, &_GeometryPanel, _DCB, &Image_TubeBig);
  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, 16);
  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(XeNrecomputeSize, False);
  EXtSetArg(XeNfontList, EGUI_LabelFontList);
  EXtSetArg(XeNset, LInfo->Smooth);
  _SmoothW=XtCreateManagedWidget("Smooth", xeToggleButtonWidgetClass, LMatrixW, Args, ArgCnt);
  XtAddCallback(_SmoothW, XeNvalueChangedCallback, _XtDCB, (XtPointer)E3dCR_SMOOTH);

/*
  EXtStart;
  EXtSetArg(XeNmarginLeft, 0);EXtSetArg(XeNmarginRight, 0);EXtSetArg(XeNmarginTop, 0);EXtSetArg(XeNmarginBottom, 0);
  EXtSetArg(XeNbackground, EGUI_BackgroundColor);
  EXtSetArg(XeNbuttonColor, EGUI_ButtonColor);
  EXtSetArg(XeNforeground, EGUI_ForegroundColor);
  EXtSetArg(XeNtopShadowColor, EGUI_TopShadowColor);
  EXtSetArg(XeNbottomShadowColor, EGUI_BottomShadowColor);
  EXtSetArg(XeNfontList, EGUI_MenuFontList);
  EXtSetArg(XeNlabelFontList, EGUI_LabelFontList);
  EXtSetArg(XeNcastShadowWidth, 4);
  EXtSetArg(XeNmenuPaneBackground, EGUI_OptionMenuColor);
  _TypesOptionMenuW=EGUI_OptionMenuCreate(LMatrixW, NULL, _TubeTypes, sizeof(_TubeTypes)/sizeof(EguiOptionMenuRec), LInfo->Type, "Type:", _DCB_SetType, Args, ArgCnt);
  XtManageChild(_TypesOptionMenuW);
*/


  EXtStart;
  EXtSetArg(XeNbackground, EGUI_BackgroundColor);
  EXtSetArg(XeNorientation, XeHORIZONTAL);
  EXtSetArg(XeNxSpacing, 4);
  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(XeNrecomputeSize, False);
  EXtSetArg(XeNfontList, EGUI_LabelFontList);
  EXtSetArg(XeNset, LInfo->TopCap);
  _TopW=XtCreateManagedWidget("Cap top", xeToggleButtonWidgetClass, LMatrixW, Args, ArgCnt);
  XtAddCallback(_TopW, XeNvalueChangedCallback, _XtDCB, (XtPointer)E3dCR_TOPCAP);

  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(XeNrecomputeSize, False);
  EXtSetArg(XeNfontList, EGUI_LabelFontList);
  EXtSetArg(XeNset, LInfo->BottomCap);
  E3d_BottomW=XtCreateManagedWidget("Cap bottom", xeToggleButtonWidgetClass, LMatrixW, Args, ArgCnt);
  XtAddCallback(E3d_BottomW, XeNvalueChangedCallback, _XtDCB, (XtPointer)E3dCR_BOTTOMCAP);

  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(XeNrecomputeSize, False);
  EXtSetArg(XeNfontList, EGUI_LabelFontList);
  EXtSetArg(XeNset, LInfo->OriginAtBottom);
  _OriginAtBottomW=XtCreateManagedWidget("Origin at bottom", xeToggleButtonWidgetClass, LMatrixW, Args, ArgCnt);
  XtAddCallback(_OriginAtBottomW, XeNvalueChangedCallback, _XtDCB, (XtPointer)E3dCR_ORIGIN_AT_BOTTOM);


  EXtStart;
  EXtSetArg(XeNshadowThickness, 2);
  EXtSetArg(XeNshadowType, XeSHADOW_ETCHED_IN);
  EXtSetArg(XeNtopShadowColor, EGUI_TopShadowColor);
  EXtSetArg(XeNbottomShadowColor, EGUI_BottomShadowColor);
  XtCreateManagedWidget("", xeSeparatorWidgetClass, LTopMatrixW, Args, ArgCnt);

  LTraverseN=0;
  LIndentN=0;
  EXtStart;
  EXtSetArg(XeNscaleLengthInPixels, 160);
  EXtSetArg(XeNtextFieldDigits, 8);EXtSetArg(XeNdecimalPoints, 4);
  EXtSetArg(XeNminimum, 1);EXtSetArg(XeNmaximum, 10000000);EXtSetArg(XeNvalue, (int)(LInfo->OuterRadius*10000.0));
  EXtSetArg(XeNincrement, 10);
  EXtSetArg(XeNorientation, XeHORIZONTAL);
  E3dp_SetScaleArgs(Args, &ArgCnt);
  LIndentWs[LIndentN++]=LTraverseWs[LTraverseN++]=_OuterRadiusScaleW=XtCreateManagedWidget("Outer radius:", xeScaleWidgetClass, LTopMatrixW, Args, ArgCnt);
  XtAddCallback(_OuterRadiusScaleW, XeNvalueChangedCallback, _XtDCB, (XtPointer)E3dCR_SET_OUTER_RADIUS);

  EXtStart;
  EXtSetArg(XeNscaleLengthInPixels, 160);
  EXtSetArg(XeNtextFieldDigits, 8);EXtSetArg(XeNdecimalPoints, 4);
  EXtSetArg(XeNminimum, 1);EXtSetArg(XeNmaximum, 10000000);EXtSetArg(XeNvalue, (int)(LInfo->InnerRadius*10000.0));
  EXtSetArg(XeNincrement, 10);
  EXtSetArg(XeNorientation, XeHORIZONTAL);
  E3dp_SetScaleArgs(Args, &ArgCnt);
  LIndentWs[LIndentN++]=LTraverseWs[LTraverseN++]=_InnerRadiusScaleW=XtCreateManagedWidget("Inner radius:", xeScaleWidgetClass, LTopMatrixW, Args, ArgCnt);
  XtAddCallback(_InnerRadiusScaleW, XeNvalueChangedCallback, _XtDCB, (XtPointer)E3dCR_SET_INNER_RADIUS);

  EXtStart;
  EXtSetArg(XeNscaleLengthInPixels, 160);
  EXtSetArg(XeNtextFieldDigits, 8);EXtSetArg(XeNdecimalPoints, 4);
  EXtSetArg(XeNminimum, 1);EXtSetArg(XeNmaximum, 100000000);EXtSetArg(XeNvalue, (int)(LInfo->Height*10000.0));
  EXtSetArg(XeNscaleMaximum, 1000000);
  EXtSetArg(XeNincrement, 10);
  EXtSetArg(XeNorientation, XeHORIZONTAL);
  E3dp_SetScaleArgs(Args, &ArgCnt);
  LIndentWs[LIndentN++]=LTraverseWs[LTraverseN++]=_HeightScaleW=XtCreateManagedWidget("Height:", xeScaleWidgetClass, LTopMatrixW, Args, ArgCnt);
  XtAddCallback(_HeightScaleW, XeNvalueChangedCallback, _XtDCB, (XtPointer)E3dCR_SET_HEIGHT);

  XeIndentWidgetsFromLeft(LIndentWs, LIndentN, 4);


  LIndentN=0;
  EXtStart;
  EXtSetArg(XeNscaleLengthInPixels, 60);
  EXtSetArg(XeNtextFieldDigits, 6);EXtSetArg(XeNdecimalPoints, 2);
  EXtSetArg(XeNminimum, -36000);EXtSetArg(XeNmaximum, 36000);EXtSetArg(XeNvalue, (int)(LInfo->StartAngle*100.0));
  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, _XtDCB, (XtPointer)E3dCR_SET_START_ANGLE);

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

  XeIndentWidgetsFromLeft(LIndentWs, LIndentN, 4);


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

  EXtStart;
  EXtSetArg(XeNtextFieldDigits, 4);
  EXtSetArg(XeNminimum, 1);EXtSetArg(XeNmaximum, 100);EXtSetArg(XeNvalue, LInfo->RadialSections);
  EXtSetArg(XeNscaleMinimum, 0);EXtSetArg(XeNscaleMaximum, 10);
  EXtSetArg(XeNorientation, XeHORIZONTAL);
  E3dp_SetScaleArgs(Args, &ArgCnt);
  LIndentWs[LIndentN++]=LTraverseWs[LTraverseN++]=_RadialSectionsScaleW=XtCreateManagedWidget("Radial segments:", xeScaleWidgetClass, LTopMatrixW, Args, ArgCnt);
  XtAddCallback(_RadialSectionsScaleW, XeNvalueChangedCallback, _XtDCB, (XtPointer)E3dCR_SET_RADIAL_SECTIONS);

  EXtStart;
  EXtSetArg(XeNtextFieldDigits, 4);
  EXtSetArg(XeNminimum, 1);EXtSetArg(XeNmaximum, 100);EXtSetArg(XeNvalue, LInfo->HeightSections);
  EXtSetArg(XeNscaleMinimum, 1);EXtSetArg(XeNscaleMaximum, 20);
  EXtSetArg(XeNorientation, XeHORIZONTAL);
  E3dp_SetScaleArgs(Args, &ArgCnt);
  LIndentWs[LIndentN++]=LTraverseWs[LTraverseN++]=_HeightSectionsScaleW=XtCreateManagedWidget("Height sections:", xeScaleWidgetClass, LTopMatrixW, Args, ArgCnt);
  XtAddCallback(_HeightSectionsScaleW, XeNvalueChangedCallback, _XtDCB, (XtPointer)E3dCR_SET_HEIGHT_SECTIONS);

  XeIndentWidgetsFromLeft(LIndentWs, LIndentN, 4);


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


  _SetGUIToType(LInfo);
 }
 else _UpdateDialog(LInfo);

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


/*==============================================*/
/* Menu callback				*/
/*==============================================*/
static void _MCB(EguiItem LGUIItem, EPointer LClientData, EPointer LCallData)
{
 E3dTpl_ACB_CHist(E3dTube, _);
}


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


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


 _Plugin=LPlugin;

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

  EpM_RGBA8ImageFromCStruct(LImage, Image_Tube);
  EpM_RGBA8ImageFromCStruct(LActiveImage, Image_TubeActive);
  EpM_RGBA8ImageFromCStruct(LArmImage, Image_TubeArm);
  _ToolPanelButton=EGUI_AddPushButtonImg("Tool->Create", "Tube", '\0', NULL, NULL, TRUE, "Create tube\nRight button:  dialog box for this tool", _MCB, (EPointer)0, LImage, LActiveImage, LArmImage);

  _Info.InnerRadius=2.0;_Info.OuterRadius=3.0;_Info.Height=5.0;
  _Info.Sides=24;
  _Info.RadialSections=1;
  _Info.HeightSections=1;

  _Info.Type=E3dTubePOLYGONS;

  _Info.StartAngle=0.0;_Info.EndAngle=360.0;
  _Info.Smooth=TRUE;
  _Info.TopCap=TRUE;_Info.BottomCap=TRUE;
  _Info.OriginAtBottom=FALSE;
  _Info.AllowHoledPolys=FALSE;

  _MenuButton=EGUI_AddPushButton("Menu->Create", "Tube", '\0', NULL, NULL, TRUE, NULL, _MCB, (EPointer)0);
 }

 return(0);
}


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

 if(_Info.Class) E3d_GeometryClassDeactivate(_Info.Class);

 return(0);
}
