unit wDraw3DFrame2;

{ Feel free to use this unit as you want.
  First developped by Bernard Gisin
  This unit uses object programming to simplify the developpment
  of programs.

  This unit helps creating bodies (=objects), defined in the space, such as
  cubes, torres, spheres, and more complicated ones.
----------------------------------}

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

INTERFACE
//"""""""

uses wDraw3DFrame0, xVectR3, wGlobals2;

type
  PBody3DCreator0 = ^TBody3DCreator0;
  TBody3DCreator0 = object
  //""""""""""""""""""""""
  poDraw3D : PDraw3DFrame0;
  // Pointer on the main 3D-drawing object.

  // These data are used in the Setup of the Body
  ColorBody3DSup, // Color of the superior face of the Body
  ColorBody3DInf, // Color of the inferior face of the Body
  ColorBody3DAxe  // Color of the axis
                  : TypeColor;

  strBody3DName : string[100];
  // The body can have a name, to reconise it.

  constructor Init(poDrawP:PDraw3DFrame0);
  destructor  Done; virtual;

  function Body3DCreate:PBody3D0; virtual;

  end; // TBody3DCreator0 = object
  TBody3DCreator = TBody3DCreator0;
  PBody3DCreator = ^TBody3DCreator;
///////////////////////////////////
  PBody3DTorusCreator = ^TBody3DTorusCreator;
  TBody3DTorusCreator = object(TBody3DCreator0)
  //"""""""""""""""""""""""""""""""""""""""""""
  // Units are in centimeters

  nNbPointsTheta, nNbPointsPhi : int32;
  // Number of points horizontally and vertically.

  vRadiusCenter : Float;
  // Radius from the center of the Torus to the center of the
  // tube of the Torus.

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

  nNbTwistTheta, nNbTwistPhi : integer;
  // To twist the Torus. =0 by default
  // nNbTwistTheta should be a multiple of  nNbPointsPhi
  // nNbTwistPhi   To make spirals.
  // nNbTwistTheta To twist the Torus allong the tube.

  constructor Init(poDrawP:PDraw3DFrame0);

  function Body3DCreate:PBody3D0; virtual;
  // Create a Torus

  end; // TBody3DTorusCreator = object(TBody3DCreator0)
///////////////////////////////////
  PBody3DEllipsoideCreator = ^TBody3DEllipsoideCreator;
  TBody3DEllipsoideCreator = object(TBody3DCreator0)
  //""""""""""""""""""""""""""""""""""""""""""""""""
  // Units are in centimeters

  nNbPointsTheta, nNbPointsPhi : int32;
  // Number of points horizontally and vertically.

  vRadiusAxeX,vRadiusAxeY,vRadiusAxeZ : Float;
  // Three axis length in cm

  nNbTwistTheta, nNbTwistPhi : integer;
  // To twist the Torus. =0 by default
  // nNbTwistTheta should be a multiple of  nNbPointsPhi
  // nNbTwistPhi   To make spirals.
  // nNbTwistTheta To twist the Torus allong the tube.

  constructor Init(poDrawP:PDraw3DFrame0);

  function Body3DCreate:PBody3D0; virtual;
  // Create an Ellipsoide

  end; // TBody3DEllipsoideCreator = object(TBody3DCreator0)
///////////////////////////////////
  PBody3DFromPathCreator = ^TBody3DFromPathCreator;
  TBody3DFromPathCreator = object(TBody3DCreator0)
  //""""""""""""""""""""""""""""""""""""""""""""""""
  // Create a Body from a Path given in the space
  // Units are in centimeters

  pavecV : PArrayRVectR3Float;
  // Determine the path in the space.

  nNbPointsPath : int32;
  // Number of points in the path.

  nNbPointsPhi : int32;
  // Number of points on the ellipses orthogonal to the path.

  vRadiusAxeX,vRadiusAxeY : Float;
  // Axis length in cm of the ellipses

  vTwistFactorX, // To twist the tube
  vTwistFactorY  // The twists can be different, giving stange effects.
                 : Float;
  // Number between 0 and 1, if bigger, only the fractional part will have an effect

  fClosedPath : boolean;
  // If set to TRUE, the path is closed, and the last point is
  // connected to the first point

  constructor Init(poDrawP:PDraw3DFrame0);

  function Body3DModify(poBodyP:PBody3D0):PBody3D0; virtual;
  // If  PBody3D0=NIL  then create a new Body and return a pointer on it.
  // Else Modify the Body.
  // Suppose that the Body correspond to a  TBody3DFromPathCreator  object
  // Return a pointer on the Body (modified or created).

  function Body3DCreate:PBody3D0; virtual;
  // Create a Body from a Path given in the space

  end; // TBody3DFromPathCreator = object(TBody3DCreator0)
///////////////////////////////////
  PBody3DSurfaceSquareCreator = ^TBody3DSurfaceSquareCreator;
  TBody3DSurfaceSquareCreator = object(TBody3DCreator0)
  //"""""""""""""""""""""""""""""""""""""""""""""""""""
  // Units are in centimeters
  // From a matrix designing the height, define a surface

  pavMat : PTypeArrayFloat;
  // Matrix

  nMatMinIndexX,
  nMatMaxIndexX,
  nMatMinIndexY,
  nMatMaxIndexY : int32;
  // Tells which are the real indexes and dimensions of the matrix.
  // This means that the matrix has been defined as an
  // array[nMatMinIndexX..nMatMaxIndexX, nMatMinIndexY..nMatMaxIndexY] of Float;

  nDefinedMinIndexX,
  nDefinedMaxIndexX,
  nDefinedMinIndexY,
  nDefinedMaxIndexY : int32;
  // Tells which indexes have been assigned to the matrix.
  // if  Mat  is the matrix defined with the above type, then
  // Mat[nx,ny] has been defined for
  // nx in [nDefinedMinIndexX..nDefinedMaxIndexX]
  // ny in [nDefinedMinIndexY..nDefinedMaxIndexY]
  // For other values of nx and ny, the matrix could not have been assigned values.

  vSurfXOrg, vSurfYOrg, // (X, Y) position of the point  Mat[0,0]  in [cm]
  vSurfDX, vSurfDY : Float;
  // Sizes in centimeters of the grid, i.e. distance between
  // two points of the matrix, in the X direction and Y direction.

  constructor Init(poDrawP:PDraw3DFrame0);

  function Body3DCreate:PBody3D0; virtual;
  // Create The surface from the data in the matrix

  end; // TBody3DSurfaceSquareCreator = object(TBody3DCreator0)
///////////////////////////////////////////////////////
  PBody3DSurfaceRoundCreator = ^TBody3DSurfaceRoundCreator;
  TBody3DSurfaceRoundCreator = object(TBody3DCreator0)
  //""""""""""""""""""""""""""""""""""""""""""""""""""
  // Units are in centimeters
  // From a matrix designing the radius and the angle, define a surface

  pavMat : PTypeArrayFloat;
  // Matrix. X index = radius, Y index = angle

  nMatMinIndexX,
  nMatMaxIndexX,
  nMatMinIndexY,
  nMatMaxIndexY : int32;
  // Tells which are the real indexes and dimensions of the matrix.
  // This means that the matrix has been defined as an
  // array[nMatMinIndexX..nMatMaxIndexX, nMatMinIndexY..nMatMaxIndexY] of Float;

  nDefinedMinIndexX,
  nDefinedMaxIndexX,
  nDefinedMinIndexY,
  nDefinedMaxIndexY : int32;
  // Tells which indexes have been assigned to the matrix.
  // if  Mat  is the matrix defined with the above type, then
  // Mat[nx,ny] has been defined for
  // nx in [nDefinedMinIndexX..nDefinedMaxIndexX]
  // ny in [nDefinedMinIndexY..nDefinedMaxIndexY]
  // For other values of nx and ny, the matrix could not have been assigned values.

  vAngleMin, vAngleMax : Float;
  // The Y index varies corresponding to :
  // Y=nDefinedMinIndexY  =>  angle = vAngleMin  in radian
  // Y=nDefinedMaxIndexY  =>  angle = vAngleMax  in radian
  // The default values are 0 and 2*Pi

  vSurfXOrg, vSurfYOrg, // (X, Y) position of the point  Mat[0,0]  in [cm]
  vSurfDR : Float;
  // Sizes in centimeters of the increment in the radius

  constructor Init(poDrawP:PDraw3DFrame0);

  function Body3DCreate:PBody3D0; virtual;
  // Create The surface from the data in the matrix

  end; // TBody3DSurfaceRoundCreator = object(TBody3DCreator0)
///////////////////////////////////////////////////////

IMPLEMENTATION
//""""""""""""

uses
   wObjectCreatorLOW2,
// wUtiles0,
   wDebug0;

var glnNotUsed : integer; // To avoid an error in the DevPascal 1.9 environment

//***************************************************
// TBody3DCreator0 = object
//***************************************************

constructor TBody3DCreator0.Init(poDrawP:PDraw3DFrame0);
//======================================================
begin
poDraw3D:=poDrawP;
                     // Blue     Green        Red
ColorBody3DSup.wColor :=(255*256 + 255)*256 + 255;
ColorBody3DInf.wColor :=(192*256 + 192)*256 + 196;
ColorBody3DAxe.wColor :=(  0*256 + 192)*256 +   0;

strBody3DName  :='New Body';
end;  // TBody3DCreator0.Init

destructor TBody3DCreator0.Done;
//==============================
begin
end;  // TBody3DCreator0.Done

function TBody3DCreator0.Body3DCreate:PBody3D0;
//=============================================
begin
// Do nothing, to be overriden.
Body3DCreate:=NIL;
end;  // TBody3DCreator0.Body3DCreate;

//***************************************************
// TBody3DTorusCreator = object(TBody3DCreator0)
//***************************************************

constructor TBody3DTorusCreator.Init(poDrawP:PDraw3DFrame0);
//==========================================================
begin
inherited Init(poDrawP);

nNbPointsTheta :=32;
nNbPointsPhi   :=32;

vRadiusCenter :=4.0; // [cm]
vRadiusDisk   :=1.0; // [cm];
nNbTwistTheta :=0;
nNbTwistPhi   :=0;
end;  // TBody3DTorusCreator.Init

function TBody3DTorusCreator.Body3DCreate:PBody3D0;
//=================================================
var
  nDPhi,
  nTheta,nPhi      : int32;
  nPhi0,nTheta0    : int32;
  nPhi1,nTheta1    : int32;
  nTheta10         : int32; // = (nTheta + 1) mod nNbPointsTheta
  vPhi,vTheta      : Float;
  parPtsL          : PArrayPoint3D;

begin
nDPhi:=nNbPointsPhi;

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

   // Creates a new body
   inc(wNumBodies);
   papoBodies^[wNumBodies]:=PBody3D0(gglpoObjCreate^.CreateBody3D(poDraw3D));

   // Add the points. Units = cm
   // Twisting is quite complicate...
   for nTheta:=0 to nNbPointsTheta-1 do
   for nPhi:=0 to nNbPointsPhi-1 do begin
      vPhi:=( nPhi *
              (1 + (nNbTwistPhi*nNbTwistTheta/Float(nNbPointsTheta))) +
             nNbTwistTheta * nTheta * Float(nNbPointsPhi) / Float(nNbPointsTheta)
            ) * 2*Pi / Float(nNbPointsPhi);
      vTheta :=2*Pi*( nTheta +
                      nNbTwistPhi   * nPhi   / Float(nNbPointsPhi)) /
               Float(nNbPointsTheta);

      // Add one point
      with parPtsL^[nTheta*nDPhi + nPhi] do begin
         vecV[1]:=vRadiusCenter +  vRadiusDisk * Cos(vPhi);
         vecV[2]:=0.0;
         vecV[3]:=                 vRadiusDisk * Sin(vPhi);
         vecV:=V3RotationOrg(vecV, gglvecV3AxisZ, vTheta );
         end; // with
      end;

   wNumPoints:=wNumPoints + word32((nNbPointsTheta-1)*nDPhi + nNbPointsPhi);

   // Add the faces
   for nTheta :=0 to nNbPointsTheta-1 do
   for nPhi   :=0 to nNbPointsPhi-1 do begin
      nPhi0    :=nPhi;    nPhi1   :=nPhi+1;
      nTheta0  :=nTheta;  nTheta1 :=nTheta+1;
      nTheta10 :=nTheta1 mod nNbPointsTheta;

      // To have nPhi1 in [0..nNbPointsPhi-1]
      if (nPhi1 >= nNbPointsPhi) then begin
         nPhi1   :=nPhi1   - nNbPointsPhi;
         nTheta0 :=nTheta  + nNbTwistPhi;
         nTheta1 :=nTheta0 + 1;
         end;

      // To have nTheta1 in [0..nNbPointsTheta-1]
      while (nTheta1 < 0) do nTheta1:=nTheta1 + nNbPointsTheta;
      nTheta1:=nTheta1 mod nNbPointsTheta;

      // To have nTheta0 in [0..nNbPointsTheta-1]
      while (nTheta0 < 0) do nTheta0:=nTheta0 + nNbPointsTheta;
      nTheta0:=nTheta0 mod nNbPointsTheta;

      inc(wNumFaces);
      with parFaces^[wNumFaces] do begin
         nNbPoint:=4;
         aprPoint[1]:=@parPtsL^[nTheta   * nDPhi + nPhi0];
         aprPoint[2]:=@parPtsL^[nTheta10 * nDPhi + nPhi0];
         aprPoint[3]:=@parPtsL^[nTheta1  * nDPhi + nPhi1];
         aprPoint[4]:=@parPtsL^[nTheta0  * nDPhi + nPhi1];
         end; // with
      end; // for

   // Set the minimum data which should be set before the  BodySetup
   with papoBodies^[wNumBodies]^ do begin
      ColorSup    :=ColorBody3DSup;
      ColorInf    :=ColorBody3DInf;
      ColorAx     :=ColorBody3DAxe;
      strBodyName :=strBody3DName;
      end; // with

   // Final Body Setup
   papoBodies^[wNumBodies]^.BodySetup(0,0,0);

   Body3DCreate:=papoBodies^[wNumBodies];
   end; // with poDraw3D^ do begin
end; // TBody3DTorusCreator.Body3DCreate

//***************************************************
// TBody3DEllipsoideCreator = object(TBody3DCreator0)
//***************************************************

constructor TBody3DEllipsoideCreator.Init(poDrawP:PDraw3DFrame0);
//==========================================================
begin
inherited Init(poDrawP);

nNbPointsTheta :=32;
nNbPointsPhi   :=32;

vRadiusAxeX :=1.0;
vRadiusAxeY :=2.0;
vRadiusAxeZ :=4.0;

nNbTwistTheta :=0;
nNbTwistPhi   :=0;
end;  // TBody3DEllipsoideCreator.Init

function TBody3DEllipsoideCreator.Body3DCreate:PBody3D0;
//======================================================
var
  nDPhi,
  nTheta,nPhi      : int32;
  nPhi0,nTheta0    : int32;
  nPhi1,nTheta1    : int32;
  nTheta10         : int32; // = (nTheta + 1) mod nNbPointsTheta
  vPhi,vTheta      : Float;
  parPtsL          : PArrayPoint3D;

begin
nDPhi:=nNbPointsPhi+1;

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

   // Creates a new body
   inc(wNumBodies);
   papoBodies^[wNumBodies]:=PBody3D0(gglpoObjCreate^.CreateBody3D(poDraw3D));

   // Add the points. Units = cm
   // Twisting is quite complicate...
   for nTheta:=0 to nNbPointsTheta do
   for nPhi:=0 to nNbPointsPhi do begin
      vTheta :=2*Pi*nTheta / Float(nNbPointsTheta); // in [0..2*Pi]
      vPhi   :=2*Pi*nPhi   / Float(nNbPointsPhi); // in [0..2*Pi]
      // Add one point
      with parPtsL^[nTheta*nDPhi + nPhi] do begin
         vecV[1]:=vRadiusAxeX * Cos(vPhi) * Cos(vTheta);
         vecV[2]:=vRadiusAxeY * Sin(vPhi) * Cos(vTheta);
         vecV[3]:=vRadiusAxeZ *             Sin(vTheta);
         end; // with
      end;

   wNumPoints:=wNumPoints + word32((nNbPointsTheta)*nDPhi + nNbPointsPhi+1);

   // Add the faces
   for nTheta :=0 to nNbPointsTheta-1 do
   for nPhi   :=0 to nNbPointsPhi-1 do begin
      nPhi0    :=nPhi;    nPhi1   :=nPhi+1;
      nTheta0  :=nTheta;  nTheta1 :=nTheta+1;
      nTheta10 :=nTheta1 mod nNbPointsTheta;
{
      // To have nPhi1 in [0..nNbPointsPhi-1]
      if (nPhi1 >= nNbPointsPhi) then begin
         nPhi1   :=nPhi1   - nNbPointsPhi;
         nTheta0 :=nTheta  + nNbTwistPhi;
         nTheta1 :=nTheta0 + 1;
         end;

      // To have nTheta1 in [0..nNbPointsTheta-1]
      while (nTheta1 < 0) do nTheta1:=nTheta1 + nNbPointsTheta;
      nTheta1:=nTheta1 mod nNbPointsTheta;

      // To have nTheta0 in [0..nNbPointsTheta-1]
      while (nTheta0 < 0) do nTheta0:=nTheta0 + nNbPointsTheta;
      nTheta0:=nTheta0 mod nNbPointsTheta;
}
      inc(wNumFaces);
      with parFaces^[wNumFaces] do begin
         nNbPoint:=4;
         aprPoint[1]:=@parPtsL^[nTheta   * nDPhi + nPhi0];
         aprPoint[2]:=@parPtsL^[nTheta10 * nDPhi + nPhi0];
         aprPoint[3]:=@parPtsL^[nTheta1  * nDPhi + nPhi1];
         aprPoint[4]:=@parPtsL^[nTheta0  * nDPhi + nPhi1];
         end; // with
      end; // for

   // Set the minimum data which should be set before the  BodySetup
   with papoBodies^[wNumBodies]^ do begin
      ColorSup    :=ColorBody3DSup;
      ColorInf    :=ColorBody3DInf;
      ColorAx     :=ColorBody3DAxe;
      strBodyName :=strBody3DName;
      end; // with

   // Final Body Setup
   papoBodies^[wNumBodies]^.BodySetup(0,0,0);

   Body3DCreate:=papoBodies^[wNumBodies];
   end; // with poDraw3D^ do begin
end; // TBody3DEllipsoideCreator.Body3DCreate

//***************************************************
// TBody3DFromPathCreator = object(TBody3DCreator0)
//***************************************************

constructor TBody3DFromPathCreator.Init(poDrawP:PDraw3DFrame0);
//=============================================================
begin
inherited Init(poDrawP);

pavecV :=NIL;

nNbPointsPath :=0;

nNbPointsPhi :=16;

vRadiusAxeX :=0.5;
vRadiusAxeY :=0.5;

vTwistFactorX :=0.0;
vTwistFactorY :=0.0;

fClosedPath:=FALSE;
end;  // TBody3DFromPathCreator.Init

function TBody3DFromPathCreator.Body3DModify(poBodyP:PBody3D0):PBody3D0;
//======================================================================
// If  PBody3D0=NIL  then create a new Body and return a pointer on it.
// Else Modify the Body.
// Suppose that the Body correspond to a  TBody3DFromPathCreator  object
// Return a pointer on the Body (modified or created).
var
  nDPhi,
  nPhi,
  nPhi1,
  nPhi2,
  nPhi3,
  nNbPointsPathLoc, // Number of points in the path (+1 if the path is closed)
  nPhiStart   : int32; // for closed path
  vDist,
  vDistMin,
  vPhi        : Float;
  parPtsL     : PArrayPoint3D;
  vecW,       // Vector paralelle to the path
  vecW1,      // for computation of the first and last circle of the tube
  vecE10,     // Vectors orthogonal to the path
  vecE11,
  vecE20,
  vecE21      : RVectR3Float;
  nn          : integer;

begin
if (pavecV = NIL) or (nNbPointsPath = 0) or (nNbPointsPhi = 0) then begin
   Body3DModify:=NIL;
   exit;
   end;

nDPhi:=nNbPointsPhi;

with poDraw3D^ do begin
   if (poBodyP = NIL) then begin
      // Creates a new body
      inc(wNumBodies);
      papoBodies^[wNumBodies]:=PBody3D0(gglpoObjCreate^.CreateBody3D(poDraw3D));
      poBodyP:=papoBodies^[wNumBodies];
      end
   else begin
      // Test if the size in memory changed. If yes, must destroy this body
      // and create a new one
      // Must reset the number of points and number of Faces.
      wNumPoints :=poBodyP^.wIndPtsMin-1;
      wNumFaces  :=poBodyP^.wIndFaceMin-1;
      end;

   // Pointer on the starting of the points of the Body
   parPtsL:=@parPoints^[wNumPoints+1];

   // Initialisation
   vecE20:=gglvecV3AxisZ;
   vecE10:=gglvecV3AxisX;

   if fClosedPath then begin
      // The points computed for  nn=nNbPointsPath  are only  temporarly used
      pavecV^[nNbPointsPath]:=pavecV^[0];
      nNbPointsPathLoc:=nNbPointsPath;
      end
   else nNbPointsPathLoc:=nNbPointsPath-1;

   // Add the points. Units = cm
   for nn:=0 to nNbPointsPathLoc do begin
      // Compute an orthogonal basis allong the path, having one
      // vector paralelle to the bath.
      if (nn = 0) then begin
         if fClosedPath then vecW:=pavecV^[1] - pavecV^[nNbPointsPath-1]
         else begin
            vecW :=pavecV^[1] - pavecV^[0];
            vecW1:=pavecV^[2] - pavecV^[0];
            if (Norme2(vecW) <> 0) then begin // normal case
               vecW:=2*((vecW*vecW1)/Norme2(vecW))*vecW - vecW1;
               end;
            end;
         end
      else if (nn = nNbPointsPathLoc) then begin
         if fClosedPath then vecW:=pavecV^[1] - pavecV^[nNbPointsPathLoc-1]
         else begin
            vecW :=pavecV^[nn] - pavecV^[nn-1];
            vecW1:=pavecV^[nn] - pavecV^[nn-2];
            if (Norme2(vecW) <> 0) then begin // normal case
               vecW:=2*((vecW*vecW1)/Norme2(vecW))*vecW - vecW1;
               end;
            end;
         end
      else vecW:=pavecV^[nn+1] - pavecV^[nn-1];

      // First basis vector, normal to the path direction and the last
      // second normal vector
      vecE11:=vecW ** vecE20;  // cross product
      if (vecE11 = gglvecV3Zero) then vecE11:=vecE10;
      V3Normalise(vecE11);

      vecE21:=vecE11 ** vecW;
      V3Normalise(vecE21);

      // Remember these vectors for next loop
      vecE10:=vecE11;
      vecE20:=vecE21;

      // Points normal to the path at the point pavecV^[nn]
      for nPhi:=0 to nNbPointsPhi-1 do begin
         vPhi   :=2*Pi*nPhi   / Float(nNbPointsPhi); // in [0..2*Pi]
         // Add one point
         with parPtsL^[nn*nDPhi + nPhi] do begin
            vecV:=pavecV^[nn] + vRadiusAxeX * Cos(vPhi + nn*2*Pi*vTwistFactorX) * vecE11
                              + vRadiusAxeY * Sin(vPhi + nn*2*Pi*vTwistFactorY) * vecE21;
            end; // with
         end; // for nPhi:=0 to nNbPointsPhi-1 do begin
      end; // for nn:=0 to nNbPointsPathLoc do begin

   // Do not use the last points, if the path is closed, i.e.
   // use  nNbPointsPath  and not  nNbPointsPathLoc
   wNumPoints:=wNumPoints + word32(nNbPointsPath*nDPhi);

   // Add the faces
   for nn:=0 to nNbPointsPath-2 do
   for nPhi   :=0 to nNbPointsPhi-1 do begin
      // To have nPhi1 in [0..nNbPointsPhi-1]
      if (nPhi = nNbPointsPhi-1) then nPhi1 :=0
      else                            nPhi1 :=nPhi+1;

      inc(wNumFaces);
      with parFaces^[wNumFaces] do begin
         nNbPoint:=4;
         aprPoint[1]:=@parPtsL^[ nn    * nDPhi + nPhi];
         aprPoint[2]:=@parPtsL^[(nn+1) * nDPhi + nPhi];
         aprPoint[3]:=@parPtsL^[(nn+1) * nDPhi + nPhi1];
         aprPoint[4]:=@parPtsL^[ nn    * nDPhi + nPhi1];
         end; // with
      end; // for

   // If the path is closed, add the last segment
   if fClosedPath then begin
      // One difficulty is to avoid a twist at the last juction

      // Look for the best starting point for the last junction
      // Here we use computed points,
      nPhiStart:=0;
      vDistMin:=1.0e10; // big
      for nPhi :=0 to nNbPointsPhi-1 do begin
         vDist:= Norme2(parPtsL^[nNbPointsPath * nDPhi].vecV -
                        parPtsL^[                 nPhi].vecV);
         if (vDistMin > vDist) then begin
            vDistMin := vDist;
            nPhiStart:=nPhi;
            end;
         end; // for

      for nPhi :=0 to nNbPointsPhi-1 do begin
         // To have nPhi1 and nPhi2 in [0..nNbPointsPhi-1]
         nPhi1:=(nPhiStart + nPhi) mod nNbPointsPhi;

         if (nPhi1 = nNbPointsPhi-1) then nPhi2 :=0
         else                             nPhi2 :=nPhi1+1;

         if (nPhi = nNbPointsPhi-1) then nPhi3 :=0
         else                            nPhi3 :=nPhi+1;

         inc(wNumFaces);
         with parFaces^[wNumFaces] do begin
            nNbPoint:=4;
            aprPoint[1]:=@parPtsL^[(nNbPointsPath-1) * nDPhi + nPhi];
            aprPoint[2]:=@parPtsL^[(              0) * nDPhi + nPhi1];
            aprPoint[3]:=@parPtsL^[(              0) * nDPhi + nPhi2];
            aprPoint[4]:=@parPtsL^[(nNbPointsPath-1) * nDPhi + nPhi3];
            end; // with
         end; // for
      end; // if fClosedPath then

   // Set the minimum data which should be set before the  BodySetup
   with poBodyP^ do begin
      ColorSup    :=ColorBody3DSup;
      ColorInf    :=ColorBody3DInf;
      ColorAx     :=ColorBody3DAxe;
      strBodyName :=strBody3DName;
      end; // with

   // Final Body Setup
   poBodyP^.BodySetup(0,0,0);

   Body3DModify:=poBodyP;
   end; // with poDraw3D^ do begin
end; // TBody3DFromPathCreator.Body3DModify

function TBody3DFromPathCreator.Body3DCreate:PBody3D0;
//====================================================
begin
Body3DCreate:=Body3DModify(NIL);
end; // TBody3DFromPathCreator.Body3DCreate

//***************************************************
// TBody3DSurfaceSquareCreator = object(TBody3DCreator0)
//***************************************************

constructor TBody3DSurfaceSquareCreator.Init(poDrawP:PDraw3DFrame0);
//==================================================================
begin
inherited Init(poDrawP);

pavMat :=NIL;

nMatMinIndexX     :=0;
nMatMaxIndexX     :=0;
nMatMinIndexY     :=0;
nMatMaxIndexY     :=0;

nDefinedMinIndexX :=0;
nDefinedMaxIndexX :=0;
nDefinedMinIndexY :=0;
nDefinedMaxIndexY :=0;

vSurfXOrg :=0.0;
vSurfYOrg :=0.0;
vSurfDX   :=0.2;
vSurfDY   :=0.2;
end;  // TBody3DSurfaceSquareCreator.Init

function TBody3DSurfaceSquareCreator.Body3DCreate:PBody3D0;
//=========================================================
var
  nDimYL,  // Dimension of the matrix in the second index
  nDYL,  // number of points in the Y direction
  nDXL,  // number of points in the X direction
  nXL,
  nYL      : int32;
  vXL,vYL  : Float;
  parPtsL          : PArrayPoint3D;

begin
nDimYL :=nMatMaxIndexY - nMatMinIndexY + 1;
nDYL   :=nDefinedMaxIndexY - nDefinedMinIndexY + 1;
nDXL   :=nDefinedMaxIndexX - nDefinedMinIndexX + 1;

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

   // Creates a new body
   inc(wNumBodies);
   papoBodies^[wNumBodies]:=PBody3D0(gglpoObjCreate^.CreateBody3D(poDraw3D));

   // Add the points. Units = cm
   for nXL:=nDefinedMinIndexX to nDefinedMaxIndexX do
   for nYL:=nDefinedMinIndexY to nDefinedMaxIndexY do begin
      vXL :=vSurfXOrg + nXL * vSurfDX;
      vYL :=vSurfYOrg + nYL * vSurfDY;
      // Add one point
      with parPtsL^[(nXL-nDefinedMinIndexX)*nDYL + nYL-nDefinedMinIndexY] do begin
         vecV[1]:=vXL;
         vecV[2]:=vYL;
         vecV[3]:=pavMat^[(nXL-nMatMinIndexX)*nDimYL + nYL-nMatMinIndexY];
         end; // with
      end;

   wNumPoints:=wNumPoints + word32(nDYL*nDXL);

   // Add the faces
   for nXL:=0 to nDefinedMaxIndexX-nDefinedMinIndexX-1 do
   for nYL:=0 to nDefinedMaxIndexY-nDefinedMinIndexY-1 do begin
      inc(wNumFaces);
      with parFaces^[wNumFaces] do begin
         nNbPoint:=4;
         aprPoint[1]:=@parPtsL^[(nXL  )*nDYL + nYL];
         aprPoint[2]:=@parPtsL^[(nXL+1)*nDYL + nYL];
         aprPoint[3]:=@parPtsL^[(nXL+1)*nDYL + nYL+1];
         aprPoint[4]:=@parPtsL^[(nXL  )*nDYL + nYL+1];
         end; // with
      end; // for

   // Set the minimum data which should be set before the  BodySetup
   with papoBodies^[wNumBodies]^ do begin
      ColorSup    :=ColorBody3DSup;
      ColorInf    :=ColorBody3DInf;
      ColorAx     :=ColorBody3DAxe;
      strBodyName :=strBody3DName;
      end; // with

   // Final Body Setup
   papoBodies^[wNumBodies]^.BodySetup(0,0,0);

   Body3DCreate:=papoBodies^[wNumBodies];
   end; // with poDraw3D^ do begin
end; // TBody3DSurfaceSquareCreator.Body3DCreate

//***************************************************
// TBody3DSurfaceRoundCreator = object(TBody3DCreator0)
//***************************************************

constructor TBody3DSurfaceRoundCreator.Init(poDrawP:PDraw3DFrame0);
//==================================================================
begin
inherited Init(poDrawP);

pavMat :=NIL;

nMatMinIndexX     :=0;
nMatMaxIndexX     :=0;
nMatMinIndexY     :=0;
nMatMaxIndexY     :=0;

nDefinedMinIndexX :=0;
nDefinedMaxIndexX :=0;
nDefinedMinIndexY :=0;
nDefinedMaxIndexY :=0;

vAngleMin :=2*Pi;
vAngleMax :=2*Pi;

vSurfXOrg :=0.0;
vSurfYOrg :=0.0;
vSurfDR   :=0.2;
end;  // TBody3DSurfaceRoundCreator.Init

function TBody3DSurfaceRoundCreator.Body3DCreate:PBody3D0;
//=========================================================
var
  nDimYL,  // Dimension of the matrix in the second index
  nDYL,  // number of points in the Y direction
  nDXL,  // number of points in the X direction
  nXL,
  nYL      : int32;
  vAngleDeltaLoc,
  vAngleMinLoc,
  vRL,vPhiL  : Float;
  parPtsL          : PArrayPoint3D;

begin
nDimYL :=nMatMaxIndexY - nMatMinIndexY + 1;
nDYL   :=nDefinedMaxIndexY - nDefinedMinIndexY + 1;
nDXL   :=nDefinedMaxIndexX - nDefinedMinIndexX + 1;

vAngleDeltaLoc:=(vAngleMax - vAngleMin) / (nDefinedMaxIndexY - nDefinedMinIndexY);
vAngleMinLoc  :=vAngleMin - vAngleDeltaLoc * nDefinedMinIndexY;

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

   // Creates a new body
   inc(wNumBodies);
   papoBodies^[wNumBodies]:=PBody3D0(gglpoObjCreate^.CreateBody3D(poDraw3D));

   // Add the points. Units = cm
   for nXL:=nDefinedMinIndexX to nDefinedMaxIndexX do
   for nYL:=nDefinedMinIndexY to nDefinedMaxIndexY do begin
      vRL   :=nXL * vSurfDR;
      vPhiL :=vAngleMinLoc + vAngleDeltaLoc * nYL;
      // Add one point
      with parPtsL^[(nXL-nDefinedMinIndexX)*nDYL + nYL-nDefinedMinIndexY] do begin
         vecV[1]:=vSurfXOrg + vRL * cos(vPhiL);
         vecV[2]:=vSurfYOrg + vRL * sin(vPhiL);
         vecV[3]:=pavMat^[(nXL-nMatMinIndexX)*nDimYL + nYL-nMatMinIndexY];
         end; // with
      end;

   wNumPoints:=wNumPoints + word32(nDYL*nDXL);

   // Add the faces
   for nXL:=0 to nDefinedMaxIndexX-nDefinedMinIndexX-1 do
   for nYL:=0 to nDefinedMaxIndexY-nDefinedMinIndexY-1 do begin
      inc(wNumFaces);
      with parFaces^[wNumFaces] do begin
         nNbPoint:=4;
         aprPoint[1]:=@parPtsL^[(nXL  )*nDYL + nYL];
         aprPoint[2]:=@parPtsL^[(nXL+1)*nDYL + nYL];
         aprPoint[3]:=@parPtsL^[(nXL+1)*nDYL + nYL+1];
         aprPoint[4]:=@parPtsL^[(nXL  )*nDYL + nYL+1];
         end; // with
      end; // for

   // Set the minimum data which should be set before the  BodySetup
   with papoBodies^[wNumBodies]^ do begin
      ColorSup    :=ColorBody3DSup;
      ColorInf    :=ColorBody3DInf;
      ColorAx     :=ColorBody3DAxe;
      strBodyName :=strBody3DName;
      end; // with

   // Final Body Setup
   papoBodies^[wNumBodies]^.BodySetup(0,0,0);

   Body3DCreate:=papoBodies^[wNumBodies];
   end; // with poDraw3D^ do begin
end; // TBody3DSurfaceRoundCreator.Body3DCreate

end.  // unit wDraw3DFrame2;

