unit CadreVisualisateur3DGDI;
// Visualisateur 3D GDI générique
// devrait à terme remplacer le visualisateur 3D GDI de GHTopo
{$INCLUDE CompilationParameters.inc}


interface
uses
  {$INCLUDE SelectionLangues.inc} // insère les unités en fonction de la langue
  StructuresDonnees, GHTopoGeneralFunctions, unitDatesUtils, unitGHTopoViseesUtils, unitGHTopoStringUtils,  unitColorsUtils, unitSolveEquationsPolynomes,  Classes, SysUtils, Math, // anciennement Include
  LCLIntf, LCLType,
  UnitTParametrageVue3D,
  ToporobotClasses2012,
  UnitEntitesExtended,
  UnitListesSimplesWithGeneriques,
  UnitTTubeGalerie,
  UnitClasseMaillage,
  unitProfilTopo,
  UnitTGradientAltimetrique,
  CallDialogsStdVersion,
  unitUtilsComposants,
  Clipbrd,
  Forms, Controls, ExtCtrls, Graphics, ActnList, Buttons, ComCtrls, StdCtrls, Dialogs, PairSplitter,
  BGRABitmap, BGRAClasses, curredit,
  SVGCanvasUnit, Types;

type TProc3DTo2DCoordinates = function (const VueWidth, VueHeight: integer; const QX, QY: double): TPoint of object;
type TProcSetParamsTransformations = procedure (const VueWidth, VueHeight: integer; const QTheta, QPhi, QZoom, QMagnification: double) of object;
type TProcCalcOrigineEtGrandeDiagonale = procedure () of object;
type

  { TCdrVisualisateur3DGDI }

  TCdrVisualisateur3DGDI = class(TFrame)
    acProjISO: TAction;
    acProjXY: TAction;
    acProjYZ: TAction;
    acProjXZ: TAction;
    acExportSVG: TAction;
    acParamVue3D: TAction;
    acExportGCP: TAction;
    acExporterEnImage: TAction;
    acExportBabylonJS: TAction;
    actionsVue3DGDI: TActionList;
    btnColorZMaxi: TColorButton;
    btnColorZMini: TColorButton;
    btnApplyGradientColors: TButton;
    btnEditMagnZ: TButton;
    btnValidate: TButton;
    btnCopyListePOI: TButton;
    Button5: TButton;
    chkDispEntrancesNames: TCheckBox;
    chkDispEntrances: TCheckBox;
    chkDispCenterlines: TCheckBox;
    chkDispMaillages: TCheckBox;
    chkDispPOIs: TCheckBox;
    chkDispVolumes: TCheckBox;
    chkDispRadiantShots: TCheckBox;
    cmbVue3DModeBy: TComboBox;
    editMagnificationZ: TCurrencyEdit;
    editValue: TCurrencyEdit;
    hcLineFillStyles: THeaderControl;
    imgListVue3DGDI: TImageList;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    lbNomValeur: TLabel;
    lbValueMax: TStaticText;
    lbValueMin: TStaticText;
    lsbPOIs: TListBox;
    PairSplitter1: TPairSplitter;
    PairSplitterSide1: TPairSplitterSide;
    PairSplitterSide2: TPairSplitterSide;
    Panel1: TPanel;
    pnlParamZ: TPanel;
    pnlButtons: TPanel;
    pnlSetValue: TPanel;
    pnlVue: TPanel;
    sclValeur: TScrollBar;
    SpeedButton10: TSpeedButton;
    SpeedButton11: TSpeedButton;
    SpeedButton12: TSpeedButton;
    SpeedButton13: TSpeedButton;
    SpeedButton14: TSpeedButton;
    SpeedButton15: TSpeedButton;
    SpeedButton6: TSpeedButton;
    SpeedButton8: TSpeedButton;
    SpeedButton9: TSpeedButton;
    Vue: TPaintBox;

    procedure acExportBabylonJSExecute(Sender: TObject);
    procedure acExporterEnImageExecute(Sender: TObject);
    procedure acExportGCPExecute(Sender: TObject);
    procedure acExportSVGExecute(Sender: TObject);
    procedure acParamVue3DExecute(Sender: TObject);
    procedure acProjISOExecute(Sender: TObject);
    procedure acProjXYExecute(Sender: TObject);
    procedure acProjXZExecute(Sender: TObject);
    procedure acProjYZExecute(Sender: TObject);
    procedure btnApplyGradientColorsClick(Sender: TObject);
    procedure btnColorZMaxiClick(Sender: TObject);
    procedure btnDetourerMNTClick(Sender: TObject);
    procedure btnEditMagnZClick(Sender: TObject);
    procedure btnValidateClick(Sender: TObject);
    procedure btnCopyListePOIClick(Sender: TObject);
    procedure Button5Click(Sender: TObject);
    procedure FrameResize(Sender: TObject);
    procedure hcLineFillStylesSectionResize(HeaderControl: TCustomHeaderControl; Section: THeaderSection);
    procedure lsbPOIsDrawItem(Control: TWinControl; Index: Integer; ARect: TRect; State: TOwnerDrawState);
    procedure PairSplitterSide1Resize(Sender: TObject);
    procedure pnlVueResize(Sender: TObject);
    procedure sclValeurChange(Sender: TObject);

    procedure VueMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure VueMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    procedure VueMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure VuePaint(Sender: TObject);

  strict private
    FClassesRepartDepthDiagram: TArrayOfFloats;
    FRedessinInProcess   : boolean;

    FDoDraw              : boolean;
    FTmpBuffer           : TBGRABitmap;

    FFiltres             : string;

    FLineStyleDefault    : TLineAttributes;
    FTextStyleDefault    : TTexteAttributs;
    FLineStyleDraft      : TLineAttributes;
    FParametrageVue3D    : TParametrageVue3D;
    FCubeEnglobant       : array[1..8] of TPoint;
    FProcRefreshControlsBy3DView: TProcRefreshControlsBy3DView;

    // variables géométriques calculées
    FOldMousePos         : TPoint;
    FOffset              : TPoint3Df;      // décalage après centrage
    FLongueurDiagonale   : double;         // longueur de la diagonale du cube enveloppe de la cavité
    // pour les coordonnées écran
    FViewportWidth       : integer;
    FViewportHeight      : integer;

    FTransfoInt          : TPoint;
    FOffsetInt           : TPoint;
    FRapportME           : double;





    function CalcFontHeightFromPercentOfViewport(const QBmp: TBGRABitmap; const PC: double): integer;
    procedure CalcImgOrigineEtGrandeDiagonale();
    procedure CalcScreenOrigineEtGrandeDiagonale();
    procedure DefineBrosse(const QBmp: TBGRABitmap; const QColor: TGHTopoColor; const QStyle: TBrushStyle);
    procedure DefineCrayon(const QBmp: TBGRABitmap; const LS: TLineAttributes);
    procedure DefineFonte(const QBmp: TBGRABitmap; const TS: TTexteAttributs);
    procedure ExportAnImage(const ImgWidth: integer; const QFilename: string);
    procedure ExportGCP(const QFilename: TStringDirectoryFilename);
    function  GetVPCoordinates(const QX, QY: double): TPoint;

    function  ProjPointXYZ(const QX, QY, QZ: double; out QDepth: double): TPoint;
    function  ProjPoint3DF(constref P: TPoint3Df; out QDepth: double): TPoint; overload;
    procedure RedessinVue();
    procedure SetChkDisplayed(const P: TVue3DDisplayedParts);
    procedure SetParamsTransformation(const VueWidth, VueHeight: integer; const QTheta, QPhi, QZoom, QMagnification: double);
    procedure SetPanelEditValues(const QValue: double);
    procedure SetQuickParamsInForm(constref P: TParametrageVue3D);

  private
    FCoinBasGauche            : TPoint3Df;
    FCoinHautDroit            : TPoint3Df;

    FListeLineFillAttributes      : TListeLineFillAttributes;
    FListeTubesVisees             : TListeTubesVisees;
    FListeSegments3DForVisu3D     : TListeSegments3DForVisu3D;
    FListeSymbolesOrTextForVisu3D : TListeSymbolesOrTextForVisu3D;
    FListePOIs                    : TListeSymbolesOrTextForVisu3D;
    FDocumentToporobot            : TToporobotStructure2012;
    FBDDEntites                   : TBDDEntites;
    FListeOfMaillages             : TListeOfMaillages;

    function  Get2DDepthCoordinatesFromXYZ(const QX, QY, QZ: double): TPoint2DDepth; inline;
    procedure DrawAllTubes(const QBmp: TBGRABitmap);
    procedure DrawProfilsTopo(const QBmp: TBGRABitmap);
    procedure DrawDraftPolygonale();
    procedure DrawReferentielXYZ(const QBmp: TBGRABitmap);
    procedure DrawCube(const QBmp: TBGRABitmap);
    procedure DrawAllSymboles(const QBmp: TBGRABitmap);
    procedure DrawBarGradientAlti(const QBmp: TBGRABitmap; const QX, QY, QL, QH: integer; const ColorStart, ColorStop: TGHTopoColor);
    procedure DrawAllSegments(const QBmp: TBGRABitmap);
    procedure DrawPOIs(const QBmp: TBGRABitmap);
    procedure DrawMaillage(const QBmp: TBGRABitmap; const M: TMaillage);
    procedure DrawReferentiel(const QBmp: TBGRABitmap; const TailleUnitaire: double);
    procedure DrawDepthHistogram(const QBmp: TBGRABitmap;
                                 const QX1, QY1, QY2, QL: integer;
                                 const ColorStart, ColorStop: TGHTopoColor);


  public
    property Filtres:     string read FFiltres;
    property ListeOfMaillages: TListeOfMaillages read FListeOfMaillages;

    function  Initialiser(constref CBG, CHD: TPoint3Df;
                          const QTheta, QPhi, QZoom, QMagnification: double;
                          const FD: TToporobotStructure2012;
                          const BDE: TBDDEntites;
                          const AM: TListeOfMaillages;
                          constref QClassesRepartDepthDiagram: TArrayOfFloats;
                          const QFiltres: string): boolean;

    procedure Finaliser();

    procedure AddTubeGalerie(constref T: TTubeGalerie); inline;
    function  getNbTubesGaleries(): integer; inline;

    procedure AddSegment3D(constref T: TSegment3DForVisu3D); inline;
    function  getNbSegments3D(): integer; inline;

    procedure AddSymbole3D(constref T: TSymboleOrTextOrPOIForVisu3D); inline;
    function  getNbSymboles3D(): integer; inline;

    procedure AddPOI(constref P: TSymboleOrTextOrPOIForVisu3D);
    function  getNbPOIs(): integer;
    procedure ListerPOIs();

    procedure AddLineFillStyle(constref LFS: TLineFillAttributes);
    function  getNbLineFilltyles(): integer; inline;


    procedure BeginScene();
    procedure EndScene();

    procedure SetProcRefreshControlsBy3DView(const P: TProcRefreshControlsBy3DView);
    function  SetAndRedessVue3D(const QTheta, QPhi, QZoom, QMagnification: double): boolean;
    procedure SetAnglesVue(const QTheta, QPhi: double; const DoResetOffsets: boolean);
    procedure ExporterVue3DEnSVG_TSVGCanvas(const FileName: string; const Mode: TVue3DColorTubesMode);
    function  IsInCubeLimits(constref P: TPoint3Df): boolean;
    procedure Redess();


  end;

implementation
uses DGCDummyUnit;
{$R *.lfm}
const
  //LOW_INDEX       = 0;// anciennement 1
  REFERENTIEL_RSZ = 40;
  REFERENTIEL_RX0 = 5 + REFERENTIEL_RSZ;
  REFERENTIEL_LBL_X  = 'E';
  REFERENTIEL_LBL_Y  = 'N';
  REFERENTIEL_LBL_Z  = 'Z';

const
  NOM_GROUPE_CUBE_ENGLOBANT     = 'CubeEnglobant';
  NOM_GROUPE_MAILLAGE           = 'Maillage';
  NOM_GROUPE_CAVITE             = 'Cavite001';
  NOM_GROUPE_CENTERLINES        = 'Centerline001';

  NOM_GROUPE_PROFILS            = 'ListeProfils';
  NOM_STYLE_PROFIL_DEFAUT       = 'ProfilDefaut';

  NOM_STYLE_SILHOUETTE_DEFAUT   = 'SilhouetteDefaut';
  NOM_STYLE_FOND_CUBE_ENGLOBANT = 'FondCubeEnglobant';
  NOM_STYLE_CUBE_ENGLOBANT      = 'CubeEnglobant';
  NOM_STYLE_MAILLAGE            = 'Maillage';
  FMT_STYLE_ENTRANCE            = 'Entrance%d';
  FMT_STYLE_RESEAU              = 'Reseau%d';
  FMT_STYLE_SECTEUR             = 'Secteur%d';
  FMT_STYLE_SEANCE              = 'Seance%d';

{ TCdrVisualisateur3DGDI }


procedure TCdrVisualisateur3DGDI.ListerPOIs;
var
  i, Nb: Integer;
begin
  Nb := FListePOIs.GetNbElements();
  if (0 = nb) then exit;
  lsbPOIs.ShowHint := false;
  lsbPOIs.Clear;
  lsbPOIs.ItemHeight := 20;
  for i := 0 to Nb-1 do lsbPOIs.Items.add('');
  lsbPOIs.ItemIndex := 0;
end;

function TCdrVisualisateur3DGDI.Initialiser(constref CBG, CHD: TPoint3Df;
                                            const QTheta, QPhi, QZoom, QMagnification: double;
                                            const FD: TToporobotStructure2012;
                                            const BDE: TBDDEntites;
                                            const AM: TListeOfMaillages;
                                            constref QClassesRepartDepthDiagram: TArrayOfFloats;
                                            const QFiltres: string): boolean;
var
  MyDefaultLineFillStyle: TLineFillAttributes;
  procedure S666(const ACDC: TAction; const QCaption: string; const QEnabled: boolean = True);
  begin
    ACDC.Caption := GetResourceString(QCaption);
    ACDC.Hint    := GetResourceString(QCaption);
    ACDC.Enabled := QEnabled;
  end;
  procedure ResetCubeEnglobant();
  var
    i: Integer;
  begin
    for i := Low(FCubeEnglobant) to High(FCubeEnglobant) do FCubeEnglobant[i].Create(0, 0);
  end;

begin
  Result := false;
  AfficherMsgClass(self, 'Initialiser()');

  // diagramme des altitudes
  FClassesRepartDepthDiagram := QClassesRepartDepthDiagram;
  // drapeaux
  FRedessinInProcess := false;
  FDoDraw            := false;

  FFiltres := QFiltres;
  // inits params internes
  ResetCubeEnglobant();
  FTransfoInt.Create(0, 0);
  FOffsetInt.Create(0, 0);
  // i18n
  S666(acProjISO     , rsVUE3D_AC_PROJ_ISO);
  S666(acProjXY      , rsVUE3D_AC_PROJ_XY);
  S666(acProjYZ      , rsVUE3D_AC_PROJ_YZ);
  S666(acProjXZ      , rsVUE3D_AC_PROJ_XZ);
  S666(acExportSVG   , rsVUE3D_AC_EXPORT_SVG);
  S666(acExportGCP   , rsVUE3D_AC_EXPORT_GCP);
  S666(acParamVue3D  , rsVUE3D_AC_PARAMETRAGE);
  S666(acExporterEnImage, rsVUE3D_AC_EXPORT_PNG);
  S666(acExportBabylonJS, rsVUE3D_AC_EXPORT_BABYLON_JS);


  // paramètrage de la vue 3D
  FParametrageVue3D.ColorBackGround.setFrom(clWhite, 255);
  FParametrageVue3D.LineCube.SetAttributes(SVG_STYLE_NAME_LineCube, clRed , 255, 0, 0.10);
  FParametrageVue3D.LineAxes.SetAttributes(SVG_STYLE_NAME_LineAxes, clBlue, 255, 0, 0.10);
  FParametrageVue3D.setColorsForGradientZ(clAqua, clRed, 128);
  FParametrageVue3D.MagnificationZ := QMagnification;
  FParametrageVue3D.Vue3DColorTubesMode := lmvByEXPES; // par défault: Par séances
  FParametrageVue3D.Vue3DDisplayedParts := [lvpCUBE, lvpREFERENTIEL, lvpENTRANCES, lvpEntranceNames, lvpCENTERLINES, lvpVOLUMES, lvpPOIs];
  // Styles par défaut
  MyDefaultLineFillStyle.SetAttributes('LineDefault', MakeTGHTopoColor(clRed, 255), MakeTGHTopoColor(clRed, 128), 0, 0.05);
  FLineStyleDefault.SetAttributes(SVG_STYLE_NAME_LineCube, clRed, 255, 0, 0.00, psSolid);
  FLineStyleDraft.SetAttributes(SVG_STYLE_NAME_LineDraft, clBlue, 255, 0, 0, psSolid);
  FTextStyleDefault.setAttributes(SVG_STYLE_NAME_TextDefault, DEFAULT_FONT_NAME, MakeTGHTopoColor(clBlack, 255), MakeTGHTopoColor(clWhite, 255), [], 10, 2.5, 0, 0);
  // limites de la vue
  FCoinBasGauche.setFrom(Math.Min(CBG.X, CHD.X),  Math.Min(CBG.Y, CHD.Y),  Math.Min(CBG.Z, CHD.Z));
  FCoinHautDroit.setFrom(Math.Max(CBG.X, CHD.X),  Math.Max(CBG.Y, CHD.Y),  Math.Max(CBG.Z, CHD.Z));
  AfficherMsgErrClass(self, 'Initialiser(%s, %s)', [FCoinBasGauche.DebugString('Coin bas gauche:'), FCoinHautDroit.DebugString('Coin Haut Droit:')]);
  // callbacks
  FProcRefreshControlsBy3DView := nil;
  // Pour Babylon.js
  FDocumentToporobot:= FD;
  FBDDEntites := BDE;
  // Objets facultatifs
  FListeOfMaillages := AM;
  // Listes d'objets
  AfficherMessageErreur('Listes des objets');
  FListeTubesVisees             := TListeTubesVisees.Create;
  FListeSegments3DForVisu3D     := TListeSegments3DForVisu3D.Create;
  FListeLineFillAttributes      := TListeLineFillAttributes.Create;
  FListeSymbolesOrTextForVisu3D := TListeSymbolesOrTextForVisu3D.Create;
  FListePOIs                    := TListeSymbolesOrTextForVisu3D.Create;
  try
    FListeLineFillAttributes.ClearListe();
    self.AddLineFillStyle(MyDefaultLineFillStyle);
    FListeTubesVisees.ClearListe();
    FListeTubesVisees.SetBounds(FCoinBasGauche, FCoinHautDroit);
    FListeTubesVisees.SetFunctionProjection(Get2DDepthCoordinatesFromXYZ);
    FListeSegments3DForVisu3D.ClearListe();
    FListeSegments3DForVisu3D.SetFunctionProjection(Get2DDepthCoordinatesFromXYZ);
    FListeSymbolesOrTextForVisu3D.ClearListe();
    FListeSymbolesOrTextForVisu3D.SetFunctionProjection(Get2DDepthCoordinatesFromXYZ);
    FListePOIs.ClearListe();
    FListePOIs.SetFunctionProjection(Get2DDepthCoordinatesFromXYZ);
    // renseigner les contrôles pour le paramétrage rapide sans passer par le paramétrage complet
    SetQuickParamsInForm(FParametrageVue3D);
    result := SetAndRedessVue3D(QTheta, QPhi, QZoom, QMagnification);
  except
    on e: exception do ShowMessage(e.Message);
  end;
end;
procedure TCdrVisualisateur3DGDI.SetQuickParamsInForm(constref P: TParametrageVue3D);
  procedure PrepareCmbVue3DModeBy(const P: TVue3DColorTubesMode);
  begin
    RemplirComboBox(cmbVue3DModeBy, [
                   rsVUE3D_CMB_MODE_VUE_Reseaux,
                   rsVUE3D_CMB_MODE_VUE_Entrances,
                   rsVUE3D_CMB_MODE_VUE_Secteurs,
                   rsVUE3D_CMB_MODE_VUE_Codes,
                   rsVUE3D_CMB_MODE_VUE_Expes,
                   rsVUE3D_CMB_MODE_VUE_GradientZ
                   ], Ord(P));
  end;
begin
  PrepareCmbVue3DModeBy(P.Vue3DColorTubesMode);                // Mode de visu: par réseaux, séances, etc ...
  SetChkDisplayed(FParametrageVue3D.Vue3DDisplayedParts);      // Elements dessinés: centerlines, cube, etc ...
  btnColorZMini.ButtonColor  := FParametrageVue3D.ColorZMini.toTColor();
  btnColorZMaxi.ButtonColor  := FParametrageVue3D.ColorZMaxi.toTColor();
  SetPanelEditValues(-1.00);
  editMagnificationZ.Value   := FParametrageVue3D.MagnificationZ;
end;

procedure TCdrVisualisateur3DGDI.SetChkDisplayed(const P: TVue3DDisplayedParts);
var
  EWE: Boolean;
begin
  // Cubes et référentiels
  pass;
  // Cavités
  chkDispEntrances.Checked          := (lvpENTRANCES     in P);
  chkDispEntrancesNames.Checked     := (lvpEntranceNames in P);
  chkDispCenterlines.Checked        := (lvpCENTERLINES   in P);
  chkDispVolumes.Checked            := (lvpVOLUMES       in P);
  chkDispRadiantShots.Checked       := (lvpRADIANT_SHOTS in P);
  chkDispPOIs.Checked               := (lvpPOIs          in P);
  // Maillages
  EWE := false;

  if (FListeOfMaillages.getNbElements()  > 0) then EWE := FListeOfMaillages.GetElement(0).IsValid;
  chkDispMaillages.Checked     := (lvpMAILLAGE      in P);
  chkDispMaillages.Enabled     := EWE;
end;




procedure TCdrVisualisateur3DGDI.BeginScene();
begin
  AfficherMsgErrClass(self, 'BeginScene()');
  FDoDraw := false;
end;

procedure TCdrVisualisateur3DGDI.AddTubeGalerie(constref T: TTubeGalerie);
begin
  FListeTubesVisees.AddElement(T);
end;

function TCdrVisualisateur3DGDI.getNbTubesGaleries(): integer;
begin
  result := FListeTubesVisees.GetNbElements();
end;

procedure TCdrVisualisateur3DGDI.AddSegment3D(constref T: TSegment3DForVisu3D);
begin
  FListeSegments3DForVisu3D.AddElement(T);
end;

function TCdrVisualisateur3DGDI.getNbSegments3D(): integer;
begin
  result := FListeSegments3DForVisu3D.GetNbElements();
end;

procedure TCdrVisualisateur3DGDI.AddSymbole3D(constref T: TSymboleOrTextOrPOIForVisu3D);
begin
  FListeSymbolesOrTextForVisu3D.AddElement(T);
end;

function TCdrVisualisateur3DGDI.getNbSymboles3D(): integer;
begin
  result := FListeSymbolesOrTextForVisu3D.GetNbElements();
end;

procedure TCdrVisualisateur3DGDI.AddPOI(constref P: TSymboleOrTextOrPOIForVisu3D);
begin
  FListePOIs.addElement(P);
end;

function TCdrVisualisateur3DGDI.getNbPOIs: integer;
begin
  FListePOIs.GetNbElements();
end;

procedure TCdrVisualisateur3DGDI.AddLineFillStyle(constref LFS: TLineFillAttributes);
begin
  FListeLineFillAttributes.AddElement(LFS);
end;

function TCdrVisualisateur3DGDI.getNbLineFilltyles(): integer;
begin
  result := FListeLineFillAttributes.GetNbElements();
end;

procedure TCdrVisualisateur3DGDI.EndScene();
begin
  AfficherMsgErrClass(self, 'EndScene()');
  AfficherMessageErreur('-- %d tubes', [getNbTubesGaleries()]);
  SetAndRedessVue3D(FParametrageVue3D.Theta, FParametrageVue3D.Phi, FParametrageVue3D.Zoom, FParametrageVue3D.MagnificationZ);
  FDoDraw := True;
end;


procedure TCdrVisualisateur3DGDI.SetProcRefreshControlsBy3DView(const P: TProcRefreshControlsBy3DView);
begin
  FProcRefreshControlsBy3DView := P;
end;

procedure TCdrVisualisateur3DGDI.Finaliser();
begin
  try
    FListeLineFillAttributes.ClearListe();
    FListeTubesVisees.ClearListe();
    FListeSegments3DForVisu3D.ClearListe();
    FListeSymbolesOrTextForVisu3D.ClearListe();
  finally
    FreeandNil(FListeSegments3DForVisu3D);
    FreeAndNil(FListeTubesVisees);
    FreeAndNil(FListeLineFillAttributes);
    FreeAndNil(FListeSymbolesOrTextForVisu3D);
  end;
end;


// paramètres de transformation angles en degrés
procedure TCdrVisualisateur3DGDI.SetParamsTransformation(const VueWidth, VueHeight: integer; const QTheta, QPhi, QZoom, QMagnification: double);
  function QToto(const QX, QY, QZ: double): TPoint;
  var
    c: TPoint2DDepth;
  begin
    c := Get2DDepthCoordinatesFromXYZ(QX, QY, QZ);
    Result := GetVPCoordinates(c.X, c.Y);
  end;
begin
  FViewportWidth   := VueWidth;
  FViewportHeight  := VueHeight;
  FParametrageVue3D.setParamThetaPhiZoomMagn(QTheta, QPhi, QZoom, QMagnification);


  FTransfoInt.X := 0;
  FTransfoInt.Y := 0;
  FTransfoInt.X := VueWidth  div 2;
  FTransfoInt.Y := VueHeight div 2;
  FRapportME    := (VueWidth/FLongueurDiagonale) *  FParametrageVue3D.Zoom;

  // cube
  FCubeEnglobant[1] := QToto(FCoinBasGauche.X, FCoinBasGauche.Y, FCoinBasGauche.Z);
  FCubeEnglobant[2] := QToto(FCoinHautDroit.X, FCoinBasGauche.Y, FCoinBasGauche.Z);
  FCubeEnglobant[3] := QToto(FCoinHautDroit.X, FCoinHautDroit.Y, FCoinBasGauche.Z);
  FCubeEnglobant[4] := QToto(FCoinBasGauche.X, FCoinHautDroit.Y, FCoinBasGauche.Z);

  FCubeEnglobant[5] := QToto(FCoinBasGauche.X, FCoinBasGauche.Y, FCoinHautDroit.Z);
  FCubeEnglobant[6] := QToto(FCoinHautDroit.X, FCoinBasGauche.Y, FCoinHautDroit.Z);
  FCubeEnglobant[7] := QToto(FCoinHautDroit.X, FCoinHautDroit.Y, FCoinHautDroit.Z);
  FCubeEnglobant[8] := QToto(FCoinBasGauche.X, FCoinHautDroit.Y, FCoinHautDroit.Z);
end;

procedure TCdrVisualisateur3DGDI.CalcScreenOrigineEtGrandeDiagonale();
begin
  FLongueurDiagonale := DistanceBetweenTwoTPoint3Df(FCoinBasGauche, FCoinHautDroit);
  FOffset.setFrom(-(FCoinBasGauche.X + 0.5*(FCoinHautDroit.X - FCoinBasGauche.X)),
                  -(FCoinBasGauche.Y + 0.5*(FCoinHautDroit.Y - FCoinBasGauche.Y)),
                  -(FCoinBasGauche.Z + 0.5*(FCoinHautDroit.Z - FCoinBasGauche.Z)));
end;
procedure TCdrVisualisateur3DGDI.CalcImgOrigineEtGrandeDiagonale();
begin
  FLongueurDiagonale := DistanceBetweenTwoTPoint3Df(FCoinBasGauche, FCoinHautDroit);
  FOffset.setFrom(-(FCoinBasGauche.X + 0.5*(FCoinHautDroit.X - FCoinBasGauche.X)),
                  -(FCoinBasGauche.Y + 0.5*(FCoinHautDroit.Y - FCoinBasGauche.Y)),
                  -(FCoinBasGauche.Z + 0.5*(FCoinHautDroit.Z - FCoinBasGauche.Z)));
end;



function TCdrVisualisateur3DGDI.SetAndRedessVue3D(const QTheta, QPhi, QZoom, QMagnification: double): boolean;
begin
  FDoDraw := false;
  result := false;
  AfficherMsgErrClass(self, 'SetAndRedessVue3D()');

  CalcScreenOrigineEtGrandeDiagonale();
  try
    SetParamsTransformation(Vue.Width, Vue.Height, QTheta, QPhi, QZoom, QMagnification);
    FListeTubesVisees.ReProjeter();
    FListeSegments3DForVisu3D.Reprojeter();
    FListeSymbolesOrTextForVisu3D.Reprojeter(True);
    FListePOIs.Reprojeter(false);
    FDoDraw := True;
    result  := True;
    Vue.Invalidate;                      // redessin
  except
    on e: exception do ShowMessage(e.Message);
  end;
end;

procedure TCdrVisualisateur3DGDI.SetAnglesVue(const QTheta, QPhi: double; const DoResetOffsets: boolean);
begin
  if (DoResetOffsets) then
  begin
    FOffsetInt.Create(0,0);
  end;
  SetAndRedessVue3D(QTheta, QPhi, FParametrageVue3D.Zoom, FParametrageVue3D.MagnificationZ);
end;

function TCdrVisualisateur3DGDI.Get2DDepthCoordinatesFromXYZ(const QX, QY, QZ: double): TPoint2DDepth;
begin
  Result := FParametrageVue3D.ProjXYZPoint(QX + FOffset.X, QY + FOffset.Y, QZ + FOffset.Z);
end;

function TCdrVisualisateur3DGDI.GetVPCoordinates (const QX, QY: double): TPoint;
begin
  Result.X :=                     round(QX * FRapportME) + FTransfoInt.X + FOffsetInt.X;
  Result.Y := FViewportHeight  - (round(QY * FRapportME) + FTransfoInt.Y + FOffsetInt.Y);
end;

function TCdrVisualisateur3DGDI.ProjPointXYZ(const QX, QY, QZ: double;  out QDepth: double): TPoint;
var
  P: TPoint2DDepth;
begin
  P := Get2DDepthCoordinatesFromXYZ(QX, QY, QZ);
  QDepth := P.Depth;
  Result := GetVPCoordinates(P.X, P.Y);
end;

function TCdrVisualisateur3DGDI.ProjPoint3DF(constref P: TPoint3Df; out QDepth: double): TPoint;
begin
  result := ProjPointXYZ(P.X, P.Y, P.Z, QDepth);
end;

procedure TCdrVisualisateur3DGDI.SetPanelEditValues(const QValue: double);
const FATXE = 28;
  procedure MiouMiou(const QMin, QMax: double; const QPrompt: string; const QY: integer);
  begin
    pnlSetValue.Anchors := [akTop, akLeft, akRight];
    pnlSetValue.Left    := pnlParamZ.Left  + 2;
    pnlSetValue.Width   := pnlParamZ.Width - 2;
    pnlSetValue.Top     := pnlParamZ.Top   + QY;
    lbNomValeur.Caption := QPrompt;
    editValue.Value     := QValue;
    lbValueMax.Caption  := Format(FORMAT_NB_REAL_3_DEC, [QMax]);
    lbValueMin.Caption  := Format(FORMAT_NB_REAL_3_DEC, [QMin]);

    sclValeur.Position  := Trunc(100 * QValue);
    sclValeur.Min       := Trunc(100 * QMin);
    sclValeur.Max       := Trunc(100 * QMax);
  end;
begin
  pnlSetValue.Visible := (QValue >= 0.00);
  MiouMiou(0.50,  16.00, 'Magnification' , editMagnificationZ.Top  + FATXE);
end;


procedure TCdrVisualisateur3DGDI.VuePaint(Sender: TObject);
begin
  RedessinVue();
end;

procedure TCdrVisualisateur3DGDI.VueMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
{$DEFINE QQ1}
{$UNDEF QQ1}
var
  WU: Integer;
begin
  if (not FDoDraw) then exit;
  if (Shift = [ssLeft]) then
  {$IFDEF QQ1}   // schéma provisoire
  begin
    WU := -(X - (self.Width div 2)) MOD 360;
    FParametrageVue3D.Theta := WU * 1.00; //-(X - FPos0.X);

    FParametrageVue3D.Phi   :=  Y - (self.Height div 2); //Y - FPos0.Y;
    if (FParametrageVue3D.Phi >  90.0) then FParametrageVue3D.Phi :=  90.0;
    if (FParametrageVue3D.Phi <   0.0) then FParametrageVue3D.Phi :=   0.0;
    SetParamsTransformation(FParametrageVue3D.Theta,
                            FParametrageVue3D.Phi,
                            FParametrageVue3D.Zoom,
                            FParametrageVue3D.Magnification);
    // provisoire: recalcul de la scene
    FListeTubesVisees.ReProjeterVertexTubes();
    Vue.Invalidate;
  end;
  {$ELSE QQ1}  // schéma définitif
  begin
    WU := -(X - (self.Width div 2)) MOD 360;
    FParametrageVue3D.Theta := 1.00 * WU;

    FParametrageVue3D.Phi   :=  Y - (self.Height div 2); //Y - FPos0.Y;
    if (FParametrageVue3D.Phi >  90.0) then FParametrageVue3D.Phi :=  90.0;
    if (FParametrageVue3D.Phi <   0.0) then FParametrageVue3D.Phi :=   0.0;
    SetParamsTransformation(Vue.Width, Vue.Height, FParametrageVue3D.Theta, FParametrageVue3D.Phi, FParametrageVue3D.Zoom, FParametrageVue3D.MagnificationZ);
    DrawDraftPolygonale();
    //if (Assigned(FProcRefreshControls)) then FProcRefreshControls();
  end;
  {$ENDIF QQ1}
  (*
  if (Shift = [ssRight]) then
  {$IFDEF QQ1}   // schéma provisoire
  begin
    WU := -(X - (self.Width div 2)) MOD 360;
    FParametrageVue3D.Theta := WU * 1.00; //-(X - FPos0.X);

    FParametrageVue3D.Phi   :=  Y - (self.Height div 2); //Y - FPos0.Y;
    if (FParametrageVue3D.Phi >  90.0) then FParametrageVue3D.Phi :=  90.0;
    if (FParametrageVue3D.Phi <   0.0) then FParametrageVue3D.Phi :=   0.0;
    SetParamsTransformation(FParametrageVue3D.Theta,
                            FParametrageVue3D.Phi,
                            FParametrageVue3D.Zoom,
                            FParametrageVue3D.Magnification);
    // provisoire: recalcul de la scene
    FListeTubesVisees.ReProjeterVertexTubes();
    Vue.Invalidate;
  end;
  {$ELSE QQ1}  // schéma définitif
  begin

    FOffsetInt.X +=    X - FOldMousePos.X;
    FOffsetInt.Y +=  -(Y - FOldMousePos.Y);
    SetParamsTransformation(FParametrageVue3D.Theta, FParametrageVue3D.Phi, FParametrageVue3D.Zoom, FParametrageVue3D.Magnification);
    DrawDraftPolygonale();
    //if (Assigned(FProcRefreshControls)) then FProcRefreshControls();
  end;
  {$ENDIF QQ1}
  // *)
  FOldMousePos.X := X; FOldMousePos.Y := Y;
end;

procedure TCdrVisualisateur3DGDI.VueMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  try
    SetAndRedessVue3D(FParametrageVue3D.Theta, FParametrageVue3D.Phi, FParametrageVue3D.Zoom, FParametrageVue3D.MagnificationZ);
  except
    pass;
  end;
end;

procedure TCdrVisualisateur3DGDI.VueMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  pass;
end;

procedure TCdrVisualisateur3DGDI.FrameResize(Sender: TObject);
begin
  PairSplitterSide1.Width := self.width - 210;
  if (FDoDraw) then
  begin
    SetParamsTransformation(Vue.Width, Vue.Height, FParametrageVue3D.Theta, FParametrageVue3D.Phi, FParametrageVue3D.Zoom, FParametrageVue3D.MagnificationZ);
    Vue.Invalidate;
  end;
end;

procedure TCdrVisualisateur3DGDI.hcLineFillStylesSectionResize(
  HeaderControl: TCustomHeaderControl; Section: THeaderSection);
begin
  lsbPOIs.Invalidate;
end;

procedure TCdrVisualisateur3DGDI.lsbPOIsDrawItem(Control: TWinControl; Index: Integer; ARect: TRect; State: TOwnerDrawState);
const
  Q4  = 4; // compensation du décalage entre le header et la liste
  mg  = 1;
var
  EWE: TSymboleOrTextOrPOIForVisu3D;
  procedure DessineItem(const bg,tc: TColor);
    procedure QDrawRectangleColore(const QHS: THeaderSection; const QBG, PC, BC: TColor);
    begin
      lsbPOIs.canvas.Pen.Color   := PC;
      lsbPOIs.canvas.Brush.Color := BC;
      lsbPOIs.canvas.Rectangle(Rect(QHS.Left + mg, ARect.Top + mg, QHS.Left + QHS.Width - mg, ARect.Bottom - mg));
      // et on restaure l'ancienne couleur
      lsbPOIs.Canvas.Brush.Color := QBG;
      lsbPOIs.canvas.Pen.Color   := clSilver;
    end;
    procedure QResetColorRow(const qbg, qtc: TColor);
    begin
      lsbPOIs.Canvas.Brush.Color:= qbg;
      lsbPOIs.Canvas.Font.Color := qtc;
      lsbPOIs.Canvas.Pen.Color  := clSilver; // pour les filets
      lsbPOIs.Canvas.FillRect(ARect);
    end;
    procedure QDrawFiletColonne(const TB: integer); inline;
    begin
      lsbPOIs.canvas.Pen.Color   := clSilver;
      lsbPOIs.Canvas.Line(TB, ARect.Top, TB, ARect.Bottom);
    end;
    procedure QDrawColTexte(const QHS: THeaderSection; const DoDrawFilet: boolean; const QText: string);
    var
      QR: TRect;
    begin
      QR := Rect(QHS.Left, ARect.Top + mg, QHS.Right - 8, ARect.Bottom - mg);
      if (DoDrawFilet) then QDrawFiletColonne(QHS.Left - Q4);
      lsbPOIs.canvas.TextRect(QR, QHS.Left + 4, ARect.Top+1, QText);
    end;
  begin
    QResetColorRow(bg, tc);
    QDrawColTexte(hcLineFillStyles.Sections.Items[0], False, MakeLabelStationFromTIDBaseStation(EWE.IDBaseStation));
    QDrawColTexte(hcLineFillStyles.Sections.Items[1], True , EWE.Caption);
  end;
begin
  //if (lsbPOIs.Count = 0) then exit;
  EWE := FListePOIs.GetElement(Index);
  if (odSelected in State) then DessineItem(clBlue , clWhite)
                           else DessineItem(clwhite, clBlack);
end;

procedure TCdrVisualisateur3DGDI.PairSplitterSide1Resize(Sender: TObject);
begin

end;

procedure TCdrVisualisateur3DGDI.pnlVueResize(Sender: TObject);
begin
  Vue.Invalidate;
end;



procedure TCdrVisualisateur3DGDI.sclValeurChange(Sender: TObject);
begin
  editValue.Value := sclValeur.Position / 100.00;
end;

procedure TCdrVisualisateur3DGDI.acProjISOExecute(Sender: TObject);
begin
  SetAnglesVue(45, 32, True);
end;

procedure TCdrVisualisateur3DGDI.acExportSVGExecute(Sender: TObject);
var
  EWE: TStringDirectoryFilename;
  QFilterIndex: integer;
begin
  EWE := 'SVG_3D_001.svg';
  if (DoDialogSaveFile('Scalable Vector Graphics (*.svg)|*.svg|', '.svg', EWE, QFilterIndex)) then
  begin
    ExporterVue3DEnSVG_TSVGCanvas(EWE, FParametrageVue3D.Vue3DColorTubesMode); // TODO: En attente pour les profils
  end;
end;

procedure TCdrVisualisateur3DGDI.acExportGCPExecute(Sender: TObject);
var
  QFileName: TStringDirectoryFilename;
  QIdxFilter: integer;
begin
  if (Trunc(FParametrageVue3D.Phi) > 0) then
  begin
    ShowMessage(GetResourceString(rsMSG_EXPORT_GCP_PHI_MUST_BE_ZERO));
    exit();
  end;
  QFilename := Format('Elevation_%d_%d_001.gcp', [trunc(FParametrageVue3D.Theta), trunc(FParametrageVue3D.Phi)]);
  if (DoDialogSaveFile('Fichier GCP|*.gcp|Tous|*.*', '.gcp', QFileName, QIdxFilter)) then
  begin
    ExportGCP(QFileName);
  end;
end;

procedure TCdrVisualisateur3DGDI.acExporterEnImageExecute(Sender: TObject);
var
  QImgWidth: Integer;
  QFilename: TStringDirectoryFilename;
begin
  QImgWidth := 4000;

  QFilename := GetGHTopoDirectory() + 'Vue3D001.png';
  ExportAnImage(QImgWidth, QFilename);
  ShowMessage(GetResourceString(rsDONE_ANY_PROCESS));
  RedessinVue();

end;

procedure TCdrVisualisateur3DGDI.acExportBabylonJSExecute(Sender: TObject);
begin
  //ShowMessage(FFiltres);
  DisplayExportBabylonJS(FDocumentToporobot, FBDDEntites, FListeOfMaillages, FFiltres);
end;

procedure TCdrVisualisateur3DGDI.acParamVue3DExecute(Sender: TObject);
begin
  if (ParametrerVue3D(FParametrageVue3D)) then
  begin
    SetChkDisplayed(FParametrageVue3D.Vue3DDisplayedParts);
    Vue.Invalidate;
  end;
end;

procedure TCdrVisualisateur3DGDI.acProjXYExecute(Sender: TObject);
begin
  SetAnglesVue(270, 90, True);
end;

procedure TCdrVisualisateur3DGDI.acProjXZExecute(Sender: TObject);
begin
  SetAnglesVue(270, 0, True);
end;

procedure TCdrVisualisateur3DGDI.acProjYZExecute(Sender: TObject);
begin
  SetAnglesVue(0, 0, True);
end;

procedure TCdrVisualisateur3DGDI.btnApplyGradientColorsClick(Sender: TObject);
begin
  FParametrageVue3D.Vue3DColorTubesMode := TVue3DColorTubesMode(cmbVue3DModeBy.ItemIndex);
  FParametrageVue3D.setColorsForGradientZ(btnColorZMini.ButtonColor, btnColorZMaxi.ButtonColor, 128);
  FParametrageVue3D.MagnificationZ  := editMagnificationZ.Value;
  FParametrageVue3D.setPartsDisplayed(chkDispEntrances.Checked,
                                      chkDispEntrancesNames.Checked,
                                      chkDispCenterlines.Checked,
                                      chkDispVolumes.Checked,
                                      chkDispRadiantShots.Checked,
                                      chkDispPOIs.Checked,
                                      chkDispMaillages.Checked);
  ListerPOIs();
  Vue.Invalidate;
end;

procedure TCdrVisualisateur3DGDI.Redess();
begin
  Vue.Invalidate;
end;

procedure TCdrVisualisateur3DGDI.btnColorZMaxiClick(Sender: TObject);
begin

end;

procedure TCdrVisualisateur3DGDI.btnDetourerMNTClick(Sender: TObject);
begin

end;

procedure TCdrVisualisateur3DGDI.btnEditMagnZClick(Sender: TObject);
begin
  SetPanelEditValues(editMagnificationZ.Value);
end;

procedure TCdrVisualisateur3DGDI.btnValidateClick(Sender: TObject);
begin
  editMagnificationZ.Value := editValue.Value;
  FParametrageVue3D.MagnificationZ  := editMagnificationZ.Value;
  SetPanelEditValues(-1);
  Vue.Invalidate;
end;

procedure TCdrVisualisateur3DGDI.btnCopyListePOIClick(Sender: TObject);
var
  i, Nb: LongInt;
  MyClipBoard: TClipboard;
  EWE: String;
  MyPOI: TSymboleOrTextOrPOIForVisu3D;
begin
  Nb := FListePOIs.GetNbElements();
  if (0 = Nb) then exit;
  MyClipBoard := TClipboard.Create(ctClipboard);
  try
    MyClipBoard.Clear;
    // contenu
    EWE := Format('Points of interest (%d items)' + System.LineEnding, [Nb]);
    EWE += 'ID'               + FIELD_SEPARATOR_TAB +
           'Station'          + FIELD_SEPARATOR_TAB +
           'X'                + FIELD_SEPARATOR_TAB +
           'Y'                + FIELD_SEPARATOR_TAB +
           'Z'                + FIELD_SEPARATOR_TAB +
           'Description'      + FIELD_SEPARATOR_TAB +
           System.LineEnding;
    for i := 0 to Nb -1 do
    begin
      MyPOI := FListePOIs.GetElement(i);
      EWE += Format('%d' + FIELD_SEPARATOR_TAB +         // ID
                    '%s' + FIELD_SEPARATOR_TAB +         // Station
                    '%s' + FIELD_SEPARATOR_TAB +         // X
                    '%s' + FIELD_SEPARATOR_TAB +         // Y
                    '%s' + FIELD_SEPARATOR_TAB +         // Z
                    '%s' + FIELD_SEPARATOR_TAB +         // Description
                    System.LineEnding,
                    [i,
                     MakeLabelStationFromTIDBaseStation(MyPOI.IDBaseStation),
                     FormatterNombreOOo(MyPOI.Position.X),
                     FormatterNombreOOo(MyPOI.Position.Y),
                     FormatterNombreOOo(MyPOI.Position.Z),
                     MyPOI.Caption
                    ]);
    end;
    MyClipBoard.AsText := EWE;
  finally
    FreeAndNil(MyClipBoard);
  end;
end;

procedure TCdrVisualisateur3DGDI.Button5Click(Sender: TObject);
begin
  SetPanelEditValues(-1);
end;



procedure TCdrVisualisateur3DGDI.DrawAllTubes(const QBmp: TBGRABitmap);
var
  Nb, i: Integer;
  MyGradientAltimetrique: TGradientAltimetrique;
  procedure QDrawTube(const MyTube: TTubeGalerie);
  var
    s: Integer;
    VA, VB, VC: TVertexNormale;
    F: TFacette;
    PPA, PPB, PPC: TPoint;
    LS: TLineAttributes;
    QCZ: TGHTopoColor;
  begin
    // dégradé au niveau du tube, suffisant ici
    case FParametrageVue3D.Vue3DColorTubesMode of
      lmvByRESEAUX   : QCZ := MyTube.ColorTubeOfReseau;
      lmvByENTRANCES : QCZ := MyTube.ColorTubeOfEntrance;
      lmvBySECTEURS  : QCZ := MyTube.ColorTubeOfSecteur;
      lmvByCODES     : QCZ := MyTube.ColorTubeOfCode;
      lmvByEXPES     : QCZ := MyTube.ColorTubeOfExpe;
      lmvByGRADIENT_Z: QCZ := MyGradientAltimetrique.GetColorFromZ(0.50*(MyTube.GetPosExtr0().Z + MyTube.GetPosStation().Z));
    end;
    LS.SetAttributes(SVG_STYLE_NAME_Dummy, QCZ.toTColor(), 255, 0, 0.0, psSolid);
    DefineCrayon(QBmp, LS);
    DefineBrosse(QBmp, QCZ, bsSolid);
    if (lvpCENTERLINES in FParametrageVue3D.Vue3DDisplayedParts) then
    begin
      VA := MyTube.GetVertex(0);
      VB := MyTube.GetLastVertex();
      PPA := GetVPCoordinates(VA.Proj3dTo2d.X, VA.Proj3dTo2d.Y);
      PPB := GetVPCoordinates(VB.Proj3dTo2d.X, VB.Proj3dTo2d.Y);
      QBmp.CanvasBGRA.Line(PPA, PPB);
    end;
    if (lvpVOLUMES in FParametrageVue3D.Vue3DDisplayedParts) then
    begin
      for s := 1 to MyTube.NbFacettes() - 1 do  // seules les facettes latérales sont tracées
      begin
        F := MyTube.GetFacette(s);
        if (F.FacetteVisible(FParametrageVue3D.LookAt)) then // Test de visibilité à mettre dans ReProjeterVertexTubes();
        begin
          VA := MyTube.GetVertex(F.IdxVA);
          VB := MyTube.GetVertex(F.IdxVB);
          VC := MyTube.GetVertex(F.IdxVC);
          PPA := GetVPCoordinates(VA.Proj3dTo2d.X, VA.Proj3dTo2d.Y);
          PPB := GetVPCoordinates(VB.Proj3dTo2d.X, VB.Proj3dTo2d.Y);
          PPC := GetVPCoordinates(VC.Proj3dTo2d.X, VC.Proj3dTo2d.Y);
          QBmp.CanvasBGRA.Polygon([PPA, PPB, PPC]);
          // tracer les normales *** DEBUG ONLY
          (*
          V0.setFrom((VA.Position.X + VB.Position.X + VC.Position.X) / 3,
                     (VA.Position.Y + VB.Position.Y + VC.Position.Y) / 3,
                     (VA.Position.Z + VB.Position.Z + VC.Position.Z) / 3);
          PPA := ProjPointXYZ(V0.X, V0.Y, V0.Z, QDepth);
          VN.setFrom(V0.X + F.NormaleFacette.X * MUL_NORMALE,
                     V0.Y + F.NormaleFacette.Y * MUL_NORMALE,
                     V0.Z + F.NormaleFacette.Z * MUL_NORMALE);
          PPB := ProjPointXYZ(VN.X, VN.Y, VN.Z, QDepth);
          LS.SetAttributes(clBlue, 255, 0, 0.0, psSolid);
          DefineCrayon(LS);
          FTmpBuffer.CanvasBGRA.Line(PPA, PPB);
          //*)
        end;
      end;
    end; //  if (lvpVOLUMES in FParametrageVue3D.LocalVisuPartsVue3D) then
  end;
begin
  Nb := FListeTubesVisees.GetNbElements();
  if (0 = Nb) then exit;
  MyGradientAltimetrique.setFrom(FCoinBasGauche.Z, FCoinHautDroit.z, FParametrageVue3D.ColorZMini, FParametrageVue3D.ColorZMaxi);
  for i := 0 to Nb-1 do QDrawTube(FListeTubesVisees.GetElement(i));
end;
// tracé rapide de la polygonale
// /!\ Ne pas utiliser d'optimisations de niveau supérieur à 2
// (sinon crash SIGILL en -O3 et plus)

procedure TCdrVisualisateur3DGDI.DrawDraftPolygonale();
var
  R        : TRect;
  i, NbV   : Integer;
  MyTube   : TTubeGalerie;
  PP0, PP1: TPoint;
  QDepth: double;
begin
  if (Not FDoDraw) then Exit;
  try
    try
      FTmpBuffer := TBGRABitmap.Create(Vue.Width, Vue.Height);
      R.Left   := Vue.Left;
      R.Top    := Vue.Top;
      R.Bottom := Vue.Top  + Vue.Height;
      R.Right  := Vue.Left + Vue.Width;
      DefineCrayon(FTmpBuffer, FLineStyleDraft);
      DefineBrosse(FTmpBuffer, FParametrageVue3D.ColorBackGround, bsSolid);
      FTmpBuffer.CanvasBGRA.FillRect(R);
      NbV := FListeTubesVisees.GetNbElements();
      for i := 0 to NbV - 1 do
      begin
        MyTube := FListeTubesVisees.GetElement(i);
        PP0    := ProjPoint3DF(MyTube.GetPosExtr0(), QDepth);
        PP1    := ProjPoint3DF(MyTube.GetPosStation(), QDepth);
        FTmpBuffer.CanvasBGRA.Line(PP0, PP1);
      end;
      DrawCube(FTmpBuffer);
      DrawReferentielXYZ(FTmpBuffer);
      FTmpBuffer.Draw(Vue.Canvas, 0, 0, True);
    except
    end;
  finally
    FreeAndNil(FTmpBuffer);
  end;
end;

procedure TCdrVisualisateur3DGDI.RedessinVue();
var
  ColorZMaxi, ColorZMini: TGHTopoColor;
  R        : TRect;
  QDepth: double;
  PC1, PC2: TPoint;
  NbMaillages, m: Integer;
begin
  if (Not FDoDraw) then Exit;
  if (FRedessinInProcess) then exit; // sémaphore 'FRedessinInProcess' est armé ? = On quitte (le dessin est en cours)
  FRedessinInProcess := True;        //
  try
    try
      FTmpBuffer := TBGRABitmap.Create(Vue.Width, Vue.Height);
        R.Left   := Vue.Left;
        R.Top    := Vue.Top;
        R.Bottom := Vue.Top+Vue.Height;
        R.Right  := Vue.Left+Vue.Width;
        FTmpBuffer.CanvasBGRA.Pen.Width := 0;
        DefineCrayon(FTmpBuffer, FLineStyleDefault);
        DefineBrosse(FTmpBuffer, FParametrageVue3D.ColorBackGround, bsSolid);
        FTmpBuffer.CanvasBGRA.FillRect(R);
        DrawAllTubes(FTmpBuffer);
        DrawAllSegments(FTmpBuffer);
        DrawProfilsTopo(FTmpBuffer);
        DrawCube(FTmpBuffer);            // cube (en dernier)
        DrawReferentielXYZ(FTmpBuffer);     // dessin du référentiel
        DrawAllSymboles(FTmpBuffer);
        DrawPOIs(FTmpBuffer);
        NbMaillages := FListeOfMaillages.getNbElements();
        if (NbMaillages > 0) then
        begin
          for m := 0 to NbMaillages -1 do DrawMaillage(FTmpBuffer, FListeOfMaillages.GetElement(m));

        end;
        DrawBarGradientAlti(FTmpBuffer, 4, 10, 16, 160, FParametrageVue3D.ColorZMini, FParametrageVue3D.ColorZMaxi);
        DrawReferentiel(FTmpBuffer, 100.00);

        PC1 := ProjPoint3DF(FCoinBasGauche, QDepth);
        PC2 := ProjPoint3DF(FCoinHautDroit, QDepth);
        DrawDepthHistogram(FTmpBuffer, 10 , PC1.Y, PC2.Y, 200, FParametrageVue3D.ColorZMini, FParametrageVue3D.ColorZMaxi);
        AfficherMessage('Vue 3D ok');
      //DrawBandeauDeuil(FTmpBuffer.CanvasBGRA);          // bandeau de deuil
      FTmpBuffer.Draw(Vue.Canvas, 0, 0, True);
    except
      AfficherMessageErreur('***');
    end;
    //if (Assigned(FProcRefreshControls)) then FProcRefreshControls();
  finally
    FreeAndNil(FTmpBuffer);//FTmpBuffer.Free;
    FRedessinInProcess := false; // désarmement du sémaphore d'occupation
  end;
end;

procedure TCdrVisualisateur3DGDI.ExportAnImage(const ImgWidth: integer; const QFilename: string);
const
  GRADIENT_HEIGHT_IN_PERCENT_OF_VP = 15;  // Hauteur du gradient en pourcentage de la hauteur du viewport (écran ou image)
var
  ratio: Extended;
  ImgHeight: Int64;
  MyBuffer: TBGRABitmap;
  R: TRect;
  QGH, QGW, m, NbMaillages: integer;
begin
  ratio := Vue.Height / Vue.Width;
  ImgHeight := trunc(ImgWidth * ratio);
  SetParamsTransformation(ImgWidth, ImgHeight, FParametrageVue3D.Theta, FParametrageVue3D.Phi, FParametrageVue3D.Zoom, FParametrageVue3D.MagnificationZ);
  AfficherMsgErrClass(self, 'ExportAnImage(): %s %dx%d', [QFilename, ImgWidth, ImgHeight]);
  if (Not FDoDraw) then Exit;
  try
    try
      MyBuffer := TBGRABitmap.Create(ImgWidth, ImgHeight);
        R.Create(0, 0, ImgWidth, ImgHeight);
        MyBuffer.CanvasBGRA.Pen.Width := 0;
        DefineCrayon(MyBuffer, FLineStyleDefault);
        DefineBrosse(MyBuffer, FParametrageVue3D.ColorBackGround, bsSolid);
        MyBuffer.CanvasBGRA.FillRect(R);
        DrawAllTubes(MyBuffer);
        DrawAllSegments(MyBuffer);
        DrawProfilsTopo(MyBuffer);
        DrawCube(MyBuffer);            // cube (en dernier)
        DrawReferentielXYZ(MyBuffer);     // dessin du référentiel
        DrawAllSymboles(MyBuffer);
        DrawPOIs(MyBuffer);
        NbMaillages := FListeOfMaillages.getNbElements();
        if (NbMaillages > 0) then
        begin
          for m := 0 to NbMaillages - 1 do DrawMaillage(MyBuffer, FListeOfMaillages.GetElement(m));
        end;

        QGH := round(ImgHeight * GRADIENT_HEIGHT_IN_PERCENT_OF_VP / 100);
        QGW := QGH div 5;
        DrawBarGradientAlti(MyBuffer, 10, 10, QGW, QGH, FParametrageVue3D.ColorZMini, FParametrageVue3D.ColorZMaxi);
        DrawReferentiel(MyBuffer, 100.00);
        AfficherMessage('Vue 3D ok');
      //DrawBandeauDeuil(MyBuffer.CanvasBGRA);          // bandeau de deuil
      MyBuffer.Draw(Vue.Canvas, 0, 0, True);
      MyBuffer.SaveToFile(QFilename);
    except
      AfficherMessageErreur('***');
    end;
  finally
    FreeAndNil(MyBuffer);//FTmpBuffer.Free;
    SetParamsTransformation(Vue.Width, Vue.Height, FParametrageVue3D.Theta, FParametrageVue3D.Phi, FParametrageVue3D.Zoom, FParametrageVue3D.MagnificationZ);
  end;
end;

// dessin
procedure TCdrVisualisateur3DGDI.DrawCube(const QBmp: TBGRABitmap);
var
  i: integer;
begin
  QBMP.CanvasBGRA.Pen.Color := clBlue;
  QBMP.CanvasBGRA.Pen.Width := 0;
  for i := 2 to 4 do QBMP.CanvasBGRA.Line(FCubeEnglobant[i], FCubeEnglobant[i+4]);
  QBMP.CanvasBGRA.MoveTo(FCubeEnglobant[1]);
  for i := 2 to 4 do QBMP.CanvasBGRA.LineTo(FCubeEnglobant[i]);
  QBMP.CanvasBGRA.LineTo(FCubeEnglobant[1]);
  for i := 5 to 8 do QBMP.CanvasBGRA.LineTo(FCubeEnglobant[i]);
  QBMP.CanvasBGRA.LineTo(FCubeEnglobant[5]);
end;

procedure TCdrVisualisateur3DGDI.DrawBarGradientAlti(const QBmp: TBGRABitmap; const QX, QY, QL, QH: integer; const ColorStart, ColorStop: TGHTopoColor);
var
  C: TGradientAltimetrique;
  QCMini, QCMaxi: TGHTopoColor;
begin
  if (lmvByGRADIENT_Z = FParametrageVue3D.Vue3DColorTubesMode) then
  begin
    QCMini.setFrom(ColorStart.toTColor());
    QCMaxi.setFrom(ColorStop.toTColor());
    C.setFrom(FCoinBasGauche.Z, FCoinHautDroit.Z, QCMini, QCMaxi);
    C.setRect(QX, QY, QX+QL, QY+QH);
    C.DrawGradientScale(QBmp, CalcFontHeightFromPercentOfViewport(QBmp, 2.00));
  end;
end;

procedure TCdrVisualisateur3DGDI.DrawAllSegments(const QBmp: TBGRABitmap);
var
  i, Nb: Integer;
  MySegment: TSegment3DForVisu3D;
  PP1, PP2: TPoint;
begin
  if (lvpRADIANT_SHOTS in FParametrageVue3D.Vue3DDisplayedParts) then
  begin
    Nb := FListeSegments3DForVisu3D.GetNbElements();
    if (0 = Nb) then exit;
    QBmp.CanvasBGRA.Pen.Color   := clGray;
    QBmp.CanvasBGRA.Pen.Width   := 0;
    QBmp.CanvasBGRA.Pen.Opacity := 192;
    QBmp.CanvasBGRA.Pen.Style   := psSolid;
    for i := 0 to Nb - 1 do
    begin
      MySegment := FListeSegments3DForVisu3D.GetElement(i);
      PP1 := GetVPCoordinates(MySegment.Proj3dTo2dP1.X, MySegment.Proj3dTo2dP1.Y);
      PP2 := GetVPCoordinates(MySegment.Proj3dTo2dP2.X, MySegment.Proj3dTo2dP2.Y);
      QBmp.CanvasBGRA.Line(PP1, PP2);
    end;
  end;
end;
function TCdrVisualisateur3DGDI.CalcFontHeightFromPercentOfViewport(const QBmp: TBGRABitmap; const PC: double): integer;
begin
  result := round(QBmp.Height * PC/100);

end;
procedure TCdrVisualisateur3DGDI.DrawAllSymboles(const QBmp: TBGRABitmap);
var
  i, Nb: Integer;
  MySymbole: TSymboleOrTextOrPOIForVisu3D;
  PP: TPoint;
begin
  if (lvpENTRANCES in FParametrageVue3D.Vue3DDisplayedParts) then
  begin
    Nb := FListeSymbolesOrTextForVisu3D.GetNbElements();
    if (0 = Nb) then exit;
    QBmp.CanvasBGRA.Pen.Color   := clRed;
    QBmp.CanvasBGRA.Pen.Width   := 0;
    QBmp.CanvasBGRA.Pen.Opacity := 255;
    QBmp.CanvasBGRA.Pen.Style   := psSolid;
    QBmp.CanvasBGRA.Font.Color  := clBlue;
    QBmp.CanvasBGRA.Font.Height :=  CalcFontHeightFromPercentOfViewport(QBMP, 2.00);
    QBmp.CanvasBGRA.Brush.Opacity := 128;
    for i := 0 to Nb - 1 do
    begin
      MySymbole := FListeSymbolesOrTextForVisu3D.GetElement(i);
      if (not IsInCubeLimits(MySymbole.Position)) then Continue;
      PP := GetVPCoordinates(MySymbole.Proj3dTo2d.X, MySymbole.Proj3dTo2d.Y);
      QBmp.CanvasBGRA.Brush.Color := clRed;
      QBmp.CanvasBGRA.EllipseC(PP.X, PP.Y, 4, 4);
      QBmp.CanvasBGRA.Brush.Color := FParametrageVue3D.ColorBackGround.toTColor();
      if (lvpEntranceNames in FParametrageVue3D.Vue3DDisplayedParts) then
        QBmp.CanvasBGRA.TextOut(PP.X, PP.Y, Format('%s: %s', [MakeLabelStationFromTIDBaseStation(MySymbole.IDBaseStation), MySymbole.Caption]));
     end;
  end;
end;


procedure TCdrVisualisateur3DGDI.DrawPOIs(const QBmp: TBGRABitmap);
var
  i, Nb: LongInt;
  MyPOI: TSymboleOrTextOrPOIForVisu3D;
  PP: TPoint;
begin
  if (lvpPOIs in FParametrageVue3D.Vue3DDisplayedParts) then
  begin
    Nb := FListePOIs.GetNbElements();
    if (0 = Nb) then exit;
    QBmp.CanvasBGRA.Pen.Color   := clRed;
    QBmp.CanvasBGRA.Pen.Width   := 0;
    QBmp.CanvasBGRA.Pen.Opacity := 255;
    QBmp.CanvasBGRA.Pen.Style   := psSolid;
    QBmp.CanvasBGRA.Brush.Color := clRed;

    QBmp.CanvasBGRA.Font.Color  := clRed;
    QBmp.CanvasBGRA.Font.Height := CalcFontHeightFromPercentOfViewport(QBMP, 2.0);
    QBmp.CanvasBGRA.Brush.Opacity := 128;
    for i := 0 to Nb - 1 do
    begin
      MyPOI := FListePOIs.GetElement(i);
      if (not IsInCubeLimits(MyPOI.Position)) then continue;
      //AfficherMessageErreur(MySymbole.Caption);
      PP := GetVPCoordinates(MyPOI.Proj3dTo2d.X, MyPOI.Proj3dTo2d.Y);
      QBmp.CanvasBGRA.Brush.Color := clRed;
      QBmp.CanvasBGRA.EllipseC(PP.X, PP.Y, 6, 6);
      QBmp.CanvasBGRA.Brush.Color := FParametrageVue3D.ColorBackGround.toTColor();
      QBmp.CanvasBGRA.TextOut(PP.X, PP.Y, Format('%s: %s', [MakeLabelStationFromTIDBaseStation(MyPOI.IDBaseStation), MyPOI.Caption]));
     end;
  end;

end;

procedure TCdrVisualisateur3DGDI.DrawMaillage(const QBmp: TBGRABitmap; const M: TMaillage);
var
  CF: TGHTopoColor;
  LS: TLineAttributes;
  Nb, i: Integer;
  TR: TMNTTriangleABC;
  VA, VB, VC: TMNTVertex;
  PPA, PPB, PPC: TPoint;
  QDepth: double;
begin
  AfficherMsgClass(self, 'DrawMaillage()');
  if (not Assigned(M)) then exit;
  if (not M.IsValid) then exit;
  if (lvpMAILLAGE in FParametrageVue3D.Vue3DDisplayedParts) then
  begin
    LS.SetAttributes('MaillageLS', M.ColorDefault, 128, 0, 0.0);
    CF.setFrom(M.ColorDefault, 64);
    DefineCrayon(QBmp, LS);
    DefineBrosse(QBmp, CF, bsSolid);
    Nb := M.GetNbTriangles();
    for i := 0 to Nb-1 do
    begin
      TR := M.GetTriangle(i);
      VA := M.GetVertex(TR.PointA);
      VB := M.GetVertex(TR.PointB);
      VC := M.GetVertex(TR.PointC);
      PPA := ProjPoint3DF(VA.Position, QDepth);
      PPB := ProjPoint3DF(VB.Position, QDepth);
      PPC := ProjPoint3DF(VC.Position, QDepth);
      QBmp.CanvasBGRA.Polygon([PPA, PPB, PPC]);
    end;
  end;
end;

procedure TCdrVisualisateur3DGDI.DrawReferentiel(const QBmp: TBGRABitmap; const TailleUnitaire: double);
var
  LS: TLineAttributes;
  CF: TGHTopoColor;
  PP0, PP1, PP2, PP3, PPA, PPB, PPC: TPoint;
  QDepth, LF: double;
begin
  LS.SetAttributes('Referentiel', clBlack, 255, 2, 0.5);
  CF.setFrom(clGray, 127);
  DefineCrayon(QBmp, LS);
  DefineBrosse(QBmp, CF, bsSolid);
  PP0 := ProjPoint3DF(FCoinBasGauche, QDepth);
  PP1 := ProjPointXYZ(FCoinBasGauche.X + TailleUnitaire, FCoinBasGauche.Y, FCoinBasGauche.Z, QDepth);   // Est
  PP2 := ProjPointXYZ(FCoinBasGauche.X, FCoinBasGauche.Y + TailleUnitaire, FCoinBasGauche.Z, QDepth);   // Nord
  PP3 := ProjPointXYZ(FCoinBasGauche.X, FCoinBasGauche.Y, FCoinBasGauche.Z + TailleUnitaire, QDepth);   // Altitude
  QBmp.CanvasBGRA.Line(PP0, PP1);
  QBmp.CanvasBGRA.Line(PP0, PP2);
  QBmp.CanvasBGRA.Line(PP0, PP3);
  // flèche du Nord (asymétrique pour ne pas gêner le tracé de la cavité
  LF := 0.12*TailleUnitaire;
  PPA := PP2;
  PPB := ProjPointXYZ(FCoinBasGauche.X - LF, FCoinBasGauche.Y + 0.5*TailleUnitaire, FCoinBasGauche.Z, QDepth);
  PPC := ProjPointXYZ(FCoinBasGauche.X     , FCoinBasGauche.Y + 0.5*TailleUnitaire, FCoinBasGauche.Z, QDepth);
  QBmp.CanvasBGRA.Polygon([PPA, PPB, PPC]);
end;

procedure TCdrVisualisateur3DGDI.DrawDepthHistogram(const QBmp: TBGRABitmap;
                                                    const QX1, QY1, QY2, QL: integer;
                                                    const ColorStart, ColorStop: TGHTopoColor);
var
  i, Nb, QH: Integer;
  FArrBarsVals  : TArrayOfIntegers;
  PcMax: Double;
  R: TRect;
  C: TColor;
  QR: Extended;
  // barres
  procedure DefineBars();
  var
    b: Integer;
  begin
    FArrBarsVals.redim(Nb);
    PcMax :=  FClassesRepartDepthDiagram.getMaxValue();
    for b := 0 to Nb-1 do FArrBarsVals.setFrom(b, round(FClassesRepartDepthDiagram.getFrom(b) * QL));
  end;

begin
  if (not IsZero(FParametrageVue3D.Phi)) then exit;
  AfficherMessageErreur('DrawDepthHistogram(%f: X=%d, Y1 = %d, Y2 = %d, L = %d)', [FParametrageVue3D.Phi, QX1, QY1, QY2, QL]);
  Nb := FClassesRepartDepthDiagram.getNbElements();
  QH := abs(QY1 - QY2);
  DefineBars();
  AfficherMessageErreur('-- Nb = %d - PercentMax: %.2f', [Nb, PcMax]);
  // ligne de gauche
  QBmp.CanvasBGRA.Pen.Style   := psSolid;
  QBmp.CanvasBGRA.Pen.Opacity := 255;
  QBmp.CanvasBGRA.Pen.Color   := clBlue;
  QR := QH / Nb;
  QBmp.CanvasBGRA.Brush.Style   := bsSolid;
  QBmp.CanvasBGRA.Brush.Opacity := 128;
  for i := 0 to Nb-1 do
  begin
    AfficherMessageErreur('%d = %.2f%% - %d', [i, 100 * FClassesRepartDepthDiagram.getFrom(i), FArrBarsVals.getFrom(i)]);
    R.Left   := QX1;
    R.Bottom := round(QY1 -  i    * QR);
    R.Top    := round(QY1 - (i+1) * QR);
    R.Width  := FArrBarsVals.getFrom(i);
    C := GetColorDegrade(R.Bottom, QY2, QY1, ColorStop.toTColor(), ColorStart.toTColor());
    QBmp.CanvasBGRA.Pen.Color     := C;
    QBmp.CanvasBGRA.Brush.Color   := C;
    QBmp.CanvasBGRA.Rectangle(R);
  end;
end;

procedure TCdrVisualisateur3DGDI.DefineBrosse(const QBmp: TBGRABitmap; const QColor: TGHTopoColor; const QStyle: TBrushStyle);
begin
  QBmp.CanvasBGRA.Brush.Style   := QStyle;
  QBmp.CanvasBGRA.Brush.Color   := QColor.toTColor();
  QBmp.CanvasBGRA.Brush.Opacity := QColor.Alpha;
end;
procedure TCdrVisualisateur3DGDI.DefineCrayon(const QBmp: TBGRABitmap; const LS: TLineAttributes);
begin
  QBmp.CanvasBGRA.Pen.Style   := LS.LineStyle;
  QBmp.CanvasBGRA.Pen.Color   := LS.ColorRGBA.toTColor();
  QBmp.CanvasBGRA.Pen.Opacity := LS.ColorRGBA.Alpha;
  QBmp.CanvasBGRA.Pen.Width   := LS.LineWidthInPixels;
end;
procedure TCdrVisualisateur3DGDI.DefineFonte(const QBmp: TBGRABitmap; const TS: TTexteAttributs);
begin
  QBmp.CanvasBGRA.Font.Name    := TS.FontName;
  QBmp.CanvasBGRA.Font.Style   := TS.FontStyle;
  QBmp.CanvasBGRA.Font.Color   := TS.FontColor.toTColor();
  QBmp.CanvasBGRA.Font.Opacity := 255;
end;

procedure TCdrVisualisateur3DGDI.DrawProfilsTopo(const QBmp: TBGRABitmap);
var
  Nb, i: Integer;
  procedure QDrawProfil(const P: TProfilTopo);
  var
    t, NbTraces: Integer;
    procedure QdrawTrace(const MyTrace: TListePointsOfTrace);
    var
      NbPts, s: Integer;
      VX: TPoint3DfOrderedByP;
      c: TPoint2DDepth;
      PP: TPoint;
    begin
      NbPts := MyTrace.GetNbElements();
      //AfficherMessageErreur('  QDrawTrace(): %d points', [NbPts]);
      if (NbPts < 2) then exit;
      VX := MyTrace.GetElement(0);

      DefineCrayon(QBmp, P.LineAttributes);
      c := Get2DDepthCoordinatesFromXYZ(VX.X, VX.Y, VX.Z);
      PP := GetVPCoordinates(c.X, c.Y);

      QBmp.CanvasBGRA.MoveTo(PP);
      for s := 1 to NbPts-1 do
      begin
        VX := MyTrace.GetElement(s);
        c := Get2DDepthCoordinatesFromXYZ(VX.X, VX.Y, VX.Z);
        PP := GetVPCoordinates(c.X, c.Y);
        QBmp.CanvasBGRA.LineTo(PP);
      end;
    end;
  begin

    NbTraces := P.getNbTraces();
    //AfficherMessageErreur('  QDrawProfil(): %s - %d', [P.ProfilName, NbTraces]);
    if (0 = NbTraces) then exit;
    for t := 0 to NbTraces - 1 do QdrawTrace(P.getTrace(t));
  end;
begin
  if (Not FDoDraw) then Exit;
  Nb := FListeOfMaillages.GetNbProfilsTopo();
  AfficherMsgErrClass(self, 'DrawProfilsTopo(): %d', [Nb]);
  if (Nb = 0) then exit;
  for i := 0 to Nb-1 do QDrawProfil(FListeOfMaillages.GetProfilTopo(i));
end;

procedure TCdrVisualisateur3DGDI.DrawReferentielXYZ(const QBmp: TBGRABitmap);
var
  rYo       : integer;
  X1, X2, X3: TPoint3Df;
  Rp        : TPoint;
  R1, R2, R3: TPoint2DDepth;
begin
  // calculs préliminaires
  X1.setfrom(1.0, 0.0, 0.0);
  X2.setfrom(0.0, 1.0, 0.0);
  X3.setfrom(0.0, 0.0, 1.0);
  // transformations en repère local (ne pas utiliser Get2DDepthCoordinates())
  R1 := FParametrageVue3D.ProjXYZPoint(X1);
  R2 := FParametrageVue3D.ProjXYZPoint(X2);
  R3 := FParametrageVue3D.ProjXYZPoint(X3);
  rYo := QBmp.Height - REFERENTIEL_RSZ shl 1;

  DefineBrosse(QBmp, FParametrageVue3D.ColorBackGround, bsSolid);
  DefineCrayon(QBmp, FParametrageVue3D.LineAxes);
  QBmp.CanvasBGRA.Font.Color  := clBlue;
  QBmp.CanvasBGRA.Font.Height := 16;
  QBmp.CanvasBGRA.Font.Style  := [];

  QBmp.CanvasBGRA.Pen.Width   := 0;
  QBmp.CanvasBGRA.Pen.Color   := clRed;

  Rp.Create(REFERENTIEL_RX0 + round(REFERENTIEL_RSZ * R1.X), rYo - round(REFERENTIEL_RSZ * R1.Y));

  QBmp.CanvasBGRA.Line(REFERENTIEL_RX0, rYo,Rp.X, Rp.Y);
  QBmp.CanvasBGRA.TextOut(Rp.x+2, Rp.y+2, REFERENTIEL_LBL_X);

  Rp.Create(REFERENTIEL_RX0 + round(REFERENTIEL_RSZ * R2.X), rYo - round(REFERENTIEL_RSZ * R2.Y));
  QBmp.CanvasBGRA.Line(REFERENTIEL_RX0, rYo, Rp.X, Rp.Y);
  QBmp.CanvasBGRA.TextOut(Rp.x+2, Rp.y+2, REFERENTIEL_LBL_Y);

  Rp.Create(REFERENTIEL_RX0 + round(REFERENTIEL_RSZ * R3.X), rYo - round(REFERENTIEL_RSZ * R3.Y));
  QBmp.CanvasBGRA.Line(REFERENTIEL_RX0, rYo, Rp.X, Rp.Y);
  QBmp.CanvasBGRA.TextOut(Rp.x+2, Rp.y+2, REFERENTIEL_LBL_Z);


  QBmp.CanvasBGRA.Font.Color  := clBlue;
  QBmp.CanvasBGRA.Font.Height := 20;
  QBmp.CanvasBGRA.Font.Style  := [fsBold];

  QBmp.CanvasBGRA.TextOut(10, 10, format('Orientation: %.03d° - Elevation: %.02d°', [Trunc(FParametrageVue3D.Theta + 360.00) mod 360, Trunc(FParametrageVue3D.Phi)]));

end;



procedure TCdrVisualisateur3DGDI.ExporterVue3DEnSVG_TSVGCanvas(const FileName: string; const Mode: TVue3DColorTubesMode);
var
  FSVGCanvas  : TSVGCanvas;
  MyGradientAltimetrique: TGradientAltimetrique;
  FBoundingBox: array[1 .. 8] of TPoint3Df;
  QViewBoxXMini, QViewBoxYMini: double;
  QViewBoxXMaxi, QViewBoxYMaxi: double;

  procedure QSVGAddVertex(const QX, QY, QZ: double); overload;
  var
    PT: TPoint2DDepth;
  begin
    PT := FParametrageVue3D.ProjXYZPoint(QX, QY, QZ);
    FSVGCanvas.AddVertex(PT.X, PT.Y);
  end;
  procedure QSVGAddVertex(constref PT: TPoint3Df); overload; inline;
  begin
    QSVGAddVertex(PT.X, PT.Y, PT.Z);
  end;
  procedure QSVGDrawLine(const CSSClass: string; const QX1, QY1, QZ1, QX2, QY2, QZ2: double); overload;
  var
    PT1, PT2: TPoint2DDepth;
  begin
    PT1 := FParametrageVue3D.ProjXYZPoint(QX1, QY1, QZ1);
    PT2 := FParametrageVue3D.ProjXYZPoint(QX2, QY2, QZ2);
    FSVGCanvas.DrawLine(CSSClass, PT1.X, PT1.Y, PT2.X, PT2.Y);
  end;

  procedure QSVGDrawLine(const CSSClass: string; constref P1, P2: TPoint3Df); overload; inline;
  begin
    QSVGDrawLine(CSSClass, P1.X, P1.Y, P1.Z, P2.X, P2.Y, P2.Z);
  end;


  procedure CalcViewBox();
  const QQMax = 20000000000;
  var
    PT: TPoint2DDepth;
    i: Integer;
  begin
    SetParamsTransformation(Vue.Width, Vue.Height, FParametrageVue3D.Theta, FParametrageVue3D.Phi, FParametrageVue3D.Zoom, FParametrageVue3D.MagnificationZ);
    FBoundingBox[1].setFrom(FCoinBasGauche.X, FCoinBasGauche.Y, FCoinBasGauche.Z);
    FBoundingBox[2].setFrom(FCoinHautDroit.X, FCoinBasGauche.Y, FCoinBasGauche.Z);
    FBoundingBox[3].setFrom(FCoinHautDroit.X, FCoinHautDroit.Y, FCoinBasGauche.Z);
    FBoundingBox[4].setFrom(FCoinBasGauche.X, FCoinHautDroit.Y, FCoinBasGauche.Z);

    FBoundingBox[5].setFrom(FCoinBasGauche.X, FCoinBasGauche.Y, FCoinHautDroit.Z);
    FBoundingBox[6].setFrom(FCoinHautDroit.X, FCoinBasGauche.Y, FCoinHautDroit.Z);
    FBoundingBox[7].setFrom(FCoinHautDroit.X, FCoinHautDroit.Y, FCoinHautDroit.Z);
    FBoundingBox[8].setFrom(FCoinBasGauche.X, FCoinHautDroit.Y, FCoinHautDroit.Z);
    // et calculer la BB du cube
    QViewBoxXMini :=  QQMax;
    QViewBoxYMini :=  QQMax;
    QViewBoxXMaxi := -QQMax;
    QViewBoxYMaxi := -QQMax;
    // Cube englobant = contient tout le dessin     +++

    for i := Low(FBoundingBox) to High(FBoundingBox) do
    begin
      PT := FParametrageVue3D.ProjXYZPoint(FBoundingBox[i].X, FBoundingBox[i].Y, FBoundingBox[i].Z);
      QViewBoxXMini := Math.Min(QViewBoxXMini, PT.X);
      QViewBoxYMini := Math.Min(QViewBoxYMini, PT.Y);
      QViewBoxXMaxi := Math.Max(QViewBoxXMaxi, PT.X);
      QViewBoxYMaxi := Math.Max(QViewBoxYMaxi, PT.Y);
    end;
  end;
  procedure DrawCube();
  var
    i: integer;
  begin
    //AfficherMessage('  --> Cube');
    FSVGCanvas.BeginGroupe(NOM_GROUPE_CUBE_ENGLOBANT, '', 0.00, 0.00);
      FSVGCanvas.BeginListeVertex();
        for i := 1 to 4 do QSVGAddVertex(FBoundingBox[i]);
      FSVGCanvas.EndListeVertex();
      FSVGCanvas.DrawPolygon(NOM_STYLE_FOND_CUBE_ENGLOBANT, false);
      // aretes verticales
      for i := 1 to 4 do QSVGDrawLine(NOM_STYLE_CUBE_ENGLOBANT, FBoundingBox[i], FBoundingBox[i+4]);
      // carré sup
      for i := 5 to 8 do
        QSVGDrawLine(NOM_STYLE_CUBE_ENGLOBANT, FBoundingBox[i], FBoundingBox[IIF((i+1) > 8, 5, i+1)]);
    FSVGCanvas.EndGroupe(NOM_GROUPE_CUBE_ENGLOBANT);
  end;

  procedure QDrawATube(const CSSClass: string; constref T: TTubeGalerie);
  var
    f: Integer;
    MyFacette: TFacette;
    VA, VB, VC: TVertexNormale;
    P1, P2, P3: TPoint2DDepth;
    QC: TGHTopoColor;
  begin
    for f := 0 to T.NbFacettes() - 1 do
    begin
      MyFacette := T.GetFacette(f);
      if (MyFacette.FacetteVisible(FParametrageVue3D.LookAt)) then
      begin
        VA := T.GetVertex(MyFacette.IdxVA);
        VB := T.GetVertex(MyFacette.IdxVB);
        VC := T.GetVertex(MyFacette.IdxVC);
        P1 := FParametrageVue3D.ProjXYZPoint(VA.Position);
        P2 := FParametrageVue3D.ProjXYZPoint(VB.Position);
        P3 := FParametrageVue3D.ProjXYZPoint(VC.Position);
        case FParametrageVue3D.Vue3DColorTubesMode of
          lmvByRESEAUX   : QC := T.ColorTubeOfReseau;
          lmvByENTRANCES : QC := T.ColorTubeOfEntrance;
          lmvBySECTEURS  : QC := T.ColorTubeOfSecteur;
          lmvByCODES     : QC := T.ColorTubeOfCode;
          lmvByEXPES     : QC := T.ColorTubeOfExpe;
          lmvByGRADIENT_Z: QC := MyGradientAltimetrique.GetColorFromZ(0.50 * (T.GetPosExtr0().Z + T.GetPosStation().Z));
        end;
        FSVGCanvas.DrawTriangleWithoutStyle(QC.toTColor(), QC.toTColor(), QC.Alpha, 0.01, P1.X, P1.Y,   P2.X, P2.Y,   P3.X, P3.Y);
      end;
    end;
  end;

  procedure DrawTubes();
  var
    i, Nb: Integer;
  begin
    Nb := FListeTubesVisees.GetNbElements();
    FSVGCanvas.BeginGroupe(NOM_GROUPE_CAVITE, '', 0.00, 0.00);
      for i := 0 to Nb-1 do QDrawATube(NOM_STYLE_SILHOUETTE_DEFAUT, FListeTubesVisees.GetElement(i));
    FSVGCanvas.EndGroupe(NOM_GROUPE_CAVITE);
  end;
  procedure DrawCenterlines();
  var
    i, Nb: Integer;
  begin
    Nb := FListeTubesVisees.GetNbElements();
    FSVGCanvas.BeginGroupe(NOM_GROUPE_CENTERLINES, '', 0.00, 0.00);
    FSVGCanvas.EndGroupe(NOM_GROUPE_CENTERLINES);
  end;
begin
  if (Not FDoDraw) then Exit;
  MyGradientAltimetrique.setFrom(FCoinBasGauche.Z, FCoinHautDroit.z, FParametrageVue3D.ColorZMini, FParametrageVue3D.ColorZMaxi);
  AfficherMsgClass(self, 'ExporterVue3DEnSVG(): %s ; %.0f, %.0f', [FileName, FParametrageVue3D.Theta, FParametrageVue3D.Phi]);
  CalcViewBox();
  FSVGCanvas := TSVGCanvas.Create;
  try
    if (Not FSVGCanvas.InitializeDocument(FileName,
                                          false, 'Vue 3D by ' + ApplicationName,
                                          Format('%s: Vue 3D (%.0f deg / %.0f deg / Magn: %.3f)', [ExtractFileName(FileName), FParametrageVue3D.Theta, FParametrageVue3D.Phi, FParametrageVue3D.MagnificationZ]),
                                          QViewBoxXMini, QViewBoxYMini,
                                          QViewBoxXMaxi, QViewBoxYMaxi,
                                          nil)) then Exit;

    FSVGCanvas.BeginPatternsSection;                                                              // patterns
    FSVGCanvas.EndPatternsSection;                                                                // les patterns ici
    FSVGCanvas.BeginStylesSection;                                                                // section de styles
      FSVGCanvas.WriteStyleLinePolygoneTexte(NOM_STYLE_CUBE_ENGLOBANT,
                                             FParametrageVue3D.LineCube.toTColor(), FParametrageVue3D.LineCube.getOpacity(), FParametrageVue3D.LineCube.LineWidthInMillimeters, psSolid,
                                             clWhite, 128, bsClear,
                                             DEFAULT_FONT_NAME, DEFAULT_FONT_HEIGHT_IN_MM ,
                                             clBlack, 255, [],
                                             'bounding box cube');   // styles de cube englobant

      FSVGCanvas.WriteStyleLinePolygoneTexte(NOM_STYLE_SILHOUETTE_DEFAUT,
                                             clRed   , 255, 0.01, psSolid,
                                             clYellow, 192, bsSolid,
                                             DEFAULT_FONT_NAME, DEFAULT_FONT_HEIGHT_IN_MM, clBlack, 255, [], 'Default silhouettes');

      FSVGCanvas.WriteStyleLinePolygoneTexte(NOM_STYLE_FOND_CUBE_ENGLOBANT,
                                             clBlack, 255, 0.05, psSolid,
                                             clSilver, 128, bsSolid,
                                             DEFAULT_FONT_NAME, DEFAULT_FONT_HEIGHT_IN_MM, clBlack, 255, [], 'Background');
      (*
      FSVGCanvas.WriteStyleLinePolygoneTexte(NOM_STYLE_MAILLAGE,
                                             FParametrageVue3D.LineMaillage.toTColor(), FParametrageVue3D.LineMaillage.getOpacity(), FParametrageVue3D.LineMaillage.LineWidthInMillimeters, psSolid,
                                             clOlive, 64, bsSolid,
                                             DEFAULT_FONT_NAME, DEFAULT_FONT_HEIGHT_IN_MM, clBlack, 255, [], 'Maillage');
      FSVGCanvas.WriteStyleLinePolygoneTexte(NOM_STYLE_PROFIL_DEFAUT,
                                             clGreen, 192, DEFAULT_PEN_WIDTH_IN_MM, psSolid,
                                             clGreen, 255, bsClear,
                                             DEFAULT_FONT_NAME, DEFAULT_FONT_HEIGHT_IN_MM, clBlack, 255, [], 'Profils');
      //*)
      (*
      case FParametrageVue3D.ModeRepresentation of          // les styles de galeries ici
        rgENTRANCES: ListerStylesEntrances();
        rgRESEAUX  : ListerStylesReseaux();
        rgSECTEURS : ListerStylesSecteurs();
        rgSEANCES  : ListerStylesSeances();
      end;
      //*)
      FSVGCanvas.EndStylesSection();
      FSVGCanvas.BeginDrawingSection();
        DrawCube();                 // cube
        DrawTubes();
      FSVGCanvas.EndDrawingSection();
    FSVGCanvas.FinalizeDocument();         // cloture
  finally
    FreeAndNil(FSVGCanvas);//FSVGCanvas.Free;
  end;
end;

function TCdrVisualisateur3DGDI.IsInCubeLimits(constref P: TPoint3Df): boolean;
begin
  result := InRange(P.X, FCoinBasGauche.X, FCoinHautDroit.X) AND
            InRange(P.Y, FCoinBasGauche.Y, FCoinHautDroit.Y) AND
            InRange(P.Z, FCoinBasGauche.Z, FCoinHautDroit.Z);
end;

// export de la coupe projetée en GCP  GHCaveDraw Centerline
procedure TCdrVisualisateur3DGDI.ExportGCP(const QFilename: TStringDirectoryFilename);
var
  i, Nb, NbShots, NbSplays: LongInt;
  MyVisee: TSegment3DForVisu3D;
begin
  Nb := FListeSegments3DForVisu3D.GetNbElements();
  AfficherMsgClass(self, 'ExportGCP(): %s - %d shots or splays', [QFileName, NbShots]);
  NbShots   := 0;
  NbSplays  := 0;
  //les shots
  for i := 0 to Nb-1 do
  begin
    MyVisee := FListeSegments3DForVisu3D.GetElement(i);
    if (MyVisee.TypeDeVisee <> TTypeDeVisee.tgVISEE_RADIANTE) then
    begin
      pass;
    end;
  end;

  //les splays
  for i := 0 to Nb-1 do
  begin
    MyVisee := FListeSegments3DForVisu3D.GetElement(i);
    if (MyVisee.TypeDeVisee =  TTypeDeVisee.tgVISEE_RADIANTE) then
    begin
      pass;
    end;
  end;

  //NbVisees   := self.  FBDDEntites.NbEntitesVisees;
  //NbAntennes := FBDDEntites.GetNbEntitesAntennes();

end;
end.
// export de la coupe projetée en GCP  GHCaveDraw Centerline
procedure TCdrVisualisateur3DGDI.ExportGCP(const FilenameGCP: TStringDirectoryFilename);
var
  fp: TextFile;
  i, NbVisees, NbAntennes: Integer;
  EWE  : TBaseStation;
  V    : TViseesTopo2D;
  PtOutExtr1, PtOutExtr2: TPoint2DDepth;
  PD, PG                : TPoint2DDepth;
  QID: Int64;
  procedure WriteLine(const S: string); overload; inline;
  begin
    writeln(fp, S);
  end;
  procedure WriteLine(const FMT: string; const ARGV: array of const); overload; inline;
  begin
    writeln(fp, format(FMT, ARGV);
  end;
begin
  //if (Not FDoDraw) then Exit;
  if (Abs(FVue3DParams.Phi) > 0.05) then Exit;

  NbVisees   := FBDDEntites.NbEntitesVisees;
  NbAntennes := FBDDEntites.GetNbEntitesAntennes();
  AfficherMsgClass(self, 'ExportGCP(): %s - %d shots - %d radiants', [QFileName, NbVisees, NbAntennes]));
  AssignFile(fp, QFileName);
  try
    ReWrite(fp);

    WriteLine('# Coupe projetee: %s - %s', [QFileName, DateTimeToStr(Now())]));
    WriteLine('# Theta = %s; Phi = %s; Magn = %s', [
                   FormatterNombreWithDotDecimal(FparVue3DParams.Theta, 2),
                   FormatterNombreWithDotDecimal(FparVue3DParams.Phi  , 2),
                   FormatterNombreWithDotDecimal(FparVue3DParams.CoefMagnification, 3)]);

    WriteLine('# Base points');
    WriteLine('');
    WriteLine('basepoints');
    for i := 0 to NbVisees - 1 do
    begin
      EWE := FBDDEntites.GetEntiteVisee(i);
      // NOTA: Les visées en antennes ne sont pas prises en compte par
      //       les fonctions de recherche et d'indexation de GHCaveDraw
      QID := EWE.getGHCaveDrawIDPtCenterline();
      PtOutExtr1 :=  Get2DDepthCoordinatesFromP3D(EWE.PosExtr0);
      PtOutExtr2 :=  Get2DDepthCoordinatesFromP3D(EWE.PosStation);
      WriteLine(GCD_FMT_BASEPOINTS,
                  [QID  ,
                   EWE.IDTerrain,
                   EWE.Type_Entite,
                   EWE.CouleurOfExpe, //FBDDEntites.GetCouleurEntiteByExpe(EWE).toTColor(), //EWE.ColorEntite,
                   PtOutExtr1.X, PtOutExtr1.Y, EWE.PosStation.Z,
                   PtOutExtr2.X, PtOutExtr2.Y, EWE.PosStation.Z,
                   EWE.PosPG.X, EWE.PosPG.Y, EWE.PosOPG.Z,        // EWE.X2PG, EWE.Y2PG,  EWE.Z1PB,
                   EWE.PosPD.X, EWE.PosPD.Y, EWE.PosOPD.Z         // EWE.X2PD, EWE.Y2PD,  EWE.Z1PH
                   ]);
    end;
    // antennes
    if (NbAntennes > 0) then
    begin
      for i := 0 to NbAntennes - 1 do
      begin
        EWE := FBDDEntites.GetEntiteAntenne(i);
        // NOTA: Les visées en antennes ne sont pas prises en compte par
        //       les fonctions de recherche et d'indexation de GHCaveDraw
        QID := EWE.getGHCaveDrawIDPtAntenne();//i
        PtOutExtr1 :=  Get2DDepthCoordinatesFromP3D(EWE.PosExtr0);
        PtOutExtr2 :=  Get2DDepthCoordinatesFromP3D(EWE.PosStation);
        WriteLine(GCD_FMT_BASEPOINTS,
                    [QID  ,
                     EWE.IDTerrain,
                     EWE.Type_Entite,
                     EWE.CouleurOfExpe, //FBDDEntites.GetCouleurEntiteByExpe(EWE).toTColor(), //EWE.ColorEntite,
                     PtOutExtr1.X, PtOutExtr1.Y, EWE.PosStation.Z,
                     PtOutExtr2.X, PtOutExtr2.Y, EWE.PosStation.Z,
                     EWE.PosPG.X, EWE.PosPG.Y, EWE.PosOPG.Z,        // EWE.X2PG, EWE.Y2PG,  EWE.Z1PB,
                     EWE.PosPD.X, EWE.PosPD.Y, EWE.PosOPD.Z         // EWE.X2PD, EWE.Y2PD,  EWE.Z1PH
                     ]);
      end;
    end;
    WriteLin(fp, 'endbasepoints');
  finally
    Closefile(fp);
  end;
end;
end.

