//========================================================================
// EQUINOX-3D plugin
//
// - Regular N-gon, circle / disc and gears with involute curve teeth
//
// AUTHOR:  Gabor Nagy
// DATE:    2025-Nov-12 14:31:04
//
// Copyright (C) 1996-2026 By Gabor Nagy. All rights reserved.
//========================================================================
#include <assert.h>
#include <stdio.h>
#include <math.h>


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

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

#include <E3D/Matrix.h>
#include <E3D/Mesh.h>

#ifndef _E3D_GLVertexArrays_h
#include <E3D/GL/VertexArrays.h>
#endif


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



static EPlugin*  _Plugin = NULL;


#define BgLigata     0
#define BgWang       1
#define BgAlDaccak   2

const EIndex  _MaxCirclePoints = 720000;

#define E3dCONSTRAIN_START_END_ANGLE  0

#define DebugSINGLE_CURVE                0   // Only generate a single curve (no mirroring and no tooth repeat)

#define BevelGearDEBUG                   1
#define BevelGearSHOW_MESHING_POINTS     0   // Generate meshing (contact) points, instead of the involute tooth profiles
#define BevelGearSPLINE_NORMAL_RATIO   0.2

//#define BevelGearALGORITHM  BgLigata
#define BevelGearALGORITHM  BgWang
//#define BevelGearALGORITHM  BgAlDaccak  // The "old paper"

#if BevelGearDEBUG
	#if BevelGearALGORITHM == BgLigata
		static const char*  _BevelGearAlgorithmName = "Ligata";
	#elif BevelGearALGORITHM == BgWang
		static const char*  _BevelGearAlgorithmName = "Wang";
	#endif
#endif


typedef struct
{
	XeWidget  FillBW, LinearSplineBW, BezierSplineBW, CardinalSplineBW, BSplineBW, 
	          CircleRadiusW, StartAngleW, EndAngleW, CirclePointsW, SpiralPitchW;

#if BevelGearDEBUG
	XeWidget  BevelGearAlgorithmNameW;  // For debugging only
#endif

	XeWidget  GearTeethW, GearPressureAngleW, GearDiametralPitchW,
	          GearRootCircleScaleFactorW,
	          GearToothBaseBevelRadiusW, GearToothBaseBevelStepsW,
	          GearToothTipBevelRadiusW, GearToothTipBevelStepsW,
	          BevelGearSphereRadiusW,
	          GearInternalW;

	XeWidget  GearRadioMatrixW, Gear1W, Gear2W, Gear2TeethW;



// 20.0deg pressure angle 2:1 ratio 0.100,  4.20, 1.20
// 22.5deg pressure angle 2:1 ratio 0.100,  4.70, 1.20
// 24.0deg pressure angle 2:1 ratio 0.105,  5.50, 1.24
// 28.0deg pressure angle 2:1 ratio 0.108,  6.85, 1.24
// 30.0deg pressure angle 2:1 ratio 0.110,  7.60, 1.24
// 30.0deg pressure angle 3:1 ratio 0.145, 15.40, 1.26
// 30.0deg pressure angle 4:1 ratio 0.167, 26.60, 1.26

#if BevelGearDEBUG
	XeWidget  Gear2AlphaMulW;
	XeWidget  Gear2ToothRotOffsetW;
	XeWidget  OuterCircleScaleW;
#endif
} E3dResourcesGUI;


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



// Internal involute curve
//
/*
https://www.desmos.com/calculator/wx3aoahvrc
Create an equation curve.
Use the following:-
rb=(pitch/2)*cos(p_a)
x=rb*(cos(t*180/pi)+t*sin(t*180/pi))
y=rb*(sin(t*180/pi)-t*cos(t*180/pi))
z=0

First line brackets can be parameterised or type your required pitch radius then pressure angle.
IE rb= 30*cos25.
This technique was first suggested by Frank de Simone (PTC)

A video can be found on the PTC Website:
http://www.ptc.com/appserver/it/icm/cda/template_lib/replay/replay_v.jsp?im_dbkey=10028&icg_dbkey=362
*/





EpIconDef3I(static, CircleDisc);
EpIconDef3I(static, CircleLinear);
EpIconDef3I(static, CircleBezier);
EpIconDef3I(static, CircleBSpline);
EpIconDef3I(static, CircleCardinal);

EpIconDef3I(static, Gear);


enum
{
	CR_SET_RADIUS = XeCR_CUSTOM0,
	CR_SET_SIDES
};


#define C_SPLINE  0x00010
#define C_FACE    0x00020


typedef struct
{
	int            Teeth;
	E3dCoordinate  DiametralPitch, PressureAngle, RootCircleScaleFactor;

	E3dCoordinate  ToothBaseBevelRadius;  // Relative to the total tooth height
	int16_t        ToothBaseBevelSteps;

	E3dCoordinate  ToothTipBevelRadius;   // Relative to the total tooth height
	int16_t        ToothTipBevelSteps;

	E3dCoordinate  BevelGearSphereRadius;

	uint8_t        GearIndex;        // 1 or 2
	int            Gear2Teeth;

	EBool          Internal;


#if BevelGearDEBUG
	float          Gear2AlphaMul;
	float          Gear2ToothRotOffset;
	float          OuterCircleScale;
#endif
} E3dGear;


typedef struct
{
	E3dObjectComponentCore;

// E3dCircleInfo part
//
	E3dCoordinate  Radius,
	               StartAngle, EndAngle,
	               SpiralPitch;
	int            Segments;
	int            GeometryType;
	short          SplineType;
	EBool          LastCVSegmentLinear;

	E3dGear        Gear;
} E3dCircleInfo;


#define ROffset(field)  (void*)(EPtrOffset(E3dCircleInfo, field))
EResource  _CircleResources[]=
{
	{ "Radius",                      E3dCOORDINATE_TYPE,  ROffset(Radius) },
	{ "Start angle",                 E3dCOORDINATE_TYPE,  ROffset(StartAngle) },
	{ "End angle",                   E3dCOORDINATE_TYPE,  ROffset(EndAngle) },
	{ "Spiral pitch",                E3dCOORDINATE_TYPE,  ROffset(SpiralPitch) },
	{ "Points",                      EResINT16,           ROffset(Segments) },
	{ "GeoType",                     EResINT16,           ROffset(GeometryType) },
	{ "SplType",                     EResINT16,           ROffset(SplineType) },

	{ "Gear.Teeth",                  EResINT32,           ROffset(Gear.Teeth) },
	{ "Gear.DiametralPitch",         E3dCOORDINATE_TYPE,  ROffset(Gear.DiametralPitch) },
	{ "Gear.PressureAngle",          E3dCOORDINATE_TYPE,  ROffset(Gear.PressureAngle) },
	{ "Gear.BevelGearSphereRadius",  E3dCOORDINATE_TYPE,  ROffset(Gear.BevelGearSphereRadius) },
	{ "Gear.Internal",               EResBOOLEAN,         ROffset(Gear.Internal) },
	{ "Gear.RootCircleScaleFactor",  E3dCOORDINATE_TYPE,  ROffset(Gear.RootCircleScaleFactor) },
	{ "Gear.ToothBaseBevelRadius",   E3dCOORDINATE_TYPE,  ROffset(Gear.ToothBaseBevelRadius) },
	{ "Gear.ToothBaseBevelSteps",    EResINT16,           ROffset(Gear.ToothBaseBevelSteps) },
	{ "Gear.ToothTipBevelRadius",    E3dCOORDINATE_TYPE,  ROffset(Gear.ToothTipBevelRadius) },
	{ "Gear.ToothTipBevelSteps",     EResINT16,           ROffset(Gear.ToothTipBevelSteps) },

	{ "Gear.GearIndex",              EResUINT8,           ROffset(Gear.GearIndex) },
	{ "Gear.Gear2Teeth",             EResINT32,           ROffset(Gear.Gear2Teeth) },

#if BevelGearDEBUG
	{ "Gear.Gear2AlphaMul",          EResFLOAT32,  ROffset(Gear.Gear2AlphaMul) },
	{ "Gear.Gear2ToothRotOffset",    EResFLOAT32,  ROffset(Gear.Gear2ToothRotOffset) },
	{ "Gear.OuterCircleScale",  EResFLOAT32,  ROffset(Gear.OuterCircleScale) },
#endif
};
#undef ROffset

static E3dObjectComponentClass*  _ComponentClass = NULL;


static void _CreateCircleSpline(E3dSpline* PSpline, E3dCoordinate PRadius, const E3dCoordinate PStartAngle, const E3dCoordinate PEndAngle, const E3dCoordinate PAscentPerRevolution, const int PNumOfSegments)
{
	const E3dCoordinate  LAngleRange = PEndAngle - PStartAngle;const EBool  LFull = LAngleRange == 360.0;
	const E3dCoordinate  LAscentPerSegment = PAscentPerRevolution * ((LAngleRange / 360.0) / (E3dCoordinate)PNumOfSegments);
	const E3dCoordinate  LAngleStep = LAngleRange * E3dDEGREES_TO_RADIANS / (E3dCoordinate)PNumOfSegments;
	const int            LNumOfPoints = LFull ? PNumOfSegments : PNumOfSegments + 1;

	switch(PSpline->SplineType)
	{
		case E3dSplineLINEAR:
		case E3dSplineBSPLINE:
		case E3dSplineCARDINAL:
			{
				if(PSpline->SplineType == E3dSplineBSPLINE)
				{
					const E3dCoordinate  LD = (E3dCoordinate)PNumOfSegments - 1.0, LDSQ = LD * LD, LDCB = LD * LD * LD;

					switch(PNumOfSegments)
					{
						case 3:   PRadius *= 2.0;break;
						case 4:   PRadius *= 1.5;break;
						case 5:   PRadius *= 1.31;break;
						case 6:   PRadius *= 1.203;break;
						case 7:   PRadius *= 1.145;break;
						case 8:   PRadius *= 1.11;break;
						case 9:   PRadius *= 1.085;break;
						case 10:  PRadius *= 1.068;break;
						case 11:  PRadius *= 1.056;break;
						case 12:  PRadius *= 1.0465;break;
						default:  PRadius = PRadius * (1.0 + 6.0 / LDSQ - 3.0 / LDCB);break;
					}
				}
				else if(PSpline->SplineType == E3dSplineCARDINAL)
				{
					switch(PNumOfSegments)
					{
						case 4:   PSpline->Tension = -0.65684;break;
						case 5:   PSpline->Tension = -0.36624;break;
						case 6:   PSpline->Tension = -0.23716;break;
						case 7:   PSpline->Tension = -0.16745;break;
						case 8:   PSpline->Tension = -0.12500;break;
						case 9:   PSpline->Tension = -0.09759;break;
						case 10:  PSpline->Tension = -0.07809;break;
						case 11:  PSpline->Tension = -0.06378;break;
						case 12:  PSpline->Tension = -0.05325;break;
						case 13:  PSpline->Tension = -0.04515;break;
						case 14:  PSpline->Tension = -0.03870;break;
						case 15:  PSpline->Tension = -0.03360;break;
						case 16:  PSpline->Tension = -0.02950;break;
						case 17:  PSpline->Tension = -0.02610;break;
						case 18:  PSpline->Tension = -0.02320;break;
						case 19:  PSpline->Tension = -0.02070;break;
						case 20:  PSpline->Tension = -0.01870;break;
						case 21:  PSpline->Tension = -0.01700;break;
						case 22:  PSpline->Tension = -0.01550;break;
						case 23:  PSpline->Tension = -0.01420;break;
						case 24:  PSpline->Tension = -0.01310;break;
						case 25:  PSpline->Tension = -0.01200;break;
						case 26:  PSpline->Tension = -0.01100;break;
						case 27:  PSpline->Tension = -0.01040;break;
						case 28:  PSpline->Tension = -0.00950;break;
						case 29:  PSpline->Tension = -0.00900;break;
						case 30:  PSpline->Tension = -0.00850;break;
						case 31:  PSpline->Tension = -0.00800;break;
						case 32:  PSpline->Tension = -0.00750;break;
						case 33:  PSpline->Tension = -0.00700;break;
						case 34:  PSpline->Tension = -0.00650;break;
						case 35:  PSpline->Tension = -0.00600;break;
						case 36:  PSpline->Tension = -0.00570;break;
						case 37:  PSpline->Tension = -0.00520;break;
						case 38:  PSpline->Tension = -0.00500;break;
						case 39:  PSpline->Tension = -0.00480;break;
						case 40:  PSpline->Tension = -0.00460;break;
						case 41:  PSpline->Tension = -0.00440;break;
						case 42:  PSpline->Tension = -0.00420;break;
						default:  PSpline->Tension = 0.0;break;
					}
				}

				if(PSpline->CVs.Items == NULL) PSpline->CVs.Items = E3dSplineCVs_New(PSpline->SplineType, LNumOfPoints);

				E3dSplineCV*  LSplineCV = PSpline->CVs.Items;if(LSplineCV)
				{
					PSpline->CVs.Count = PSpline->CVs.CountAllocated = LNumOfPoints;

					E3dCoordinate  LZ = 0.0, LAngle = PStartAngle * E3dDEGREES_TO_RADIANS;
					EIndex  LI = 0;for(;LI < LNumOfPoints;LI++, LAngle += LAngleStep, LSplineCV++, LZ -= LAscentPerSegment)
					{
						LSplineCV->Position.X = E3d_Cos(LAngle) * PRadius;
						LSplineCV->Position.Y = E3d_Sin(LAngle) * PRadius;
						LSplineCV->Position.Z = LZ;
					}
				}
			}
		break;

		case E3dSplineBEZIER:
			{
				if(PSpline->CVs.Items == NULL) PSpline->CVs.Items = E3dSplineCVs_New(PSpline->SplineType, LNumOfPoints);
				if(PSpline->CVs.Items)
				{
					PSpline->CVs.Count = PSpline->CVs.CountAllocated = LNumOfPoints;

					const E3dCoordinate  LTangentLen = E3d_CircleArcBezierTangentLength(PNumOfSegments, LAngleRange);

//	PSpline->NumOfDrawSteps = 500;

					{
						EIndex  LI = 0;E3dCoordinate  LZ = 0.0, LAngle = PStartAngle * E3dDEGREES_TO_RADIANS;E3dSplineCV*  LCV = PSpline->CVs.Items;for(;LI < LNumOfPoints;LI++, LAngle += LAngleStep, LCV++, LZ -= LAscentPerSegment)
						{
							const E3dCoordinate  LX = E3d_Cos(LAngle) * PRadius, LY = E3d_Sin(LAngle) * PRadius;
							const E3dCoord2      LTangent = E3dCoord2_s(-LY, LX);

							LCV->Position.X = LX;
							LCV->Position.Y = LY;
							LCV->Position.Z = LZ;

							LCV->Previous.X = LX - LTangent.X * LTangentLen;
							LCV->Previous.Y = LY - LTangent.Y * LTangentLen;
							LCV->Previous.Z = LZ;

							LCV->Next.X = LX + LTangent.X * LTangentLen;
							LCV->Next.Y = LY + LTangent.Y * LTangentLen;
							LCV->Next.Z = LZ;
						}
					}
				}
			}
		break;
	}
}


static inline E3dCoordinate _Quadratic_ac_pos(const E3dCoordinate a, const E3dCoordinate c)
{
	return(E3d_Sqrt(4.0 * a * c) / (2.0 * a));
}


static inline E3dCoordinate _Quadratic_ac_neg(const E3dCoordinate a, const E3dCoordinate c)
{
	return(E3d_Sqrt(-4.0 * a * c) / (2.0 * a));
}


static inline E3dCoordinate _IntersectLines2D(const E3dCoord3 PP0, const E3dCoord3 PD0,  const E3dCoord3 PP1, const E3dCoord3 PD1)
{
// t0 = (P0y - P1y - D1y * (P0x - P1x) / D1x) / (D1y * D0x / D1x - D0y)
//
	const E3dCoordinate  LNumer = PP0.Y - PP1.Y - PD1.Y * (PP0.X - PP1.X);
	const E3dCoordinate  LDenom = PD1.Y * PD0.X / PD1.X - PD0.Y;
	return(LNumer / LDenom);
}


#if BevelGearALGORITHM == BgLigata
static inline E3dCoord3 _BevelGearPoint_Ligata(const E3dCoordinate PCosAlphaB, const E3dCoordinate PSinAlphaB,  const E3dCoordinate PBeta)
{
	const E3dCoordinate  LCosBeta = E3d_Cos(PBeta);
	const E3dCoordinate  LSinBeta = E3d_Sin(PBeta);
	const E3dCoordinate  LCos_BetaxSinAlphaB = E3d_Cos(PBeta * PSinAlphaB);
	const E3dCoordinate  LSin_BetaxSinAlphaB = E3d_Sin(PBeta * PSinAlphaB);

	E3dCoord3  LResult;
	LResult.X = (LCos_BetaxSinAlphaB * PSinAlphaB * LCosBeta + LSin_BetaxSinAlphaB * LSinBeta);
	LResult.Y = (LCos_BetaxSinAlphaB * PSinAlphaB * LSinBeta - LSin_BetaxSinAlphaB * LCosBeta);
	LResult.Z =  LCos_BetaxSinAlphaB * PCosAlphaB;

	return(LResult);
}
#endif

static inline E3dCoord3 _BevelGearPoint_Wang(const E3dGear* PGear, const uint8_t PGearIndex, const E3dCoordinate Prd, const E3dCoordinate Pb, const E3dCoordinate PTheta, const E3dCoordinate PSBGBeta,
 const E3dCoordinate PCosAlphaB, const E3dCoordinate PSinAlphaB,  const E3dCoordinate PPhi,  E3dCoord3* PNormalR,
 const E3dCoordinate PGear1SinBaseConeAngle,
 const int PCrownRackTeeth, const int PGear1Teeth, const int PGear2Teeth)
{
const E3dCoordinate  PCosAlphaB1 = PCosAlphaB, PSinAlphaB1 = PGear1SinBaseConeAngle;
const E3dCoordinate  PCosAlphaB2 = PCosAlphaB, PSinAlphaB2 = PSinAlphaB;

	E3dCoordinate  LAlpha = 0.0;  // With a 1:1 gear ratio, this is always 0.0, otherwise the base cones move apart during pure-rolling...

	E3dCoordinate  LPhi = PPhi;

	if(PGearIndex == 2)
	{
//		const E3dCoordinate  LRackDist = PPhi * E3dPI * 2.0;
		const E3dCoordinate  LGear1DistAlongGreatCircle = PPhi;
		const E3dCoordinate  LGear2DistAlongGreatCircle = PPhi * PSinAlphaB2 / PSinAlphaB1;

		LAlpha = (LGear1DistAlongGreatCircle - LGear2DistAlongGreatCircle) / 4.0;
printf("%s() %s:%d  PPhi %f    Dist1 %f Dist2 %f  Alpha %f  Alpha by user %f\n", __FUNCTION__, __FILE__, __LINE__, PPhi, LGear1DistAlongGreatCircle, LGear2DistAlongGreatCircle, LAlpha, LPhi * PGear->Gear2AlphaMul);fflush(stdout);

LAlpha *= PGear->Gear2AlphaMul;
//LAlpha = 0.001;
	}



	const E3dCoordinate  LA = LPhi - PSBGBeta;
	const E3dCoordinate  LB = LPhi - PSBGBeta - LAlpha;

	const E3dCoordinate  LPsi1 = LPhi / PSinAlphaB1, LCosPsi1 = E3d_Cos(LPsi1), LSinPsi1 = E3d_Sin(LPsi1);
	const E3dCoordinate  LCosA = E3d_Cos(LA), LSinA = E3d_Sin(LA);
	const E3dCoordinate  LCosA_minus_Theta = E3d_Cos(LA - PTheta);
	const E3dCoordinate  LSinA_minus_Theta = E3d_Sin(LA - PTheta);
	const E3dCoordinate  Prd_x_CosIAminusThetaI_Plus_b_x_SinA         =  Prd * LCosA_minus_Theta + Pb * LSinA;
	const E3dCoordinate  LMinus_rd_x_SinIA_minus_ThetaI_plus_b_x_CosA = -Prd * LSinA_minus_Theta + Pb * LCosA;

	const E3dCoordinate  LPsi2 = LPhi / PSinAlphaB2, LCosPsi2 = E3d_Cos(LPsi2), LSinPsi2 = E3d_Sin(LPsi2);
	const E3dCoordinate  LCosB = E3d_Cos(LB), LSinB = E3d_Sin(LB);
	const E3dCoordinate  LCosB_minus_Theta = E3d_Cos(LB - PTheta);
	const E3dCoordinate  LSinB_minus_Theta = E3d_Sin(LB - PTheta);
	const E3dCoordinate  Prd_x_CosIBminusThetaI_Plus_b_x_SinB         =  Prd * LCosB_minus_Theta + Pb * LSinB;
	const E3dCoordinate  LMinus_rd_x_SinIB_minus_ThetaI_plus_b_x_CosB = -Prd * LSinB_minus_Theta + Pb * LCosB;


	E3dCoord3  LResult;

// Equation (4) and (5)
// r1 surface in coordinate system S1
//
	const E3dCoordinate  LX1 =  PCosAlphaB1 * Prd_x_CosIAminusThetaI_Plus_b_x_SinA;
	const E3dCoordinate  LY1 =  LCosPsi1 * LMinus_rd_x_SinIA_minus_ThetaI_plus_b_x_CosA + LSinPsi1 * PSinAlphaB1 * Prd_x_CosIAminusThetaI_Plus_b_x_SinA;
	const E3dCoordinate  LZ1 =  LSinPsi1 * LMinus_rd_x_SinIA_minus_ThetaI_plus_b_x_CosA - LCosPsi1 * PSinAlphaB1 * Prd_x_CosIAminusThetaI_Plus_b_x_SinA;

	const E3dCoordinate  LX2 =  PCosAlphaB2 * Prd_x_CosIBminusThetaI_Plus_b_x_SinB;
	const E3dCoordinate  LY2 =  LCosPsi2 * LMinus_rd_x_SinIB_minus_ThetaI_plus_b_x_CosB + LSinPsi2 * PSinAlphaB2 * Prd_x_CosIBminusThetaI_Plus_b_x_SinB;
//	const E3dCoordinate  LZ2 = -LSinPsi2 * LMinus_rd_x_SinIB_minus_ThetaI_plus_b_x_CosB + LCosPsi2 * PSinAlphaB2 * Prd_x_CosIBminusThetaI_Plus_b_x_SinB;
	const E3dCoordinate  LZ2 =  LSinPsi2 * LMinus_rd_x_SinIB_minus_ThetaI_plus_b_x_CosB - LCosPsi2 * PSinAlphaB2 * Prd_x_CosIBminusThetaI_Plus_b_x_SinB;


// Equation (10): normal
//
	const E3dCoordinate  LNX1 = PCosAlphaB1 * LCosA_minus_Theta;
	const E3dCoordinate  LNY1 = -LCosPsi1 * LSinA_minus_Theta + PSinAlphaB1 * LSinPsi1 * LCosA_minus_Theta;
	const E3dCoordinate  LNZ1 = -LSinPsi1 * LSinA_minus_Theta - PSinAlphaB1 * LCosPsi1 * LCosA_minus_Theta;


	if(BevelGearSHOW_MESHING_POINTS)
	{
// Meshing point vector in the fixed coordinate system
//
		const E3dCoordinate  LCosPhi1 = E3d_Cos(LPhi);
		const E3dCoordinate  LSinPhi1 = E3d_Sin(LPhi);
		const E3dCoordinate  LSinAlphaB_x_X1_plus_CosAlphaB_x_Z1 = PSinAlphaB1 * LX1 + PCosAlphaB1 * LZ1;
		E3dCoordinate  LX = PCosAlphaB1 * LX1 - PSinAlphaB1 * LZ1;
		E3dCoordinate  LY = LCosPhi1 * LY1 - LSinPhi1 * LSinAlphaB_x_X1_plus_CosAlphaB_x_Z1;
		E3dCoordinate  LZ = LSinPhi1 * LY1 + LCosPhi1 * LSinAlphaB_x_X1_plus_CosAlphaB_x_Z1;


		LResult.X =  LZ;
		LResult.Y = -LY;
		LResult.Z =  LX;


// Equation (24): meshing point normal
//
		const E3dCoordinate  LNX = PCosAlphaB1 * LNX1 - PSinAlphaB1 * LNZ1;
		const E3dCoordinate  LNY = LCosPhi1 * LNY1 - LSinPhi1 * (PSinAlphaB1 * LNX1 + PCosAlphaB1 * LNZ1);
		const E3dCoordinate  LNZ = LSinPhi1 * LNY1 + LCosPhi1 * (PSinAlphaB1 * LNX1 + PCosAlphaB1 * LNZ1);

		PNormalR->X =  LNZ;
		PNormalR->Y = -LNY;
		PNormalR->Z =  LNX;


// Transform to S1 (it's in the fixed coordinate system, S)
{
E3dMatrix  LMatrixS1;E3dMatrix_LoadIdentity(LMatrixS1);

LMatrixS1[M00] = PCosAlphaB1;LMatrixS1[M10] = 0.0;LMatrixS1[M20] = -PSinAlphaB1;
LMatrixS1[M01] =         0.0;LMatrixS1[M11] = 1.0;LMatrixS1[M21] =          0.0;
LMatrixS1[M02] = PSinAlphaB1;LMatrixS1[M12] = 0.0;LMatrixS1[M22] =  PCosAlphaB1;
E3dMatrix_Invert3x3(LMatrixS1, LMatrixS1);
//E3dMatrix_Print3x3();


E3dMatrix  LMatrixPhi1;E3dMatrix_LoadIdentity(LMatrixPhi1);
LMatrixPhi1[M00] = 1.0;LMatrixPhi1[M10] =      0.0;LMatrixPhi1[M20] =       0.0;
LMatrixPhi1[M01] = 0.0;LMatrixPhi1[M11] = LCosPhi1;LMatrixPhi1[M21] = -LSinPhi1;
LMatrixPhi1[M02] = 0.0;LMatrixPhi1[M12] = LSinPhi1;LMatrixPhi1[M22] =  LCosPhi1;
E3dMatrix_Invert3x3(LMatrixPhi1, LMatrixPhi1);

E3dMatrix  LM;E3dMatrix_Mult3x3_3(LMatrixPhi1, LMatrixS1, LM);

EMatrix_Transform3x3_v(LM,    LResult);
EMatrix_Transform3x3_p(LM,    PNormalR);
}

	}
	else
	{
// Return X1,Y1,Z1 (different coordinate order for us that Wang's paper)
//

		switch(PGearIndex)  // Gear 1 and gear 2 have different surface functions...
		{
			case 1:
			default:
				LResult.X =  LZ1;
				LResult.Y = -LY1;
				LResult.Z =  LX1;
			break;

			case 2:
				LResult.X =  LZ2;
				LResult.Y = -LY2;
				LResult.Z =  LX2;
			break;
		}

		PNormalR->X =  LNZ1;
		PNormalR->Y = -LNY1;
		PNormalR->Z =  LNX1;
	}

	return(LResult);
}


#if BevelGearALGORITHM == BgAlDaccak
static inline E3dCoord3 _BevelGearPoint_AlDaccak(const E3dCoordinate PCosAlphaB, const E3dCoordinate PSinAlphaB,  const E3dCoordinate PPsi)
{
	const int      LIterations = 15;
	E3dCoordinate  LPhi = 89.0 * E3dDEGREES_TO_RADIANS;    // Starting value for Phi

// Newton-Rhapson numerical solver
//
	int  LI = 0;for(;LI < LIterations;LI++)
	{
		const E3dCoordinate  LA = (PPsi + LPhi) * PSinAlphaB;
		const E3dCoordinate  LB = E3d_Cos(LA);
		const E3dCoordinate  LCosPhi = E3d_Cos(LPhi);
		const E3dCoordinate  LDerivative = PSinAlphaB * (1.0 / (LB * LB) - 1.0 / (LCosPhi * LCosPhi));
		const E3dCoordinate  LFPhiK = (E3d_Tan(LA) - E3d_Tan(LPhi) * PSinAlphaB);
//printf("%s() %s:%d  %f %f\n", __FUNCTION__, __FILE__, __LINE__, E3d_Tan(LA), E3d_Tan(LPhi));fflush(stdout);
//printf("%s() %s:%d  %d:  LFPhiK %f  LDerivative %f  LPhi %f\n", __FUNCTION__, __FILE__, __LINE__, LI, LFPhiK * E3dRADIANS_TO_DEGREES, LDerivative, LPhi * E3dRADIANS_TO_DEGREES);fflush(stdout);
		LPhi = LPhi - LFPhiK / LDerivative;
	}


	const E3dCoordinate  LA = (PPsi + LPhi) * PSinAlphaB;
	const E3dCoordinate  LSigma = E3d_ATan(E3d_Tan(LA) / (E3d_Sin(LPhi) * PCosAlphaB));

	const E3dCoordinate  LSinSigma = E3d_Sin(LSigma);

	E3dCoord3  LResult;
	LResult.X = LSinSigma * E3d_Cos(PPsi);
	LResult.Y = LSinSigma * E3d_Sin(PPsi);
	LResult.Z = E3d_Cos(LSigma);

	return(LResult);
}
#endif

//========================================================
// Thanks to:
// http://www.cartertools.com/involute.html
// for the excellent description of the involute gear
//========================================================
static void _CreateGearSpline(E3dGear* PGear, E3dSpline* PSpline, const uint8_t PGearIndex, const int PTeeth, const int PGear1Teeth, const EBool PInternal, const E3dCoordinate PDiametralPitch, const E3dCoordinate PPressureAngle, const int PToothSideSegments,
 const E3dCoordinate PBevelGearSphereRadius,
 const E3dCoordinate PStartAngle, const E3dCoordinate PEndAngle, const E3dCoordinate PRootDiameterScale,
 const E3dCoordinate PToothBaseBevelRadius, const int PToothBaseBevelSteps,
 const E3dCoordinate PToothTipBevelRadius, const int PToothTipBevelSteps)
{
	if(PSpline->CVs.Items) E3dSpline_FreeCVs(PSpline);

	const E3dCoordinate  LTeethF1 = (E3dCoordinate)PGear1Teeth;
	const E3dCoordinate  LTeethF = (E3dCoordinate)PTeeth;
	EIndex               LTeeth_Count = PTeeth;

	const E3dCoordinate  LStartAngle_rad = PStartAngle * E3dDEGREES_TO_RADIANS;
	const E3dCoordinate  LAngleRange = PEndAngle - PStartAngle;const EBool  LFull = LAngleRange == 360.0;
	if(!LFull)
	{
		LTeeth_Count = (EIndex)((LAngleRange / 360.0) * LTeethF);
		PSpline->Closed = FALSE;
	}

#if DebugSINGLE_CURVE
LTeeth_Count = 1;
PSpline->Closed = FALSE;
#endif

	const E3dCoordinate  LDedendumMult = 1.157;                       // E3dFIXME Different for pressure angles other than 14.5 degrees. Look up formula!

	const E3dCoordinate  LPressureAngleCos = E3d_Cos(PPressureAngle * E3dDEGREES_TO_RADIANS);

	const E3dCoordinate  D = LTeethF / PDiametralPitch;               // Pitch diameter
	const E3dCoordinate  RD = D / 2.0;                                // Pitch radius : this is how far two gears need to be from each-other (the sum of their pitch radii)
	const E3dCoordinate  DB = D * LPressureAngleCos;                  // Base circle diameter
	const E3dCoordinate  RB = DB / 2.0;                               // Base circle radius


	const E3dCoordinate  FCB = RB / 20.0;                             // 1 / 20th of the base circle radius is arbitrary, it gives good precision
	const E3dCoordinate  GT = 2.0 * E3dPI / LTeethF;                  // Gear tooth spacing
	const E3dCoordinate  CB = E3dPI * DB;                             // Base circle circumference
	const E3dCoordinate  NCB = CB / FCB;
	const E3dCoordinate  ACB = 360.0 / NCB;

	const E3dCoordinate  a = 1.0 / PDiametralPitch;                   // Addendum
	const E3dCoordinate  DO = PInternal ? D + 2.5 * a : D + 2.0 * a;  // Outer diameter = pitch diameter + 2 * addendum. Increase addendum for internal gear (ring) to eliminate internal collision with meshing gear's teeth.
	const E3dCoordinate  RO = DO / 2.0;
	const E3dCoordinate  d = LDedendumMult / PDiametralPitch;
	const E3dCoordinate  DR = (D - 2 * d) * PRootDiameterScale;				// Root circle diameter
	const E3dCoordinate  RR = DR / 2.0;                               // Root circle radius


//printf("%s() %s:%d  Root circle radius %5.4f  Base circle radius %5.4f\n", __FUNCTION__, __FILE__, __LINE__, RR, RB);fflush(stdout);



// We'll rotate the involute, to make creating the other side of
// the tooth a simple matter of inverting the Y coordinates of the points.
//
	{
		E3dMatrix  LHalfToothRotMatrix;E3dMatrix_LoadIdentity(LHalfToothRotMatrix);


// Pitch cone angle
//
// BevelGear
//
		const E3dCoordinate  LAlphaP1 = E3dPI * 0.5 * LTeethF1 / PDiametralPitch;   // Gear 1 pitch cone angle (this is how many degrees the 0,0,1 vector must be rotated around the X axis to get to the new pitch circle)
		const E3dCoordinate  LAlphaP  = E3dPI * 0.5 * LTeethF  / PDiametralPitch;   // Current gear pitch cone angle (this is how many degrees the 0,0,1 vector must be rotated around the X axis to get to the new pitch circle)
//		EBool                LUseCrownGearForMeshing = TRUE;

		if(PTeeth * 2 == (int)PDiametralPitch)
		{
// 90 degree bevel gear : 1:1 ratio
printf("%s() %s:%d  %s1:1%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal);fflush(stdout);
//			LUseCrownGearForMeshing = FALSE;
		}

		if(PTeeth == (int)PDiametralPitch)
		{
// 180 degree bevel gear : crown gear
printf("%s() %s:%d  %scrown gear%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, ECodeNormal);fflush(stdout);
//			LUseCrownGearForMeshing = FALSE;
		}

		const E3dCoordinate  LBevelGearDedendum = LDedendumMult * E3dPI * 0.5 / PDiametralPitch;
		const E3dCoordinate  LAlphaR = (LAlphaP - LBevelGearDedendum) * PRootDiameterScale;       // Root cone angle

		const E3dCoordinate  LBevelGearAddendum = E3dPI * 0.5 / PDiametralPitch;
		const E3dCoordinate  LAlphaO = LAlphaP + LBevelGearAddendum;                              // Outer cone angle

		const E3dCoordinate  LSinAlphaB1 = E3d_Sin(LAlphaP1) * LPressureAngleCos;                 // Gear 1 base cone angle sin and cos

		const E3dCoordinate  LSinAlphaB = E3d_Sin(LAlphaP) * LPressureAngleCos;                   // Current gear base cone angle sin and cos
		const E3dCoordinate  LCosAlphaB = E3d_Sqrt(1.0 - LSinAlphaB * LSinAlphaB);

#if BevelGearDEBUG
printf("%s() %s:%d  AlphaB (base-cone angle) = %f   sin %f  cos %f\n", __FUNCTION__, __FILE__, __LINE__, E3d_ASin(LSinAlphaB) * E3dRADIANS_TO_DEGREES, LSinAlphaB, LCosAlphaB);fflush(stdout);
#endif

//printf("%s() %s:%d  LAlphaR = %f  LAlphaP = %f  LAlphaO %f\n", __FUNCTION__, __FILE__, __LINE__, LAlphaR * E3dRADIANS_TO_DEGREES,  LAlphaP * E3dRADIANS_TO_DEGREES, LAlphaO * E3dRADIANS_TO_DEGREES);fflush(stdout);

		if(PBevelGearSphereRadius == 0.0)
		{
// Find intersection point between involute and pitch circle (D)
// This reduces to the quadratic equation : RB^2 + FCB^2 * n^2 - RD^2 = 0, where LRadialAngle = n * LRadialAngleStep
//
			{
				const E3dCoordinate  qa = FCB * FCB, qc = RB * RB - RD * RD;		// Simplified quadratic solver
				const E3dCoordinate  n1 = E3d_Sqrt(-4.0 * qa * qc) / (2.0 * qa);

				const E3dCoordinate  LRadialAngle = -ACB * E3dDEGREES_TO_RADIANS * n1;
				const E3dCoordinate  LCos = E3d_Cos(LRadialAngle), LSin = E3d_Sin(LRadialAngle);

				const E3dCoordinate  LTangentStartPositionX = LCos * RB, LTangentStartPositionY = LSin * RB;
				const E3dCoordinate  LTangentDirX = -LSin, LTangentDirY = LCos;

				const E3dCoordinate  LTangentLen = FCB * n1;
				const E3dCoordinate  LX = LTangentStartPositionX + LTangentDirX * LTangentLen;
				const E3dCoordinate  LY = LTangentStartPositionY + LTangentDirY * LTangentLen;

				const E3dCoordinate  LA = -E3d_GetAngle2D_rad(LX, LY) + GT / 4.0;
//printf("%s() %s:%d  %f\n", __FUNCTION__, __FILE__, __LINE__, LA * E3dRADIANS_TO_DEGREES);fflush(stdout);
				E3dMatrix_RotateRad(LHalfToothRotMatrix, 'z', LA);
			}
		}
		else
		{
// Bevel gear
// Spherical involute
//

// Find the intersection between the spherical involute and the pitch circle (both are on the surface of the generating sphere)
// LHalfToothRotMatrix will contain a rotation to move that point to Y = 0, plus a quarter of the tooth spacing
//
			{
				const E3dCoordinate  LPitchCircleZ = E3d_Cos(LAlphaP);  // Z value of the pitch circle
				const E3dCoordinate  LCos_BetaxSinAlphaB = LPitchCircleZ / LCosAlphaB;
				const E3dCoordinate  LSin_BetaxSinAlphaB = E3d_Sqrt(1.0 - LCos_BetaxSinAlphaB * LCos_BetaxSinAlphaB);
				const E3dCoordinate  LBeta = E3d_ACos(LCos_BetaxSinAlphaB) / LSinAlphaB;


//printf("%s() %s:%d  LAlphaP %5.4f %5.4f %5.4f  LAlphaB %5.4f %5.4f %5.4f", __FUNCTION__, __FILE__, __LINE__, LAlphaP, E3d_Sin(LAlphaP), LPitchCircleZ,  LAlphaB, LSinAlphaB, LCosAlphaB);fflush(stdout);
//printf(" LCos_BetaxSinAlphaB %5.4f  LSin_BetaxSinAlphaB %5.4f\n", LCos_BetaxSinAlphaB, LSin_BetaxSinAlphaB);fflush(stdout);

				const E3dCoordinate  LCosBeta = E3d_Cos(LBeta);
				const E3dCoordinate  LSinBeta = E3d_Sin(LBeta);

//printf(" LSinAlphaB %5.4f  LCosBeta %5.4f  LSinBeta %5.4f\n", LSinAlphaB, LCosBeta, LSinBeta);fflush(stdout);

				E3dCoord2  LV;LV.X = LCos_BetaxSinAlphaB * LSinAlphaB * LCosBeta + LSin_BetaxSinAlphaB * LSinBeta;LV.Y = LCos_BetaxSinAlphaB * LSinAlphaB * LSinBeta - LSin_BetaxSinAlphaB * LCosBeta;
				E3d_Normalize2_v(LV);

				const E3dCoordinate  LA = E3d_GetAngle2D_rad(LV.X, LV.Y) + GT / 4.0;

printf("%s() %s:%d  %f  GT = %f\n", __FUNCTION__, __FILE__, __LINE__, E3d_GetAngle2D_deg(LV.X, LV.Y), GT);fflush(stdout);

				E3dMatrix_LoadIdentity(LHalfToothRotMatrix);
				E3dMatrix_RotateRad(LHalfToothRotMatrix,  'z',  -LA);

#if BevelGearDEBUG
				E3dMatrix_RotateRad(LHalfToothRotMatrix,  'z',  -PGear->Gear2ToothRotOffset * E3dDEGREES_TO_RADIANS);
#endif
//printf("%s() %s:%d  LV.XY % 5.4f % 5.4f   % 5.4f % 5.4f\n", __FUNCTION__, __FILE__, __LINE__, XY_s(LV), E3d_GetAngle2D_rad(LV.X, LV.Y), -LA * E3dRADIANS_TO_DEGREES);fflush(stdout);
			}
		}




		EBool   LAdd_CIS_i_RootCirclePoint;

		EIndex  LNumOfSegments = PToothSideSegments + PToothBaseBevelSteps + PToothTipBevelSteps;

// If the root circle radius is larger than the base circle radius, we need to start the involute at the root circle
//
		E3dCoordinate  n0 = 0.0;

if(PBevelGearSphereRadius == 0.0)
{
		if(RR > RB)
		{
			n0 = _Quadratic_ac_neg(FCB * FCB, RB * RB - RR * RR);

			LAdd_CIS_i_RootCirclePoint = FALSE;
		}
		else
		{
			LNumOfSegments++;  // Otherwise each tooth will have 2 extra CVs (intersections of center to involute-start vector and the root circle)
			LAdd_CIS_i_RootCirclePoint = TRUE;
		}
}
else
{
// E3dFIXME. Implement this for bevel gears too
//
	LAdd_CIS_i_RootCirclePoint = FALSE;
}

		EIndex  LCVsPerTooth = LNumOfSegments * 2;
		PSpline->CVs.Count = LCVsPerTooth * LTeeth_Count;
//		PSpline->CVs.Count = LNumOfSegments;

		if(LTeeth_Count)
		{
			if(PSpline->CVs.Items == NULL)
			{
				PSpline->CVs.Items = E3dSplineCVs_New(E3dSplineLINEAR, LCVsPerTooth * LTeeth_Count);
				PSpline->CVs.Count = PSpline->CVs.CountAllocated = LCVsPerTooth * LTeeth_Count;
			}

			E3dSplineCV*  LSplineCV = PSpline->CVs.Items;if(LSplineCV)
			{
				LSplineCV += PToothBaseBevelSteps;
				E3dSplineCV*  LFirstCVAfterBaseBevel = LSplineCV;

				if(PBevelGearSphereRadius == 0.0)
				{
// Find intersection point between involute and outer circle (DO)
// This reduces to the quadratic equation : RB^2 + FCB^2 * n^2 - RO^2 = 0, where LRadialAngle = n * LRadialAngleStep
//
					const E3dCoordinate  n1 = _Quadratic_ac_neg(FCB * FCB, RB * RB - RO * RO);
//printf("%s() %s:%d %f\n", __FUNCTION__, __FILE__, __LINE__, n1);fflush(stdout);
					const E3dCoordinate  lr = (n1 - n0) / (E3dCoordinate)(PToothSideSegments - 1);
					E3dCoordinate        LRadialAngle = -ACB * E3dDEGREES_TO_RADIANS * n0;const E3dCoordinate  LRadialAngleStep = ACB * E3dDEGREES_TO_RADIANS * lr;

// Skip point that will be the intersection of the center-to-involutestart vector and the root circle.
// It will be set by (A)
//
					if(LAdd_CIS_i_RootCirclePoint) LSplineCV++;

					const E3dSplineCV*  LInvoluteStartCV = LSplineCV;

					{
						EIndex  LI = 0;for(;LI < PToothSideSegments;LI++, LRadialAngle -= LRadialAngleStep, LSplineCV++)
						{
							const E3dCoordinate  LCos = E3d_Cos(LRadialAngle), LSin = E3d_Sin(LRadialAngle);

							const E3dCoordinate  LTangentStartPositionX = LCos * RB, LTangentStartPositionY = LSin * RB;
							const E3dCoordinate  LTangentDirX = -LSin, LTangentDirY = LCos;

							const E3dCoordinate  LTangentLen = FCB * ((E3dCoordinate)LI * lr + n0);
//printf("%s() %s:%d %f\n", __FUNCTION__, __FILE__, __LINE__, LTangentLen);fflush(stdout);
							LSplineCV->Position.X = LTangentStartPositionX + LTangentDirX * LTangentLen;
							LSplineCV->Position.Y = LTangentStartPositionY + LTangentDirY * LTangentLen;
							LSplineCV->Position.Z = 0.0;

							EMatrix_Transform3x3_v(LHalfToothRotMatrix, LSplineCV->Position);
							LSplineCV->Position.Y = -LSplineCV->Position.Y;  // Mirror involute around X axis, to draw spline in the proper (counter-clockwise) order
						}
					}

// (A) 
//
					if(LAdd_CIS_i_RootCirclePoint)
					{
						const E3dCoordinate  RR2 = RR * RR;
						E3dCoord3            LInvoluteStartDir;E3d_Normalize3_vv(LInvoluteStartCV->Position,  LInvoluteStartDir);
						const E3dCoordinate  t = _Quadratic_ac_pos(LInvoluteStartDir.X * LInvoluteStartDir.X + LInvoluteStartDir.Y * LInvoluteStartDir.Y, RR2);

						E3dSplineCV*  LCV = LFirstCVAfterBaseBevel;
						LCV->Position.X = LInvoluteStartDir.X * t;
						LCV->Position.Y = LInvoluteStartDir.Y * t;
					}
				}
				else
				{
// BevelGear
//

// Create spherical involute curve
//
#if BevelGearALGORITHM == BgWang
// rd=1, b=0, Theta=E3dPI/2.0 and LSBGBeta=0 will make it a straight bevel gear
//
					const E3dCoordinate  Lrd = 1.0;             // Spiral generating line (arc) radius
					const E3dCoordinate  Lb = 0.0;              // Distance of spiral generating line (arc) origin from the cone apex (origin)

					const E3dCoordinate  LTheta = E3dPI / 2.0;  // Parameter of the spiral generating line (arc)
					const E3dCoordinate  LSBGBeta = 0.0;        // Generating line (arc) Yd axis angle from Yq

					{
// Figure out how much to rotate the involute, as Wang's formula doesn't start it nicely on a principal axis.
// It touches the base cone / shphere great circle at Phi = PI/2
//
						E3dCoord3  LNormal;
#if BevelGearSHOW_MESHING_POINTS
						E3dCoord3  LV = _BevelGearPoint_Wang(PInfo, PGearIndex, Lrd, Lb, LTheta, LSBGBeta,  LCosAlphaB, LSinAlphaB,  0.0, &LNormal,  (int)PDiametralPitch, (int)PGear1Teeth, (int)PGear2Teeth);

						E3dMatrix_LoadIdentity(LHalfToothRotMatrix);
						E3d_Normalize2_v(LV);E3dCoordinate  LA = E3d_GetAngle2D_rad(LV.X, LV.Y);

//					E3dMatrix_RotateRad(LHalfToothRotMatrix, 'z', -LA);
#else
//					E3dCoord3  LV = _BevelGearPoint_Wang(PGear, PGearIndex, Lrd, Lb, LTheta, LSBGBeta,  LCosAlphaB, LSinAlphaB,  PGearIndex == 1 ? E3dPI / 2.0 : 0.0, &LNormal,  (int)PDiametralPitch, (int)PGear1Teeth, (int)PGear2Teeth);
						E3dCoord3  LV = _BevelGearPoint_Wang(PGear, PGearIndex, Lrd, Lb, LTheta, LSBGBeta,  LCosAlphaB, LSinAlphaB,  E3dPI / 2.0, &LNormal,  LSinAlphaB1, (int)PDiametralPitch, (int)PGear1Teeth, (int)PTeeth);

						E3d_Normalize2_v(LV);E3dCoordinate  LA = E3d_GetAngle2D_rad(LV.X, LV.Y);

						E3dMatrix_RotateRad(LHalfToothRotMatrix, 'z', -LA);
#endif


#if DebugSINGLE_CURVE
	E3dMatrix_LoadIdentity(LHalfToothRotMatrix);
#endif
	printf("%s() %s:%d  Wang %f\n", __FUNCTION__, __FILE__, __LINE__, LA * E3dRADIANS_TO_DEGREES);fflush(stdout);


// Find Z = 0 numerically (base cone intersecting the great circle of BevelGearSphere)
//

#if BevelGearDEBUG
	if(0)
	{
		EIndex  LI = 0;const EIndex  LN = 90;E3dCoordinate  LStep = 2.0 * E3dPI / (E3dCoordinate)(LN - 1);E3dCoordinate  LAngle = 0.0;for(;LI < LN;LI++, LAngle += LStep)
		{
//		E3dCoord3  LV1 = _BevelGearPoint_Ligata(LCosAlphaB, LSinAlphaB, LAngle);
			E3dCoord3  LTNormal;E3dCoord3  LV1 = _BevelGearPoint_Wang(PGear, PGearIndex, Lrd, Lb, LTheta, LSBGBeta,  LCosAlphaB, LSinAlphaB,  E3dPI - LAngle, &LTNormal,  LSinAlphaB1, (int)PDiametralPitch, (int)PGear1Teeth, (int)PTeeth);

EMatrix_Transform3x3_v(LHalfToothRotMatrix, LV1);

printf("%s() %s:%d  %s%3d%s: LAngle % 11.5f%c   LV (% 9.5f % 9.5f % 9.5f)\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (int)LI, ECodeNormal, LAngle * E3dRADIANS_TO_DEGREES, 0xB0, XYZ_s(LV1));fflush(stdout);
		}
	}
#endif
					}


// Derive LPhi (=LBetaStart) from LRootCircleZ
//
					const E3dCoordinate  LRootCircleZ = E3d_Cos(LAlphaR);
					E3dCoordinate        LAA = LRootCircleZ / (LCosAlphaB * Lrd);if(LAA > 1.0) LAA = 1.0;  // ACos is invalid for values > 1...

// 22.5 presure angle crown rack, 48 teeth, base cone angle (AlphaB) 67.499994 degrees
// rotate meshing points curve -75.1692 degree around X

#if BevelGearSHOW_MESHING_POINTS
					const E3dCoordinate  LBetaStart = -E3dPI / 16.0;
					const E3dCoordinate  LBetaEnd   =  E3dPI / 16.0;
#else
					const E3dCoordinate  LOuterCircleZ = E3d_Cos(LAlphaO);
					const E3dCoordinate  LBetaStart = -E3d_ACos(LAA) + LSBGBeta + LTheta;
					const E3dCoordinate  LBetaEnd = (-E3d_ACos(LOuterCircleZ / (LCosAlphaB * Lrd)) + LSBGBeta + LTheta) / PGear->OuterCircleScale;
#endif


	printf("%s() %s:%d  LBetaStart % 10.5f  LBetaEnd % 10.5f\n", __FUNCTION__, __FILE__, __LINE__, LBetaStart, LBetaEnd);fflush(stdout);
//assert(0);

#endif // BgWang

//printf("%s() %s:%d Wang  B %f %f  R %f %f  P %f %f  O %f %f   %f ... %f  %f\n", __FUNCTION__, __FILE__, __LINE__, LSinAlphaB, LCosAlphaB,  E3d_Sin(LAlphaR), E3d_Cos(LAlphaR),  E3d_Sin(LAlphaP), E3d_Cos(LAlphaP),  E3d_Sin(LAlphaO), E3d_Cos(LAlphaO), LBetaStart, LBetaEnd, LAlphaR);fflush(stdout);

#if BevelGearALGORITHM == BgLigata
//				const E3dCoordinate  LAlphaB = E3d_ASin(LSinAlphaB);
//				const E3dCoordinate  LBetaStart = -E3dPI / 2.0, LBetaEnd = E3dPI / 2.0;
					const E3dCoordinate  LRootCircleZ = E3d_Cos(LAlphaR);
					E3dCoordinate        LAA = LRootCircleZ / LCosAlphaB;if(LAA > 1.0) LAA = 1.0;  // ACos is invalid for values > 1...
					const E3dCoordinate  LBetaStart = E3d_ACos(LAA) / LSinAlphaB;

					const E3dCoordinate  LOuterCircleZ = E3d_Cos(LAlphaO);
					const E3dCoordinate  LBetaEnd = E3d_ACos(LOuterCircleZ / LCosAlphaB) / LSinAlphaB;
	printf("%s() %s:%d Ligata  %f ... %f\n", __FUNCTION__, __FILE__, __LINE__, LBetaStart, LBetaEnd);fflush(stdout);
#elif BevelGearALGORITHM == BgAlDaccak
	printf("%s() %s:%d Ligata\n", __FUNCTION__, __FILE__, __LINE__);fflush(stdout);
					const E3dCoordinate  LBetaStart = 0.0, LBetaEnd = 7.0 * E3dDEGREES_TO_RADIANS;
#endif


					const E3dCoordinate  LBetaRange = LBetaEnd - LBetaStart;
					const E3dCoordinate  LBetaStep = LBetaRange / (E3dCoordinate)(PToothSideSegments - 1);
					E3dCoordinate        LBeta = LBetaStart;  // Parameter of the involute

	printf("%s() %s:%d  LBetaStart % 10.5f  LBetaEnd % 10.5f\n", __FUNCTION__, __FILE__, __LINE__, LBetaStart, LBetaEnd);fflush(stdout);

					EIndex  LI = 0;const EIndex  LN = PToothSideSegments;for(;LI < LN;LI++, LSplineCV++, LBeta += LBetaStep)
					{
#if BevelGearALGORITHM == BgLigata
						LSplineCV->Position = _BevelGearPoint_Ligata(LCosAlphaB, LSinAlphaB, LBeta);
#endif

#if BevelGearALGORITHM == BgWang
						const E3dCoordinate  LPhi = E3dPI - LBeta;

						LSplineCV->Position = _BevelGearPoint_Wang(PGear, PGearIndex, Lrd, Lb, LTheta, LSBGBeta,  LCosAlphaB, LSinAlphaB,  LPhi,  &LSplineCV->Normal,  LSinAlphaB1, (int)PDiametralPitch, (int)PGear1Teeth, (int)PTeeth);

						const E3dCoordinate  LNL = PBevelGearSphereRadius * BevelGearSPLINE_NORMAL_RATIO;
						LSplineCV->Normal.X *= LNL;
						LSplineCV->Normal.Y *= LNL;
						LSplineCV->Normal.Z *= LNL;

//printf("%s() %s:%d  LBeta % 10.5f  LPhi % 10.5f  Normal % 10.5f % 10.5f % 10.5f\n", __FUNCTION__, __FILE__, __LINE__, LBeta, LPhi,  XYZ_s(LSplineCV->Normal));fflush(stdout);
#endif

#if BevelGearALGORITHM == BgAlDaccak
						LSplineCV->Position = _BevelGearPoint_AlDaccak(LCosAlphaB, LSinAlphaB, LBeta);
#endif

						LSplineCV->Position.X *= PBevelGearSphereRadius;
						LSplineCV->Position.Y *= PBevelGearSphereRadius;
						LSplineCV->Position.Z *= PBevelGearSphereRadius;


//printf("%s() %s:%d  Z %5.4f  LSinAlphaB %5.4f  LCosAlphaB %5.4f", __FUNCTION__, __FILE__, __LINE__, LSplineCV->Position.Z, LSinAlphaB, LCosAlphaB);fflush(stdout);
//printf(" LCos_BetaxSinAlphaB %5.4f  LSin_BetaxSinAlphaB %5.4f\n", LCos_BetaxSinAlphaB, LSin_BetaxSinAlphaB);fflush(stdout);
//printf(" LSinAlphaB %5.4f  LCosBeta %5.4f  LSinBeta %5.4f\n", LSinAlphaB, LCosBeta, LSinBeta);fflush(stdout);

						EMatrix_Transform3x3_v(LHalfToothRotMatrix, LSplineCV->Position);
						EMatrix_Transform3x3_v(LHalfToothRotMatrix, LSplineCV->Normal);
					}
				}




// Bevels
//
				const E3dCoordinate  LToothTotalHeight = RO - RB;


// Bevel tooh base, if requested
//
				if(PToothBaseBevelSteps)
				{
					E3dCoordinate  LBevelRadius = LToothTotalHeight * PToothBaseBevelRadius;


// Rotate first tooth point down, to get the end of the previous tooth for the beginning of the tooth base
//
					const E3dCoord3  LToothBaseSegmentEnd = LFirstCVAfterBaseBevel->Position;
					E3dCoord3        LToothBaseSegmentStart = LToothBaseSegmentEnd;LToothBaseSegmentStart.Y *= -1.0;
					E3dMatrix        LMatrix;E3dMatrix_LoadIdentity(LMatrix);E3dMatrix_RotateRad(LMatrix, 'z', -GT);
					EMatrix_Transform3x3_vv(LMatrix, LToothBaseSegmentStart, LToothBaseSegmentStart);
					const E3dCoord3  E_Sub3i_vv(LToothBaseSegment,  LToothBaseSegmentEnd, LToothBaseSegmentStart);
					E3dCoordinate    LToothBaseSegmentLength;E3dCoord3  LToothBaseSegmentDir;E3d_NormalizeGetLength3_vv(LToothBaseSegment, LToothBaseSegmentDir, LToothBaseSegmentLength);

//printf("%s() %s:%d  % 5.4f % 5.4f - % 5.4f % 5.4f\n", __FUNCTION__, __FILE__, __LINE__, XY_s(LToothBaseSegmentStart), XY_s(LToothBaseSegmentEnd));fflush(stdout);

					const E3dCoord3  LFirstToothSegmentEnd = LFirstCVAfterBaseBevel[1].Position;
					const E3dCoord3  E_Sub3i_vv(LFirstToothSegment,  LFirstToothSegmentEnd, LToothBaseSegmentEnd);
					E3dCoordinate    LFirstToothSegmentLength;E3dCoord3  LFirstToothSegmentDir;E3d_NormalizeGetLength3_vv(LFirstToothSegment, LFirstToothSegmentDir, LFirstToothSegmentLength);

//printf("%s() %s:%d  % 5.4f % 5.4f - % 5.4f % 5.4f\n", __FUNCTION__, __FILE__, __LINE__, XY_s(LToothBaseSegmentEnd), XY_s(LFirstToothSegmentEnd));fflush(stdout);


					if(LBevelRadius > LToothBaseSegmentLength * 0.48) LBevelRadius = LToothBaseSegmentLength * 0.48;
					if(LBevelRadius > LFirstToothSegmentLength * 0.96) LBevelRadius = LFirstToothSegmentLength * 0.96;


// Get vector that is perpendicular to LToothBaseSegment and points toward the inside of the tooth, via
// the cross-product of the Z axis (we're building on the X-Y plane) and LToothBaseSegmentDir.
//
					const E3dCoord3  LTmpCross = E3dCoord3_s(0.0, 0.0, -1.0);
					E3dCoord3  LToothBaseSegmentPerpendicular;E_Cross3_vvv(LTmpCross, LToothBaseSegmentDir,  LToothBaseSegmentPerpendicular);
					E3dCoord3  LFirstToothSegmentPerpendicular;E_Cross3_vvv(LTmpCross, LFirstToothSegmentDir,  LFirstToothSegmentPerpendicular);

// The center of the bevel arc is at the intersection of these two vectors offset parallel from LToothBaseSegment and LFirstToothSegment by LBevelRadius
//
					const E3dCoord3  E_AddScaled3i_vv(LToothBaseOffsetSegmentP0,   LToothBaseSegmentStart, LToothBaseSegmentPerpendicular,  LBevelRadius);
					const E3dCoord3  E_AddScaled3i_vv(LFirstToothOffsetSegmentP0,  LToothBaseSegmentEnd,   LFirstToothSegmentPerpendicular, LBevelRadius);

//printf("%s() %s:%d  % 5.4f % 5.4f\n", __FUNCTION__, __FILE__, __LINE__, XY_s(LToothBaseOffsetSegmentP0));fflush(stdout);
//printf("%s() %s:%d  % 5.4f % 5.4f\n", __FUNCTION__, __FILE__, __LINE__, XY_s(LToothBaseSegmentDir));fflush(stdout);
//printf("%s() %s:%d  % 5.4f % 5.4f\n", __FUNCTION__, __FILE__, __LINE__, XY_s(LFirstToothOffsetSegmentP0));fflush(stdout);
//printf("%s() %s:%d  % 5.4f % 5.4f\n", __FUNCTION__, __FILE__, __LINE__, XY_s(LFirstToothSegmentDir));fflush(stdout);


//				const E3dCoordinate  LT0 = (LToothBaseOffsetSegmentP0.Y - LFirstToothOffsetSegmentP0.Y) * LFirstToothOffsetSegmentP0.X - (LToothBaseOffsetSegmentP0.X - LFirstToothOffsetSegmentP0.X) * LFirstToothOffsetSegmentP0.Y;
					const E3dCoordinate  LT0 = _IntersectLines2D(LToothBaseOffsetSegmentP0, LToothBaseSegmentDir,  LFirstToothOffsetSegmentP0, LFirstToothSegmentDir);
					const E3dCoord2      E_AddScaled2i_vv(LArcCenter,   LToothBaseOffsetSegmentP0, LToothBaseSegmentDir,  LT0);
//printf("%s() %s:%d  Center % 5.4f % 5.4f\n", __FUNCTION__, __FILE__, __LINE__, XY_s(LArcCenter));fflush(stdout);

					const E3dCoordinate  LStartAngle = E3d_GetAngle2D_deg(-LFirstToothSegmentPerpendicular.X, -LFirstToothSegmentPerpendicular.Y);
					const E3dCoordinate  LEndAngle   = E3d_GetAngle2D_deg(-LToothBaseSegmentPerpendicular.X, -LToothBaseSegmentPerpendicular.Y);

					E3dCoord2  LUnitArcPoints[PToothBaseBevelSteps];E3d_CreateUnitArc(PToothBaseBevelSteps, LStartAngle, LEndAngle,  LUnitArcPoints);

					EIndex  LI = 0;E3dSplineCV*  LCV = PSpline->CVs.Items;for(;LI <= PToothBaseBevelSteps;LI++, LCV++)
					{
						const EIndex  LIdx = PToothBaseBevelSteps - LI;
						LCV->Position.X = LUnitArcPoints[LIdx].X * LBevelRadius + LArcCenter.X;
						LCV->Position.Y = LUnitArcPoints[LIdx].Y * LBevelRadius + LArcCenter.Y;
						LCV->Position.Z = 0.0;
					}

				}



// Bevel tooh tip, if requested
//
				if(PToothTipBevelSteps)
				{
					E3dCoordinate    LBevelRadius = LToothTotalHeight * PToothTipBevelRadius;
					const E3dCoord3  LLastInvoluteSegmentStart = LSplineCV[-2].Position;
					const E3dCoord3  LLastInvoluteSegmentEnd   = LSplineCV[-1].Position;
					const E3dCoord3  E_Sub3i_vv(LLastInvoluteSegment,  LLastInvoluteSegmentEnd, LLastInvoluteSegmentStart);
					E3dCoordinate    LLastInvoluteSegmentLength;E3dCoord3  LLastInvoluteSegmentDir;E3d_NormalizeGetLength3_vv(LLastInvoluteSegment, LLastInvoluteSegmentDir, LLastInvoluteSegmentLength);

//printf("%s() %s:%d   % 5.4f % 5.4f % 5.4f  % 5.4f % 5.4f % 5.4f  LToothTotalHeight %f LBevelRadius %f LLastInvoluteSegmentLength %f\n", __FUNCTION__, __FILE__, __LINE__, XYZ_s(LLastInvoluteSegmentStart), XYZ_s(LLastInvoluteSegmentEnd),  LToothTotalHeight, LBevelRadius, LLastInvoluteSegmentLength);fflush(stdout);

					if(LBevelRadius > LLastInvoluteSegmentLength * 0.96) LBevelRadius = LLastInvoluteSegmentLength * 0.96;
					if(LBevelRadius > LLastInvoluteSegmentEnd.Y * -0.96) LBevelRadius = LLastInvoluteSegmentEnd.Y * -0.96;

// Get vector that is perpendicular to LLastInvoluteSegment and points toward the inside of the tooth, via
// the cross-product of the Z axis (we're building on the X-Y plane) and LLastInvoluteSegmentDir.
//
					E3dCoord3  LPerpendicular;{ const E3dCoord3  LTmpCross = E3dCoord3_s(0.0, 0.0, 1.0);E_Cross3_vvv(LTmpCross, LLastInvoluteSegmentDir,  LPerpendicular); }

// The center of the bevel circle is at the intersection of 2 lines that are parallel with the LLastInvoluteSegment
// and the LLastSegmentDir, offset by LBevelRadius toward the inside of the tooth.
// The last segment is simply a vertical line, so we've done a lot of simplifications here.
//
					const E3dCoord3      E_AddScaled3i_vv(LP0,  LLastInvoluteSegmentStart, LPerpendicular, LBevelRadius);
					const E3dCoordinate  LArcCenterX = LLastInvoluteSegmentEnd.X - LBevelRadius;
					const E3dCoordinate  LT = (LArcCenterX - LP0.X) / LLastInvoluteSegmentDir.X;
					const E3dCoordinate  LArcCenterY = LP0.Y + LLastInvoluteSegmentDir.Y * LT;

//printf("%s() %s:%d   P0' % 5.4f % 5.4f % 5.4f  T % 5.4f  % 5.4f % 5.4f\n", __FUNCTION__, __FILE__, __LINE__, XYZ_s(LP0), LT, LArcCenterX, LArcCenterY);fflush(stdout);

// We have the radius and the center of the arc.
// The end angle is also known. It's 0, because the arc ends where it meets the last segment (again, a vertical line).
// Now, compute the start angle.
// We can derive it from LLastInvoluteSegmentDir, as the starting radial is 90 degrees to that vector.
// 
					const E3dCoordinate  LStartAngle = E3d_GetAngle2D_deg(LLastInvoluteSegmentDir.X, LLastInvoluteSegmentDir.Y) - 90.0;
					const E3dCoordinate  LEndAngle = 0.0;


					E3dCoord2  LUnitArcPoints[PToothTipBevelSteps];E3d_CreateUnitArc(PToothTipBevelSteps, LStartAngle, LEndAngle,  LUnitArcPoints);

					LSplineCV -= 1;
					
					EIndex  LI = 0;for(;LI <= PToothTipBevelSteps;LI++, LSplineCV++)
					{
						LSplineCV->Position.X = LUnitArcPoints[LI].X * LBevelRadius + LArcCenterX;
						LSplineCV->Position.Y = LUnitArcPoints[LI].Y * LBevelRadius + LArcCenterY;
						LSplineCV->Position.Z = 0.0;
					}
				}



// Create the other side of the tooth
//
#if DebugSINGLE_CURVE == 0
				{
					const E3dSplineCV*  LSrcCV = LSplineCV - 1;
					EIndex  LI = 0;for(;LI < LNumOfSegments;LI++, LSrcCV--, LSplineCV++)
					{
						LSplineCV->Position.X =  LSrcCV->Position.X;
						LSplineCV->Position.Y = -LSrcCV->Position.Y;
						LSplineCV->Position.Z =  LSrcCV->Position.Z;

						LSplineCV->Normal.X =  LSrcCV->Normal.X;
						LSplineCV->Normal.Y = -LSrcCV->Normal.Y;
						LSplineCV->Normal.Z =  LSrcCV->Normal.Z;
					}
				}
#endif


// Generate the rest of the teeth
//
				{
					EIndex  LToothC = 1;for(;LToothC < LTeeth_Count;LToothC++)
					{
						E3dMatrix  LMatrix;E3dMatrix_LoadIdentity(LMatrix);E3dMatrix_RotateRad(LMatrix, 'z', GT * (E3dCoordinate)LToothC + LStartAngle_rad);

						EIndex  LI = 0;const E3dSplineCV*  LSrcCV = PSpline->CVs.Items;for(;LI < LCVsPerTooth;LI++, LSrcCV++, LSplineCV++)
						{
							EMatrix_Transform3x3_vv(LMatrix, LSrcCV->Position, LSplineCV->Position);
							EMatrix_Transform3x3_vv(LMatrix, LSrcCV->Normal,   LSplineCV->Normal);
						}
					}

// Now, rotate first tooth to PStartAngle
//
					{
						E3dMatrix  LMatrix;E3dMatrix_LoadIdentity(LMatrix);E3dMatrix_RotateRad(LMatrix, 'z', LStartAngle_rad);
						EIndex  LI = 0;const E3dSplineCV*  LSrcCV = PSpline->CVs.Items;LSplineCV = PSpline->CVs.Items;for(;LI < LCVsPerTooth;LI++, LSrcCV++, LSplineCV++)
						{
							EMatrix_Transform3x3_vv(LMatrix, LSrcCV->Position, LSplineCV->Position);
							EMatrix_Transform3x3_vv(LMatrix, LSrcCV->Normal, LSplineCV->Normal);
						}
					}
				}
			}
		}
	}
}


//========================================
// Create the Geometry
//========================================
static void _Create(E3dObject* PObject, E3dCircleInfo* PInfo, const int PSplineType, const E3dCoordinate PRadius, const E3dCoordinate PStartAngle, const E3dCoordinate PEndAngle, const int PNumOfSegments)
{
	E3dSpline*           LSpline = NULL;
	const E3dCoordinate  LAngleRange = PEndAngle - PStartAngle;const EBool  LFull = LAngleRange == 360.0;


	if(EObject_IsSubClass(PObject, E3dSpline))
	{
		LSpline = (E3dSpline*)PObject;
		LSpline->Closed = LFull && PInfo->SpiralPitch == 0.0;
	}
	else if(EObject_IsSubClass(PObject, E3dFace))
	{
		E3dFace*  LFace = (E3dFace*)PObject;
		if(LFace->Exterior == NULL)
		{
			E_New(E3dSpline, LSpline);
			E3dFace_SetExterior(LFace, LSpline);
			LSpline->SplineType = PSplineType;
		}
		else LSpline = LFace->Exterior;

		LSpline->Closed = TRUE;
	}

	if(EObject_IsSubClass(PObject, E3dSpline) || EObject_IsSubClass(PObject, E3dFace))
	{
		if(LSpline->CVs.Items) E3dSpline_FreeCVs(LSpline);
		LSpline->SplineType = PSplineType;
		if(PInfo->Gear.Teeth)
		{
//			PInfo->Gear.DiametralPitch = (E3dCoordinate)(PInfo->Gear.Teeth) / (PInfo->Radius * 2.0);

			_CreateGearSpline(&PInfo->Gear, LSpline, PInfo->Gear.GearIndex, PInfo->Gear.GearIndex == 2 ? PInfo->Gear.Gear2Teeth : PInfo->Gear.Teeth, PInfo->Gear.Teeth, PInfo->Gear.Internal, PInfo->Gear.DiametralPitch, PInfo->Gear.PressureAngle, PInfo->Segments,
			PInfo->Gear.BevelGearSphereRadius,
      PInfo->StartAngle, PInfo->EndAngle,  PInfo->Gear.RootCircleScaleFactor,
      PInfo->Gear.ToothBaseBevelRadius, PInfo->Gear.ToothBaseBevelSteps,
      PInfo->Gear.ToothTipBevelRadius,  PInfo->Gear.ToothTipBevelSteps);
		}
		else
		{
			_CreateCircleSpline(LSpline, PRadius,  PStartAngle, PEndAngle,  PInfo->SpiralPitch,  PNumOfSegments);

			if(PInfo->LastCVSegmentLinear && !LFull)
			{
				E3dSplineCV*  LCV = LSpline->CVs.Items + LSpline->CVs.Count - 1;LCV->Segment.Shape = E3dSplineLINEAR;
			}
		}
	}
}


//================================================
// Copy editable fields
//================================================
static void _CopyFields(const E3dCircleInfo* PSrc, E3dCircleInfo* PDst)
{
#define _CopyField(p)  PDst->p = PSrc->p
	_CopyField(Radius);
	_CopyField(StartAngle);_CopyField(EndAngle);
	_CopyField(SpiralPitch);
	_CopyField(Segments);
	_CopyField(GeometryType);
	_CopyField(SplineType);
	_CopyField(LastCVSegmentLinear);

	_CopyField(Gear);
#undef _CopyField
}


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

	E3dCircleInfo*  LInfo;

	if(PObject == NULL)
	{
		int  LGeometryType = C_SPLINE;

		if(PComponentTemplate && PComponentTemplate->Class == _ComponentClass) { LInfo = (E3dCircleInfo*)PComponentTemplate;LGeometryType = LInfo->GeometryType; }
		
		switch(LGeometryType)
		{
			case C_SPLINE:
				E_NewC(E3dSpline_class, PObject);
				((E3dSpline*)PObject)->Closed = TRUE;
			break;

			case C_FACE:
				E_NewC(E3dFace_class, PObject);
			break;
		}

		LInfo = (E3dCircleInfo*)E3dObjectComponent_Add(PObject, _ComponentClass);
		if(PComponentTemplate && PComponentTemplate->Class == _ComponentClass) _CopyFields((E3dCircleInfo*)PComponentTemplate, LInfo);
	}
	else
	{
		LInfo = (E3dCircleInfo*)E3dObjectComponent_ByClass(PObject, _ComponentClass, FALSE);
		if(!PCheckOnly && LInfo->AttributeChangeCount == 0 && LInfo->InputChangeCount == 0) return(NULL);    // If no attributes or inputs changed, bail
	}


	_Create(PObject, LInfo, LInfo->SplineType, LInfo->Radius, LInfo->StartAngle, LInfo->EndAngle, LInfo->Segments);


// If the Geometry we created is a gear and it's an E3dFace, create Polygons for it that nicely tesselate the teeth.
// This makes things faster and it makes it possible to create proper bevel gears.
//
	E3dFace*  LFace = E_DynamicCast(PObject, E3dFace);if(LFace)
	{
		E3dFace_CreateVertices(LFace);

		E3dPolyGroup*  LPolyGroup = LFace->PolyGroup;if(LPolyGroup)
		{
			E3dPolyGroup_FreePolygons(LPolyGroup);

			if(LInfo->Gear.Teeth > 0)
			{
				E3dSpline*  LSpline = LFace->Exterior;if(LSpline->CVs.Count > 0)
				{
					const E3dVertexNodeSize  LVertexNodeSize = LPolyGroup->VertexNodeSize;
					const EIndex             LTeeth = LInfo->Gear.Teeth;
					const EIndex             LPointsPerTooth = LSpline->CVs.Count / LTeeth, LPointsPerToothHalf = LPointsPerTooth / 2;
					const EIndex             LPolygonsPerTooth = LPointsPerToothHalf - 1;

					E3dPolygon*  LPolygon = E3dPolyGroup_AddPolygons(LPolyGroup, LTeeth * LPolygonsPerTooth + 1);

//printf("%s() %s:%d\n", __FUNCTION__, __FILE__, __LINE__);fflush(stdout);

// Teeth
//
					EIndex  LTC = 0, LBase = 0;for(;LTC < LTeeth;LTC++, LBase += LPointsPerTooth)
					{
						EIndex  LI = 0;for(;LI < LPolygonsPerTooth;LI++, LPolygon++)
						{
/*
		assert(LPolygon - LPolyGroup->Polygons < LTeeth * LPolygonsPerTooth);

		assert(LBase + LI < LSpline->CVs_Count);
		assert(LBase + LI + 1 < LSpline->CVs_Count);
		assert(LBase + LPointsPerTooth - LI - 2 < LSpline->CVs_Count);
		assert(LBase + LPointsPerTooth - LI - 1 < LSpline->CVs_Count);
*/

//						E3dPolygon_InitVa(LPolygon, LVertexNodeSize, 4,  LBase + LI, LBase + LI + 1,  LBase + LPointsPerTooth - LI - 2,  LBase + LPointsPerTooth - LI - 1);
//						E3dPolygon_InitVa(LPolygon, LVertexNodeSize, 4,  0, 1, 2, 3);
							E3dPolygon_InitAsQuad(LPolygon, LVertexNodeSize,  LBase + LI, LBase + LI + 1,  LBase + LPointsPerTooth - LI - 2,  LBase + LPointsPerTooth - LI - 1);

							ENewN(2, LPolygon->ITriangles);LPolygon->ITriangles_Count = 2;
							E3dITriangle*  LITriangle = LPolygon->ITriangles;
							LITriangle->VertexNodes[0] = 0;LITriangle->VertexNodes[1] = 1;LITriangle->VertexNodes[2] = 2;LITriangle++;
							LITriangle->VertexNodes[0] = 2;LITriangle->VertexNodes[1] = 3;LITriangle->VertexNodes[2] = 0;
//printf("%s() %s:%d  %d %d %d %d\n", __FUNCTION__, __FILE__, __LINE__, LBase + LI, LBase + LI + 1,  LBase + LPointsPerTooth - LI - 2,  LBase + LPointsPerTooth - LI - 1);fflush(stdout);
						}
					}


// Face
//
					{
						const EIndex  LNumOfVertices = LTeeth * 2;
						LPolygon->NumOfVertices = LPolygon->NumOfExteriorVertices = LPolygon->NumOfVertexNodes = LNumOfVertices;
						LPolygon->VertexNodes = E3dVertexNodes_New(LNumOfVertices, LVertexNodeSize);
						memset(LPolygon->VertexNodes, 0, LVertexNodeSize * LNumOfVertices);

						EIndex  LI = 0, LIdx = 0;E3dVertexNode*  LVertexNode = LPolygon->VertexNodes;for(;LI < LTeeth;LI++)
						{
							LVertexNode->VertexID = LIdx;LIdx += LPointsPerTooth - 1;EStruct_Inc(LVertexNode, LVertexNodeSize);
							LVertexNode->VertexID = LIdx;LIdx++;EStruct_Inc(LVertexNode, LVertexNodeSize);
						}
						E3dPolygon_Triangulate(LPolygon, LVertexNodeSize,  LFace->Vertices.Items, LFace->Vertices.Count, LFace->VertexSize,  TRUE);
					}


//printf("%s() %s:%d  LFace->PolyGroup.Polygons_Count %d\n", __FUNCTION__, __FILE__, __LINE__, (int)LFace->PolyGroup.Polygons_Count);fflush(stdout);
					E3dPolyGroup_RefreshPolygonNormals(LPolyGroup,  LFace->Vertices.Items, LFace->Vertices.Count, LFace->VertexSize);

// Gears create their own Polygons in the E3dFace (e.g. bevel gears need Polygons not on the main plane of the Face).
// So, we can't call E3dObject_UpdateForDisplay, which means we have to call all these update functions from here.
//
					{
						E3dPolyGroup*  LFacePolyGroup = LFace->PolyGroup;if(LFacePolyGroup)
						{
							E3dMaterial*   LMaterial = LFacePolyGroup->Material ? LFacePolyGroup->Material : E3dObject_GetDrawingMaterial(NULL, (E3dObject*)LFace, TRUE);
							E3dMaterial_UpdateForDisplay(LMaterial, NULL, E3dUpdateMaterialALL, E3dTxtUF_ALL);
						}
					}

#ifdef USEOpenGL
					E3dPolyGroup_GLFreeRenderData(LPolyGroup);
					E3dPolyGroup_GLCreateVertexArray(LPolyGroup,  LFace->Vertices.Items, LFace->Vertices.Count, LFace->VertexSize);
#endif  // USEOpenGL
				}
			}
		}
	}

	if(PTriggeredUpdateFlags) *PTriggeredUpdateFlags = E3dUpdateALL;

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

	return(PObject);
}



static E3dCoordinate _GearGetPitchRadius(E3dCircleInfo* PInfo)
{
	const E3dCoordinate  LTeethF = (E3dCoordinate)PInfo->Gear.Teeth;
	const E3dCoordinate  LDiametralPitch = PInfo->Gear.DiametralPitch;

	const E3dCoordinate  D = LTeethF / LDiametralPitch;  // Pitch diameter
	const E3dCoordinate  RD = D / 2.0;                   // Pitch radius : this is how far two gears need to be from each-other (the sum of their pitch radii)

	return(RD);
}


//================================================================
// Set Circle / Disc option
//
// E3dFIXME: This is a bit hack-ish because of the Object type change...
// We may switch to runtime-typed Geometries at some point...
//================================================================
static void _CB_Fill(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dScene*        LScene = E3d_Scene;
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LOldObject = LPanel->Object;
	E3dCircleInfo*   LOldInfo = (E3dCircleInfo*)E3dObjectComponent_ByClass(LPanel->Object, _ComponentClass, FALSE);
	EChangeFlags     LTriggeredChanges = 0;

	if(((XeToggleButtonCallbackStruct*)PCallData)->Set) LOldInfo->GeometryType = C_FACE;
	else LOldInfo->GeometryType = C_SPLINE;

	LOldInfo->AttributeChangeCount++;

	E3dObject*  LNewObject = E3dObject_CustomUpdate(NULL, LOldInfo,  E3dCallALL,  &LTriggeredChanges);

	EObject_SetName(LNewObject, EStrDup(LOldObject->Name), FALSE);EObject_SetName(LOldObject, EStrDupSuffix(LOldObject->Name, "-old"), FALSE);
	LNewObject->Locks = LOldObject->Locks;LOldObject->Locks = NULL;
	LNewObject->Locks_Count = LOldObject->Locks_Count;LOldObject->Locks_Count = 0;

	LNewObject->Models.Items = LOldObject->Models.Items;LOldObject->Models.Items = NULL;
	LNewObject->Models.Count = LOldObject->Models.Count;LOldObject->Models.Count = 0;
	LNewObject->Models.CountAllocated = LOldObject->Models.CountAllocated;LOldObject->Models.CountAllocated = 0;

	LNewObject->Selection = LOldObject->Selection;
	LNewObject->Visible = LOldObject->Visible;
	LNewObject->Highlighted = LOldObject->Highlighted;


// Replace Object on instantiating Models
//
	EIndex  LMI = 0;const EIndex  LMN = LNewObject->Models.Count;for(;LMI < LMN;LMI++)
	{
		E3dModel*  LModel = LNewObject->Models.Items[LMI];
		EIndex  LI = 0;const EIndex  LN = LModel->Objects.Count;for(;LI < LN;LI++)
		{
			if(LModel->Objects.Items[LI] == LOldObject)
			{
				E_AssignToMember(LNewObject, LModel,Objects.Items[LI]);
			}
		}
	}

	E3dObjectPanel_Update(LPanel, LNewObject,  NULL, NULL);
	E3dTransformPanel_Update(NULL, 0, 0, 0,  E3dpDO_NOT_CHANGE,  E3dTrPanelOBJECT_LIST);
	E3dScene_ObjectsUpdateModelList(LScene);
	E3dScene_LayoutOnSchematics(LScene, TRUE);
	E3dWindows_RedrawByChangeFlags(LScene, E3dChgSplineSHAPE | E3dChgSplineCONNECTIVITY | E3dChgFACE | E3dChgITEM_NAME | LTriggeredChanges);
}


static EChangeFlags _CallOutputs(E3dObject* PObject, const EChangeFlags PChangeFlags)
{
	EChangeFlags  LChanged = PChangeFlags;

	if(EObject_IsSubClass(PObject, E3dSpline))
	{
		LChanged |= E3dSpline_CallOutputs((E3dSpline*)PObject, PChangeFlags, TRUE);
	}
	else if(EObject_IsSubClass(PObject, E3dFace)) LChanged |= E3dObject_CallOutputs((E3dBaseObject*)PObject, PChangeFlags);

	return(LChanged);
}


static void _SegmentsRangeCheck(E3dCircleInfo* PInfo, E3dResourcesGUI* PResourcesGUI)
{
	const E3dCoordinate  LAngleRange = PInfo->EndAngle - PInfo->StartAngle;const EBool  LFull = LAngleRange == 360.0;
	int                  LSegmentsMin = 4;

	switch(PInfo->SplineType)
	{
		case E3dSplineBEZIER:   if(LFull) LSegmentsMin = 3;else LSegmentsMin = 1;break;
		case E3dSplineLINEAR:   if(LFull) LSegmentsMin = 3;else LSegmentsMin = 1;break;
		case E3dSplineBSPLINE:  if(LFull) LSegmentsMin = 4;else LSegmentsMin = 4;break;
		case E3dSplineCARDINAL: if(LFull) LSegmentsMin = 4;else LSegmentsMin = 4;break;
	}

	if(PInfo->Segments < LSegmentsMin) PInfo->Segments = LSegmentsMin;
	XeScale_SetValueAndRanges(PResourcesGUI->CirclePointsW,  PInfo->Segments, LSegmentsMin, _MaxCirclePoints,  LSegmentsMin, 360,  FALSE);
}


//========================================
// Set Spline type
//========================================
static void _CB_SplineTypeLINEAR(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dCircleInfo*   LInfo = (E3dCircleInfo*)E3dObjectComponent_ByClass(LObject, _ComponentClass, FALSE);

	if(LInfo->SplineType != E3dSplineLINEAR)
	{
		XeComponentClassDrawer*  LDrawer = XeInfoPanel_GetFolderByClass((XeInfoPanel*)LPanel, _ComponentClass);
		E3dResourcesGUI*         LResourcesGUI = (E3dResourcesGUI*)(LDrawer->ResourcesGUI);

		LInfo->SplineType = E3dSplineLINEAR;LInfo->AttributeChangeCount++;

		_SegmentsRangeCheck(LInfo, LResourcesGUI);

//printf("%s() %s:%d {%s}\n", __FUNCTION__, __FILE__, __LINE__, LObject->Class->Name);fflush(stdout);
		EChangeFlags  LChanged = E3dObject_GetChangeFlagsByClass(LObject);
		E3dObject_CustomUpdate(LObject, LInfo,  0,  &LChanged);
		E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
		LChanged |= _CallOutputs(LObject, E3dChgSplineSHAPE|E3dChgSplineCONNECTIVITY);
		E3dScene*  LScene = E3d_Scene;
		E3dWindows_RedrawByChangeFlags(LScene, LChanged);
	}
}


//========================================
// Set Spline type
//========================================
static void _CB_SplineTypeBEZIER(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dCircleInfo*   LInfo = (E3dCircleInfo*)E3dObjectComponent_ByClass(LObject, _ComponentClass, FALSE);

	if(LInfo->SplineType != E3dSplineBEZIER)
	{
		XeComponentClassDrawer*  LDrawer = XeInfoPanel_GetFolderByClass((XeInfoPanel*)LPanel, _ComponentClass);
		E3dResourcesGUI*         LResourcesGUI = (E3dResourcesGUI*)(LDrawer->ResourcesGUI);

		LInfo->SplineType = E3dSplineBEZIER;LInfo->AttributeChangeCount++;

		_SegmentsRangeCheck(LInfo, LResourcesGUI);

		EChangeFlags  LChanged = E3dObject_GetChangeFlagsByClass(LObject);
		E3dObject_CustomUpdate(LObject, LInfo,  0,  &LChanged);
		E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
		LChanged |= _CallOutputs(LObject, E3dChgSplineSHAPE|E3dChgSplineCONNECTIVITY);
		E3dScene*  LScene = E3d_Scene;
		E3dWindows_RedrawByChangeFlags(LScene, LChanged);
	}
}


//========================================
// Set Spline type
//========================================
static void _CB_SplineTypeBSPLINE(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dCircleInfo*   LInfo = (E3dCircleInfo*)E3dObjectComponent_ByClass(LObject, _ComponentClass, FALSE);

	if(LInfo->SplineType != E3dSplineBSPLINE)
	{
		XeComponentClassDrawer*  LDrawer = XeInfoPanel_GetFolderByClass((XeInfoPanel*)LPanel, _ComponentClass);
		E3dResourcesGUI*         LResourcesGUI = (E3dResourcesGUI*)(LDrawer->ResourcesGUI);

		LInfo->SplineType = E3dSplineBSPLINE;LInfo->AttributeChangeCount++;

		_SegmentsRangeCheck(LInfo, LResourcesGUI);

		EChangeFlags  LChanged = E3dObject_GetChangeFlagsByClass(LObject);
		E3dObject_CustomUpdate(LObject, LInfo,  0,  &LChanged);
		E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
		LChanged |= _CallOutputs(LObject, E3dChgSplineSHAPE|E3dChgSplineCONNECTIVITY);
		E3dScene*  LScene = E3d_Scene;
		E3dWindows_RedrawByChangeFlags(LScene, LChanged);
	}
}


//========================================
// Set Spline type
//========================================
static void _CB_SplineTypeCARDINAL(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel* LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*      LObject = LPanel->Object;
	E3dCircleInfo*  LInfo = (E3dCircleInfo*)E3dObjectComponent_ByClass(LObject, _ComponentClass, FALSE);

	if(LInfo->SplineType != E3dSplineCARDINAL)
	{
		XeComponentClassDrawer*  LDrawer = XeInfoPanel_GetFolderByClass((XeInfoPanel*)LPanel, _ComponentClass);
		E3dResourcesGUI*         LResourcesGUI = (E3dResourcesGUI*)(LDrawer->ResourcesGUI);

		if(LInfo->Segments < 4) LInfo->Segments = 4;
		XeScale_SetValueAndRanges(LResourcesGUI->CirclePointsW,  LInfo->Segments,  4, _MaxCirclePoints,  4, 360,  FALSE);

		LInfo->SplineType = E3dSplineCARDINAL;LInfo->AttributeChangeCount++;

		_SegmentsRangeCheck(LInfo, LResourcesGUI);

		EChangeFlags  LChanged = E3dObject_GetChangeFlagsByClass(LObject);
		E3dObject_CustomUpdate(LObject, LInfo,  0,  &LChanged);
		E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
		LChanged |= _CallOutputs(LObject, E3dChgSplineSHAPE|E3dChgSplineCONNECTIVITY);
		E3dScene*  LScene = E3d_Scene;
		E3dWindows_RedrawByChangeFlags(LScene, LChanged);
	}
}


//========================================
// Dialog callback
//========================================
static void _CB_Radius(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*      LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*           LObject = LPanel->Object;
	E3dCircleInfo*       LInfo = (E3dCircleInfo*)E3dObjectComponent_ByClass(LPanel->Object, _ComponentClass, FALSE);
  const E3dCoordinate  LNewRadius = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;

	if(LInfo->Radius != LNewRadius)
	{
		if(LInfo->Gear.Teeth)
		{
			const E3dCoordinate  LPR = _GearGetPitchRadius(LInfo);
			LInfo->Gear.DiametralPitch *= LPR / LNewRadius;
		}

		LInfo->Radius = LNewRadius;LInfo->AttributeChangeCount++;

		_Create(LObject, LInfo, LInfo->SplineType, LInfo->Radius, LInfo->StartAngle, LInfo->EndAngle, LInfo->Segments);

		E3dScene*  LScene = E3d_Scene;

		EChangeFlags  LChanged = E3dObject_GetChangeFlagsByClass(LObject);
		LChanged |= E3dObject_UpdateForDisplay(LObject,  E3dUpdateSplineSHAPE, 0,  LScene);
		E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
		LChanged |= _CallOutputs(LObject, E3dChgSplineSHAPE);
		E3dWindows_RedrawByChangeFlags(LScene, LChanged);
	}
}


//========================================
// Dialog callback
//========================================
static void _CB_Sides(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dCircleInfo*   LInfo = (E3dCircleInfo*)E3dObjectComponent_ByClass(LPanel->Object, _ComponentClass, FALSE);


	LInfo->Segments = ((XeScaleCallbackStruct*)PCallData)->Value.i32;LInfo->AttributeChangeCount++;

	EChangeFlags     LChanged = E3dObject_GetChangeFlagsByClass(LObject);
	E3dObject_CustomUpdate(LObject, LInfo,  0,  &LChanged);
	E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
	LChanged |= _CallOutputs(LObject, E3dChgSplineSHAPE|E3dChgSplineCONNECTIVITY);
	E3dScene*  LScene = E3d_Scene;
	E3dWindows_RedrawByChangeFlags(LScene, LChanged);
}


static void _DCB_StartAngle(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*           LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*                LObject = LPanel->Object;
	E3dObjectComponentClass*  LClass = NULL;

	Xe_GetValuesVa(PW,  XeNUserData0, &LClass,  NULL);
	{
		E3dCircleInfo*           LInfo = (E3dCircleInfo*)E3dObjectComponent_ByClass(LObject, LClass, FALSE);
		XeComponentClassDrawer*  LDrawer = XeInfoPanel_GetFolderByClass((XeInfoPanel*)LPanel, LClass);if(LDrawer)
		{
			LInfo->StartAngle = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;LInfo->AttributeChangeCount++;


#if E3dCONSTRAIN_START_END_ANGLE
			E3dResourcesGUI*  LResourcesGUI = (E3dResourcesGUI*)(LDrawer->ResourcesGUI);

			if(LInfo->SpiralPitch == 0.0)
			{
				if(LInfo->StartAngle > LInfo->EndAngle)
				{
					LInfo->EndAngle = LInfo->StartAngle;
					XeScale_SetValue_Coord(LResourcesGUI->EndAngleW, (double)(LInfo->EndAngle), FALSE);
				}

				if(LInfo->StartAngle < LInfo->EndAngle - 360.0)
				{
					LInfo->EndAngle = LInfo->StartAngle + 360.0;
					XeScale_SetValue_Coord(LResourcesGUI->EndAngleW, (double)(LInfo->EndAngle), FALSE);
				}
			}
#endif


//printf("%s() %f-%f  span: %f\n", __FUNCTION__, LInfo->StartAngle, LInfo->EndAngle, LInfo->EndAngle - LInfo->StartAngle);fflush(stdout);
			EChangeFlags  LChanged = E3dObject_GetChangeFlagsByClass(LObject);
			E3dObject_CustomUpdate(LObject, LInfo,  0,  &LChanged);
			E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
			LChanged |= _CallOutputs(LObject, E3dChgSplineSHAPE|E3dChgSplineCONNECTIVITY);
			E3dScene*  LScene = E3d_Scene;
			E3dWindows_RedrawByChangeFlags(LScene, LChanged);
		}
	}
}


static void _DCB_EndAngle(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*           LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*                LObject = LPanel->Object;
	E3dObjectComponentClass*  LClass = NULL;

	Xe_GetValuesVa(PW,  XeNUserData0, &LClass,  NULL);

	{
		E3dCircleInfo*           LInfo = (E3dCircleInfo*)E3dObjectComponent_ByClass(LObject, LClass, FALSE);
		XeComponentClassDrawer*  LDrawer = XeInfoPanel_GetFolderByClass((XeInfoPanel*)LPanel, LClass);if(LDrawer)
		{
//			EBool  LWasFull;if((LInfo->EndAngle - LInfo->StartAngle) == 360.0) LWasFull = TRUE;else LWasFull = FALSE;

			LInfo->EndAngle = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;LInfo->AttributeChangeCount++;


#if E3dCONSTRAIN_START_END_ANGLE
			E3dResourcesGUI*  LResourcesGUI = (E3dResourcesGUI*)(LDrawer->ResourcesGUI);

			if(LInfo->SpiralPitch == 0.0)
			{
				if(LInfo->StartAngle > LInfo->EndAngle)
				{
					LInfo->StartAngle = LInfo->EndAngle;
					XeScale_SetValue_Coord(LResourcesGUI->StartAngleW, (double)(LInfo->StartAngle), FALSE);
				}
				if(LInfo->StartAngle < LInfo->EndAngle - 360.0)
				{
					LInfo->StartAngle = LInfo->EndAngle - 360.0;
					XeScale_SetValue_Coord(LResourcesGUI->StartAngleW, (double)(LInfo->StartAngle), FALSE);
				}
			}
#endif


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

			EChangeFlags  LChanged = E3dObject_GetChangeFlagsByClass(LObject);
			E3dObject_CustomUpdate(LObject, LInfo,  0,  &LChanged);
			E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
			LChanged |= _CallOutputs(LObject, E3dChgSplineSHAPE|E3dChgSplineCONNECTIVITY);
			E3dScene*  LScene = E3d_Scene;
			E3dWindows_RedrawByChangeFlags(LScene, LChanged);
		}
	}
}


//========================================
// Dialog callback
//========================================
static void _DCB_SpiralPitch(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*      LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*           LObject = LPanel->Object;
	E3dCircleInfo*       LInfo = (E3dCircleInfo*)E3dObjectComponent_ByClass(LPanel->Object, _ComponentClass, FALSE);
  const E3dCoordinate  LNewSpiralPitch = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;

	if(LInfo->SpiralPitch != LNewSpiralPitch)
	{
		LInfo->SpiralPitch = LNewSpiralPitch;

		_Create(LObject, LInfo, LInfo->SplineType, LInfo->Radius, LInfo->StartAngle, LInfo->EndAngle, LInfo->Segments);

		E3dScene*  LScene = E3d_Scene;

		EChangeFlags  LChanged = E3dObject_GetChangeFlagsByClass(LObject);
		LChanged |= E3dObject_UpdateForDisplay(LObject,  E3dUpdateSplineSHAPE, 0,  LScene);
		E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
		LChanged |= _CallOutputs(LObject, E3dChgSplineSHAPE|E3dChgSplineCONNECTIVITY);
		E3dWindows_RedrawByChangeFlags(LScene, LChanged);
	}
}


//========================================
// Dialog callback
//========================================
static void _CB_GearTeeth(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dCircleInfo*   LInfo = (E3dCircleInfo*)E3dObjectComponent_ByClass(LPanel->Object, _ComponentClass, FALSE);

	LInfo->Gear.Teeth = ((XeScaleCallbackStruct*)PCallData)->Value.i32;LInfo->AttributeChangeCount++;

	EChangeFlags  LChanged = E3dObject_GetChangeFlagsByClass(LObject);
	E3dObject_CustomUpdate(LObject, LInfo,  0,  &LChanged);
	E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
	LChanged |= _CallOutputs(LObject, E3dChgSplineSHAPE|E3dChgSplineCONNECTIVITY);
	E3dScene*  LScene = E3d_Scene;
	E3dWindows_RedrawByChangeFlags(LScene, LChanged);
}


static void _CB_Gear2Teeth(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dCircleInfo*   LInfo = (E3dCircleInfo*)E3dObjectComponent_ByClass(LPanel->Object, _ComponentClass, FALSE);

	LInfo->Gear.Gear2Teeth = ((XeScaleCallbackStruct*)PCallData)->Value.i32;LInfo->AttributeChangeCount++;

	EChangeFlags  LChanged = E3dObject_GetChangeFlagsByClass(LObject);
	E3dObject_CustomUpdate(LObject, LInfo,  0,  &LChanged);
	E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
	LChanged |= _CallOutputs(LObject, E3dChgSplineSHAPE|E3dChgSplineCONNECTIVITY);
	E3dScene*  LScene = E3d_Scene;
	E3dWindows_RedrawByChangeFlags(LScene, LChanged);
}


static void _CB_GearPressureAngle(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dCircleInfo*   LInfo = (E3dCircleInfo*)E3dObjectComponent_ByClass(LPanel->Object, _ComponentClass, FALSE);

	LInfo->Gear.PressureAngle = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;LInfo->AttributeChangeCount++;

	EChangeFlags  LChanged = E3dObject_GetChangeFlagsByClass(LObject);
	E3dObject_CustomUpdate(LObject, LInfo,  0,  &LChanged);
	E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
	LChanged |= _CallOutputs(LObject, E3dChgSplineSHAPE|E3dChgSplineCONNECTIVITY);
	E3dScene*  LScene = E3d_Scene;
	E3dWindows_RedrawByChangeFlags(LScene, LChanged);
}


//========================================
// Dialog callback
//========================================
static void _CB_DiametralPitch(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dCircleInfo*   LInfo = (E3dCircleInfo*)E3dObjectComponent_ByClass(LPanel->Object, _ComponentClass, FALSE);

	LInfo->Gear.DiametralPitch = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;LInfo->AttributeChangeCount++;

	EChangeFlags  LChanged = E3dObject_GetChangeFlagsByClass(LObject);
	E3dObject_CustomUpdate(LObject, LInfo,  0,  &LChanged);
	E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
	LChanged |= _CallOutputs(LObject, E3dChgSplineSHAPE|E3dChgSplineCONNECTIVITY);
	E3dWindows_RedrawByChangeFlags(E3d_Scene, LChanged);
}


//========================================
// Dialog callback
//========================================
static void _CB_ToothBaseBevelRadius(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dCircleInfo*   LInfo = (E3dCircleInfo*)E3dObjectComponent_ByClass(LPanel->Object, _ComponentClass, FALSE);

	LInfo->Gear.ToothBaseBevelRadius = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;LInfo->AttributeChangeCount++;

	EChangeFlags  LChanged = E3dObject_GetChangeFlagsByClass(LObject);
	E3dObject_CustomUpdate(LObject, LInfo,  0,  &LChanged);
	E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
	LChanged |= _CallOutputs(LObject, E3dChgSplineSHAPE|E3dChgSplineCONNECTIVITY);
	E3dWindows_RedrawByChangeFlags(E3d_Scene, LChanged);
}


//========================================
// Dialog callback
//========================================
static void _CB_ToothBaseBevelSteps(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dCircleInfo*   LInfo = (E3dCircleInfo*)E3dObjectComponent_ByClass(LPanel->Object, _ComponentClass, FALSE);

	LInfo->Gear.ToothBaseBevelSteps = ((XeScaleCallbackStruct*)PCallData)->Value.i32;LInfo->AttributeChangeCount++;

	EChangeFlags  LChanged = E3dObject_GetChangeFlagsByClass(LObject);
	E3dObject_CustomUpdate(LObject, LInfo,  0,  &LChanged);
	E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
	LChanged |= _CallOutputs(LObject, E3dChgSplineSHAPE|E3dChgSplineCONNECTIVITY);
	E3dWindows_RedrawByChangeFlags(E3d_Scene, LChanged);
}


//========================================
// Dialog callback
//========================================
static void _CB_ToothTipBevelRadius(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dCircleInfo*   LInfo = (E3dCircleInfo*)E3dObjectComponent_ByClass(LPanel->Object, _ComponentClass, FALSE);

	LInfo->Gear.ToothTipBevelRadius = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;LInfo->AttributeChangeCount++;

	EChangeFlags  LChanged = E3dObject_GetChangeFlagsByClass(LObject);
	E3dObject_CustomUpdate(LObject, LInfo,  0,  &LChanged);
	E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
	LChanged |= _CallOutputs(LObject, E3dChgSplineSHAPE|E3dChgSplineCONNECTIVITY);
	E3dWindows_RedrawByChangeFlags(E3d_Scene, LChanged);
}


//========================================
// Dialog callback
//========================================
static void _CB_ToothTipBevelSteps(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dCircleInfo*   LInfo = (E3dCircleInfo*)E3dObjectComponent_ByClass(LPanel->Object, _ComponentClass, FALSE);

	LInfo->Gear.ToothTipBevelSteps = ((XeScaleCallbackStruct*)PCallData)->Value.i32;LInfo->AttributeChangeCount++;

	EChangeFlags  LChanged = E3dObject_GetChangeFlagsByClass(LObject);
	E3dObject_CustomUpdate(LObject, LInfo,  0,  &LChanged);
	E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
	LChanged |= _CallOutputs(LObject, E3dChgSplineSHAPE|E3dChgSplineCONNECTIVITY);
	E3dWindows_RedrawByChangeFlags(E3d_Scene, LChanged);
}





static void _CB_GearRootCircleScaleFactor(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dCircleInfo*   LInfo = (E3dCircleInfo*)E3dObjectComponent_ByClass(LPanel->Object, _ComponentClass, FALSE);

	LInfo->Gear.RootCircleScaleFactor = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;LInfo->AttributeChangeCount++;

	EChangeFlags  LChanged = E3dObject_GetChangeFlagsByClass(LObject);
	E3dObject_CustomUpdate(LObject, LInfo,  0,  &LChanged);
	E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
	LChanged |= _CallOutputs(LObject, E3dChgSplineSHAPE|E3dChgSplineCONNECTIVITY);
	E3dWindows_RedrawByChangeFlags(E3d_Scene, LChanged);
}


//========================================
// Dialog callback
//========================================
static void _CB_BevelGearSphereRadius(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dCircleInfo*   LInfo = (E3dCircleInfo*)E3dObjectComponent_ByClass(LPanel->Object, _ComponentClass, FALSE);
	const EBool      LWasFlatBefore = LInfo->Gear.BevelGearSphereRadius == 0.0;

	LInfo->Gear.BevelGearSphereRadius = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;LInfo->AttributeChangeCount++;

	if(LInfo->Gear.BevelGearSphereRadius != 0.0)
	{
		if(LWasFlatBefore)
		{
// Bevel gears have a narrower sensible range for the parameters
//
			LInfo->Gear.Teeth          = 24;
			LInfo->Gear.PressureAngle  = 22.5;
			LInfo->Gear.DiametralPitch = 48.0;
			LInfo->Gear.DiametralPitch = 48.0;

			LInfo->Gear.RootCircleScaleFactor = 0.98;

			LInfo->Gear.ToothBaseBevelRadius = 0.1;
			LInfo->Gear.ToothBaseBevelSteps  = 0;

			LInfo->Gear.ToothTipBevelRadius = 0.2;    // Relative to the total tooth height
			LInfo->Gear.ToothTipBevelSteps = 0;

			LInfo->Gear.Internal = FALSE;
		}
	}

	EChangeFlags  LChanged = E3dObject_GetChangeFlagsByClass(LObject);
	E3dObject_CustomUpdate(LObject, LInfo,  0,  &LChanged);
	E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
	LChanged |= _CallOutputs(LObject, E3dChgSplineSHAPE|E3dChgSplineCONNECTIVITY);
	E3dWindows_RedrawByChangeFlags(E3d_Scene, LChanged);
}


static void _CB_Gear1(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dCircleInfo*   LInfo = (E3dCircleInfo*)E3dObjectComponent_ByClass(LPanel->Object, _ComponentClass, FALSE);

	if(((XeToggleButtonCallbackStruct*)PCallData)->Set)
	{
		LInfo->Gear.GearIndex = 1;LInfo->AttributeChangeCount++;

		EChangeFlags  LChanged = E3dObject_GetChangeFlagsByClass(LObject);
		E3dObject_CustomUpdate(LObject, LInfo,  0,  &LChanged);
		E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
		LChanged |= _CallOutputs(LObject, E3dChgSplineSHAPE|E3dChgSplineCONNECTIVITY);
		E3dWindows_RedrawByChangeFlags(E3d_Scene, LChanged);
	}
}

static void _CB_Gear2(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dCircleInfo*   LInfo = (E3dCircleInfo*)E3dObjectComponent_ByClass(LPanel->Object, _ComponentClass, FALSE);

	if(((XeToggleButtonCallbackStruct*)PCallData)->Set)
	{
		LInfo->Gear.GearIndex = 2;LInfo->AttributeChangeCount++;

		EChangeFlags  LChanged = E3dObject_GetChangeFlagsByClass(LObject);
		E3dObject_CustomUpdate(LObject, LInfo,  0,  &LChanged);
		E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
		LChanged |= _CallOutputs(LObject, E3dChgSplineSHAPE|E3dChgSplineCONNECTIVITY);
		E3dWindows_RedrawByChangeFlags(E3d_Scene, LChanged);
	}
}




#if BevelGearDEBUG
//========================================
// Dialog callback
//========================================
static void _CB_Gear2AlphaMul(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dCircleInfo*   LInfo = (E3dCircleInfo*)E3dObjectComponent_ByClass(LPanel->Object, _ComponentClass, FALSE);

	LInfo->Gear.Gear2AlphaMul = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;LInfo->AttributeChangeCount++;

	EChangeFlags  LChanged = E3dObject_GetChangeFlagsByClass(LObject);
	E3dObject_CustomUpdate(LObject, LInfo,  0,  &LChanged);
	E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
	LChanged |= _CallOutputs(LObject, E3dChgSplineSHAPE|E3dChgSplineCONNECTIVITY);
	E3dWindows_RedrawByChangeFlags(E3d_Scene, LChanged);
}


static void _CB_Gear2ToothRotOffset(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dCircleInfo*   LInfo = (E3dCircleInfo*)E3dObjectComponent_ByClass(LPanel->Object, _ComponentClass, FALSE);

	LInfo->Gear.Gear2ToothRotOffset = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;LInfo->AttributeChangeCount++;

	EChangeFlags  LChanged = E3dObject_GetChangeFlagsByClass(LObject);
	E3dObject_CustomUpdate(LObject, LInfo,  0,  &LChanged);
	E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
	LChanged |= _CallOutputs(LObject, E3dChgSplineSHAPE|E3dChgSplineCONNECTIVITY);
	E3dWindows_RedrawByChangeFlags(E3d_Scene, LChanged);
}

static void _CB_OuterCircleScale(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dCircleInfo*   LInfo = (E3dCircleInfo*)E3dObjectComponent_ByClass(LPanel->Object, _ComponentClass, FALSE);

	LInfo->Gear.OuterCircleScale = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;LInfo->AttributeChangeCount++;

	EChangeFlags  LChanged = E3dObject_GetChangeFlagsByClass(LObject);
	E3dObject_CustomUpdate(LObject, LInfo,  0,  &LChanged);
	E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
	LChanged |= _CallOutputs(LObject, E3dChgSplineSHAPE|E3dChgSplineCONNECTIVITY);
	E3dWindows_RedrawByChangeFlags(E3d_Scene, LChanged);
}
#endif


static void _CB_GearInternal(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dCircleInfo*   LInfo = (E3dCircleInfo*)E3dObjectComponent_ByClass(LPanel->Object, _ComponentClass, FALSE);

	LInfo->Gear.Internal = ((XeToggleButtonCallbackStruct*)PCallData)->Set;LInfo->AttributeChangeCount++;

	EChangeFlags  LChanged = E3dObject_GetChangeFlagsByClass(LObject);
	E3dObject_CustomUpdate(LObject, LInfo,  0,  &LChanged);
	E3dObjectPanel_Update(LPanel, LObject,  NULL, NULL);
	LChanged |= _CallOutputs(LObject, E3dChgSplineSHAPE|E3dChgSplineCONNECTIVITY);
	E3dWindows_RedrawByChangeFlags(E3d_Scene, LChanged);
}


static void _GUIAdjust(E3dCircleInfo* PInfo, E3dResourcesGUI* PResourcesGUI)
{
	XeChangeManage  LChildren[16];XeChangeManage*  LChild = LChildren;

	_SegmentsRangeCheck(PInfo, PResourcesGUI);

	if(PInfo->Gear.Teeth)
	{
#if BevelGearDEBUG
		LChild->W = PResourcesGUI->BevelGearAlgorithmNameW;   LChild->Manage = TRUE;LChild++;
#endif
		LChild->W = PResourcesGUI->GearTeethW;                LChild->Manage = TRUE;LChild++;
		LChild->W = PResourcesGUI->GearPressureAngleW;        LChild->Manage = TRUE;LChild++;
		LChild->W = PResourcesGUI->GearDiametralPitchW;       LChild->Manage = TRUE;LChild++;
		LChild->W = PResourcesGUI->GearRootCircleScaleFactorW;LChild->Manage = TRUE;LChild++;
		LChild->W = PResourcesGUI->GearInternalW;             LChild->Manage = TRUE;LChild++;
		LChild->W = PResourcesGUI->GearToothBaseBevelRadiusW; LChild->Manage = TRUE;LChild++;
		LChild->W = PResourcesGUI->GearToothBaseBevelStepsW;  LChild->Manage = TRUE;LChild++;
		LChild->W = PResourcesGUI->GearToothTipBevelRadiusW;  LChild->Manage = TRUE;LChild++;
		LChild->W = PResourcesGUI->GearToothTipBevelStepsW;   LChild->Manage = TRUE;LChild++;
		LChild->W = PResourcesGUI->BevelGearSphereRadiusW;    LChild->Manage = TRUE;LChild++;
		LChild->W = PResourcesGUI->GearRadioMatrixW;          LChild->Manage = PInfo->Gear.BevelGearSphereRadius != 0.0;LChild++;
		LChild->W = PResourcesGUI->Gear2TeethW;               LChild->Manage = PInfo->Gear.BevelGearSphereRadius != 0.0;LChild++;

		LChild->W = PResourcesGUI->SpiralPitchW;              LChild->Manage = FALSE;LChild++;
	}
	else
	{
#if BevelGearDEBUG
		LChild->W = PResourcesGUI->BevelGearAlgorithmNameW;   LChild->Manage = FALSE;LChild++;
#endif
		LChild->W = PResourcesGUI->GearTeethW;                LChild->Manage = FALSE;LChild++;
		LChild->W = PResourcesGUI->GearPressureAngleW;        LChild->Manage = FALSE;LChild++;
		LChild->W = PResourcesGUI->GearDiametralPitchW;       LChild->Manage = FALSE;LChild++;
		LChild->W = PResourcesGUI->GearRootCircleScaleFactorW;LChild->Manage = FALSE;LChild++;
		LChild->W = PResourcesGUI->GearInternalW;             LChild->Manage = FALSE;LChild++;
		LChild->W = PResourcesGUI->GearToothBaseBevelRadiusW; LChild->Manage = FALSE;LChild++;
		LChild->W = PResourcesGUI->GearToothBaseBevelStepsW;  LChild->Manage = FALSE;LChild++;
		LChild->W = PResourcesGUI->GearToothTipBevelRadiusW;  LChild->Manage = FALSE;LChild++;
		LChild->W = PResourcesGUI->GearToothTipBevelStepsW;   LChild->Manage = FALSE;LChild++;

		LChild->W = PResourcesGUI->BevelGearSphereRadiusW;    LChild->Manage = FALSE;LChild++;
		LChild->W = PResourcesGUI->GearRadioMatrixW;          LChild->Manage = FALSE;LChild++;
		LChild->W = PResourcesGUI->Gear2TeethW;               LChild->Manage = FALSE;LChild++;

		LChild->W = PResourcesGUI->SpiralPitchW;              LChild->Manage = TRUE;LChild++;
	}

	if(LChild - LChildren) XeWidgets_ChangeManage(LChildren, LChild - LChildren);
}


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

	switch(PAction)
	{
		case E_UPDATE:
			{
				E3dResourcesGUI*     LResourcesGUI;
				const E3dCoordinate  LRadius = LInfo->Gear.Teeth ? _GearGetPitchRadius(LInfo) : LInfo->Radius;

				if(LDrawer->ResourcesGUI)
				{
					LResourcesGUI = (E3dResourcesGUI*)(LDrawer->ResourcesGUI);

					XeToggleButton_SetState(LResourcesGUI->FillBW, LInfo->GeometryType != C_SPLINE, FALSE);

					XeToggleButton_SetState(LResourcesGUI->LinearSplineBW,   LInfo->SplineType == E3dSplineLINEAR,   FALSE);
					XeToggleButton_SetState(LResourcesGUI->BezierSplineBW,   LInfo->SplineType == E3dSplineBEZIER,   FALSE);
					XeToggleButton_SetState(LResourcesGUI->CardinalSplineBW, LInfo->SplineType == E3dSplineCARDINAL, FALSE);
					XeToggleButton_SetState(LResourcesGUI->BSplineBW,        LInfo->SplineType == E3dSplineBSPLINE,   FALSE);

					XeScale_SetValue_Coord(LResourcesGUI->CircleRadiusW, LRadius, FALSE);

					XeScale_SetValue_Coord(LResourcesGUI->StartAngleW,   LInfo->StartAngle, FALSE);
					XeScale_SetValue_Coord(LResourcesGUI->EndAngleW,     LInfo->EndAngle, FALSE);
					XeScale_SetValue(LResourcesGUI->CirclePointsW,       LInfo->Segments, FALSE);
					XeScale_SetValue_Coord(LResourcesGUI->SpiralPitchW,  LInfo->SpiralPitch, FALSE);


					XeScale_SetValue(LResourcesGUI->GearTeethW,                       LInfo->Gear.Teeth,                 FALSE);
					XeScale_SetValue(LResourcesGUI->Gear2TeethW,                      LInfo->Gear.Gear2Teeth,            FALSE);
					XeScale_SetValue_Coord(LResourcesGUI->GearPressureAngleW,         LInfo->Gear.PressureAngle,         FALSE);
					XeScale_SetValue_Coord(LResourcesGUI->GearRootCircleScaleFactorW, LInfo->Gear.RootCircleScaleFactor, FALSE);
					XeScale_SetValue_Coord(LResourcesGUI->GearDiametralPitchW,        LInfo->Gear.DiametralPitch,        FALSE);

					XeScale_SetValue_Coord(LResourcesGUI->GearToothBaseBevelRadiusW, LInfo->Gear.ToothBaseBevelRadius, FALSE);
					XeScale_SetValue(LResourcesGUI->GearToothBaseBevelStepsW,        LInfo->Gear.ToothBaseBevelSteps,  FALSE);

					XeScale_SetValue_Coord(LResourcesGUI->GearToothTipBevelRadiusW, LInfo->Gear.ToothTipBevelRadius, FALSE);
					XeScale_SetValue(LResourcesGUI->GearToothTipBevelStepsW,        LInfo->Gear.ToothTipBevelSteps,  FALSE);

					XeScale_SetValue_Coord(LResourcesGUI->BevelGearSphereRadiusW, LInfo->Gear.BevelGearSphereRadius, FALSE);
					XeToggleButton_SetState(LResourcesGUI->Gear1W,   LInfo->Gear.GearIndex == 1, FALSE);
					XeToggleButton_SetState(LResourcesGUI->Gear2W,   LInfo->Gear.GearIndex == 2, FALSE);

					XeToggleButton_SetState(LResourcesGUI->GearInternalW, LInfo->Gear.Internal, FALSE);

#if BevelGearDEBUG
					XeScale_SetValue_f32(LResourcesGUI->Gear2AlphaMulW,       LInfo->Gear.Gear2AlphaMul, FALSE);
					XeScale_SetValue_f32(LResourcesGUI->Gear2ToothRotOffsetW, LInfo->Gear.Gear2ToothRotOffset, FALSE);
					XeScale_SetValue_f32(LResourcesGUI->OuterCircleScaleW,    LInfo->Gear.OuterCircleScale, FALSE);
#endif
				}
				else
				{
					XeWidget      LTopMatrixW = LDrawer->Drawer.DrawerW, LW;
					XeWidget      LTraverseWs[32];int  LTraverseN;
					XeWidget      LIndentWs[32];int  LIndentN;
					EcRGBAiColor  LButtonRGBAiColor, LButtonActiveRGBAiColor, LButtonArmRGBAiColor;
					EArg          Args[256];int  ArgCnt;

					ENew(LResourcesGUI);LDrawer->ResourcesGUI = LResourcesGUI;

					Ec_GainRGBAi(&Xe_BackgroundRGBAiColor, 1.0, &LButtonRGBAiColor);
					Ec_GainRGBAi(&Xe_BackgroundRGBAiColor, XeActiveColorFactor, &LButtonActiveRGBAiColor);
					Ec_GainRGBAi(&Xe_BackgroundRGBAiColor, XeArmColorFactor, &LButtonArmRGBAiColor);

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

					{
						EXeStart;
						EXeSetArg(XeNRadioBehavior, TRUE);
						EXeSetArg(XeNOrientation, XeHORIZONTAL);
						EXeSetArg(XeNSpacingLeft, 4);
						EXeSetArg(XeNSpacingX, 5);
						EXeSetArg(XeNMarginSizeX, 0);EXeSetArg(XeNMarginSizeY, 0);
						EXeSetArg(XeNResizableX, FALSE);EXeSetArg(XeNAlignmentX, XeAlignBEGINNING);
						EXeSetArg(XeNResizableY, FALSE);
						XeWidget  LMatrixW = XeWidget_ManagedNew("", XeMatrixWidget_class, LTopMatrixW,  Args, ArgCnt);

						LIndentN = 0;
						EXeStart;
						EXeSetArg(XeNMarginSizeX, 0);EXeSetArg(XeNMarginSizeY, 0);
						EXeSetArg(XeNResizableX, FALSE);
						EXeSetArg(XeNResizableY, FALSE);
						XeWidget_ManagedNew("Spline type", XeLabelWidget_class, LMatrixW,  Args, ArgCnt);

						EXeStart;
						EXeSetArg(XeNShadowThickness, 0);
						EXeSetArg(XeNIndicatorOn, FALSE);
						EXeSetArg(XeNHighlightThickness, 0);
						EXeSetArg(XeNMarginSizeX, 0);EXeSetArg(XeNMarginSizeY, 0);
						EXeSetArg(XeNFontList, Xe_LabelFontList);
						EXeSetArg(XeNSet, LInfo->SplineType == E3dSplineLINEAR);
						EXeSetArg(XeNResizableX, FALSE);
						EXeSetArg(XeNActivateOnArm, TRUE);
						EXeSetArg(XeNLabelType, XeIMAGE);
						EXeSetImages3(_Image_CircleLinear);
						XeToolTip8("Linear");
						LResourcesGUI->LinearSplineBW = LW = XeWidget_ManagedNew("", XeToggleButton_class, LMatrixW,  Args, ArgCnt);
						XeAddCallback(LW, XeNValueChangedCallback, _CB_SplineTypeLINEAR, (EPointer)LPanel);

						EXeStart;
						EXeSetArg(XeNShadowThickness, 0);
						EXeSetArg(XeNIndicatorOn, FALSE);
						EXeSetArg(XeNHighlightThickness, 0);
						EXeSetArg(XeNMarginSizeX, 0);EXeSetArg(XeNMarginSizeY, 0);
						EXeSetArg(XeNFontList, Xe_LabelFontList);
						EXeSetArg(XeNSet, LInfo->SplineType == E3dSplineBEZIER);
						EXeSetArg(XeNResizableX, FALSE);
						EXeSetArg(XeNActivateOnArm, TRUE);
						EXeSetArg(XeNLabelType, XeIMAGE);
						EXeSetImages3(_Image_CircleBezier);
						XeToolTip8("Bezier");
						LResourcesGUI->BezierSplineBW = LW = XeWidget_ManagedNew("", XeToggleButton_class, LMatrixW,  Args, ArgCnt);
						XeAddCallback(LW, XeNValueChangedCallback, _CB_SplineTypeBEZIER, (EPointer)LPanel);


						EXeStart;
						EXeSetArg(XeNShadowThickness, 0);
						EXeSetArg(XeNIndicatorOn, FALSE);
						EXeSetArg(XeNHighlightThickness, 0);
						EXeSetArg(XeNMarginSizeX, 0);EXeSetArg(XeNMarginSizeY, 0);
						EXeSetArg(XeNFontList, Xe_LabelFontList);
						EXeSetArg(XeNSet, LInfo->SplineType == E3dSplineBSPLINE);
						EXeSetArg(XeNResizableX, FALSE);
						EXeSetArg(XeNActivateOnArm, TRUE);
						EXeSetArg(XeNLabelType, XeIMAGE);
						EXeSetImages3(_Image_CircleBSpline);
						XeToolTip8("BSpline");
						LResourcesGUI->BSplineBW = LW = XeWidget_ManagedNew("", XeToggleButton_class, LMatrixW,  Args, ArgCnt);
						XeAddCallback(LW, XeNValueChangedCallback, _CB_SplineTypeBSPLINE, (EPointer)LPanel);


						EXeStart;
						EXeSetArg(XeNShadowThickness, 0);
						EXeSetArg(XeNIndicatorOn, FALSE);
						EXeSetArg(XeNHighlightThickness, 0);
						EXeSetArg(XeNMarginSizeX, 0);EXeSetArg(XeNMarginSizeY, 0);
						EXeSetArg(XeNFontList, Xe_LabelFontList);
						EXeSetArg(XeNSet, LInfo->SplineType == E3dSplineCARDINAL);
						EXeSetArg(XeNResizableX, FALSE);
						EXeSetArg(XeNActivateOnArm, TRUE);
						EXeSetArg(XeNLabelType, XeIMAGE);
						EXeSetImages3(_Image_CircleCardinal);
						XeToolTip8("Cardinal");
						LResourcesGUI->CardinalSplineBW = LW = XeWidget_ManagedNew("", XeToggleButton_class, LMatrixW,  Args, ArgCnt);
						XeAddCallback(LW, XeNValueChangedCallback, _CB_SplineTypeCARDINAL, (EPointer)LPanel);
					}


					EXeStart;
					EXeSetArg(XeNShadowThickness, 0);
					EXeSetArg(XeNHighlightThickness, 0);EXeSetArg(XeNMarginSizeY, 0);
					EXeSetArg(XeNMarginTop, 0);EXeSetArg(XeNMarginBottom, 0);
					EXeSetArg(XeNSet, (EBool)(LInfo->GeometryType != C_SPLINE));
					EXeSetArg(XeNResizableX, FALSE);EXeSetArg(XeNAlignmentX, XeAlignBEGINNING);
					EXeSetArg(XeNResizableY, FALSE);
					XeToolTip8("Toggle filled (disc) / unfilled (circle)");
					LResourcesGUI->FillBW = LW = XeWidget_ManagedNew("Fill in", XeToggleButton_class, LTopMatrixW,  Args, ArgCnt);
					XeAddCallback(LW, XeNValueChangedCallback, _CB_Fill, (EPointer)LPanel);



					LTraverseN = 0;
					EXeStart;
					EXeSetArg(XeNTextFieldDigits, 8);EXeSetArg(XeNDecimalPoints, 4);
					EXeSetArg(XeNResizableY, FALSE);
					XeToolTip8("For circles, this is the radius, for gears, this is the pitch radius.\nThe distance required between two meshing gears is the sum of their pitch radii.");
					LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->CircleRadiusW = LW = XeScale_New_Coord(LTopMatrixW, "Radius",  Args, ArgCnt);
					XeScale_SetValueAndRanges_Coord(LW,  LRadius,  0.0, 10000000.0,  0.0, 100.0,  FALSE);
					XeAddCallback(LW, XeNValueChangedCallback, _CB_Radius, (EPointer)LPanel);

					EXeStart;
					EXeSetArg(XeNTextFieldDigits, 8);EXeSetArg(XeNDecimalPoints, 2);
					EXeSetArg(XeNIncrement, 10);
					EXeSetArg(XeNResizableY, FALSE);
					EXeSetArg(XeNUserData0, LInfo->Class);
					LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->StartAngleW = LW = XeScale_New_Coord(LTopMatrixW, "Start angle",  Args, ArgCnt);
					XeScale_SetValueAndRanges_Coord(LW,  LInfo->StartAngle,  -360000.0, 360000.0,  -360.0, 360.0,  FALSE);
					XeAddCallback(LW, XeNValueChangedCallback, _DCB_StartAngle, (EPointer)LPanel);

					EXeStart;
					EXeSetArg(XeNTextFieldDigits, 8);EXeSetArg(XeNDecimalPoints, 2);
					EXeSetArg(XeNIncrement, 100);
					EXeSetArg(XeNUserData0, LInfo->Class);
					EXeSetArg(XeNResizableY, FALSE);
					LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->EndAngleW = LW = XeScale_New_Coord(LTopMatrixW, "End angle",  Args, ArgCnt);
					XeScale_SetValueAndRanges_Coord(LW,  LInfo->EndAngle,  -360000.0, 360000.0,  -360.0, 360.0,  FALSE);
					XeAddCallback(LW, XeNValueChangedCallback, _DCB_EndAngle, (EPointer)LPanel);

					EXeStart;
					EXeSetArg(XeNTextFieldDigits, 8);
					EXeSetArg(XeNMinimum, 3);EXeSetArg(XeNMaximum, _MaxCirclePoints);EXeSetArg(XeNScaleMaximum, 360);EXeSetArg(XeNValue, LInfo->Segments);
					EXeSetArg(XeNResizableY, FALSE);
					LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->CirclePointsW = LW = XeWidget_ManagedNew("Points", XeScaleWidget_class, LTopMatrixW,  Args, ArgCnt);
					XeAddCallback(LW, XeNValueChangedCallback, _CB_Sides, (EPointer)LPanel);

					EXeStart;
					EXeSetArg(XeNTextFieldDigits, 8);EXeSetArg(XeNDecimalPoints, 2);
					EXeSetArg(XeNIncrement, 100);
					EXeSetArg(XeNUserData0, LInfo->Class);
					EXeSetArg(XeNResizableY, FALSE);
					LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->SpiralPitchW = LW = XeScale_New_Coord(LTopMatrixW, "Spiral",  Args, ArgCnt);
					XeScale_SetValueAndRanges_Coord(LW,  LInfo->SpiralPitch,  -10.0, 10.0,  -10000.0, 10000.0,  FALSE);
					XeAddCallback(LW, XeNValueChangedCallback, _DCB_SpiralPitch, (EPointer)LPanel);



					{
						EXeStart;
						EXeSetArg(XeNTextFieldDigits, 8);
						EXeSetArg(XeNMinimum, 6);EXeSetArg(XeNMaximum, 240);EXeSetArg(XeNValue, LInfo->Gear.Teeth);
						EXeSetArg(XeNResizableY, FALSE);
						LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->GearTeethW = LW = XeWidget_ManagedNew("Gear teeth", XeScaleWidget_class, LTopMatrixW,  Args, ArgCnt);
						XeAddCallback(LW, XeNValueChangedCallback, _CB_GearTeeth, (EPointer)LPanel);

						EXeStart;
						EXeSetArg(XeNTextFieldDigits, 8);
						EXeSetArg(XeNMinimum, 6);EXeSetArg(XeNMaximum, 240);EXeSetArg(XeNValue, LInfo->Gear.Gear2Teeth);
						EXeSetArg(XeNResizableY, FALSE);
						LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->Gear2TeethW = LW = XeWidget_ManagedNew("Gear 2 teeth", XeScaleWidget_class, LTopMatrixW,  Args, ArgCnt);
						XeAddCallback(LW, XeNValueChangedCallback, _CB_Gear2Teeth, (EPointer)LPanel);


						EXeStart;
						EXeSetArg(XeNTextFieldDigits, 8);EXeSetArg(XeNDecimalPoints, 2);
						EXeSetArg(XeNIncrement, 100);
						EXeSetArg(XeNUserData0, LInfo->Class);
						EXeSetArg(XeNResizableY, FALSE);
						LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->GearPressureAngleW = LW = XeScale_New_Coord(LTopMatrixW, "Pressure angle",  Args, ArgCnt);
						XeScale_SetValueAndRange_Coord(LW,  LInfo->Gear.PressureAngle,  1.0, 50.0,  FALSE);
						XeAddCallback(LW, XeNValueChangedCallback, _CB_GearPressureAngle, (EPointer)LPanel);

						EXeStart;
						EXeSetArg(XeNTextFieldDigits, 8);EXeSetArg(XeNDecimalPoints, 4);
						EXeSetArg(XeNIncrement, 100);
						EXeSetArg(XeNUserData0, LInfo->Class);
						EXeSetArg(XeNResizableY, FALSE);
						XeToolTip8("Scale the size of the gear's root circle by this much.");
						LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->GearRootCircleScaleFactorW = LW = XeScale_New_Coord(LTopMatrixW, "Root circle scale",  Args, ArgCnt);
						XeScale_SetValueAndRange_Coord(LW,  LInfo->Gear.RootCircleScaleFactor,  0.8, 1.4,  FALSE);
						XeAddCallback(LW, XeNValueChangedCallback, _CB_GearRootCircleScaleFactor, (EPointer)LPanel);

						EXeStart;
						EXeSetArg(XeNTextFieldDigits, 8);EXeSetArg(XeNDecimalPoints, 4);
						EXeSetArg(XeNResizableY, FALSE);
						XeToolTip8("Create this many teeth per unit diameter,\nor per 180 degrees of latitude for bevel gears.");
						LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->GearDiametralPitchW = LW = XeScale_New_Coord(LTopMatrixW, "Diametral pitch",  Args, ArgCnt);
						XeScale_SetValueAndRange_Coord(LW,  LInfo->Gear.DiametralPitch,  2.0, 256.0,  FALSE);
						XeAddCallback(LW, XeNValueChangedCallback, _CB_DiametralPitch, (EPointer)LPanel);




// Bevels
//
						{
							EXeStart;
							EXeSetArg(XeNTextFieldDigits, 8);EXeSetArg(XeNDecimalPoints, 4);
							EXeSetArg(XeNResizableY, FALSE);
							XeToolTip8("Radius of bevel at the base of teeth.\nGiven as a proportion of the total tooth height.");
							LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->GearToothBaseBevelRadiusW = LW = XeScale_New_Coord(LTopMatrixW, "Tooth base bevel radius",  Args, ArgCnt);
							XeScale_SetValueAndRange_Coord(LW,  LInfo->Gear.ToothBaseBevelRadius,  0.01, 0.5,  FALSE);
							XeAddCallback(LW, XeNValueChangedCallback, _CB_ToothBaseBevelRadius, (EPointer)LPanel);

							EXeStart;
							EXeSetArg(XeNTextFieldDigits, 8);
							EXeSetArg(XeNMinimum, 0);EXeSetArg(XeNMaximum, 10);EXeSetArg(XeNValue, LInfo->Gear.ToothBaseBevelSteps);
							EXeSetArg(XeNResizableY, FALSE);
							LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->GearToothBaseBevelStepsW = LW = XeWidget_ManagedNew("Tooth base bevel steps", XeScaleWidget_class, LTopMatrixW,  Args, ArgCnt);
							XeAddCallback(LW, XeNValueChangedCallback, _CB_ToothBaseBevelSteps, (EPointer)LPanel);


							EXeStart;
							EXeSetArg(XeNTextFieldDigits, 8);EXeSetArg(XeNDecimalPoints, 4);
							EXeSetArg(XeNResizableY, FALSE);
							XeToolTip8("Radius of bevel at the tips of teeth.\nGiven as a proportion of the total tooth height.");
							LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->GearToothTipBevelRadiusW = LW = XeScale_New_Coord(LTopMatrixW, "Tooth tip bevel radius",  Args, ArgCnt);
							XeScale_SetValueAndRange_Coord(LW,  LInfo->Gear.ToothTipBevelRadius,  0.01, 0.5,  FALSE);
							XeAddCallback(LW, XeNValueChangedCallback, _CB_ToothTipBevelRadius, (EPointer)LPanel);

							EXeStart;
							EXeSetArg(XeNTextFieldDigits, 8);
							EXeSetArg(XeNMinimum, 0);EXeSetArg(XeNMaximum, 10);EXeSetArg(XeNValue, LInfo->Gear.ToothTipBevelSteps);
							EXeSetArg(XeNResizableY, FALSE);
							LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->GearToothTipBevelStepsW = LW = XeWidget_ManagedNew("Tooth tip bevel steps", XeScaleWidget_class, LTopMatrixW,  Args, ArgCnt);
							XeAddCallback(LW, XeNValueChangedCallback, _CB_ToothTipBevelSteps, (EPointer)LPanel);
						}


// Bevel gear
//
#if BevelGearDEBUG
						EXeStart;
						EXeSetArg(XeNMarginSizeX, 0);EXeSetArg(XeNMarginSizeY, 0);
						EXeSetArg(XeNResizableX, FALSE);EXeSetArg(XeNAlignmentX, XeAlignBEGINNING);
						EXeSetArg(XeNResizableY, FALSE);
						EXeSetArg(XeNFontList, Xe_Header1FontList);
						LResourcesGUI->BevelGearAlgorithmNameW = XeWidget_ManagedNew(_BevelGearAlgorithmName, XeLabelWidget_class, LTopMatrixW,  Args, ArgCnt);

						EXeStart;
						EXeSetArg(XeNTextFieldDigits, 8);EXeSetArg(XeNDecimalPoints, 4);
						EXeSetArg(XeNResizableY, FALSE);
						LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->Gear2AlphaMulW = LW = XeScale_New_Coord(LTopMatrixW, "Gear2AlphaMul",  Args, ArgCnt);
						XeScale_SetValueAndRange_Coord(LW,  LInfo->Gear.Gear2AlphaMul,  0.0, 1.5,  FALSE);
						XeAddCallback(LW, XeNValueChangedCallback, _CB_Gear2AlphaMul, (EPointer)LPanel);

						EXeStart;
						EXeSetArg(XeNTextFieldDigits, 8);EXeSetArg(XeNDecimalPoints, 4);
						EXeSetArg(XeNResizableY, FALSE);
						LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->Gear2ToothRotOffsetW = LW = XeScale_New_Coord(LTopMatrixW, "Gear2ToothRotOffset",  Args, ArgCnt);
						XeScale_SetValueAndRange_Coord(LW,  LInfo->Gear.Gear2ToothRotOffset,  -20.0, 50.0,  FALSE);
						XeAddCallback(LW, XeNValueChangedCallback, _CB_Gear2ToothRotOffset, (EPointer)LPanel);

						EXeStart;
						EXeSetArg(XeNTextFieldDigits, 8);EXeSetArg(XeNDecimalPoints, 4);
						EXeSetArg(XeNResizableY, FALSE);
						LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->OuterCircleScaleW = LW = XeScale_New_Coord(LTopMatrixW, "OuterCircleScale",  Args, ArgCnt);
						XeScale_SetValueAndRange_Coord(LW,  LInfo->Gear.OuterCircleScale,  0.8, 1.6,  FALSE);
						XeAddCallback(LW, XeNValueChangedCallback, _CB_OuterCircleScale, (EPointer)LPanel);
#endif

// Gear index (1 or 2)
//
						{
							EXeStart;
							EXeSetArg(XeNRadioBehavior, TRUE);
							EXeSetArg(XeNMarginRight, 8);
							EXeSetArg(XeNSpacingX, 8);
							EXeSetArg(XeNAdjustEntrySizesX, FALSE);
							EXeSetArg(XeNAdjustEntrySizesY, FALSE);
							LResourcesGUI->GearRadioMatrixW = XeWidget_ManagedNew("MatrixW", XeMatrixWidget_class, LTopMatrixW,  Args, ArgCnt);


							XeWidget_ManagedNew("Gear", XeLabelWidget_class, LResourcesGUI->GearRadioMatrixW,  NULL, 0);


							EXeStart;
							EXeSetArg(XeNHighlightThickness, 0);
							EXeSetArg(XeNSet, LInfo->Gear.GearIndex == 1);
							EXeSetArg(XeNResizableY, FALSE);
							LResourcesGUI->Gear1W = LW = XeWidget_ManagedNew("1", XeToggleButton_class, LResourcesGUI->GearRadioMatrixW,  Args, ArgCnt);
							XeAddCallback(LW, XeNValueChangedCallback, _CB_Gear1, (EPointer)LPanel);

							EXeStart;
							EXeSetArg(XeNHighlightThickness, 0);
							EXeSetArg(XeNSet, LInfo->Gear.GearIndex == 2);
							EXeSetArg(XeNResizableY, FALSE);
							LResourcesGUI->Gear2W = LW = XeWidget_ManagedNew("2", XeToggleButton_class, LResourcesGUI->GearRadioMatrixW,  Args, ArgCnt);
							XeAddCallback(LW, XeNValueChangedCallback, _CB_Gear2, (EPointer)LPanel);
						}


						EXeStart;
						EXeSetArg(XeNTextFieldDigits, 8);EXeSetArg(XeNDecimalPoints, 4);
						EXeSetArg(XeNResizableY, FALSE);
						XeToolTip8("Radius of the bevel gear outer sphere,\n or 0.0, if this is a flat gear.");
						LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->BevelGearSphereRadiusW = LW = XeScale_New_Coord(LTopMatrixW, "Bevel gear s .radius",  Args, ArgCnt);
						XeScale_SetValueAndRange_Coord(LW,  LInfo->Gear.BevelGearSphereRadius,  0.0, 10.0,  FALSE);
						XeAddCallback(LW, XeNValueChangedCallback, _CB_BevelGearSphereRadius, (EPointer)LPanel);


						EXeStart;
						EXeSetArg(XeNShadowThickness, 0);
						EXeSetArg(XeNHighlightThickness, 0);EXeSetArg(XeNMarginSizeY, 0);
						EXeSetArg(XeNMarginTop, 2);EXeSetArg(XeNMarginBottom, 0);
						EXeSetArg(XeNSet, LInfo->Gear.Internal);
						EXeSetArg(XeNResizableX, FALSE);EXeSetArg(XeNAlignmentX, XeAlignBEGINNING);
						EXeSetArg(XeNResizableY, FALSE);
						XeToolTip8("Put the teeth of the gear on the inside,\nas in the ring gear of a planetary gear system.");
						LResourcesGUI->GearInternalW = LW = XeWidget_ManagedNew("Internal gear", XeToggleButton_class, LTopMatrixW,  Args, ArgCnt);
						XeAddCallback(LW, XeNValueChangedCallback, _CB_GearInternal, (EPointer)LPanel);
					}

					XeWidgets_IndentFromLeft(LIndentWs, LIndentN, 0);

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


//================================================
// Menu callback (create new Circle)
//================================================
static void _MCB_Circle(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dTpl_ObjectComponentActivateCB_Geometry(_ComponentClass);
}


//================================================
// Menu callback (create new Circle)
//================================================
static void _MCB_Gear(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dCircleInfo  LInfoT;E3dCircleInfo*  LInfo = &LInfoT;E3dObjectComponent_InitStatic(LInfo, _ComponentClass);

	LInfo->Gear.Teeth = 8;
	E3dTpl_ObjectComponentActivateWithCustomDefaultsCB_Geometry(E3dCircle, _, LInfo, "Gear", _Plugin);

	E3dObjectComponent_FreeContents((E3dObjectComponent*)LInfo);
}


//================================================
// Constructor
//================================================
static int _Init(E3dObject* PObject, E3dObjectComponent* PInfo)
{
	E3dCircleInfo*  LThis = (E3dCircleInfo*)PInfo;

	LThis->Radius = 1.0;
	LThis->StartAngle = 0.0;
	LThis->EndAngle = 360.0;
	LThis->Segments = 12;
	LThis->SpiralPitch = 0.0;
	LThis->GeometryType = C_SPLINE;
	LThis->SplineType = E3dSplineLINEAR;

	LThis->LastCVSegmentLinear = TRUE;

	LThis->Gear.Teeth = 0;
	LThis->Gear.DiametralPitch = 16.0;  // This many teeth per unit diameter, or in case of a bevel gear, per 180 degrees of latitude
	LThis->Gear.PressureAngle = 14.5;
	LThis->Gear.RootCircleScaleFactor = 1.0;
	LThis->Gear.Internal = FALSE;

	LThis->Gear.ToothBaseBevelRadius = 0.1;
	LThis->Gear.ToothBaseBevelSteps  = 0;

	LThis->Gear.ToothTipBevelRadius = 0.1;
	LThis->Gear.ToothTipBevelSteps  = 3;

	LThis->Gear.BevelGearSphereRadius = 0.0;  // 0.0 : not a bevel gear
	LThis->Gear.Gear2Teeth = 0;
	LThis->Gear.GearIndex  = 1;


#if BevelGearDEBUG
	LThis->Gear.Gear2AlphaMul       =  0.0;
	LThis->Gear.Gear2ToothRotOffset =  0.0;
	LThis->Gear.OuterCircleScale    =  1.0;
#endif

	return(E_SUCCESS);
}


//========================================
// Copy-constructor
//========================================
static void _Copy(const E3dObject* PSObject, const E3dObjectComponent* PSInfo, E3dObject* PDObject, E3dObjectComponent* PDInfo)
{
	const E3dCircleInfo*  LSInfo = (E3dCircleInfo*)PSInfo;
	E3dCircleInfo*        LDInfo = (E3dCircleInfo*)PDInfo;

	_CopyFields(LSInfo, LDInfo);
}


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

	{
		E3dObjectComponentClass  LCircleObjectComponentClass=
		{
			.Name=        "Circle",
			.StructSize=  sizeof(E3dCircleInfo),
			.Resources=   EListS_s(_CircleResources),

			.Init=        _Init,
			.Copy=        _Copy,
			.Update1=      _Update1,
			.GUI=         _GUI
		};
		_ComponentClass = E3dObjectComponent_ClassRegister(&LCircleObjectComponentClass);
	}

	EpM_CreateIconStates3_sp(CircleLinear);
	EpM_CreateIconStates3_sp(CircleBezier);
	EpM_CreateIconStates3_sp(CircleBSpline);
	EpM_CreateIconStates3_sp(CircleCardinal);

	if(_ComponentClass)
	{
		EpM_CreateIconStates3_sp(CircleDisc);E_Assign(_Image_CircleDisc, _ComponentClass->Icon);
		EpM_CreateIconStates3_sp(Gear);//E_Assign(_Image_Gear, _ComponentClass->Icon);

		XePushButton_AddImg3(_ToolBarButtons[_ToolBarButtons_Count++], CircleDisc, "Tool->Create", "Circle/Disc", '\0', NULL, NULL, TRUE, "Create circle or disc\nRight button:  dialog box for this tool", _MCB_Circle, (EPointer)0);
		_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Create", "Circle", '\0', NULL, NULL, TRUE, NULL, _MCB_Circle, (EPointer)0);

		XePushButton_AddImg3(_ToolBarButtons[_ToolBarButtons_Count++], Gear, "Tool->Create", "Gear", '\0', NULL, NULL, TRUE, "Create gear\nRight button:  dialog box for this tool", _MCB_Gear, (EPointer)0);
		_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Create", "Gear", '\0', NULL, NULL, TRUE, NULL, _MCB_Gear, (EPointer)0);
	}

	return(E_SUCCESS);
}


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

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

	if(_ComponentClass)
	{
		if(PExiting && _ComponentClass->Instances.Count > 0) return(E_BUSY);
		E3dObjectComponent_ClassDeactivate(_ComponentClass);_ComponentClass = NULL;
	}

	EpIconFree3(CircleDisc);
	EpIconFree3(CircleLinear);
	EpIconFree3(CircleBezier);
	EpIconFree3(CircleBSpline);
	EpIconFree3(CircleCardinal);

	EpIconFree3(Gear);

	return(E_SUCCESS);
}
