{ Testing the wDraw3DFrame2.pp unit.
  Drawing of objects in the 3D space.
  Testing the Creator of Bodies : TBody3DCreator

  You can see a projection of them on the screen, of if you use
  Red and Green galsses, you can see them in the space, behind or
  in front of the screen.
  The colors can be changed in the program.
     Many different Bodies can be defines and viewed.

  At the end I was a little tired and bored...
  --------------------------------------------}
program tEx058;

// To tell FreePascal that the application is a Graphical User Interface
// i.e. is running in windows, not in text mode.
{$APPTYPE GUI}

Uses

  wApplication0
 ,wWindow0
 ,wVariableNumLink2
 ,wDialogCreatorLow2
 ,wUtiles0
 ,wDraw3DFrame2
 ,wDraw3DFrame0
 ,wDialog0
// ,wControl0
 ,wWindowBase0
 ,xVectR3
 ,wMessages2
 ,wGlobals2
 ,wDebug0
 ,Windows;

type
  PMyWindow = ^TMyWindow;
  TMyWindow = object(TWindow)
  //"""""""""""""""""""""""""
  poDraw3D : PDraw3DFrame;

  MAXFACES,  // Maximum number of faces to put in the space world
  MAXPOINTS  // Maximum number of points to put in the space world
             : word32;

  vecNewCenterPos : RVectR3Float;
  // To translate each new body, and place them on a line

  nNbPointsThetaGL : int16;
  // Number of points varying Theta while creating an object

  nNbPointsPhiGL : int16;
  // Number of points varying Phi while creating an object

  vecPositionGL : RVectR3Float;
  // Position of the new Body

  awCustomColors : array[1..16] of TypeColor;
  // To have some custom colors which can be choosen from a dialog box

  // ***** To create a new Torus
  vTorusRadiusCenter : Float;
  // Radius from the center of the Torus to the center of the tube of the Torus.

  vTorusRadiusDisk : Float;
  // Radius of the tube of the Torus.

  nTorusNbTwistPhi : int16;
  nTorusNbTwistTheta : int16;

  strTorusName : string[40];

  colorTorusFaces : TypeColor;
  colorTorusAxis  : TypeColor;

  // ***** To create a new Ellipsoide
  vEllipseRadiusAxeX,vEllipseRadiusAxeY,vEllipseRadiusAxeZ : Float;

  nEllipseNbTwistPhi   : int16;
  nEllipseNbTwistTheta : int16;

  strEllipseName : string[40];

  colorEllipseFaces : TypeColor;
  colorEllipseAxis  : TypeColor;

  // ***** To create a new Body from a curve in the space
  vFromPath1RadiusAxeX,vFromPath1RadiusAxeY : Float;

  vFromPath1TwistFactorX,
  vFromPath1TwistFactorY : Float;
  // To twist the tube allong the path.
  // If both values are different, strange effect can occure

  strFromPath1Name : string[40];

  colorFromPath1Faces : TypeColor;
  colorFromPath1Axis  : TypeColor;

  // ***** To create a new Body from a curve in the space SECOND one
  nFromPath2NbPointsPhi : int16;
  // Number of points varying Phi while creating an object
  // i.e. number of points on a circle of the tube

  nFromPath2NbPointsTube : int16;
  // Number of points allong the path

  strFromPath2Name : string[40];
  // Body name

  vFromPath2RadiusAxeX,
  vFromPath2RadiusAxeY,
  vFromPath2TwistFactorX,
  vFromPath2TwistFactorY,
  vFromPath2FactCos1,
  vFromPath2FactCos2,
  vFromPath2FactSin3,
  vFromPath2FrequCos2,
  vFromPath2FrequSin3 : Float;
  // Some factors to deforme the Body.
  // Default values are given in the dialog box 13599

  colorFromPath2Faces,
  colorFromPath2Axis  : TypeColor;

  constructor Init(strWinTitlePrm:ShortString);
  destructor Done; virtual;
  procedure WinCreate(strWinClass:ShortString); virtual;

  procedure NewSpaceWorld;

  procedure ASurface;
  procedure ATorus;
  procedure AnEllipsoide;
  procedure AnFromPath1;
  procedure AnFromPath2(fModifyP:boolean);
  // Create a new body (if bModify = FALSE), or
  // Modify the selected body, if it has been created with AnFromPath2(FALSE)

  procedure AnFromPath2Animation;
  // Animate a selected a FromPath2 Body

  procedure ASurfaceSquare;
  procedure ASurfaceSquareBis;
  procedure ASurfaceRound;

  procedure ButtonClicked(poCtrl:PWindowBase0; wCtrlIDP:Word32); virtual;
  // To choose the color of a Body

  function WMCommand(wPar:WPARAM; lPar:LPARAM):LRESULT; virtual;
  // Handles the menu messages

  function WindowProc(hWindow:HWND; wMsg:UINT; wPar:WPARAM; lPar:LPARAM):LRESULT; virtual;
  end; // TMyWindow = object(TWindow)

var
  oApp        : TApplication;
  poMyMainWin : PMyWindow;

// var nNotUsed : integer; // only to avoid an error message while compliling in DevPascal 1.9

constructor TMyWindow.Init(strWinTitlePrm:ShortString);
//=====================================================
var ww : word;
begin
inherited Init(strWinTitlePrm);

// To have a default at starting
MAXFACES  := 20100;
MAXPOINTS := 21000;

nNbPointsThetaGL :=36;
nNbPointsPhiGL   :=36;
vecPositionGL    :=V3Assign(0,0,0);

// Init the Custom colors
for ww:=1 to 16 do awCustomColors[ww].wColor:=RGB(255,255,255);

// To create a new Torus
vTorusRadiusCenter :=4.0;
vTorusRadiusDisk   :=1.0;
nTorusNbTwistPhi   :=  0;
nTorusNbTwistTheta :=  0;
//strTorusName       :=GetMsg(11002); // must be done later, at creation for example
colorTorusFaces    :=RGB(223,223,128);
colorTorusAxis     :=RGB(  0,225,  0);

// To create a new Ellipsoide
vEllipseRadiusAxeX :=1.0;
vEllipseRadiusAxeY :=2.0;
vEllipseRadiusAxeZ :=4.0;
nEllipseNbTwistPhi   :=0;
nEllipseNbTwistTheta :=0;
//strEllipseName       :=GetMsg(11003); // must be done later, at creation for example
colorEllipseFaces :=RGB( 64,255,255);
colorEllipseAxis  :=RGB(  0,255,  0);

// To create a new Body from a path in the space
vFromPath1RadiusAxeX  :=0.5;
vFromPath1RadiusAxeY  :=0.5;
vFromPath1TwistFactorX:=0.0;
vFromPath1TwistFactorY:=0.0;
strFromPath1Name      :='FromPath1';
colorFromPath1Faces   :=RGB(255,128,  0);
colorFromPath1Axis    :=RGB(  0,255,  0);

// To create a new Body from a path in the space second Body
nFromPath2NbPointsPhi:=16;
nFromPath2NbPointsTube :=64;
vFromPath2RadiusAxeX :=0.5;
vFromPath2RadiusAxeY :=0.5;
vFromPath2TwistFactorX :=0.0;
vFromPath2TwistFactorY :=0.0;
strFromPath2Name       :='FromPath2';
vFromPath2FactCos1  :=1.0;
vFromPath2FactCos2  :=2.0;
vFromPath2FactSin3  :=2.0;
vFromPath2FrequCos2 :=2.0;
vFromPath2FrequSin3 :=3.0;
colorFromPath2Faces :=RGB(255,128, 64);
colorFromPath2Axis  :=RGB(  0,255,  0);

new(poDraw3D, Init(@Self));

vecNewCenterPos:=gglvecV3Zero;

//Word32Field(1001, wMAXFACESNew); // Used
//Word32Field(1002, wMAXPOINTSNew);
//FloatField( 1003, vMemSize);
Word32Field(1011, MAXFACES);
Word32Field(1012, MAXPOINTS);
Int16Field( 1021, nNbPointsThetaGL);
Int16Field( 1022, nNbPointsPhiGL);
FloatField( 1031, vecPositionGL[1]);
FloatField( 1032, vecPositionGL[2]);
FloatField( 1033, vecPositionGL[3]);

// This line is all what is needed to have a status line.
new(poStatusLine, Init(@Self));
end;  // TMyWindow.Init

destructor TMyWindow.Done;
//=========================
begin
// Do not dispose poDraw3D, already done by the parent
inherited Done;
end;  // TMyWindow.Done

procedure TMyWindow.WinCreate(strWinClass:ShortString);
//=====================================================
// Create the window, and insert an edit box and a status line in it.
var wErrorL : word32;
begin
inherited WinCreate(strWinClass);

poDraw3D^.nYSizePix:=poDraw3D^.nYSizePix - int32(poStatusLine^.wHeight);
poDraw3D^.CtrlCreate;
SetFocus(poDraw3D^.hWin);

// Allocate memory for 11000 points and 10100 faces
poDraw3D^.AllocateMemory(MAXPOINTS, MAXFACES, wErrorL); // Hope there is no errors

// Viewing position and screen center in centimeters
// This defines also the distance between the viwer and the screen
poDraw3D^.Draw3DSetup(-9.0, -78.0, 10.0, 0, 0, 0); // Screen at (0,0,0)

strTorusName       :=GetMsg(11002); // A Torus
strEllipseName     :=GetMsg(11003); // An Ellipsoide
end;  // TMyWindow.WinCreate

procedure TMyWindow.NewSpaceWorld;
//================================
// Clear the actual object in the space, define the maximum number of
// points and face, and reset all
var
  wMAXPOINTSNew,
  wMAXFACESNew,
  wErrorL : word32;
  nRep    : integer;
  vMemSize : Float; // Memory space allocated to store the points and faces

begin
// To define a new number of points and faces
wMAXPOINTSNew:=MAXPOINTS;
wMAXFACESNew :=MAXFACES;
Word32Field(1001, wMAXFACESNew);
Word32Field(1002, wMAXPOINTSNew);
FloatField( 1003, vMemSize);

// Last size of memory allocated
vMemSize:=(MAXPOINTS * SizeOf(TypePoint3D) +
           MAXFACES * SizeOf(TypeFace3D)) / 1000000.0;

nRep:=DialogExecMSG(12299, @Self);

if (nRep <> IDOK) then exit;

vMemSize:=(wMAXPOINTSNew * SizeOf(TypePoint3D) +
           wMAXFACESNew * SizeOf(TypeFace3D)) / 1000000.0;
if (vMemSize > 50) then begin
    // Very big amount of space, ask confirmation
   nRep:=DialogExecMSG(12399, @Self);

   if (nRep <> IDOK) then exit;
   end;

// Only here, accept the modifications
MAXPOINTS:=wMAXPOINTSNew;
MAXFACES :=wMAXFACESNew;

// Dispose the actual space world
dispose(poDraw3D, Done);
vecNewCenterPos:=gglvecV3Zero;

// create a new one
new(poDraw3D, Init(@Self));

// Size on the screen
poDraw3D^.nYSizePix:=poDraw3D^.nYSizePix - int32(poStatusLine^.wHeight);

// Create the corresponding control (A very sofisticated control !)
poDraw3D^.CtrlCreate;
SetFocus(poDraw3D^.hWin);

// Allocate memory for 11000 points and 10100 faces
poDraw3D^.AllocateMemory(MAXPOINTS, MAXFACES, wErrorL); // Hope there is no errors

// Viewing position and screen center in centimeters
// This defines also the distance between the viwer and the screen
poDraw3D^.Draw3DSetup(-9.0, -78.0, 10.0, 0, 0, 0); // Screen at (0,0,0)
end;  // TMyWindow.NewSpaceWorld

procedure TMyWindow.ASurface;
//===========================
const
  nRMax   = 50;
  nPhiMax = 24;
  nDPhi   = 1 + nPhiMax;
var
  nR,nPhi     : int32;
  vR,vPhi     : Float;
  parPtsL     : PArrayPoint3D;

begin
with poDraw3D^ do begin
   parPtsL:=@parPoints^[wNumPoints+1];

   // Creates a new body
   inc(wNumBodies);
   new(papoBodies^[wNumBodies], Init(poDraw3D));

   // Add the points. Units = centimeters
   for nR   :=0 to nRMax do begin
      vR:=0.1+5.0*nR/Float(nRMax);

      // for small values of vR, the points are closer
      vR:=vR - vR / (vR+1.0);

      for nPhi :=0 to nPhiMax do begin
         // Add one point
         vPhi :=2*Pi* nPhi / Float(nPhiMax);

         with parPtsL^[nR*nDPhi + nPhi] do begin
            vecV[1]:=vR * Cos(vPhi);
            vecV[2]:=vR * Sin(vPhi);
            vecV[3]:=2.0*exp(-3*sqr(vR/4.2))*Cos(7*Pi*vR/4.2);
            end; // with
         end; // for nPhi :=0 to nPhiMax do begin
      end; // for nR   :=0 to nRMax do begin

   wNumPoints:=wNumPoints + word32((nRMax)*nDPhi + nPhiMax) +1;

   // Add the faces
   for nR   :=0 to nRMax-1 do
   for nPhi :=0 to nPhiMax-1 do begin
      inc(wNumFaces);
      with parFaces^[wNumFaces] do begin
         nNbPoint:=4;
         aprPoint[1]:=@parPtsL^[(nR  )*nDPhi + nPhi];
         aprPoint[2]:=@parPtsL^[(nR+1)*nDPhi + nPhi];
         aprPoint[3]:=@parPtsL^[(nR+1)*nDPhi + nPhi+1];
         aprPoint[4]:=@parPtsL^[(nR  )*nDPhi + nPhi+1];
         end; // with
      end; // for

   // Optional, defining if one or two colors are used, and
   // can change the default standard colors
   // The Created Pen will be destroyed automatically
   papoBodies^[wNumBodies]^.fUniColor:=FALSE; // Ask to have two colors
   papoBodies^[wNumBodies]^.colorSup.wColor:=RGB(255,255,  0);
   papoBodies^[wNumBodies]^.colorInf.wColor:=RGB(128,128,  0);
   papoBodies^[wNumBodies]^.colorAx.wColor :=RGB(  0,255,255);
   papoBodies^[wNumBodies]^.strBodyName:=GetMsg(11001); // A Surface;

   // This defines the number of points and faces in the Body
   // It defines also the colors. Must be called.
   papoBodies^[wNumBodies]^.BodySetup(0,0,0);

   // Final rotation for the viweing
   papoBodies^[wNumBodies]^.BodyRotateGC(vecRight, 35*Pi/180);

   // erase window, and draw
   Draw3DBodies;
   end; // with
end;  // TMyWindow.ASurface

procedure TMyWindow.ATorus;
//=========================
// A torus
var
  nRep             : integer;
  oBody3DCreator : TBody3DTorusCreator;
  poBody3D       : PBody3D0;
begin
// Editable variable of the dialog box, + some globals one
FloatField( 2001, vTorusRadiusCenter); // Radius from the center of the Torus to the center of the tube of the Torus.
FloatField( 2002, vTorusRadiusDisk);  // Radius of the tube of the Torus.
Int16Field( 2011, nTorusNbTwistPhi);
Int16Field( 2012, nTorusNbTwistTheta);
StrField(   2021, strTorusName, SizeOf(strTorusName));
ByteField(  2031, colorTorusFaces.bRed);
ByteField(  2032, colorTorusFaces.bGreen);
ByteField(  2033, colorTorusFaces.bBlue);
ByteField(  2034, colorTorusAxis.bRed);
ByteField(  2035, colorTorusAxis.bGreen);
ByteField(  2036, colorTorusAxis.bBlue);

nRep:=DialogExecMSG(13299, @Self);

if (nRep <> IDOK) then exit;

oBody3DCreator.Init(poDraw3D);

with oBody3DCreator do begin
   // Values which can be changed
   nNbPointsTheta :=nNbPointsThetaGL; // default = 36
   nNbPointsPhi   :=nNbPointsPhiGL; // default = 36
   // Number of points horizontally and vertically.

   vRadiusCenter :=vTorusRadiusCenter; // default = 4.0
   // Radius from the center of the Torus to the center of the
   // tube of the Torus.

   vRadiusDisk :=vTorusRadiusDisk; // default = 1.0
   // Radius of the tube of the Torus.

   nNbTwistPhi   :=nTorusNbTwistPhi;    // To make spirals . =0 by default
   nNbTwistTheta :=nTorusNbTwistTheta;   // To twist the Torus allong the tube. =0 by default

   // These data are used in the Setup of the Body
   // If changed, must be set before calling the  Body3DCreate
   ColorBody3DSup :=colorTorusFaces;
   ColorBody3DAxe :=colorTorusAxis;
   strBody3DName  :=strTorusName;
   end;  // with

// Create the Torus
poBody3D:=oBody3DCreator.Body3DCreate;

// We don't need the Body creator any more
oBody3DCreator.Done;

// Here we can change some properties of the Body
poBody3D^.fUniColor:=TRUE; // Ask to have a unique color

// Final rotation for the viweing
// poBody3D^.BodyRotateGC(gglvecV3AxisX, 35*Pi/180);

poBody3D^.BodySetPosition(vecPositionGL);

// Define the viewing direction, without modifying the viewer position
// Sets again the right horizontal.
poDraw3D^.SetViewDirection(vecPositionGL);

// Draw the Body
poDraw3D^.Draw3DBodies;
end;  // TMyWindow.ATorus

procedure TMyWindow.AnEllipsoide;
//===============================
var
  nRep             : integer;
  oBody3DCreator : TBody3DEllipsoideCreator;
  poBody3D       : PBody3D0;
begin
// Editable variable of the dialog box, + some globals one
FloatField( 2001, vEllipseRadiusAxeX);
FloatField( 2002, vEllipseRadiusAxeY);
FloatField( 2003, vEllipseRadiusAxeZ);
Int16Field( 2011, nEllipseNbTwistPhi);
Int16Field( 2012, nEllipseNbTwistTheta);
StrField(   2021, strEllipseName, SizeOf(strEllipseName));
ByteField(  2031, colorEllipseFaces.bRed);
ByteField(  2032, colorEllipseFaces.bGreen);
ByteField(  2033, colorEllipseFaces.bBlue);
ByteField(  2034, colorEllipseAxis.bRed);
ByteField(  2035, colorEllipseAxis.bGreen);
ByteField(  2036, colorEllipseAxis.bBlue);

nRep:=DialogExecMSG(13399, @Self);

if (nRep <> IDOK) then exit;

oBody3DCreator.Init(poDraw3D);

with oBody3DCreator do begin
   // Values which can be changed
   nNbPointsTheta :=nNbPointsThetaGL; // default = 36
   nNbPointsPhi   :=nNbPointsPhiGL; // default = 36
   // Number of points horizontally and vertically.

   vRadiusAxeX :=vEllipseRadiusAxeX; // default = 1.0
   vRadiusAxeY :=vEllipseRadiusAxeY; // default = 1.0
   vRadiusAxeZ :=vEllipseRadiusAxeZ; // default = 1.0

   nNbTwistPhi   :=nEllipseNbTwistPhi;    // To make spirals . =0 by default
   nNbTwistTheta :=nEllipseNbTwistTheta;   // To twist the Torus allong the tube. =0 by default

   // These data are used in the Setup of the Body
   // If changed, must be set before calling the  Body3DCreate
   ColorBody3DSup :=colorEllipseFaces;
   ColorBody3DAxe :=colorEllipseAxis;
   strBody3DName  :=strEllipseName;
   end;  // with

// Create the Torus
poBody3D:=oBody3DCreator.Body3DCreate;

// We don't need the Body creator any more
oBody3DCreator.Done;

// Here we can change some properties of the Body
poBody3D^.fUniColor:=TRUE; // Ask to have a unique color

// Final rotation for the viweing
// poBody3D^.BodyRotateGC(gglvecV3AxisX, 35*Pi/180);

poBody3D^.BodySetPosition(vecPositionGL);

// Define the viewing direction, without modifying the viewer position
// Sets again the right horizontal.
poDraw3D^.SetViewDirection(vecPositionGL);

// Draw the Body
poDraw3D^.Draw3DBodies;
end;  // TMyWindow.AnEllipsoide

procedure TMyWindow.AnFromPath1;
//==============================
var
  nRep           : integer;
  oBody3DCreator : TBody3DFromPathCreator;
  poBody3D       : PBody3D0;
  avecV          : array[1..40] of RVectR3Float;
  nn             : integer;
begin
// Editable variable of the dialog box, + some globals one
FloatField( 2001, vFromPath1RadiusAxeX);
FloatField( 2002, vFromPath1RadiusAxeY);
FloatField( 2011, vFromPath1TwistFactorX);
FloatField( 2012, vFromPath1TwistFactorY);
StrField(   2021, strFromPath1Name, SizeOf(strFromPath1Name));
ByteField(  2031, colorFromPath1Faces.bRed);
ByteField(  2032, colorFromPath1Faces.bGreen);
ByteField(  2033, colorFromPath1Faces.bBlue);
ByteField(  2034, colorFromPath1Axis.bRed);
ByteField(  2035, colorFromPath1Axis.bGreen);
ByteField(  2036, colorFromPath1Axis.bBlue);

nRep:=DialogExecMSG(13499, @Self);

if (nRep <> IDOK) then exit;

// Create the curbe in the space
for nn:=1 to 40 do begin
   avecV[nn][1]:=2.0 * Cos(2*Pi*nn/10.0);
   avecV[nn][2]:=nn/1.0;
   avecV[nn][3]:=2.0 * Sin(2*Pi*nn/10.0);
   end;

oBody3DCreator.Init(poDraw3D);

with oBody3DCreator do begin
   // Valuse which must be given
   pavecV        :=@avecV;
   nNbPointsPath :=40;

   // Values which can be changed
   nNbPointsPhi   :=nNbPointsPhiGL; // default = 36
   // Number of points horizontally and vertically.

   vRadiusAxeX :=vFromPath1RadiusAxeX; // default = 1.0
   vRadiusAxeY :=vFromPath1RadiusAxeY; // default = 1.0

   // To twist the tube.
   vTwistFactorX :=vFromPath1TwistFactorX;
   vTwistFactorY :=vFromPath1TwistFactorY;

   // These data are used in the Setup of the Body
   // If changed, must be set before calling the  Body3DCreate
   ColorBody3DSup :=colorFromPath1Faces;
   ColorBody3DAxe :=colorFromPath1Axis;
   strBody3DName  :=strFromPath1Name;
   end;  // with

// Create the Body
poBody3D:=oBody3DCreator.Body3DCreate;

// We don't need the Body creator any more
oBody3DCreator.Done;

// Here we can change some properties of the Body
poBody3D^.fUniColor:=TRUE; // Ask to have a unique color

// Final rotation for the viweing
// poBody3D^.BodyRotateGC(gglvecV3AxisX, 35*Pi/180);

poBody3D^.BodySetPosition(vecPositionGL);

// Define the viewing direction, without modifying the viewer position
// Sets again the right horizontal.
poDraw3D^.SetViewDirection(vecPositionGL);

// Draw the Body
poDraw3D^.Draw3DBodies;
end;  // TMyWindow.AnFromPath1

procedure TMyWindow.AnFromPath2(fModifyP:boolean);
//================================================
// Create a new body (if bModify = FALSE), or
// Modify the selected body, if it has been created with AnFromPath2(FALSE)
var
  nRep           : integer;
  oBody3DCreator : TBody3DFromPathCreator;
  poBody3D       : PBody3D0;
  avecV          : array[0..400] of RVectR3Float;
  nMax,
  nn             : integer;
  vT             : Float;
begin
if fModifyP then begin
   // Select the Body, to test if it is the right one
   poBody3D:=poDraw3D^.papoBodies^[poDraw3D^.wBodySelected];
   if (poBody3D^.pUserData <> pointer(102)) then begin
      StrField(   2019, poBody3D^.strBodyName, SizeOf(poBody3D^.strBodyName));
      DialogExecMSG(13510, @Self);
      exit;
      end;
   end;

// Editable variable of the dialog box, + some globals one
FloatField( 2001, vFromPath2RadiusAxeX);
FloatField( 2002, vFromPath2RadiusAxeY);
FloatField( 2011, vFromPath2TwistFactorX);
FloatField( 2012, vFromPath2TwistFactorY);
Int16Field( 2020, nFromPath2NbPointsPhi);
StrField(   2021, strFromPath2Name, SizeOf(strFromPath2Name));
Int16Field( 2022, nFromPath2NbPointsTube);
FloatField( 2023, vFromPath2FactCos1);
FloatField( 2024, vFromPath2FactCos2);
FloatField( 2025, vFromPath2FactSin3);
FloatField( 2026, vFromPath2FrequCos2);
FloatField( 2027, vFromPath2FrequSin3);
ByteField(  2031, colorFromPath2Faces.bRed);
ByteField(  2032, colorFromPath2Faces.bGreen);
ByteField(  2033, colorFromPath2Faces.bBlue);
ByteField(  2034, colorFromPath2Axis.bRed);
ByteField(  2035, colorFromPath2Axis.bGreen);
ByteField(  2036, colorFromPath2Axis.bBlue);

nRep:=DialogExecMSG(13599, @Self);

if (nRep <> IDOK) then exit;

// Create the curbe in the space
nMax:=nFromPath2NbPointsTube;
for nn:=0 to nMax do begin
   vT:=2*Pi*nn/Float(nMax+1);
   avecV[nn][1]:= vFromPath2FactCos1 * cos(vT) +
                  vFromPath2FactCos2 * cos(vFromPath2FrequCos2 * vT);
   avecV[nn][2]:= vFromPath2FactCos1 * sin(vT) -
                  vFromPath2FactCos2 * sin(vFromPath2FrequCos2 * vT);
   avecV[nn][3]:= vFromPath2FactSin3 * sin(vFromPath2FrequSin3 * vT);
   end;

oBody3DCreator.Init(poDraw3D);

with oBody3DCreator do begin
   // Valuse which must be given
   pavecV        :=@avecV;
   nNbPointsPath :=nMax+1;

   // Values which can be changed
   nNbPointsPhi   :=nFromPath2NbPointsPhi;
   // Number of points horizontally and vertically.

   vRadiusAxeX :=vFromPath2RadiusAxeX; // default = 1.0
   vRadiusAxeY :=vFromPath2RadiusAxeY; // default = 1.0

   // To twist the tube.
   vTwistFactorX :=vFromPath2TwistFactorX;
   vTwistFactorY :=vFromPath2TwistFactorY;

   // These data are used in the Setup of the Body
   // If changed, must be set before calling the  Body3DCreate
   ColorBody3DSup :=colorFromPath2Faces;
   ColorBody3DAxe :=colorFromPath2Axis;
   strBody3DName  :=strFromPath2Name;

   // To indicate that the tube is closed, and both ends are connected
   // In this case, the array  avecV  is modified by adding the point :
   // avecV[nNbPointsPath]:=avecV[0]
   fClosedPath:=TRUE;
   end;  // with

if fModifyP then begin
   // Modify the Body
   poBody3D:=oBody3DCreator.Body3DModify(poDraw3D^.papoBodies^[poDraw3D^.wBodySelected]);
   end
else begin
   // Create the Body
   poBody3D:=oBody3DCreator.Body3DCreate;
   end;

// We don't need the Body creator any more
oBody3DCreator.Done;

// Here we can change some properties of the Body
poBody3D^.fUniColor:=TRUE; // Ask to have a unique color

// To indicate that this Body is  AnFromPath2  Body
poBody3D^.pUserData:=pointer(102);

poBody3D^.BodySetPosition(vecPositionGL);

// Define the viewing direction, without modifying the viewer position
// Sets again the right horizontal.
poDraw3D^.SetViewDirection(vecPositionGL);

// Draw the Body
poDraw3D^.Draw3DBodies;
end;  // TMyWindow.AnFromPath2

procedure TMyWindow.AnFromPath2Animation;
//=======================================
// Create a new body, then animate it.
var
  nRep           : integer;
  oBody3DCreator : TBody3DFromPathCreator;
  poBody3D       : PBody3D0;
  avecV          : array[0..400] of RVectR3Float;
  nMax,
  nn             : integer;
  vT             : Float;
  vAngleRepeat   : Float;
  fModifyL       : boolean;

begin
// Editable variable of the dialog box, + some globals one
FloatField( 2001, vFromPath2RadiusAxeX);
FloatField( 2002, vFromPath2RadiusAxeY);
FloatField( 2011, vFromPath2TwistFactorX);
FloatField( 2012, vFromPath2TwistFactorY);
Int16Field( 2020, nFromPath2NbPointsPhi);
StrField(   2021, strFromPath2Name, SizeOf(strFromPath2Name));
Int16Field( 2022, nFromPath2NbPointsTube);
FloatField( 2023, vFromPath2FactCos1);
FloatField( 2024, vFromPath2FactCos2);
FloatField( 2025, vFromPath2FactSin3);
FloatField( 2026, vFromPath2FrequCos2);
FloatField( 2027, vFromPath2FrequSin3);
ByteField(  2031, colorFromPath2Faces.bRed);
ByteField(  2032, colorFromPath2Faces.bGreen);
ByteField(  2033, colorFromPath2Faces.bBlue);
ByteField(  2034, colorFromPath2Axis.bRed);
ByteField(  2035, colorFromPath2Axis.bGreen);
ByteField(  2036, colorFromPath2Axis.bBlue);

nRep:=DialogExecMSG(13599, @Self);

if (nRep <> IDOK) then exit;

oBody3DCreator.Init(poDraw3D);

// The first loop, create the body, then animate it.
fModifyL:=FALSE;

vAngleRepeat:=0;
repeat
   vFromPath2FactCos2:=4*sin(vAngleRepeat);
   vAngleRepeat:=vAngleRepeat + Pi/40;

// Create the curbe in the space
nMax:=nFromPath2NbPointsTube;
for nn:=0 to nMax do begin
   vT:=2*Pi*nn/Float(nMax);
   avecV[nn][1]:= vFromPath2FactCos1 * cos(vT) +
                  vFromPath2FactCos2 * cos(vFromPath2FrequCos2 * vT);
   avecV[nn][2]:= vFromPath2FactCos1 * sin(vT) -
                  vFromPath2FactCos2 * sin(vFromPath2FrequCos2 * vT);
   avecV[nn][3]:= vFromPath2FactSin3 * sin(vFromPath2FrequSin3 * vT);
   end;


with oBody3DCreator do begin
   // Valuse which must be given
   pavecV        :=@avecV;
   nNbPointsPath :=nMax+1;

   // Values which can be changed
   nNbPointsPhi   :=nFromPath2NbPointsPhi;
   // Number of points horizontally and vertically.

   vRadiusAxeX :=vFromPath2RadiusAxeX; // default = 1.0
   vRadiusAxeY :=vFromPath2RadiusAxeY; // default = 1.0

   // To twist the tube.
   vTwistFactorX :=vFromPath2TwistFactorX;
   vTwistFactorY :=vFromPath2TwistFactorY;

   // These data are used in the Setup of the Body
   // If changed, must be set before calling the  Body3DCreate
   ColorBody3DSup :=colorFromPath2Faces;
   ColorBody3DAxe :=colorFromPath2Axis;
   strBody3DName  :=strFromPath2Name;
   end;  // with

if fModifyL then begin
   // Modify the Body
   poBody3D:=oBody3DCreator.Body3DModify(poDraw3D^.papoBodies^[poDraw3D^.wBodySelected]);
   end
else begin
   // Create the Body
   poBody3D:=oBody3DCreator.Body3DCreate;

   // Here we can change some properties of the Body
   poBody3D^.fUniColor:=TRUE; // Ask to have a unique color

   // To indicate that this Body is  AnFromPath2  Body
   poBody3D^.pUserData:=pointer(102);

   poBody3D^.BodySetPosition(vecPositionGL);

   // Define the viewing direction, without modifying the viewer position
   // Sets again the right horizontal.
   poDraw3D^.SetViewDirection(vecPositionGL);

   fModifyL:=TRUE; // next time, modify it only.
   end;

   // We don't need the Body creator any more
   oBody3DCreator.Done;

   // Draw the Body
   poDraw3D^.Draw3DBodies;

   // Pressing the escape key will quit the loop
   // Key are transmitted to the application, thus the
   // view can change while the surface is moving
   // Be carefull, that any other action is accepted, even selecting a menu
   // This can make the application crash...
   until (oApp.IsKeyPressed(1) = VK_ESCAPE);

end;  // TMyWindow.AnFromPath2Animation

procedure TMyWindow.ASurfaceSquare;
//=================================
var
//  nRep           : integer;
  oBody3DCreator : TBody3DSurfaceSquareCreator;
  poBody3D       : PBody3D0;
  nXL,nYL     : int32;
  avMat       : array[-20..20,-25..25] of Float;
begin
// Create the surface
for nXL:=-20 to 20 do begin
   for nYL:=-24 to 24 do begin
      avMat[nXL,nYL]:=sqr(nXL/15.0) - sqr(nYL/16.0);
      end;
   end;

oBody3DCreator.Init(poDraw3D);

with oBody3DCreator do begin
   // Values which must be given
  pavMat :=@avMat;

  nMatMinIndexX :=-20;
  nMatMaxIndexX := 20;
  nMatMinIndexY :=-25;
  nMatMaxIndexY := 25;

  nDefinedMinIndexX :=-20;
  nDefinedMaxIndexX := 20;
  nDefinedMinIndexY :=-24;
  nDefinedMaxIndexY := 24;

   // Values which should be given
  vSurfXOrg :=0.0;
  vSurfYOrg :=0.0;
  vSurfDX   :=0.2;
  vSurfDY   :=0.2;

   // These data are used in the Setup of the Body
   // If changed, must be set before calling the  Body3DCreate
   ColorBody3DSup :=RGB(255,255,255);
   ColorBody3DInf :=RGB(255,  0,  0);
   ColorBody3DAxe :=RGB(  0,255,  0);
   strBody3DName  :='a surface';
   end;  // with

// Create the Body
poBody3D:=oBody3DCreator.Body3DCreate;

// We don't need the Body creator any more
oBody3DCreator.Done;

// Here we can change some properties of the Body
poBody3D^.fUniColor:=FALSE; // Ask to have the top and bottom colors

poBody3D^.BodySetPosition(vecPositionGL);

// Define the viewing direction, without modifying the viewer position
// Sets again the right horizontal.
poDraw3D^.SetViewDirection(vecPositionGL);

// Draw the Body
poDraw3D^.Draw3DBodies;
end;  // TMyWindow.ASurfaceSquare

procedure TMyWindow.ASurfaceSquareBis;
//====================================
var
//  nRep           : integer;
  oBody3DCreator : TBody3DSurfaceSquareCreator;
  poBody3D       : PBody3D0;
  nXL,nYL     : int32;
  avMat       : array[-20..20,-20..20] of Float;
begin
// Create the surface
for nXL:=-20 to 20 do begin
   for nYL:=-20 to 20 do begin
      avMat[nXL,nYL]:=1.0*(sqr(nXL/20.0) + sqr(nYL/20.0)) *
                          cos(4*AngleR2(nXL,nYL));
      end;
   end;

oBody3DCreator.Init(poDraw3D);

with oBody3DCreator do begin
   // Values which must be given
  pavMat :=@avMat;

  nMatMinIndexX :=-20;
  nMatMaxIndexX := 20;
  nMatMinIndexY :=-20;
  nMatMaxIndexY := 20;

  nDefinedMinIndexX :=-20;
  nDefinedMaxIndexX := 20;
  nDefinedMinIndexY :=-20;
  nDefinedMaxIndexY := 20;

   // Values which should be given
  vSurfXOrg :=0.0;
  vSurfYOrg :=0.0;
  vSurfDX   :=0.2;
  vSurfDY   :=0.2;

   // These data are used in the Setup of the Body
   // If changed, must be set before calling the  Body3DCreate
   ColorBody3DSup :=RGB(255,255,255);
   ColorBody3DInf :=RGB(255,  0,  0);
   ColorBody3DAxe :=RGB(  0,255,  0);
   strBody3DName  :='a surface';
   end;  // with

// Create the Body
poBody3D:=oBody3DCreator.Body3DCreate;

// We don't need the Body creator any more
oBody3DCreator.Done;

// Here we can change some properties of the Body
poBody3D^.fUniColor:=FALSE; // Ask to have the top and bottom colors

poBody3D^.BodySetPosition(vecPositionGL);

// Define the viewing direction, without modifying the viewer position
// Sets again the right horizontal.
poDraw3D^.SetViewDirection(vecPositionGL);

// Draw the Body
poDraw3D^.Draw3DBodies;
end;  // TMyWindow.ASurfaceSquareBis

procedure TMyWindow.ASurfaceRound;
//================================
var
//  nRep           : integer;
  oBody3DCreator : TBody3DSurfaceRoundCreator;
  poBody3D       : PBody3D0;
  nXL,nYL     : int32;
  avMat       : array[5..40,0..32] of Float;
begin
// Create the surface
for nXL:=5 to 40 do begin
   for nYL:=0 to 32 do begin
      avMat[nXL,nYL]:=1.9*(sqrt(nXL/40.0)*(nXL/40.0)) *
                          sin(4*Pi*nYL/32.0);
      end;
   end;

oBody3DCreator.Init(poDraw3D);

with oBody3DCreator do begin
   // Values which must be given
  pavMat :=@avMat;

  nMatMinIndexX :=  5;
  nMatMaxIndexX := 40;
  nMatMinIndexY :=  0;
  nMatMaxIndexY := 32;

  nDefinedMinIndexX :=  5;
  nDefinedMaxIndexX := 40;
  nDefinedMinIndexY :=  0;
  nDefinedMaxIndexY := 32;

   // Values which should be given
  vSurfXOrg :=0.0;
  vSurfYOrg :=0.0;
  vSurfDR   :=0.2;

  vAngleMax :=Pi/2;
  vAngleMin :=0;

   // These data are used in the Setup of the Body
   // If changed, must be set before calling the  Body3DCreate
   ColorBody3DSup :=RGB(255,255,255);
   ColorBody3DInf :=RGB(255,  0,  0);
   ColorBody3DAxe :=RGB(  0,255,  0);
   strBody3DName  :='a surface Round';
   end;  // with

// Create the Body
poBody3D:=oBody3DCreator.Body3DCreate;

// We don't need the Body creator any more
oBody3DCreator.Done;

// Here we can change some properties of the Body
poBody3D^.fUniColor:=FALSE; // Ask to have the top and bottom colors

poBody3D^.BodySetPosition(vecPositionGL);

// Define the viewing direction, without modifying the viewer position
// Sets again the right horizontal.
poDraw3D^.SetViewDirection(vecPositionGL);

// Draw the Body
poDraw3D^.Draw3DBodies;
end;  // TMyWindow.ASurfaceRound

procedure TMyWindow.ButtonClicked(poCtrl:PWindowBase0; wCtrlIDP:Word32);
//======================================================================
// To choose the color of a Body
var rChooseCol  : TCHOOSECOLOR;
begin
if (wCtrlIDP < 13231) or (wCtrlIDP > 14000) then exit;

// Structure passed to the choose color dialog box
with rChooseCol do begin
   lStructSize    :=SizeOf(rChooseCol);
   hwndOwner      :=hWin;
   hInstance      :=System.MainInstance;
   if (wCtrlIDP = 13231) then rgbResult:=colorTorusFaces;
   if (wCtrlIDP = 13234) then rgbResult:=colorTorusAxis;
   if (wCtrlIDP = 13331) then rgbResult:=colorEllipseFaces;
   if (wCtrlIDP = 13334) then rgbResult:=colorEllipseAxis;
   if (wCtrlIDP = 13431) then rgbResult:=colorFromPath1Faces;
   if (wCtrlIDP = 13434) then rgbResult:=colorFromPath1Axis;
   if (wCtrlIDP = 13531) then rgbResult:=colorFromPath2Faces;
   if (wCtrlIDP = 13534) then rgbResult:=colorFromPath2Axis;
   lpCustColors   :=@awCustomColors[1];
   Flags          :=CC_RGBINIT or CC_FULLOPEN;
   lCustData      :=0;   // used if a user hook function is defined
   lpfnHook       :=NIL; // no user hook function is defined
   lpTemplateName :=NIL; // Used only if the flag CC_ENABLETEMPLATE is set.
   end; // with

// Open a dialog box, to choose the color
ChooseColor(@rChooseCol);

// Set the color to the body
if (wCtrlIDP = 13231) then
   colorTorusFaces:=rChooseCol.rgbResult;

if (wCtrlIDP = 13234) then
   colorTorusAxis:=rChooseCol.rgbResult;

// Set the color to the body
if (wCtrlIDP = 13331) then
   colorEllipseFaces:=rChooseCol.rgbResult;

if (wCtrlIDP = 13334) then
   colorEllipseAxis:=rChooseCol.rgbResult;

// Set the color to the body
if (wCtrlIDP = 13431) then
   colorFromPath1Faces:=rChooseCol.rgbResult;

if (wCtrlIDP = 13434) then
   colorFromPath1Axis:=rChooseCol.rgbResult;

// Set the color to the body
if (wCtrlIDP = 13531) then
   colorFromPath2Faces:=rChooseCol.rgbResult;

if (wCtrlIDP = 13534) then
   colorFromPath2Axis:=rChooseCol.rgbResult;

// Udate the content of the dialog box
PDialog(poCtrl^.poParent)^.UpdateFromVar;
end;  // TMyWindow.ButtonClicked

function TMyWindow.WMCommand(wPar:WPARAM; lPar:LPARAM):LRESULT;
//=============================================================
// Handles the menu messages
Var wNrMenu     : word;
begin
WMCommand := 0; // Default return value when processing a message

if (lPar <> 0) then begin
   // call the parent WMCommand method.
   WMCommand:=inherited WMCommand(wPar, lPar);
   exit;
   end;

// for the menus,  lPar = 0
//=========================
wNrMenu   := LoWord(wPar);
Case wNrMenu Of
    51 : NewSpaceWorld;
   101 : ASurface;
   102 : ATorus;
   103 : AnEllipsoide;
   104 : AnFromPath1;
   105 : AnFromPath2(FALSE);
   106 : ASurfaceSquare;
   107 : ASurfaceSquareBis;
   108 : ASurfaceRound;
   115 : AnFromPath2(TRUE);
   125 : AnFromPath2Animation;

   // Indicate just to use the right click for more help and a menu
   301 : DialogExecMSG(12199, @Self);

   else WMCommand:=inherited WMCommand(wPar, lPar);
   end;  // Case NrMenu Of
end;  // TMyWindow.WMCommand

function TMyWindow.WindowProc(hWindow:HWND; wMsg:UINT; wPar:WPARAM; lPar:LPARAM):LRESULT;
//======================================================================================
begin

if (wMsg = WM_SETFOCUS) then begin
   if (poDraw3D^.hWin > 0) then  SetFocus(poDraw3D^.hWin);
   WindowProc:=0;
   exit;
   end;

// Default treatment of a message. Does nearly nothing
WindowProc := inherited WindowProc(hWindow, wMsg, wPar, lPar);
end;  // TMyWindow.WindowProc

Begin // MAIN ******
// Name under which the main window is registered. Spaces are not allowed
oApp.Init('Example058');

// Creation of the main window
new(poMyMainWin, Init('tEx058. Testing the 3D Body creator (wDraw3DFrame2.pp)'));

// The App.WindowClass can be modified here, if wanted. (Not usual)
oApp.Run;
oApp.Done;  // The main window object is disposed here.
End.

