unit UnitAnalyseSegCDM;
// importation des données de CDM
// Affichage de la page du réseau CDM
// les Tjs ne sont pas traitées
// fichier source CDM générateur de l'export : cd_cdb.c version 2.0
interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, UnitPrinc, ExtCtrls, StdCtrls, ComCtrls, UnitDebug, UnitConfig , StrUtils,
  math, Printers ;

const
   max_db = 20;  // 20 détecteurs maxi entre 2 aiguillages
   pisur180=pi/180 ;
   _180surpi=180/pi;

   // décalages d'affichages
   yCrOffset=-5;
   yCrOffset2=-14;
   yTurnoutOffset=-16;
   Zmini=50;
   Zmaxi=100;
   fond_cdm=$303000;
   precision_clic=4; // précision du clic sur un élément

type
  TFormAnalyseCDM = class(TForm)
    ScrollBox1: TScrollBox;
    ImageCDM: TImage;
    GroupBox1: TGroupBox;
    CheckConnexions: TCheckBox;
    CheckAdresses: TCheckBox;
    CheckSegments: TCheckBox;
    TrackBar1: TTrackBar;
    CheckPorts: TCheckBox;
    Label1: TLabel;
    EditPort: TEdit;
    ButtonAffPort: TButton;
    GroupBoxSegment: TGroupBox;
    Label2: TLabel;
    EditSegment: TEdit;
    LabelAdresse: TLabel;
    Memo1: TMemo;
    LabelPorts: TLabel;
    EditAdresse: TEdit;
    Label3: TLabel;
    EditDetecteur: TEdit;
    ButtonAffDet: TButton;
    CheckColorationDiff: TCheckBox;
    ButtonAllumeTs: TButton;
    Label4: TLabel;
    ButtonImprime: TButton;
    PrintDialog1: TPrintDialog;
    ButtonAnime: TButton;
    ImageLoco: TImage;
    procedure FormResize(Sender: TObject);
    procedure CheckSegmentsClick(Sender: TObject);
    procedure CheckConnexionsClick(Sender: TObject);
    procedure CheckAdressesClick(Sender: TObject);
    procedure TrackBar1Change(Sender: TObject);
    procedure ButtonAffPortClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure CheckPortsClick(Sender: TObject);
    procedure EditAdresseChange(Sender: TObject);
    procedure ImageCDMClick(Sender: TObject);
    procedure FormMouseWheel(Sender: TObject; Shift: TShiftState;
      WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
    procedure ButtonAffDetClick(Sender: TObject);
    procedure CheckColorationDiffClick(Sender: TObject);
    procedure ButtonAllumeTsClick(Sender: TObject);
    procedure ButtonImprimeClick(Sender: TObject);
    procedure ImageCDMMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure ButtonAnimeClick(Sender: TObject);
  private
    { Déclarations privées }
  public
    { Déclarations publiques }
  end;

  TdetSeg= array[1..10] of record
            index,periph : integer;
            end;

  Trec_cdm = record adresse : integer;
                  distance : integer;  // distance au port d'entrée
             end;
  tDetect_cdm= array[1..max_db] of Trec_cdm;

  TAig_CDM = record
     adresse,adrtriple,temps : integer;
     modele : TEquipement ;
     ADroit : integer ;         // (TJD:identifiant extérieur) connecté sur la position droite en talon
     ADroitB : char ;           // P D S Z

     ADevie : integer ;         // (TJD:identifiant extérieur) adresse de l'élément connecté en position déviée
     ADevieB : char;            // caractère (D ou S)si aiguillage de l'élément connecté en position déviée
     APointe : integer;         // adresse de l'élément connecté en position droite ;
     APointeB : char;
     DDroit : integer;          // destination de la TJD en position droite
     DDroitB : char ;
     DDevie : integer;          // destination de la TJD en position déviée
     DDevieB : char ;

     Adevie2 : integer;
     Adevie2B : char ;
     // états d'une TJD (2 ou 4, 4 par défaut)
     EtatTJD : integer;
     // si l'aiguillage provient d'une traversée double jonction
     bdj : boolean;
     adrCDM : integer;  // adresse de la bjd dans cdm
     IndexSeg : integer; //

    end;

     // structure segment, port et periph de CDM
     TInter =
     record
       x,y,z : integer;
       typ   : string[20];
       MirrorZ : integer;
       MirrorParent : integer;
     end;

     Tport =
     record
       numero : integer;  // numéro du port
       typ    : string[20];
       x,y,z,angle : integer;
       local : integer;    // numéro de port local
       connecte : boolean;
       ConnecteAuPort : integer; // connecté au port
       ConnecteAuSeg  : integer; // connecté au segment
     end;

     TPeriph =
     record
       numero : integer;  // numéro du port
       typ    : string[20];
       pere   : integer;
       x,y,z,angle : integer;
       bright,bdown : integer;// sens et position par rapport à la voie
       location : integer; // ?? en %
       adresse : integer;
       status : integer;
       OnDevicePort : integer;
     end;

     Tsegment =
     record
       numero : integer;
       typ    : string[20]; // arc, turnout, ...
       nport,nperiph,nInter : integer;  // nombre de ports et de peripheriques et d'intersections
       port    : array of TPort;
       periph  : array of TPeriph;
       inter   : array of TInter;
       XMin,Ymin,XMax,Ymax,StartAngle,ArcAngle,Rayon,radius0,angle0,angle,lengthdev,deltadev0 : integer;
       lXc,lYc : integer;
       // turnout
       longueur,longueurDev,DeltaDev,Curveoffset : integer;
       // turnout curve
       xc0,yc0,DeltaDev2,xc,yc : integer;
       // pour signaux complexes
       adresse,adresse2,duree,adr_CDM : integer;
     end;


var Segment : array of Tsegment;
    nInter,nPeriph,nSeg,nPort,nligne,XminiCDM,XmaxiCDM,YminiCDM,YmaxiCDM,NAig_CDM,Ndet_CDM,
    DernAdrAig,SeqAdrCroisement,nb_det,IndexClic,xAig,yAig,cadre,largeur_voie,dernierSeg,
    largeurTrain,HauteurTrain : integer;
    lignes : TStrings;
    reducX,reducY,ArcTanHautLargTrain : double;
    FormAnalyseCDM: TFormAnalyseCDM;
    sBranche,NomModuleCDM : string;
    clic,premaff : boolean;
    Aig_CDM : array[0..NbreMaxiAiguillages] of TAig_CDM;
    Det_CDM : array[1..500] of integer;
    FWICImage : tBitmap;

procedure Compilation;
procedure lit_fichier_segments_cdm;
procedure affichage(imprime : boolean);
procedure Aff_train(adr: integer;train:string;x1,y1,x2,y2 :integer);
procedure D_Arc(Canvas: TCanvas; CenterX,CenterY: integer;
                rayon: Integer; StartDegres, StopDegres: Double);
function point_Sur_Segment(x,y,x1,y1,x2,y2 : integer): Boolean;

implementation

uses Importation;

{$R *.dfm}

//
function index_aigCdm(adresse : integer) : integer;
var i : integer;
    trouve : boolean;
begin
  i:=1;
  repeat
    trouve:=aig_cdm[i].adresse=adresse;
    inc(i);
  until trouve or (i>nAig_cdm);
  if trouve then result:=i-1 else result:=0;
end;

// cherche, isole et restreint la chaine s qui contient "chercher"
function isole_valeur(var s : string; chercher : string;afficheErr : boolean) : string;
var i : integer;
    serr : string;
begin
  i:=pos(chercher,s);
  if i=0 then
  begin
    if afficheErr then
    begin
      serr:='Erreur : pas de chaine '+chercher+' dans "'+s+'" Segment '+intToSTR(segment[nSeg-1].numero);
      Affiche(serr,clred);
      AfficheDebug(serr,clred);
    end;
    isole_valeur:='';
    exit;
  end;
  delete(s,1,i+length(chercher)-1);

  repeat
    if s[1]=' ' then delete(s,1,1);
  until (s[1]<>' ') or (length(s)=0);

  i:=pos(' ',s);
  if i<>0 then isole_valeur:=copy(s,1,i-1) else isole_valeur:=s;
end;

procedure compile_periph;
var i,j,erreur : integer;
    s,s2: string;
    trouve : boolean;
begin
  //------------------------Lecture periph----------------------------
  inc(nPeriph);
  segment[nSeg-1].nPeriph:=nPeriph;
  setlength(segment[nSeg-1].Periph,nPeriph);

  s:=AnsiLowerCase(lignes[nligne]);
  s2:=isole_valeur(s,'periph #',true);
  val(s2,i,erreur);
  Segment[nSeg-1].periph[nperiph-1].numero:=i;
  if debugAnalyse then AfficheDebug('Compile periph '+intToSTR(i),clYellow);

  s2:=isole_valeur(s,'obj type',true);
  Segment[nSeg-1].periph[nperiph-1].typ:=s2;
  inc(nLigne);

  s:=AnsiLowerCase(lignes[nligne]);
  s2:=isole_valeur(s,'father #',true);
  val(s2,i,erreur);
  Segment[nSeg-1].periph[nperiph-1].pere:=i;
  inc(nLigne);

  s:=AnsiLowerCase(lignes[nligne]);
  inc(nLigne);
  s2:=isole_valeur(s,'x=',true);
  val(s2,i,erreur);
  Segment[nSeg-1].periph[nperiph-1].x:=i;

  s2:=isole_valeur(s,'y=',true);
  val(s2,i,erreur);
  Segment[nSeg-1].periph[nperiph-1].y:=i;

  s2:=isole_valeur(s,'z=',true);
  val(s2,i,erreur);
  Segment[nSeg-1].periph[nperiph-1].z:=i;

  s2:=isole_valeur(s,'angle=',true);
  val(s2,i,erreur);
  Segment[nSeg-1].periph[nperiph-1].angle:=i;

  s:=AnsiLowerCase(lignes[nligne]);
  inc(nLigne);
  s2:=isole_valeur(s,'bright =',true);
  val(s2,i,erreur);
  Segment[nSeg-1].periph[nperiph-1].bRight:=i;

  s2:=isole_valeur(s,'bdown =',true);
  val(s2,i,erreur);
  Segment[nSeg-1].periph[nperiph-1].bDown:=i;

  s2:=isole_valeur(s,'location =',true);
  val(s2,i,erreur);
  Segment[nSeg-1].periph[nperiph-1].location:=i;

  s:=AnsiLowerCase(lignes[nligne]);
  // ne pas faire inc(nligne) car on va regarder la ligne suivante en indiçant en +1

  s2:=isole_valeur(s,'address',true);
  val(s2,i,erreur);
  Segment[nSeg-1].periph[nperiph-1].Adresse:=i;
  if (Segment[nSeg-1].periph[nperiph-1].typ='detector') and (i<>0) then
  begin
    // remplit le tableau des détecteurs cdm
    trouve:=false;
    for j:=1 to nDet_cdm do
    begin
      trouve:=Det_cdm[j]=i;  // si déja stocké
    end;
    if not(trouve) then
    begin
      inc(nDet_cdm);
      Det_CDM[nDet_cdm]:=i;
    end;
  end;

  s2:=isole_valeur(s,'status',true);
  val(s2,i,erreur);
  Segment[nSeg-1].periph[nperiph-1].status:=i;

  // peut être suivi de 'On device port'
  Segment[nSeg-1].periph[nperiph-1].OnDevicePort:=-1; // marqueur d'invalidité
  s:=AnsiLowerCase(lignes[nligne+1]);
  if pos('on device port',s)<>0 then
  begin
    if nDet_cdm<>0 then
    begin
      Affiche('Detecteur '+intToSTR(Det_CDM[nDet_cdm])+' sur appareil de voie ',clorange);
      AfficheDebug('Detecteur '+intToSTR(Det_CDM[nDet_cdm])+' sur appareil de voie ',clorange);
    end;
    s2:=isole_valeur(s,'on device port #',true);
    inc(nLigne);
    val(s2,i,erreur);
    Segment[nSeg-1].periph[nperiph-1].OnDevicePort:=i;
  end;

end;

procedure compile_inter;
var i,erreur : integer;
    s,s2: string;
begin
   //------------------------Lecture inter----------------------------
  if debugAnalyse then AfficheDebug('Compile intersections',clWhite);
  inc(nInter);
  segment[nSeg-1].ninter:=nInter;
  setlength(segment[nSeg-1].inter,ninter);

  s:=AnsiLowerCase(lignes[nligne]);
  inc(nLigne);
  s2:=isole_valeur(s,'x=',true);
  val(s2,i,erreur);
  Segment[nSeg-1].inter[nInter-1].x:=i;

  s2:=isole_valeur(s,'y=',true);
  val(s2,i,erreur);
  Segment[nSeg-1].inter[nInter-1].y:=i;

  s2:=isole_valeur(s,'z=',true);
  val(s2,i,erreur);
  Segment[nSeg-1].inter[nInter-1].z:=i;

  s2:=isole_valeur(s,'type:',true);
  Segment[nSeg-1].inter[nInter-1].typ:=s2;

  s:=AnsiLowerCase(lignes[nligne]);
  inc(nLigne);
  s2:=isole_valeur(s,'z=',true);
  val(s2,i,erreur);
  Segment[nSeg-1].inter[nInter-1].MirrorZ:=i;
end;

procedure compile_port;
var i,erreur : integer;
    s,s2: string;
begin
   //------------------------Lecture port----------------------------
  inc(nport);
  segment[nSeg-1].nport:=nport;
  setlength(segment[nSeg-1].port,nport);

  s:=AnsiLowerCase(lignes[nligne]);
  i:=pos('#',s);
  if i=0 then begin Affiche('Erreur structure n°12',clOrange);exit;end;
  delete(s,1,i);
  val(s,i,erreur);
  Segment[nSeg-1].port[nPort-1].numero:=i;
  if debugAnalyse then AfficheDebug('Compile port '+intToSTR(i),clLime);

  s2:=isole_valeur(s,'obj type',true);
  Segment[nSeg-1].port[nPort-1].typ:=s2; // port  dummy_port

  s2:=isole_valeur(s,'local #',true);
  if s2='' then begin Affiche('Erreur structure n°14',clOrange);exit;end;
  val(s,i,erreur);
  Segment[nSeg-1].port[nPort-1].local:=i;
  inc(nligne,2);


  s:=AnsiLowerCase(lignes[nligne]);
  s2:=isole_valeur(s,'x=',true);
  val(s2,i,erreur);
  Segment[nSeg-1].port[nPort-1].x:=i;

  s2:=isole_valeur(s,'y=',true);
  val(s2,i,erreur);
  Segment[nSeg-1].port[nPort-1].y:=i;

  s2:=isole_valeur(s,'z=',true);
  val(s2,i,erreur);
  Segment[nSeg-1].port[nPort-1].z:=i;

  s2:=isole_valeur(s,'angle=',true);
  val(s2,i,erreur);
  Segment[nSeg-1].port[nPort-1].angle:=i;

  inc(nligne);
  s:=AnsiLowerCase(lignes[nligne]);
  s2:=isole_valeur(s,'connect status:',true);
  Segment[nSeg-1].port[nPort-1].connecte:=s2='connected';

  if Segment[nSeg-1].port[nPort-1].typ='dummy_port' then exit;
  if not(Segment[nSeg-1].port[nPort-1].connecte) then exit;

  inc(nligne);
  s:=AnsiLowerCase(lignes[nligne]);
  s2:=isole_valeur(s,'connected to port #',true);
  val(s2,i,erreur);
  Segment[nSeg-1].port[nPort-1].ConnecteAuPort:=i;

  s2:=isole_valeur(s,'segment #',true);
  val(s2,i,erreur);
  Segment[nSeg-1].port[nPort-1].ConnecteAuSeg:=i;
end;

// compile le segment, tableau dans lignes[]
procedure compile_segment;
var i,erreur : integer;
    s,s2,segType,serr: string;
begin
  //------------------------Lecture segment----------------------------
  nport:=0;
  nperiph:=0;
  nInter:=0;
  inc(nSeg);
  setlength(segment,nSeg);

  Segment[nSeg-1].nport:=0;
  Segment[nSeg-1].nperiph:=0;
  Segment[nSeg-1].nInter:=0;
  s:=AnsiLowerCase(lignes[nligne]);
  i:=pos('#',s);
  if i=0 then
  begin
    serr:='Erreur structure n°2';
    Affiche(serr,clOrange);
    AfficheDebug(serr,clOrange);
    exit;
  end;
  delete(s,1,i);
  val(s,i,erreur);
  segment[nSeg-1].numero:=i;
  if i>dernierSeg then dernierSeg:=i;

  if debugAnalyse then AfficheDebug('Compile segment '+intToSTR(i),claqua);

  delete(s,1,erreur);

  s2:=isole_valeur(s,'obj type: ',true);
  if s2<>'segment' then begin Affiche('Erreur structure n°3',clOrange);exit;end;
  s2:=isole_valeur(s,'seg type: ',true);
  if s2='' then begin Affiche('Erreur structure n°4',clOrange);exit;end;
  segType:=s2;
  segment[nSeg-1].typ:=s2;     // ARC TURNOUT(champ suppl)
  inc(nligne);

  s:=AnsiLowerCase(lignes[nligne]);
  s2:=isole_valeur(s,'module :',true);
  inc(nligne);

  s:=AnsiLowerCase(lignes[nligne]);
  s2:=isole_valeur(s,'nb ports:',true);
  val(s2,i,erreur);
  segment[nSeg-1].nport:=i;

  s:=lignes[nligne];
  s2:=isole_valeur(s,'nb periphs:',true);
  val(s2,i,erreur);
  segment[nSeg-1].nperiph:=i;
  inc(nligne);

  s:=AnsiLowerCase(lignes[nligne]);
  s2:=isole_valeur(s,'zone[0]:',true);
  s2:=isole_valeur(s,'zone[1]:',true);
  inc(nligne,2);

  s:=AnsiLowerCase(lignes[nligne]);
  s2:=isole_valeur(s,'xmin =',true);
  val(s2,i,erreur);
  segment[nSeg-1].XMin:=i;
  if i<xminiCDM then XminiCDM:=i;

  s2:=isole_valeur(s,'ymin =',true);
  val(s2,i,erreur);
  segment[nSeg-1].YMin:=i;
  if i<yminiCDM then YminiCDM:=i;

  s2:=isole_valeur(s,'xmax =',true);
  val(s2,i,erreur);
  segment[nSeg-1].XMax:=i;
  if i>xMaxiCDM then xMaxiCDM:=i;

  s2:=isole_valeur(s,'ymax =',true);
  val(s2,i,erreur);
  segment[nSeg-1].Ymax:=i;
  if i>yMaxiCDM then yMaxiCDM:=i;

  // champs variables en fonction du type
  if segType='turntable' then
  begin
    inc(nligne);
    s:=AnsiLowerCase(lignes[nligne]);
    s2:=isole_valeur(s,'start angle:',true);
    val(s2,i,erreur);
    segment[nSeg-1].startangle:=i;
    s2:=isole_valeur(s,'length:',true);
    val(s2,i,erreur);
    segment[nSeg-1].longueur:=i;
    s2:=isole_valeur(s,'lengthdev',true);
    val(s2,i,erreur);
    segment[nSeg-1].lengthdev:=i;

    inc(nligne,2);    // 1 ligne vide
    s:=AnsiLowerCase(lignes[nligne]);
    s2:=isole_valeur(s,'xc0:',true);
    val(s2,i,erreur);
    segment[nSeg-1].xc0:=i;
    s2:=isole_valeur(s,'yc0:',true);
    val(s2,i,erreur);
    segment[nSeg-1].yc0:=i;
    s2:=isole_valeur(s,'xc:',true);
    val(s2,i,erreur);
    segment[nSeg-1].xc:=i;
    s2:=isole_valeur(s,'yc:',true);
    val(s2,i,erreur);
    segment[nSeg-1].yc:=i;
    exit;
  end;

  if (segType='turnout') or (segType='turnout_3way') or (segType='dbl_slip_switch') or (segType='dbl_cross_over') then
  begin
    inc(nligne);
    s:=AnsiLowerCase(lignes[nligne]);

    // en DB_DumpSegmentInfo V3 il y a 3 lignes en plus
    s2:=isole_valeur(s,'start angle:',false);
    if s2<>'' then
    begin
      val(s2,i,erreur);
      segment[nSeg-1].StartAngle:=i;

      s2:=isole_valeur(s,'arc angle:',true);
      val(s2,i,erreur);
      segment[nSeg-1].ArcAngle:=i;

      s2:=isole_valeur(s,'radius:',true);
      val(s2,i,erreur);
      segment[nSeg-1].Rayon:=i;

      inc(nligne);
      s:=AnsiLowerCase(lignes[nligne]);

      s:=AnsiLowerCase(lignes[nligne]);
      s2:=isole_valeur(s,'length:',true);
      val(s2,i,erreur);
      segment[nSeg-1].longueur:=i;

      s2:=isole_valeur(s,'lengthdev:',true);
      val(s2,i,erreur);
      segment[nSeg-1].longueurDev:=i;

      s2:=isole_valeur(s,'deltadev:',true);
      val(s2,i,erreur);
      segment[nSeg-1].deltadev:=i;

      inc(nligne);
      s:=AnsiLowerCase(lignes[nligne]);

      s2:=isole_valeur(s,'curve offset:',true);
      val(s2,i,erreur);
      segment[nSeg-1].curveoffset:=i;

      inc(nligne);
      s:=AnsiLowerCase(lignes[nligne]);
    end;

    // V2
    s2:=isole_valeur(s,'adresse =',true);
    val(s2,i,erreur);
    segment[nSeg-1].adresse:=i;
    if i=0 then
    begin
      Affiche('Aiguillage segment '+intToSTR(segment[nSeg-1].numero)+' adresse nulle',clred);
      AfficheDebug('Aiguillage segment '+intToSTR(segment[nSeg-1].numero)+' adresse nulle',clred);
    end;
    if i>DernAdrAig then DernAdrAig:=i;

    if (segType='dbl_slip_switch') or (segType='turnout_3way') then
    begin
      s:=AnsiLowerCase(lignes[nligne]);
      s2:=isole_valeur(s,'adresse2 =',true);
      val(s2,i,erreur);
      segment[nSeg-1].adresse2:=i;
      if i>DernAdrAig then DernAdrAig:=i;
    end;

    s2:=isole_valeur(s,'duree =',true);
    val(s2,i,erreur);
    segment[nSeg-1].duree:=i;
    exit;
  end;

  if segType='straight' then exit;
  if segType='pre_curve' then exit;
  if segType='bumper_stop' then exit;

  // les autres aiguillages  (TURNOUT_SYM...)
  inc(nligne);
  s:=AnsiLowerCase(lignes[nligne]);
  s2:=isole_valeur(s,'start angle:',true);
  val(s2,i,erreur);
  segment[nSeg-1].StartAngle:=i;

  s2:=isole_valeur(s,'arc angle:',true);
  val(s2,i,erreur);
  segment[nSeg-1].ArcAngle:=i;

  if segType='crossing' then
  begin
    s2:=isole_valeur(s,'radius:',true);
    val(s2,i,erreur);
    segment[nSeg-1].Rayon:=i;

    inc(nligne);
    s:=AnsiLowerCase(lignes[nligne]);
    s2:=isole_valeur(s,'length:',true);
    val(s2,i,erreur);
    segment[nSeg-1].longueur:=i;

    s2:=isole_valeur(s,'lengthdev:',true);
    val(s2,i,erreur);
    segment[nSeg-1].longueurDev:=i;

    s2:=isole_valeur(s,'deltadev:',true);
    val(s2,i,erreur);
    segment[nSeg-1].deltadev:=i;

    inc(nligne);
    s:=AnsiLowerCase(lignes[nligne]);
    s2:=isole_valeur(s,'curve offset:',true);
    val(s2,i,erreur);
    segment[nSeg-1].curveoffset:=i;
    exit;
  end;

  if segType='turnout_sym' then
  begin
    s2:=isole_valeur(s,'radius:',true);
    val(s2,i,erreur);
    segment[nSeg-1].Rayon:=i;

    inc(nligne);
    s:=AnsiLowerCase(lignes[nligne]);
    s2:=isole_valeur(s,'lengthdev:',true);
    val(s2,i,erreur);
    segment[nSeg-1].longueur:=i;
    s2:=isole_valeur(s,'deltadev:',true);
    val(s2,i,erreur);
    segment[nSeg-1].DeltaDev:=i;

    inc(nligne);
    s:=AnsiLowerCase(lignes[nligne]);
    s2:=isole_valeur(s,'curve offset:',true);
    val(s2,i,erreur);
    segment[nSeg-1].Curveoffset:=i;

    // signaux complexes
    inc(nligne);
    s:=AnsiLowerCase(lignes[nligne]);

    s2:=isole_valeur(s,'adresse =',true);
    val(s2,i,erreur);
    segment[nSeg-1].adresse:=i;
    if i>DernAdrAig then DernAdrAig:=i;

    s2:=isole_valeur(s,'duree =',true);
    val(s2,i,erreur);
    segment[nSeg-1].duree:=i;
    exit;
  end;

  if segType='turnout_curved_2r' then
  begin
    inc(nligne);
    s:=AnsiLowerCase(lignes[nligne]);
    s2:=isole_valeur(s,'radius0:',true);
    val(s2,i,erreur);
    segment[nSeg-1].radius0:=i;
    s2:=isole_valeur(s,'radius:',true);
    val(s2,i,erreur);
    segment[nSeg-1].rayon:=i;

    inc(nligne);
    s:=AnsiLowerCase(lignes[nligne]);
    s2:=isole_valeur(s,'angle0:',true);
    val(s2,i,erreur);
    segment[nSeg-1].angle0:=i;
    s2:=isole_valeur(s,'angle:',true);
    val(s2,i,erreur);
    segment[nSeg-1].angle:=i;

    inc(nligne);
    s:=AnsiLowerCase(lignes[nligne]);
    s2:=isole_valeur(s,'length:',true);
    val(s2,i,erreur);
    segment[nSeg-1].longueur:=i;
    s2:=isole_valeur(s,'deltadev0:',true);
    val(s2,i,erreur);
    segment[nSeg-1].deltadev0:=i;
    s2:=isole_valeur(s,'xc0:',true);
    val(s2,i,erreur);
    segment[nSeg-1].xc0:=i;
    s2:=isole_valeur(s,'yc0:',true);
    val(s2,i,erreur);
    segment[nSeg-1].yc0:=i;

    inc(nligne);
    s:=AnsiLowerCase(lignes[nligne]);
    s2:=isole_valeur(s,'lengthdev:',true);
    val(s2,i,erreur);
    segment[nSeg-1].lengthdev:=i;
    s2:=isole_valeur(s,'deltadev:',true);
    val(s2,i,erreur);
    segment[nSeg-1].deltadev:=i;
    s2:=isole_valeur(s,'xc:',true);
    val(s2,i,erreur);
    segment[nSeg-1].xc:=i;
    s2:=isole_valeur(s,'yc:',true);
    val(s2,i,erreur);
    segment[nSeg-1].yc:=i;

    inc(nligne);
    s:=AnsiLowerCase(lignes[nligne]);

    s2:=isole_valeur(s,'adresse =',true);
    val(s2,i,erreur);
    if i>DernAdrAig then DernAdrAig:=i;

    segment[nSeg-1].adresse:=i;
    s2:=isole_valeur(s,'duree =',true);
    val(s2,i,erreur);
    segment[nSeg-1].duree:=i;
    exit;
  end;

  if segType='turnout_curved' then
  begin
    s2:=isole_valeur(s,'radius:',true);
    val(s2,i,erreur);
    segment[nSeg-1].Rayon:=i;
    inc(nligne);
    s:=AnsiLowerCase(lignes[nligne]);

    s2:=isole_valeur(s,'length:',true);
    val(s2,i,erreur);
    segment[nSeg-1].longueur:=i;

    s2:=isole_valeur(s,'deltadev:',true);
    val(s2,i,erreur);
    segment[nSeg-1].DeltaDev:=i;

    s2:=isole_valeur(s,'xc0:',true);
    val(s2,i,erreur);
    segment[nSeg-1].xc0:=i;

    s2:=isole_valeur(s,'yc0:',true);
    val(s2,i,erreur);
    segment[nSeg-1].yc0:=i;

    inc(nligne);
    s:=AnsiLowerCase(lignes[nligne]);

    s2:=isole_valeur(s,'lengthdev:',true);
    val(s2,i,erreur);
    segment[nSeg-1].longueurDev:=i;

    s2:=isole_valeur(s,'deltadev:',true);
    val(s2,i,erreur);
    segment[nSeg-1].DeltaDev2:=i;

    s2:=isole_valeur(s,'xc:',true);
    val(s2,i,erreur);
    segment[nSeg-1].xc:=i;

    s2:=isole_valeur(s,'yc:',true);
    val(s2,i,erreur);
    segment[nSeg-1].yc:=i;

    // signaux complexes
    inc(nligne);
    s:=AnsiLowerCase(lignes[nligne]);

    s2:=isole_valeur(s,'adresse =',true);
    val(s2,i,erreur);
    segment[nSeg-1].adresse:=i;
    if i>DernAdrAig then DernAdrAig:=i;

    s2:=isole_valeur(s,'duree =',true);
    val(s2,i,erreur);
    segment[nSeg-1].duree:=i;
    exit;
  end;

  s2:=isole_valeur(s,'radius:',true);
  val(s2,i,erreur);
  segment[nSeg-1].Rayon:=i;
  inc(nligne);
  s:=AnsiLowerCase(lignes[nligne]);

  if segment[nseg-1].typ='turnout' then
  begin
    s2:=isole_valeur(s,'length:',true);
    val(s2,i,erreur);
    segment[nSeg-1].longueur:=i;

    s2:=isole_valeur(s,'lengthdev:',true);
    val(s2,i,erreur);
    segment[nSeg-1].longueurDev:=i;

    s2:=isole_valeur(s,'deltadev:',true);
    val(s2,i,erreur);
    segment[nSeg-1].DeltaDev:=i;

    inc(nligne);
    s:=AnsiLowerCase(lignes[nligne]);
    s2:=isole_valeur(s,'curve offset:',true);
    val(s2,i,erreur);
    segment[nSeg-1].CurveOffset:=i;
  end;

  if (SegType='arc') or (Segtype='curve') then
  begin
    s2:=isole_valeur(s,'lxc:',true);
    val(s2,i,erreur);
    segment[nSeg-1].lXc:=i;

    s2:=isole_valeur(s,'lyc:',true);
    val(s2,i,erreur);
    segment[nSeg-1].lYc:=i;
  end;
end;

// coordonnées image en CDM
procedure inv_coords(var x,y : integer);
begin
  x:=x-(cadre div 2);
  x:=(round(x*1000/reducX))+xMiniCDM;

  y:=y-(cadre div 2);
  y:=ymaxiCDM-(round(y*1000/reducY));
end;

// coordonnées CDM en image
procedure Coords(var x,y : integer);
begin
  x:=(round( (x - xMiniCDM) * reducX/1000 )) + (cadre div 2);
  y:=(round( (yMaxiCDM-y)   * reducY/1000 )) + (cadre div 2);
end;

function degtoRad(angle : double) : double;
begin
  degtoRad:=angle*pisur180;
end;

procedure D_Arc(Canvas: TCanvas; CenterX,CenterY: integer;
                rayon: Integer; StartDegres, StopDegres: Double);
var
  sinA,cosA   : extended;
  x1,x2,x3,x4: Integer;
  y1,y2,y3,y4: Integer;
begin
  if StopDegres<0 then setArcDirection(Canvas.Handle,AD_COUNTERCLOCKWISE)
    else setArcDirection(Canvas.Handle,AD_CLOCKWISE);

  StartDegres:=startDegres*pisur180;
  stopDegres:=StartDegres+stopDegres*pisur180;
  x1:=CenterX - rayon;
  y1:=CenterY - rayon;
  x2:=CenterX + rayon;
  y2:=CenterY + rayon;
  SinCos(StartDegres,SinA,CosA);
  x4:=CenterX + Round(rayon*CosA);
  y4:=Centery - Round(rayon*SinA);
  SinCos(StopDegres,SinA,CosA);
  x3:=CenterX + Round(rayon*CosA);
  y3:=Centery - Round(rayon*SinA);
  Canvas.Arc(x1,y1,x2,y2,x3,y3,x4,y4);
end;


// trouve si le point (x,y) est sur le segment (droit) x1,y1 x2,y2
// s*l = distance du point au segment
function point_Sur_Segment(x,y,x1,y1,x2,y2 : integer): Boolean;
var l,r,s : Double;
    p : integer;
begin
  Result:=False;
  p:=((x2-x1)*(x2-x1))+((y2-y1)*(y2-y1));
  l:=sqrt(p);
  if l<>0 then
  begin
    r:=((y1-y)*(y1-y2)-(x1-x)*(x2-x1))/(l*l);
    s:=((y1-y)*(x2-x1)-(x1-x)*(y2-y1))/(l*l);
    if (r>0) and (r<1) then
        if Abs(s*l)<=precision_clic then Result:=True;  //s*l=distance
  end;
end;

// trouve si le point x,y est sur l'arc de centre CentreX,Y de rayon ..
function point_sur_arc(x,y,CentreX,centreY,rayon : integer;StartDegres,StopDegres :double;deb : boolean) : boolean;
var a : double;
   cosA,SinA : extended;
   xArc,yArc : integer;
   trouve,inverse : boolean;
   canvas : Tcanvas;
begin
  canvas:=FormAnalyseCDM.ImageCDM.Canvas;

  //canvas.MoveTo(x,y);Canvas.LineTo(X+1,y+1);

  //Affiche(floatToSTR(startDegres)+' '+FloatToSTR(stopdegres),clyellow);
  inverse:=false;
  if stopDegres<0 then
  begin
    StartDegres:= StartDegres+StopDegres;
    StopDegres:=-StopDegres;
  end;

  StartDegres:=StartDegres*pisur180; //+3*pi/4;
  StopDegres:=StartDegres+StopDegres*pisur180;
  a:=startDegres;
  repeat
    SinCos(a,SinA,CosA);
    xArc:=CentreX+round(rayon*cosA);
    if inverse then yArc:=CentreY+round(rayon*sinA) else
      yArc:=CentreY-round(rayon*sinA);

    trouve:=(abs(x-xArc)<precision_clic) and (abs(y-yArc)<precision_clic);
     if deb then
     begin
       Affiche(intToSTR(x-xArc)+' / '+intToSTR(y-yARc),clyellow);
       canvas.Pen.Color:=clred;
       canvas.MoveTo(xArc,yArc);Canvas.LineTo(Xarc+3,yArc+3);
     end;
    a:=a+0.02;
  until (trouve) or (a>StopDegres);
  result:=trouve;
end;

procedure affiche_port(indexSeg,IndexPort: integer);
var x,y : integer;
    s : string;
begin
  x:=segment[indexSeg].port[indexport].x;
  y:=segment[indexSeg].port[IndexPort].y;
  s:=intToSTR(segment[indexSeg].port[IndexPort].numero);
  coords(x,y);
  with FormAnalyseCdm.ImageCDM.canvas do
  begin
    pen.Width:=1;
    pen.color:=clCyan;
    TextOut(x,y,s);
    Moveto(0,0);
    LineTo(x,y);
  end;
end;

procedure affiche_segment(indexSeg: integer);
var x,y,x1,y1,x2,y2 : integer;
    s : string;
begin
  x1:=segment[indexSeg].port[0].x;
  y1:=segment[indexSeg].port[0].y;
  x2:=segment[indexSeg].port[1].x;
  y2:=segment[indexSeg].port[1].y;
  x:=(x1+x2) div 2;
  y:=(y1+y2) div 2;

  s:=intToSTR(segment[indexSeg].numero);
  coords(x,y);
  with FormAnalyseCdm.ImageCDM.canvas do
  begin
    pen.Width:=1;
    pen.color:=clyellow;
    TextOut(x,y,s);
    Moveto(0,0);
    LineTo(x,y);
  end;
end;

// trace un arc selon les coordonnées CDM
procedure angle_cdm(canvas : Tcanvas;centreX,centreY:integer;debut,fin : double ;rayon : integer);
begin
  coords(centreX,centreY);
  rayon:=round(rayon*reducX) div 1000;
  D_arc(Canvas,centreX,centreY,rayon,debut,fin);
end;

// affiche le libellé de l'aiguillage du segment i
procedure coords_aff_aig(canvas : Tcanvas;i : integer);
var segType,s: string;
    a,x,y,x3,y3 : integer;
begin
  with segment[i] do
  begin
    SegType:=typ;
    if (segtype='crossing') or (segType='dbl_slip_switch') then
    begin
      x:=(port[0].x+port[2].x) div 2;
      y:=(port[0].y+port[2].y) div 2;
      coords(x,y);
      y:=y-10;
    end;
    if (segtype='turnout') or (segtype='turnout_sym')  then
    begin
      x:=(port[0].x+port[1].x) div 2;
      y:=(port[0].y+port[1].y) div 2;
      x3:=port[2].x;
      y3:=port[2].y;
      coords(x,y);
      coords(x3,y3);
      y:=y-10;
      if y3<y then x:=x-20;
    end;
    if (segtype='turnout_curved') or (segtype='turnout_curved_2r') then
    begin
      x:=(port[0].x+port[1].x) div 2;
      y:=(port[0].y+port[1].y) div 2;
      coords(x,y);
      y:=y-5;
    end;
    if (segtype='turnout_3way') then
    begin
      x:=(port[0].x+port[1].x) div 2;
      y:=(port[0].y+port[1].y) div 2;
      coords(x,y);
      y:=y-5;
    end;

    s:='A'+intToSTR(adresse)+'  ';
    canvas.Font.Color:=clLime;
    canvas.TextOut(x,y+YcrOffset,s);

    a:=adresse2;
    if a<>0 then
    begin
      s:='A'+intToSTR(adresse2)+'  ';
      Canvas.Textout(x,y+20,s);
    end;
  end;
end;

procedure ligneCDM(canvas: Tcanvas;x1,y1,x2,y2 : integer);
begin
  coords(x1,y1);
  coords(x2,y2);
  with canvas do
  begin
    MoveTo(x1,y1);
    LineTo(x2,y2);
  end;
end;

// dessine un arc orienté de xa,ya à xb,yb dans le canvas image (pas imprimante)
// coordonnées CDM
procedure arc_xy(canvas : tcanvas;centreX,centreY,rayon,xa,ya,xb,yb : integer);
var x1,y1,x2,y2: integer  ;
    arcXa,arcYa,arcxb,arcYb,angleA,angleB,
    cosA,SinA,CosB,SinB,vectoriel,AngleAB: double;
    inverse,maxA,maxB,deb : boolean;
begin
  deb:=false;
  if rayon=0 then exit;
  if deb then Affiche('Xa='+intToSTr(xa)+' Ya='+intToSTr(ya)+' Xb='+intToSTr(xb)+' Yb='+intToSTr(yb),clyellow);
  coords(centreX,centreY);
  coords(xa,ya);
  coords(xb,yb);
  rayon:=round(rayon*reducX) div 1000;


  X1:=centreX - rayon;
  Y1:=centreY - rayon;
  X2:=centreX + rayon;
  Y2:=centreY + rayon;

  // angles du point A et du point B
  // attention arcCos renvoie un angle entre 0 et pi (180°)
  // arcsin renvoie un angle entre -pi/2 et + pi/2 (-90° +90°)
  CosA:=(Xa-centreX)/rayon;
  if CosA>1 then CosA:=1;
  if CosA<-1 then CosA:=-1;
  arcXa:=arccos(CosA)*_180surpi;

  SinA:=(centreY-Ya)/rayon;
  if SinA>1 then SinA:=1;
  if SinA<-1 then SinA:=-1;
  arcYa:=arcsin(SinA)*_180surpi;

  CosB:=(Xb-centreX)/rayon;
  if CosB>1 then CosB:=1;
  if CosB<-1 then CosB:=-1;
  arcXb:=arccos(CosB)*_180surpi;

  SinB:=(centreY-Yb)/rayon;
  if SinB>1 then SinB:=1;
  if SinB<-1 then SinB:=-1;
  arcYb:=arcsin(SinB)*_180surpi;

  // quadrant des angles
  {
  quadrantA:=0;QuadrantB:=0;
  if (cosA>=0) and (sinA>=0) then quadrantA:=1;
  if (cosA<=0) and (sinA>=0) then quadrantA:=2;
  if (cosA<=0) and (sinA<=0) then quadrantA:=3;
  if (cosA>=0) and (sinA<=0) then quadrantA:=4;

  if (cosB>=0) and (sinB>=0) then quadrantB:=1;
  if (cosB<=0) and (sinB>=0) then quadrantB:=2;
  if (cosB<=0) and (sinB<=0) then quadrantB:=3;
  if (cosB>=0) and (sinB<=0) then quadrantB:=4;

  if deb then
  begin
    Affiche('QuadrantA='+intToSTR(QuadrantA),clyellow);
    Affiche('QuadrantB='+intToSTR(QuadrantB),clyellow);
  end;
  }

  vectoriel:=((rayon*rayon*cosA*cosB)+(rayon*rayon*sinA*sinB)) / (rayon*rayon);
  if vectoriel>1 then vectoriel:=1;
  if vectoriel<-1 then vectoriel:=-1;
  AngleAB:=arccos(vectoriel)*_180surpi;
  if deb then Affiche('ProdVect='+FloatToSTR(vectoriel*_180surpi)+' AngleAB='+FloatToStr(AngleAB),clYellow);


  //arcXb:=0;arcYb:=0;

  // en déduire l'angle A et B
  if arcYa<0 then angleA:=180-ArcXa+180 else angleA:=ArcXa;
  if arcYb<0 then angleB:=180-ArcXb+180 else angleB:=ArcXb;
  if angleA>=360 then angleA:=360-angleA;

  if abs(angleA-angleB)>180 then
  begin
    // ramener sur 0°
    maxA:=AngleA>angleB;
    maxB:=AngleB>angleB;
    if maxA then
    begin
      AngleB:=AngleB+360-AngleA;
      AngleA:=0;
    end;
    if maxB then
    begin
      AngleA:=AngleA+360-AngleB;
      AngleB:=0;
    end;
  end;


  inverse:=angleB>angleA;
  //inverse:=AngleAB>0;

  if deb then
  begin
    affiche('AngleXa='+intToSTR(round(ArcXa))+' AngleYa='+intToSTR(round(ArcYa)),clred);
    affiche('AngleXb='+intToSTR(round(ArcXb))+' AngleYb='+intToSTR(round(ArcYb)),clred);
  end;
  if angleA<>angleB then
  with Canvas do
  begin
    setArcDirection(Canvas.Handle,AD_COUNTERCLOCKWISE);
    if inverse then canvas.Arc(X1,Y1,X2,Y2,xa,ya,xb,yb)
    else canvas.Arc(X1,Y1,X2,Y2,xb,yb,xa,ya);
  end;
end;

// dessine un aiguillage courbe - pas encore au point
procedure dessine_aig_courbe(canvas : Tcanvas;i : integer);
var numseg,milieuX,milieuY,centreX,centreY,x0,y0,x1,y1,x2,y2,rayon : integer;
    b,pente,rayonD,alpha : double;
    coul : Tcolor;
    s : string;
begin
  numseg:=segment[i].numero;
  //Affiche('----'+intToSTR(numseg)+' aig '+intToSTR(segment[i].adresse),clorange);
  coul:=canvas.Pen.Color;
  x0:=segment[i].port[0].x;
  y0:=segment[i].port[0].y;
  x1:=segment[i].port[1].x;
  y1:=segment[i].port[1].y;
  x2:=segment[i].port[2].x;
  y2:=segment[i].port[2].y;

  // milieu des ports 0 et 1 ----------------
  milieuX:=(x0+x1) div 2;
  milieuY:=(y0+y1) div 2;

  // pente de la médiatrice des ports 0 et 1
  if y1=y0 then pente:=9999 else
  pente:=-(x1-x0)/(y1-y0);

  // équation : y=ax+b a=pente b=
  // b=y-ax
  b:=milieuY-pente*milieuX;

  //
  // méthode calculée
  str(pente:2:2, s);
  Affiche(intToSTR(segment[i].numero)+' Pente='+s,clLime);

  alpha:=arctan(pente);
  rayonD:=5750;
  centreX:=round(rayonD*cos(alpha))+MilieuX;
  centreY:=round(pente*centreX+b);
  canvas.pen.Width:=2;
  canvas.pen.color:=clred;
  //ligneCDM(canvas,milieuX,milieuY,centreX,centreY);
  //ligneCDM(canvas,x0,y0,x1,y1);
  arc_xy(canvas,centreX,centreY,round(rayonD)+250,x1,y1,x0,y0);

  centreX:=round(-rayonD*cos(alpha))+MilieuX;
  centreY:=round(pente*centreX+b);
  canvas.pen.Width:=1;
  canvas.pen.color:=clyellow;
  //ligneCDM(canvas,milieuX,milieuY,centreX,centreY);
  //ligneCDM(canvas,x0,y0,x1,y1);
  arc_xy(canvas,centreX,centreY,round(rayonD)+250,x1,y1,x0,y0);
  exit;

  // méthode directe
  centreX:=x0+3000;
  centreY:=round(pente*centreX+b);
  rayon:=round(sqrt(sqr(centreX-x0)+sqr(centreY-y0)));
  //arc_xy(centreX,centreY,rayon,x1,y1,x0,y0);

  // point de départ en vert
  //canvas.pen.color:=clLime;
  //ligneCDM(canvas,x1,y1,x1+100,y1+100);

  //milieu des ports 0 et 2 -------------------------
  milieuX:=(x0+x2) div 2;
  milieuY:=(y0+y2) div 2;

  // pente de la médiatrice des ports 0 et 2
  pente:=-(x2-x0)/(y2-y0);

  // équation : y=ax+b a=pente b=
  // b=y-ax
  b:=milieuY-pente*milieuX;

  // centre du cercle sur la médiatrice
  alpha:=arctan(pente);
  rayonD:=3000;
  centreX:=round(rayonD*cos(alpha))+MilieuX;
  centreY:=round(pente*centreX+b);
//  ligneCDM(canvas,milieuX,milieuY,centreX,centreY);


  //ligneCDM(canvas,MilieuX,MilieuY,centreX,centreY);
  //ligneCDM(canvas,x0,y0,x2,y2);

  canvas.Pen.Color:=coul;
  //arc_xy(centreX,centreY,round(rayonD),x2,y2,x0,y0);

  centreX:=x0+5000;
  centreY:=round(pente*centreX+b);
  rayon:=round(sqrt(sqr(centreX-x0)+sqr(centreY-y0)));
  arc_xy(canvas,centreX,centreY,rayon,x2,y2,x0,y0);

  // point de départ en vert
  canvas.pen.color:=clLime;
  ligneCDM(canvas,x2,y2,x2+100,y2+100);
end;


// étale un bitmap avec un nombre de pixels
// Exemple ResizeBmp(Image1.Picture.Bitmap ,100,100);
// non utilisé
procedure ResizeBmp(bitmp: TBitmap; wid, hei: Integer);
var
  TmpBmp: TBitmap;
  ARect: TRect;
begin
  TmpBmp := TBitmap.Create;
  TmpBmp.Width  := wid;
  TmpBmp.Height := hei;
  ARect := Rect(0,0, wid, hei);
  TmpBmp.Canvas.StretchDraw(ARect, Bitmp);
  bitmp.Assign(TmpBmp);
  TmpBmp.Free;
end;

// rotation matricielle autour de Centre
function XForm_Rotation(Angle : Single;Centre : TPoint) : TXForm;
var SinA,CosA: Extended;
begin
  SinCos(Angle,SinA,CosA);
  Result.eM11:=CosA;
  Result.eM12:=SinA;
  Result.eM21:=-SinA;
  Result.eM22:=CosA;
  Result.eDx:=(Centre.X - (CosA*Centre.X)) + (SinA*Centre.Y);
  Result.eDy:=(Centre.Y - (SinA*Centre.X)) - (CosA*Centre.Y);
end;

// mise à l'échelle autour du centre
function XForm_Echelle(const AZoomX : Single;const AZoomY : Single;const centre: TPoint) : TXForm;
begin
  Result.eM11:=AZoomX;
  Result.eM12:=0.0;
  Result.eM21:=0.0;
  Result.eM22:=AZoomY;
  Result.eDx:=centre.x - AZoomX*centre.x;
  Result.eDy:=centre.y - AZoomY*centre.y;
end;

function XForm_Translation(const ADistX : Integer;const ADistY : Integer) : TXForm;
begin
  Result.eM11:=1.0;
  Result.eM12:=0.0;
  Result.eM21:=0.0;
  Result.eM22:=1.0;
  Result.eDx:=ADistX;
  Result.eDy:=ADistY;
end;

// dessine le train indextrain pour son animation de déplacement
// x,y en coordonnées windows
procedure peindre(Indextrain,x,y : integer;Zoom : single);
var
  XFormScale,XFormRot,XFormXLat,XForm,XFormOld  : TXForm;  //  matrice
  GMode,x0,y0,x1,y1,x2,y2,x3,y3,larg,haut,ax,ay,l2,h2       : Integer;
  d,alpha,angle,z : double;
  sinA,cosA : extended;
  tv : array[0..3] of integer;
  ACanvas     : TCanvas;
  recta : trect;
begin
  ACanvas:=FormAnalyseCDM.ImageCDm.Canvas;
 
  l2:=largeurTrain div 2;
  h2:=hauteurTrain div 2;
  y:=y-1;  // décalage observé

  {with Acanvas do
  begin
    pen.color:=clyellow;
    MoveTo(x,y);
    LineTo(x+1,y+1);
  end; }

  //y:=y-round((hauteur div 2)*zoom);
  {x1:=x;
  y1:=y;
  x2:=x+(largeur);
  y2:=y+(hauteur);
  }
  ax:=trains[indextrain].x;
  ay:=trains[indextrain].y;
  trains[indextrain].ax:=ax;
  trains[indextrain].ay:=ay;
  trains[indextrain].x:=x;
  trains[indextrain].y:=y;

  //calcul de la pente du train en fonction de l'ancienne position (ax,ay) et de la nouvelle (x,y)
  if x<>ax then alpha:=arctan((y-ay)/(x-ax)) else alpha:=pi/2;
  angle:=alpha;
// angle:=angleX;

  GMode:=SetGraphicsMode(ACanvas.Handle, GM_ADVANCED);
  if GetWorldTransform(ACanvas.Handle, XFormOld) then  // renvoie la matrice courante dans XformOld
  begin
    // faire les transformations
    XFormRot:=XForm_Rotation(Angle,Point(l2,h2));      // rotation autour du centre
    XFormScale:=XForm_Echelle(Zoom,Zoom,point(l2,h2));   // Zoom au point central
    XFormXLat:=XForm_Translation(x-l2,y-h2);          // décalage

    // Combiner les 3 transformations
    CombineTransform(XForm,XFormRot,XFormScale);     // Xform<-- f(XformRot,XformScale)
    CombineTransform(XForm,XForm,XFormXLat);         // Xform<-- f(Xform,XformScale)

    // calcule les coordonnées des 4 points de coins de l'icone qui a tourné et zoomé
    // x0,y0               x3,y3
    // x1,y1               x2,y2
    d:=sqrt( sqr(l2)+ sqr(h2));
    //c1:=round(zoom*l2);  // pour faire la rotation à la tete du train
    //c2:=round(zoom*h2);
    z:=zoom*d;

    alpha:=ArcTanHautLargTrain+angle;
    SinCos(Alpha,SinA,CosA);
    x0:=-round(z*cosA)+x;      //c1;
    y0:=-round(z*sinA)+y;      //+c2;
    x2:=round(z*cosA)+X;       //+c1;
    y2:=round(Zoom*d*sinA)+y;  //+c2;

    alpha:=ArcTanHautLargTrain-angle;
    SinCos(Alpha,SinA,CosA);
    x1:=-round(z*cosA)+X;      //+c1;
    y1:=round(z*sinA)+y;       //+c2;
    x3:=round(z*cosA)+X;       //+c1;
    y3:=-round(z*sinA)+y;      //c2;

    // agrandir la zone un peu
    dec(x0);dec(y0);
    dec(x1);inc(y1);
    inc(x2);inc(y2);
    inc(x3);dec(y3);

    // rectangle droit contenant l'icone
    tv[0]:=x0; tv[1]:=x1; tv[2]:=x2; tv[3]:=x3;
    x0:=minIntValue(tv);
    x1:=maxIntValue(tv);
    tv[0]:=y0; tv[1]:=y1; tv[2]:=y2; tv[3]:=y3;
    y0:=minIntValue(tv);
    y1:=maxIntValue(tv);
    larg:=x1-x0;
    haut:=y1-y0;

    {
    with Acanvas do
    begin
      Pen.color:=clyellow;
      Pen.width:=1;
      rectangle(x0,y0,x1,y1);
    end;}

    // restituer l'ancien affichage
    if not(premaff) then recta:=rect(trains[IndexTrain].x0,trains[IndexTrain].y0,trains[IndexTrain].x1,trains[IndexTrain].y1);
    premaff:=false;
    Acanvas.CopyRect(recta,trains[IndexTrain].SBitmap.canvas,rect(0,0,trains[IndexTrain].x1-trains[IndexTrain].x0,trains[IndexTrain].y1-trains[IndexTrain].y0));   // copie dans Acanvas <-- SbitMap.canvas


    // sauvegarder à la nouvelle position avant d'afficher la nouvelle position
    recta:=rect(x0,y0,x1,y1);
    trains[IndexTrain].sbitmap.canvas.CopyRect(rect(0,0,larg,haut),Acanvas,recta);   // copie dans sbitmap.canvas <-- Acanvas
    //FormAnalyseCDM.Image1.Canvas.CopyRect(rect(0,0,larg,haut),Acanvas,recta );

    trains[IndexTrain].x0:=x0;
    trains[IndexTrain].y0:=y0;
    trains[IndexTrain].x1:=x1;
    trains[IndexTrain].y1:=y1;

    // appliquer les transformations dans le canvas et afficher l'icône du train
    SetWorldTransform(ACanvas.Handle,XForm);         // mettre dans le canvas
    ACanvas.Draw(0,0,FWICImage);                     // afficher le bitmap
    SetWorldTransform(ACanvas.Handle,XFormOld);
  end;
  {with Acanvas do
  begin
    pen.color:=clyellow;
    pen.width:=1;
    MoveTo(0,0);
    LineTo(x,y);
  end;}
end;


procedure Aff_train(adr: integer;train:string;x1,y1,x2,y2 :integer);
var zom : double;
    i : integer;
begin
  zom:=reducX/40;
  coords(x1,y1);
  i:=index_train_adresse(adr);
  if i>0 then peindre(i,x1,y1,zom);
end;


// Affiche le réseau CDM
procedure affichage(imprime : boolean);
const LargeurMaxi=3000;
var r : Trect;
    i,j,x1,x2,y1,y2,largeur,hauteur,rayon,centreX,centreY,Numsegment,largeurCDM,hauteurCDM,
    maxiCDM,offset,ofs,CadreImp : integer;
    SegType,s,s2,ctyp : string;
    Ech,Zoom,startAngle,StopAngle: double;
    portsSeg : array[0..40] of record x,y : integer; end;
    coul : boolean;
    canvas : tCanvas;
    couleur : Tcolor;
begin
  if (xMaxiCDM-xminiCDM=0) or (yMaxiCDM-yminiCDM=0) then exit;

  if imprime then
  begin
    if not(FormAnalyseCDM.printdialog1.Execute) then exit;
    cadre:=100;
    printer.BeginDoc;
    canvas:=printer.canvas;
    canvas.font.size:=8;
    canvas.pen.Mode:=pmCopy;
    canvas.Font.Name:='arial';
    Canvas.Brush.Style:=bsClear;
    Canvas.Brush.Color:=clWhite;

    largeur:=printer.PageWidth; // la largeur est par l'imprimante, la hauteur est calculée

    largeurCDM:=(XmaxiCDM-XminiCDM);
    hauteurCDM:=(YmaxiCDM-YminiCDM);

    maxiCDM:=max(largeurCDM,hauteurCDM);

    reducX:=1000*(largeur-(2*cadre))/maxiCDM;
    reducY:=reducX;
    largeur:=(round(largeurCDM*reducX/1000))-2*cadre;
    Hauteur:=(round(hauteurCDM*reducY/1000))-2*cadre;
  end;

  if not(imprime) then
  begin
    premaff:=true;
    cadre:=30;
    canvas:=formAnalyseCDM.ImageCDM.Canvas;
    Canvas.Brush.Style := bsClear;
    Canvas.Font.Name:='arial';
    with formAnalyseCDM do
    begin
      largeur:=3000;  // la largeur est fixée, la hauteur est calculée

      largeurCDM:=(XmaxiCDM-XminiCDM);// div 20;
      hauteurCDM:=(YmaxiCDM-YminiCDM);// div 20;

      maxiCDM:=max(largeurCDM,hauteurCDM);

      Zoom:=(2*(90-formAnalyseCDM.TrackBar1.Position)/100)+0.4;
      reducX:=1000*Zoom*(largeur)/maxiCDM;
      reducY:=reducX;

      largeur_voie:=round(reducX/8);

      largeur:=(round(largeurCDM*reducX/1000))+2*cadre;
      Hauteur:=(round(hauteurCDM*reducY/1000))+2*cadre;

      ImageCDM.Width:=largeur;
      ImageCDM.Height:=hauteur;
      ImageCDM.Picture.Bitmap.Width:=largeur;
      ImageCDM.Picture.Bitmap.height:=hauteur;

      with scrollBox1 do
      begin
        HorzScrollBar.Range:=largeur;
        HorzScrollBar.Tracking:=true;
        HorzScrollBar.Smooth:=false;   // ne pas mettre true sinon figeage dans W11 si onclique sur la trackbar!!
        VertScrollBar.Range:=hauteur;
        VertScrollBar.Tracking:=true;
        VertScrollBar.Smooth:=false;
      end;
    end;
  end;

  with Canvas do
  begin
    //effacer tout
    Pen.width:=1;
    Brush.Style:=bsSolid;
    //Brush.Color:=clblack;
    Brush.Color:=fond_cdm;
    pen.color:=clyellow;
    if not(imprime) then
    begin
      r:=rect(0,0,FormAnalyseCDM.ImageCDM.width,FormAnalyseCDM.ImageCDM.height);
      FillRect(r);
    end;
    x1:=xminiCDM;y1:=yminiCDM;
    coords(x1,y1);
    x2:=xmaxiCDM;y2:=ymaxiCDM;
    coords(x2,y2);
    Brush.Style:=bsclear;  // permet d'écrire en transparent
  end;


  coul:=false;

  // balayage ...
  for i:=0 to nSeg-1 do
  begin
   // Affiche(intToSTR(i),clyellow);
    SegType:=Segment[i].typ;
    NumSegment:=Segment[i].numero;
    coul:=not(coul);
    if imprime then canvas.pen.color:=clblack else
    begin
      if not(coloration_Diff) then begin canvas.pen.Color:=clWhite;canvas.Font.color:=clWhite;end else
      begin
        if coul then begin canvas.font.color:=clWhite;canvas.pen.Color:=clWhite;end
          else begin canvas.font.color:=clyellow;canvas.pen.color:=ClYellow;end;
      end;
    end;

    s:='S'+intToSTR(Segment[i].numero); //+' '+SegType;
    if (SegType='turnout') or (SegType='dbl_slip_switch') then
    begin
      s2:=intToSTR(Segment[i].adresse);
      s:=s+' @='+s2;
    end;

    begin
      x1:=segment[i].XMin;y1:=segment[i].YMin; coords(x1,y1);
      x2:=segment[i].XMax;y2:=segment[i].Ymax; coords(x2,y2);
      with Canvas do
      begin
        if formAnalyseCDM.CheckSegments.checked then
        begin
          Textout(x1,y1,s);
          pen.Width:=1;
          PolyGon([point(x1,y1),Point(x2,y2)]);
        end;
        {
        if SegType='straight' then
        begin
         // x1:=segment[i].xc0;
         // affiche(intToSTR(x1),clyellow);
        end;}
      end;
    end;
    //Affiche(s,ClAqua);

    // ports en vert --------------------------------------
    nport:=Segment[i].nport;

    if formAnalyseCDM.CheckPorts.checked then
    begin
      if imprime then canvas.Font.Color:=ClBlack
      else
      begin
        if coul then Canvas.font.Color:=clWhite else
          Canvas.font.color:=ClYellow;
      end;
      x1:=(Segment[i].port[0].x+Segment[i].port[1].x) div 2;
      y1:=(Segment[i].port[0].y+Segment[i].port[1].y) div 2;
      coords(x1,y1);
      s:='S'+intToSTR(NumSegment);
      Canvas.Textout(x1,y1,s);
    end;

    // balayer les ports du segment
    for j:=0 to nPort-1 do
    begin
      x1:=segment[i].port[j].X;
      y1:=segment[i].port[j].y;
      begin
        coords(x1,y1);
        if formAnalyseCDM.CheckConnexions.checked then
        begin
          with canvas do
          begin
            pen.Color:=clOrange;
            pen.width:=1;
            Ellipse(x1-5,y1-5,x1+5,y1+5);
            canvas.pen.Color:=clWhite;
          end;
        end;
        if j<40 then
        begin
          portsSeg[j].x:=x1;      // coordonnées du port j en format image.canvas
          portsSeg[j].y:=y1;
        end;
        if formAnalyseCDM.CheckPorts.checked then
        begin
          //port j
          x1:=Segment[i].port[j].x;
          y1:=Segment[i].port[j].y;

          if j=0 then
          begin
            if x1<Segment[i].port[1].x then offset:=+20 else offset:=-20;
          end;
          if j=1 then
          begin
            if x1<Segment[i].port[0].x then offset:=+20 else offset:=-20;
          end;

          coords(x1,y1);
          x1:=x1+offset;
          s:='P'+intToSTR(Segment[i].port[j].numero);
          canvas.textout(x1,y1,s);
        end;
      end;
      //Affiche(s,ClYellow);
    end;

    with Canvas do
    begin
      pen.width:=largeur_voie;
      if (segtype='crossing')  or (segType='dbl_slip_switch') then
      begin
        if imprime then pen.Color:=ClBlack else
        begin
          if coloration_diff then pen.color:=clLime else pen.color:=clWhite;
        end;
        // ne pas afficher crossing si il vient d'une BJD
        if ((segtype='crossing') and (segment[i].adr_CDM=0)) or (segType='dbl_slip_switch') then
        begin
          moveto(portsSeg[0].x,portsSeg[0].y);
          LineTo(portsSeg[2].x,portsSeg[2].y);
          moveto(portsSeg[1].x,portsSeg[1].y);
          LineTo(portsSeg[3].x,portsSeg[3].y);
        end;
        if formAnalyseCDM.CheckAdresses.checked then coords_aff_aig(canvas,i);
      end
      else
      if (segtype='turnout') or (segtype='turnout_sym')  then
      begin
        if imprime then pen.Color:=ClBlack else
        begin
          if coloration_diff then pen.color:=clLime else pen.color:=clWhite;
        end;
        moveto(portsSeg[0].x,portsSeg[0].y);
        LineTo(portsSeg[1].x,portsSeg[1].y);
        moveTo((portsSeg[0].x+portsSeg[1].x) div 2,(portsSeg[0].y+portsSeg[1].y) div 2);
        LineTo(portsSeg[2].x,portsSeg[2].y);
        if formAnalyseCDM.CheckAdresses.checked then coords_aff_aig(canvas,i);
      end
      else
      if (segtype='turnout_3way') then
      begin
        if imprime then pen.Color:=ClBlack else
        begin
          if coloration_diff then pen.color:=clLime else pen.color:=clWhite;
        end;
        moveto(portsSeg[0].x,portsSeg[0].y);LineTo(portsSeg[1].x,portsSeg[1].y);
        moveTo((portsSeg[0].x+portsSeg[1].x) div 2,(portsSeg[0].y+portsSeg[1].y) div 2);
        LineTo(portsSeg[2].x,portsSeg[2].y);
        moveTo((portsSeg[0].x+portsSeg[1].x) div 2,(portsSeg[0].y+portsSeg[1].y) div 2);
        LineTo(portsSeg[3].x,portsSeg[3].y);
        if formAnalyseCDM.CheckAdresses.checked then coords_aff_aig(canvas,i);
      end
      else
      if (segtype='turnout_curved') or (segtype='turnout_curved_2r')
      then
      begin
        if imprime then pen.Color:=ClBlack else
        begin
          if coloration_diff then pen.color:=clLime else pen.color:=clWhite;
        end;

        Moveto(portsSeg[0].x,portsSeg[0].y);
        LineTo(portsSeg[1].x,portsSeg[1].y);

        MoveTo((portsSeg[0].x+portsSeg[1].x)div 2,(portsSeg[0].y+portsSeg[1].y)div 2);
        LineTo(portsSeg[2].x,portsSeg[2].y);

        //dessine_aig_courbe(canvas,i);
        if formAnalyseCDM.CheckAdresses.checked then coords_aff_aig(canvas,i);
      end
      else
      if (segType='arc') or (segType='curve') then
      begin
        rayon:=segment[i].Rayon;
        StartAngle:=(segment[i].StartAngle/100);
        StopAngle:=segment[i].ArcAngle/100;
        centreX:=segment[i].lXc;
        CentreY:=segment[i].lyc;
        angle_cdm(canvas,centreX,CentreY,startangle,stopangle,rayon);
      end
      else
      begin
        if imprime then pen.Color:=ClBlack;
        moveto(portsSeg[0].x,portsSeg[0].y);
        for j:=1 to nPort-1 do
          LineTo(portsSeg[j].x,portsSeg[j].y);
      end;

    end;

    // périphériques en jaune --------------------------
    nperiph:=Segment[i].nperiph;
    Canvas.Pen.Color:=clYellow;
    Canvas.Font.Color:=clYellow;

    for j:=0 to nPeriph-1 do
    begin
      ctyp:=Segment[i].periph[j].typ;
      s:='Periph '+intToSTR(Segment[i].periph[j].numero)+' '+ctyp;
      s2:=intToSTR(Segment[i].periph[j].adresse);
      if Segment[i].periph[j].adresse<>0 then
      begin
        s:=s+' Adresse='+s2;
        x1:=segment[i].periph[j].X; y1:=segment[i].periph[j].y ;
        with Canvas do
        begin
          coords(x1,y1);
          if formAnalyseCDM.CheckAdresses.checked then
          begin
            ofs:=0;
            if ctyp='detector' then begin couleur:=clAqua;ofs:=-15;end;
            if ctyp='actuator' then begin couleur:=clorange;ofs:=5;end;
            if ctyp='signal' then begin couleur:=clWhite;ofs:=5;end;
            font.color:=couleur;
            pen.color:=couleur;

            textout(x1+4,y1+ofs,s2);
            pen.Width:=1;
            Ellipse(x1-5,y1-5,x1+5,y1+5);
          end;
        end;
      end;
      //Affiche(s,clOrange);
    end;

    nInter:=Segment[i].nInter;
    for j:=0 to nInter-1 do
    begin
      s:='Intersection '+Segment[i].Inter[j].typ;
      //Affiche(s,ClYellow);
    end;
  end;
  if imprime then printer.EndDoc;
end;


// supprime les espaces de la premiere position de la chaine jusqu'à ce qu'on rencontre un caractère <>' '
function Supprime_espaces(s : string) : string;
var faire : boolean;
begin
  s:=AnsiLowerCase(s);
  faire:=s<>'';
  while faire do
  begin
    if s[1]=' ' then delete(s,1,1);
    if length(s)<>0 then faire:=s[1]=' ' else faire:=false;
  end;
  result:=s;
end;

procedure liste_liens;
var i,j,connecteAuPort,ConnecteAuSeg,numPort: integer;
    segType : string;
begin
  for i:=0 to nSeg-1 do
  begin
    SegType:=Segment[i].typ;
    nport:=Segment[i].nport;
    for j:=0 to nPort-1 do
    begin
      connecteAuPort:=Segment[i].port[j].ConnecteAuPort;
      connecteAuSeg:=Segment[i].port[j].ConnecteAuSeg;
      NumPort:=Segment[i].port[j].numero;
      Affiche('Le port '+intToSTR(NumPort)+' est connecté au port '+intToSTR(connecteAuPort)+' segment '+intToSTR(ConnecteAuSeg),clyellow);
    end;
  end;
end;

// renvoie si le segment est de type aiguillage et contient une adresse
function segment_aig(s : string) : boolean;
begin
  segment_aig:=(s='turnout') or (s='dbl_slip_switch') or (s='turnout_sym') or
               (s='turnout_curved') or (s='turnout_curved_2r') or (s='turnout_3way') ; 
end;

// trouve l'index du port du segment courant connecté au segment dont l'index est Indexconnecte
function index_port_connecte(index,indexConnecte : integer;var Indexport : integer) : boolean;
var i,np1 : integer;
   trouve : boolean;
begin
   i:=0;
   np1:=segment[index].nport;
   repeat
     trouve:=segment[index].port[i].ConnecteAuSeg=segment[indexConnecte].numero;
     if not(trouve) then inc(i);
   until trouve or (i>np1-1);
   result:=trouve;
   if trouve then IndexPort:=i else indexPort:=-1;
end;


// trouve l'index du segment contenant l'aiguillage adresse
function trouve_IdSegment_aig(adresse : integer;var Id: integer) : boolean;
var i,p,p2 : integer;
    trouve : boolean;
begin
  i:=0;
  repeat
    p:=segment[i].adresse;
    p2:=segment[i].adresse2;
    //Affiche(intToSTR(p),clwhite);
    trouve:=((p=adresse) or (p2=adresse)) and segment_aig(segment[i].typ);
    inc(i);
  until (i>nSeg-1) or trouve;
  dec(i);
  if trouve then
  begin
    Id:=i;
    trouve_IdSegment_Aig:=true;
  end
  else
  begin
    id:=-1;
    trouve_IdSegment_Aig:=false;
  end
end;


// trouve les index Segment et port contenant le détecteur est detecteur
function trouve_IndexSegPortDetecteur(detecteur : integer;var indexSeg,indexPeriph : integer) : boolean;
var i,j,p,np : integer;
    trouve : boolean;
begin
  i:=0;
  trouve:=false;
  repeat
    j:=0;
    //ns:=segment[i].numero;
    //Affiche(intToSTR(ns),clred);
    np:=segment[i].nperiph; // nombre de périphériques
    repeat
      if np>0 then
      begin
        p:=segment[i].periph[j].adresse;
        //Affiche(intToSTR(p),clwhite);
        trouve:=(p=detecteur);
      end;
      inc(j);
    until (j>np-1) or trouve;
    inc(i);
  until (i>nSeg-1) or trouve;
  dec(i);dec(j);
  if trouve then
  begin
    trouve_IndexSegPortDetecteur:=true;
    indexSeg:=i;
    indexPeriph:=j;
  end
  else
  begin
    trouve_IndexSegPortDetecteur:=false;
    indexSeg:=-1;
    indexPeriph:=-1;
  end
end;

// trouve l'index du segment
function Index_Segment(seg : integer;var indexSeg : integer) : boolean;
var i,s : integer;
    trouve : boolean;
begin
  i:=0;
  repeat
    s:=segment[i].numero;
     //Affiche(intToSTR(p),clwhite);
    trouve:=(s=seg);
    inc(i);
  until (i>nSeg-1) or trouve;
  dec(i);
  if trouve then
  begin
    indexSeg:=i;
    Index_Segment:=true;
  end
  else
  begin
    indexSeg:=-1;
    Index_Segment:=false;
  end
end;


// trouve les index du segment,port
function trouve_IndexSegPort(seg,port : integer;var indexSeg,indexPort : integer) : boolean;
var i,j,p,np,ns : integer;
    trouve : boolean;
begin
  i:=0;
  repeat
    j:=0;
    np:=segment[i].nport; // nombre de ports
    ns:=segment[i].numero;
    //Affiche(intToSTR(ns),clYellow);
    repeat
      p:=segment[i].port[j].numero;
      //Affiche(intToSTR(p),clwhite);
      trouve:=(port=p) and (seg=ns);
      inc(j);
    until (j>np-1) or trouve;
    inc(i);
  until (i>nSeg-1) or trouve;
  dec(i);dec(j);
  if trouve then
  begin
    indexSeg:=i;
    indexPort:=j;
    trouve_IndexSegPort:=true;
  end
  else
  begin
    trouve_IndexSegPort:=false;
    indexSeg:=-1;
    indexPort:=-1;
  end
end;

// trouve l'index d'un port dans un segment
function trouve_IndexPort_Segment(IndexSegment,port : integer) : integer;
var i,np : integer;
    trouve : boolean;
begin
  np:=segment[indexSegment].nport;  // nombre de ports
  i:=0;
  repeat
    trouve:=segment[indexsegment].port[i].numero=port;
    inc(i);
  until (i>np-1) or trouve;
  dec(i);
  if trouve then result:=i else result:=-1;
end;

// trouve les index du port dans la liste des segments
procedure trouve_IndexPort(port : integer;var indexSeg,indexPort : integer);
var i,j,p,np : integer;
    trouve : boolean;
begin
  i:=0;
  repeat
    j:=0;
    np:=segment[i].nport;
    repeat
      p:=segment[i].port[j].numero;
     //Affiche(intToSTR(p),clwhite);
      trouve:=(port=p) ;
      inc(j);
    until (j>np-1) or trouve;
    inc(i);
  until (i>nSeg-1) or trouve;
  dec(i);dec(j);
  if trouve then
  begin
    indexSeg:=i;
    indexPort:=j;
  end
  else
  begin
    indexSeg:=-1;
    indexPort:=-1;
  end
end;

// raz du tableau des détecteurs rencontrés entre 2 aiguillages
procedure raz_detect(var t : tdetect_cdm);
var i : integer;
begin
  nb_det:=0;
  for i:=1 to max_db do
  begin
    t[i].distance:=0;t[i].adresse:=0;
  end;
end;

// trie le tableau t en fonction de la distance
procedure trier(var t :  tdetect_cdm;n :integer);
var i,j : integer;
    temp : trec_cdm;
begin
  for i:=1 to n do
  begin
    for j:=i+1 to n do
    begin
      if t[i].distance>t[j].distance then
      begin
        temp:=t[i];
        t[i]:=t[j];
        t[j]:=temp;
      end;
    end;
  end;
end;


// fonction récursive
// explore le port,segment jusqu'à trouver une adresse d'aiguillage, de croisement, ou de détecteur
// un détecteur peut être sur un port de l'aiguillage
// renvoie l'adresse de l'aiguillage ou du détecteur et dans C le port (P D S ou Z pour un détecteur)
function explore_port(seg,port : integer;var c : string) : integer;
var i,j,IdSeg,IdPort,NombrePeriph,port1,port2,portSuivant,segSuivant,portLocal,
    xp,yp,xd,yd,detect,nb_det : integer;
    typeP,serr : string;
    sdetect : Tdetect_cdm;
    trouveDet : boolean;
begin
  if seg=0 then
  begin
    explore_port:=0;
    exit;  // laisser sinon mauvais transfert de variable dans la pile dans l'itération suivante!!
  end;
  if not(trouve_IndexSegPort(seg,port,idSeg,IdPort)) then
  begin
    serr:='Erreur 1 pas trouvé le port '+intToSTR(port)+' dans la liste des segments';
    Affiche(serr,clred);
    AfficheDebug(serr,clred);
    explore_port:=0;
    c:='';
    exit;
  end;

  
  // le port a il t-il des périphériques portant des détecteurs
  NombrePeriph:=segment[idSeg].nperiph;
  if NombrePeriph<>0 then
  begin
    i:=0;
    repeat
      if segment[idSeg].periph[i].typ='detector' then
      begin
        if segment[idSeg].periph[i].OnDevicePort=Idport then  // le port du périphérique correspond au port exploré
        begin
          result:=segment[idSeg].periph[i].adresse;
          c:='Z';
          exit;
        end;
      end;
      inc(i);
    until (i>NombrePeriph-1) or (trouveDet);
  end;

  // trouver le segment contigu connecté au port de connexion
  segSuivant:=segment[idseg].port[idport].ConnecteAuSeg;
  portSuivant:=segment[idseg].port[idport].ConnecteAuPort;
  if not(trouve_IndexSegPort(segSuivant,portSuivant,idSeg,IdPort)) then
  begin
    serr:='Erreur 2 pas trouvé le port '+intToSTR(port)+' dans la liste des segments';
    Affiche(serr,clred);
    AfficheDebug(serr,clred);
    c:='';
    explore_port:=0;
    exit;
  end;

  // remonter au segment pour voir si c'est un aiguillage
  typeP:=segment[idSeg].typ;

  if typeP='dbl_slip_switch' then
  begin
    portlocal:=segment[idSeg].port[idport].local;
    case portlocal of
    0 : c:='D';
    1 : c:='S';
    2 : c:='D';
    3 : c:='S';
    end;

    // TJD 2 états
    if segment[idSeg].adresse2=0 then
    begin
      explore_port:=segment[idSeg].adresse;
      exit;
    end;

    // tjd 4 états
    // si on vient du port 0 ou 1, c'est adresse
    // si on vient du port 2 ou 3, c'est adresse2
    if (portlocal=0) or (portlocal=1) then explore_port:=segment[idSeg].adresse;
    if (portlocal=2) or (portlocal=3) then explore_port:=segment[idSeg].adresse2;
    exit;
  end;

  if segment_aig(typeP) then // est-ce un aig
  //------------- aiguillage
  begin
    portlocal:=segment[idSeg].port[idport].local;
    case portlocal of
    0 : c:='P';
    1 : c:='D';
    2 : c:='S';
    end;
    explore_port:=segment[idSeg].adresse;
    exit;
  end;
  // --- croisement
  if typeP='crossing' then
  begin
    portlocal:=segment[idSeg].port[idport].local;
    case portlocal of
    0 : c:='D';
    1 : c:='S';
    2 : c:='D';
    3 : c:='S';
    end;
    explore_port:=segment[idSeg].adresse;
    exit;
  end;

  // trouver le détecteur le plus proche.
  NombrePeriph:=segment[idSeg].nperiph;
  j:=0;detect:=0;nb_det:=0;
  if NombrePeriph<>0 then
  begin
    raz_detect(sDetect);                      // on peut rencontrer des détecteurs non appairés: ex ;  514   522   514   522
    repeat
      if segment[idSeg].periph[j].typ='detector' then
      begin
        detect:=segment[idSeg].periph[j].adresse;
        if detect<>0 then
        begin
          //if NivDebug=3 then Affichedebug('Détecteur '+inttoStr(detect),clyellow);
          // incrémenter le compteur du détecteur rencontré
          inc(nb_det);
          sDetect[nb_det].adresse:=detect ;
          // coordonnées du port
          xp:=segment[idSeg].Port[idPort].x;
          yp:=segment[idSeg].Port[idPort].y;
          // coordonnées du détecteur
          xd:=segment[idSeg].periph[j].x;
          yd:=segment[idSeg].periph[j].y;
          // calculer la distance du détecteur au port
          sDetect[nb_det].distance:=round( sqrt( sqr(xp-xd)+sqr(yp-yd) ));
        end;
      end;
      inc(j);
    until (j>NombrePeriph-1) ;
    // trier les détecteurs du segment dans l'ordre de la distance de la plus
    // courte à la plus grande au port
    if nb_det<>0 then
    begin
      trier(Sdetect,nb_det);

      // on prend le premier!!
      explore_port:=sDetect[1].adresse;
      c:='';
      exit;
    end;
  end;

  // trouver l'autre port du segment idseg
  // sur 2 ports
  NombrePeriph:=segment[idSeg].nport;
  if NombrePeriph=0 then
  begin
    c:='';
    explore_port:=0;
    exit;
  end;
  port1:=segment[idSeg].port[0].numero;
  port2:=segment[idSeg].port[1].numero;
  i:=0;
  if (port1<>portSuivant) and (segment[idSeg].port[0].connecte) then i:=explore_port(SegSuivant,port1,c);
  if (port2<>portSuivant) and (segment[idSeg].port[1].connecte) then i:=explore_port(SegSuivant,port2,c);

  explore_port:=i;
  if typeP='bumper_stop' then c:='';
  exit;

  typeP:=segment[idSeg].typ;
  // explorer l'autre port
  if (typeP='straight') or (typeP='arc') or (typeP='curve') or (typeP='pre_curve') then
  begin
    port1:=segment[idSeg].port[0].numero;
    port2:=segment[idSeg].port[1].numero;
    if port1<>portSuivant then i:=explore_port(SegSuivant,port1,c);
    if port2<>portSuivant then i:=explore_port(SegSuivant,port2,c);
    c:='';
    explore_port:=i;
    exit;
  end;

  Affiche('Segment '+typeP+' non trouvé',clred);

  explore_Port:=0;

end;

// stocke les aiguillages et les croisement dans le tableau Aig_CDM
procedure remplit_Aig_cdm;
var i,j,NumSegment,IndexSegment,IndexPort,index,index2,g,
  adresse,Adr,AdrAig,AdrAig2,adresse2,element,AdrCroisement,adrcrois,adrCDM : integer;
  t : tequipement;
  trouve : boolean;
  s,segType,c,serr : string;
begin
  // 1er segment, 1er port
  IndexSegment:=0;IndexPort:=0;
  if debugAnalyse then
  begin
    AfficheDebug(' ',clwhite);
    AfficheDebug('Liste des croisements',clWhite);
    AfficheDebug('--------------------------------',clWhite);
  end;
  NAig_CDM:=0;
  SeqAdrCroisement:=0;
  inc(DernAdrAig);

  // croisement
  for i:=0 to nseg-1 do
  begin
    segType:=segment[i].typ;
    if segType='crossing' then
    begin
      numSegment:=segment[i].numero;

      if formImportation.radioCroisSuite.checked then AdrCroisement:=DernAdrAig+SeqAdrCroisement;
      if formImportation.radioCroisBase.checked then AdrCroisement:=BaseCroisement+SeqAdrCroisement;

      segment[i].adresse:=AdrCroisement;
      inc(nAig_CDM);
      Aig_CDM[nAig_CDM].adresse:=AdrCroisement;
      Aig_CDM[nAig_CDM].modele:=crois;
      Aig_CDM[nAig_CDM].adrCDM:=segment[i].adr_CDM;

      s:='Segment '+intToSTR(numSegment)+' type='+segtype+' Adresse='+intToSTR(AdrCroisement);
      inc(SeqAdrCroisement);
      if debugAnalyse then AfficheDebug(s,clorange);

      for j:=0 to segment[i].nport-1 do
      begin
        s:='  Port '+intToSTR(j)+' ';
        // explorer les ports de l'aiguillage
        element:=explore_port(numsegment,segment[i].port[j].numero,c);
        if length(c)=0 then c:='Z';

        case j of // j=numéro de port du croisement (0 à 3)
        0 : begin s:=s+'D';Aig_cdm[nAig_cdm].ddroit:=element;if length(c)<>0 then Aig_cdm[nAig_cdm].ddroitB:=c[1];end;
        1 : begin s:=s+'S';Aig_cdm[nAig_cdm].dDevie:=element;if length(c)<>0 then Aig_cdm[nAig_cdm].ddevieB:=c[1];end;
        2 : begin s:=s+'D';Aig_cdm[nAig_cdm].ADroit:=element;if length(c)<>0 then Aig_cdm[nAig_cdm].aDroitB:=c[1];end;
        3 : begin s:=s+'S';Aig_cdm[nAig_cdm].aDevie:=element;if length(c)<>0 then Aig_cdm[nAig_cdm].aDevieB:=c[1];end;
        end;

        s:=s+' Element : '+intToStr(element)+c;
        if debugAnalyse then afficheDebug(s,clorange);

      end;
    end;
  end;

  if debugAnalyse then
  begin
    AfficheDebug('Liste des aiguillages',clWhite);
    AfficheDebug('--------------------------------',clWhite);
  end;

  for i:=0 to nseg-1 do
  begin
    segType:=segment[i].typ;
    if segment_aig(segtype) then
    begin
      numSegment:=segment[i].numero;
      adresse:=segment[i].adresse;
      if adresse<>0 then
      begin
        s:='Segment '+intToSTR(numSegment)+' type='+segtype+' Adresse='+intToSTR(adresse);
        if segType='dbl_slip_switch' then s:=s+' Adresse2='+intToSTR(segment[i].adresse2);
        if debugAnalyse then AfficheDebug(s,clorange);
        if (adresse=0) then
        begin
          serr:='   Erreur segment aiguillage ('+segType+') avec adresse nulle ; segment '+intToSTR(numSegment);
          Affiche(serr,clred);
          afficheDebug(serr,clred);
        end;


        inc(nAig_CDM);
        Aig_CDM[nAig_CDM].adresse:=adresse;
        Aig_CDM[nAig_CDM].temps:=segment[i].duree;
        Aig_CDM[nAig_CDM].bdj:=false;

        // TJD
        if (SegType='dbl_slip_switch') then
        begin
          adresse2:=segment[i].adresse2;
          Aig_CDM[nAig_CDM].modele:=tjd;
          Aig_CDM[nAig_CDM].ddroit:=Adresse2;
          Aig_CDM[nAig_CDM].ddroitB:='D';
          Aig_CDM[nAig_CDM].dDevie:=Adresse2;
          Aig_CDM[nAig_CDM].dDevieB:='S';

          if adresse2=0 then
          begin
            Aig_CDM[nAig_CDM].etatTJD:=2;
          end
          else Aig_CDM[nAig_CDM].etatTJD:=4;

          if adresse2<>0 then
          begin
            inc(nAig_CDM);   // attention deux adresses sur TJD 4 états
            // remplissage 2eme adresse
            Aig_CDM[nAig_CDM].adresse:=adresse2;
            Aig_CDM[nAig_CDM].temps:=segment[i].duree;
            Aig_CDM[nAig_CDM].modele:=tjd;
            Aig_CDM[nAig_CDM].ddroit:=Adresse;
            Aig_CDM[nAig_CDM].ddroitB:='D';
            Aig_CDM[nAig_CDM].dDevie:=Adresse;
            Aig_CDM[nAig_CDM].dDevieB:='S';
            Aig_CDM[nAig_CDM].etatTJD:=4;
          end;
        end;

        if (SegType='turnout') or (SegType='turnout_sym') or (SegType='turnout_curved') or (SegType='turnout_curved_2r') then
        begin
          Aig_CDM[nAig_CDM].modele:=aig;
          Aig_CDM[nAig_CDM].adrCDM:=segment[i].adr_CDM;
        end;
        if (SegType='turnout_3way') then
        begin
          Aig_CDM[nAig_CDM].modele:=triple;
          Aig_CDM[nAig_CDM].adrTriple:=segment[i].adresse2;
        end;


        if (segType='dbl_cross_over') then
        begin
          Affiche('Anomalie 65 : bretelle double jonction résiduelle adresse='+intToSTR(Aig_CDM[nAig_CDM].adresse),clred);
        end;

        // remplir les ports de connexion (aig et tjd)
        for j:=0 to segment[i].nport-1 do
        begin
          s:='  Port '+intToSTR(j)+' ';
          if (SegType<>'dbl_slip_switch') and (Segtype<>'turnout_3way') then
          case j of
          0 : s:=s+'P';  // le port 0 est la pointe
          1 : s:=s+'D';  // le port 1 est la position droite
          2 : s:=s+'S';  // le port 2 est la position déviée
          end;

         if (SegType='dbl_slip_switch') then //or (SegType='crossing') then
         begin
            case j of
            0 : s:=s+'D';
            1 : s:=s+'S';
            2 : s:=s+'D';
            3 : s:=s+'S';
            end;
          end;

          if (SegType='turnout_3way') then
          case j of
          0 : s:=s+'P';
          1 : s:=s+'D';
          2 : s:=s+'S';
          3 : s:=s+'S2';
          end;

          // explorer les ports de l'aiguillage
          element:=explore_port(numsegment,segment[i].port[j].numero,c);
          if length(c)=0 then c:='Z';

          s:=s+' Element : '+intToStr(element)+c;
          if debugAnalyse then AfficheDebug(s,clorange);

          if (SegType='dbl_slip_switch') then
          begin
            if adresse2<>0 then
            begin
              // 4 états
              if j=0 then begin Aig_CDM[nAig_CDM-1].Adroit:=element;if length(c)<>0 then Aig_CDM[nAig_CDM-1].AdroitB:=c[1];end;
              if j=1 then begin Aig_CDM[nAig_CDM-1].Adevie:=element;if length(c)<>0 then Aig_CDM[nAig_CDM-1].AdevieB:=c[1];end;
              if j=2 then begin Aig_CDM[nAig_CDM].Adroit:=element;if length(c)<>0 then Aig_CDM[nAig_CDM].AdroitB:=c[1];end;
              if j=3 then begin Aig_CDM[nAig_CDM].Adevie:=element;if length(c)<>0 then Aig_CDM[nAig_CDM].AdevieB:=c[1];end;
            end
            else
            begin
              // 2 états
              if j=0 then begin Aig_CDM[nAig_CDM].Adroit:=element;if length(c)<>0 then Aig_CDM[nAig_CDM].AdroitB:=c[1];end;
              if j=1 then begin Aig_CDM[nAig_CDM].Adevie:=element;if length(c)<>0 then Aig_CDM[nAig_CDM].AdevieB:=c[1];end;
              if j=2 then begin Aig_CDM[nAig_CDM].Ddroit:=element;if length(c)<>0 then Aig_CDM[nAig_CDM].DdroitB:=c[1];end;
              if j=3 then begin Aig_CDM[nAig_CDM].Ddevie:=element;if length(c)<>0 then Aig_CDM[nAig_CDM].DdevieB:=c[1];end;
            end;
          end;

          if SegType='turnout_3way' then
          begin
            if j=0 then begin Aig_CDM[nAig_CDM].APointe:=element;if length(c)<>0 then Aig_CDM[nAig_CDM].APointeB:=c[1];end;
            if j=1 then begin Aig_CDM[nAig_CDM].ADroit:=element; if length(c)<>0 then Aig_CDM[nAig_CDM].ADroitB:=c[1];end;
            if j=2 then begin Aig_CDM[nAig_CDM].ADevie:=element; if length(c)<>0 then Aig_CDM[nAig_CDM].ADevieB:=c[1];end;
            if j=3 then begin Aig_CDM[nAig_CDM].ADevie2:=element; if length(c)<>0 then Aig_CDM[nAig_CDM].ADevie2B:=c[1];end;
          end;

          if segType='crossing' then
          begin
            if j=0 then begin Aig_CDM[nAig_CDM].Adroit:=element;if length(c)<>0 then Aig_CDM[nAig_CDM].AdroitB:=c[1];end;
            if j=1 then begin Aig_CDM[nAig_CDM].Adevie:=element;if length(c)<>0 then Aig_CDM[nAig_CDM].AdevieB:=c[1];end;
            if j=2 then begin Aig_CDM[nAig_CDM].Ddroit:=element;if length(c)<>0 then Aig_CDM[nAig_CDM].DdroitB:=c[1];end;
            if j=3 then begin Aig_CDM[nAig_CDM].Ddevie:=element;if length(c)<>0 then Aig_CDM[nAig_CDM].DdevieB:=c[1];end;
          end;

          if (SegTYpe='turnout') or (SegType='turnout_curved') or (SegType='turnout_curved_2r') or (SegType='turnout_sym') then
          begin
            if j=0 then begin Aig_CDM[nAig_CDM].APointe:=element;if length(c)<>0 then Aig_CDM[nAig_CDM].APointeB:=c[1];end;
            if j=1 then begin Aig_CDM[nAig_CDM].ADroit:=element; if length(c)<>0 then Aig_CDM[nAig_CDM].ADroitB:=c[1];end;
            if j=2 then begin Aig_CDM[nAig_CDM].ADevie:=element; if length(c)<>0 then Aig_CDM[nAig_CDM].ADevieB:=c[1];end;
          end;
        end;
     end;
    end;
  end;

  // balayer pour voir si il y a des croisement de bjd
  // si oui, remplir les champs droits encore manquants des 4 aiguillages créés
  for i:=1 to NAig_CDM do
  begin
    t:=aig_cdm[i].modele;
    if (t=crois) and (aig_cdm[i].AdrCDM<>0) then     // si c'est un croisement et que son adresse cdm<>0 (c'est une BJD)
    begin
      // un croisement dispose de 4 ports
      adrcrois:=aig_cdm[i].Adresse;  // adresse croisement
      AdrCDM:=aig_cdm[i].adrCDM;

      // -------extrémités 1 et 2 du croisement
      adr:=aig_cdm[i].ADevie;     // adresse de l'aiguillage connecté à extr 1
      c:=aig_cdm[i].ADevieB;

      index:=index_aigCDM(adr);
      adrAig:=aig_cdm[index].adresse;  // adresse du premier aiguillage

      adr:=aig_cdm[i].Ddroit;     // adresse de l'aiguillage connecté à extr 2
      c:=aig_cdm[i].DdroitB;

      index2:=index_aigCDM(adr);
      adrAig2:=aig_cdm[index2].adresse;  // adresse du 2ème aiguillage

      aig_CDM[index].Adroit:=adrAig2;
      aig_CDM[index].AdroitB:='D';

      aig_CDM[index2].Adroit:=adrAig;
      aig_CDM[index2].AdroitB:='D';

      // -------extrémités 3 et 4 du croisement
      adr:=aig_cdm[i].ADroit;     // adresse de l'aiguillage connecté à extr 3  =1
      c:=aig_cdm[i].ADroitB;

      index:=index_aigCDM(adr);
      adrAig:=aig_cdm[index].adresse;  // adresse du premier aiguillage =1

      adr:=aig_cdm[i].Ddevie;     // adresse de l'aiguillage connecté à extr 4  = 12
      c:=aig_cdm[i].DdevieB;

      index2:=index_aigCDM(adr);
      adrAig2:=aig_cdm[index2].adresse;  // adresse du 2ème aiguillage  = 12

      aig_CDM[index].Adroit:=adrAig2;
      aig_CDM[index].AdroitB:='D';


      aig_CDM[index2].Adroit:=adrAig;
      aig_CDM[index2].AdroitB:='D';

   end;
  end;

  // finir balayer les aiguillages pour voir si une extrémité est connectée à une bjd
  for i:=1 to NAig_CDM do
  begin
    t:=aig_cdm[i].modele;
    if (t=aig) then // or (t=crois) or (t=tjd) or (t=triple) then
    begin
      adr:=aig_cdm[i].adresse;
      g:=ord(aig_cdm[i].AdroitB);
      if (g>=ord('0')) and (g<=ord('9')) then
      begin
        // rebalayer les aiguillages qui ont une adresseCDM<>0 et dont l'adresse est = à celle de l'aiguillage
        j:=1;
        repeat
          trouve:=(aig_cdm[j].Apointe=adr) and (aig_cdm[j].modele=aig);
          inc(j);
        until (j>NAig_CDM) or trouve;
        if trouve then
        begin
          dec(j);
          aig_cdm[i].Adroit:=aig_cdm[j].adresse;
          aig_cdm[i].AdroitB:='P';
        end;
      end;

      g:=ord(aig_cdm[i].AdevieB);
      if (g>=ord('0')) and (g<=ord('9')) then
      begin
         // rebalayer les aiguillages qui ont une adresseCDM<>0 et dont l'adresse est = à celle de l'aiguillage
        j:=1;
        repeat
          trouve:=(aig_cdm[j].Apointe=adr) and (aig_cdm[j].modele=aig);
          inc(j);
        until (j>NAig_CDM) or trouve;
        if trouve then
        begin
          dec(j);
          aig_cdm[i].Adevie:=aig_cdm[j].adresse;
          aig_cdm[i].AdevieB:='P';
        end;
      end;

      // si c'est la pointe qui est connectée à une bjd
      g:=ord(aig_cdm[i].APointeB);
      if (g>=ord('0')) and (g<=ord('9')) then
      begin
        // rebalayer les aiguillages qui ont une adresseCDM<>0 et dont l'adresse est = à celle de l'aiguillage
        j:=1;
        repeat
          trouve:=(aig_cdm[j].Apointe=adr) and (aig_cdm[j].modele=aig);
          inc(j);
        until (j>NAig_CDM) or trouve;
        if trouve then
        begin
          dec(j);
          aig_cdm[i].APointe:=aig_cdm[j].adresse;
          aig_cdm[i].APointeB:='P';
        end;
      end;

    end;
  end;

  if nAig_CDM=0 then affiche('Aucun aiguillage avec adresse',clyellow);
end;

// trouve le port de destination sur un segment en fonction du port d'origine 'port'
// exemple : (281,81) renvoie 82
function trouve_port_suivant(IndexSeg,port : integer) : integer;
var ctype: string;
    pOrg,pDest,p1,p2 : integer;
    trouve : boolean;
begin
  ctype:=segment[indexSeg].typ;

  if (ctype='crossing') or (ctype='dbl_slip_switch') then
  begin
    // déterminer le numéro du port d'origine
    pOrg:=0;
    repeat
      trouve:=segment[indexSeg].port[pOrg].numero=port;
      if not(trouve) then inc(pOrg);
    until trouve or (pOrg>3);
    if pOrg=0 then pDest:=2;  // port origine, port destination
    if pOrg=1 then pDest:=3;
    if pOrg=2 then pDest:=0;
    if pOrg=3 then pDest:=1;
    trouve_port_suivant:=segment[indexSeg].port[pDest].numero;
    exit;
  end;

  // autre aiguillage
  if segment_aig(ctype) then
  begin
    // déterminer le numéro du port d'origine
    pOrg:=0;
    repeat
      trouve:=segment[indexSeg].port[pOrg].numero=port;
      if not(trouve) then inc(pOrg);
    until trouve or (pOrg>3);
    if pOrg=0 then pDest:=1;  // port origine, port destination  : aiguillage en position droite
    if pOrg=1 then pDest:=0;
    if pOrg=2 then pDest:=0;
    if pOrg=3 then pDest:=0;  // si c'est un triple
    trouve_port_suivant:=segment[indexSeg].port[pDest].numero;
    exit;
  end;

  if ctype='turntable' then
  begin
    // on repart d'où on vient
    trouve_port_suivant:=port;
    exit;
  end;

  // si section simple
  p1:=segment[indexSeg].port[0].numero;
  p2:=segment[indexSeg].port[1].numero;
  if p1=port then
  begin
    trouve_port_suivant:=p2;
  end;
  if p2=port then
  begin
    trouve_port_suivant:=p1;
    exit;
  end;
end;

// trouve le segment suivant du segment index, opposé au port
//     170   seg=169    171  |  173   Seg=172   174   |
// exemple trouve_seg_suivant(169,171) => 172 173 dans IndexSegSuiv,IndexportSuiv
function trouve_seg_suivant(IndexSeg,Indexport : integer;var IndexSegSuiv,IndexportSuiv : integer) : boolean;
var PortSuiv,NumSegment,ips,SegSuiv,np : integer;
    ctype : string;
begin
  //Affiche('trouve le seg suivant au index '+intToSTR(IndexSeg)+' port '+intToSTR(Indexport),clyellow);
  if indexSeg<0 then
  begin
    Affiche('Erreur indexSeg<0',clred);
    result:=false;
    exit;
  end;
  numSegment:=segment[indexSeg].numero;
  numport:=segment[indexSeg].port[indexPort].numero;
  if nivDebug=3 then AfficheDebug('trouve le seg suivant au '+intToSTR(numSegment)+' port '+intToSTR(numport),clyellow);
  np:=segment[indexSeg].nport;
  if np<2 then
  begin
    // valable uniquement sur un segment à au moins 2 ports
    AfficheDebug('Erreur 68 : recherche sur un segment '+intToSTR(segment[indexSeg].numero)+' <2 ports',clred);
    trouve_seg_suivant:=false;
    IndexSegSuiv:=-1;
    indexportSuiv:=-1;
    exit;
  end;

  ctype:=segment[indexSeg].typ;

  SegSuiv:=segment[indexSeg].port[Indexport].ConnecteAuSeg;
  PortSuiv:=segment[indexSeg].port[Indexport].ConnecteAuPort;
  //trouver l'index du port du segment
  if segment[indexSeg].port[Indexport].connecte then
  begin
    if trouve_IndexSegPort(segSuiv,portSuiv,indexSeg,ips) then
    begin
      IndexSegSuiv:=indexSeg;
      IndexPortSuiv:=ips;
      //portsuiv:=segment[indexSeg].port[indexport].numero;
      result:=true;
      exit;
    end
    else
    begin
      Affiche('Pas trouvé l''index du segment port: '+intToSTR(SegSuiv)+' '+intToSTR(portSuiv),clred);
      AfficheDebug('Pas trouvé l''index du segment port: '+intToSTR(SegSuiv)+' '+intToSTR(portSuiv),clOrange);
      result:=false;
    end;
  end
  else
  begin
    //afficheDebug('Port non connecté',clOrange);
    result:=false;
  end;
end;

// vérifier si un détecteur est dans le champ d'une ligne
// exemple s='A21,533,568,566' det=533 résultat vrai
function presence_detecteur(s : string;det : integer) : boolean;
begin
  result:=pos(','+intToSTR(det),s)<>0;
end;


// renvoie le dernier champ d'une chaine séparés par des virgules
// s='aa,bb,ccc,ddd,ee'  : renvoie ee
function dernier_champ(s : string) : string;
var i,l : integer;
begin
  if s='' then
  begin
    result:='';
    exit;
  end;
  l:=length(s);
  i:=l+1;
  repeat
    dec(i);
  until (s[i]=',') or (i=1);
  if s[i]=',' then
  begin
    result:=copy(s,i+1,l-i);
    exit;
  end;
  result:='';
  exit;
end;


// créée la branche depuis un aiguillage dont un des ports est un détecteur
// indexSeg,port : index du segment et port de départ (0=pointe,1=droit,2=dévié)
// AdrAig : adresse de l'aiguillage qui a servi de départ
// et remplit la branche (sBranche)
procedure cree_branche_aig(indexSeg,Indexport,adrAig,NbreMaxiAigRencontres : integer);
var i,j,k,l,n,nper,nport,naig,IndexportSuivant,indexSegSuivant,NombrePeriph,
    detecteur,indexElBranche,AdrAigRencontre,numSegment,
    rien,erreur,det2,nb_det,xp,yp,xd,yd,Adr2,AdrDer,idx : integer;
    trouve,doublon : boolean;
    s,ss,ctype,derch: string;
    sdetect : Tdetect_cdm;
begin
  if debugBranche then AfficheDebug('Création branche depuis aiguillage '+intToSTR(AdrAig),clyellow);
  raz_detect(sdetect);
  AdrAigRencontre:=0;
  i:=0; Naig:=0;
  repeat
    // trouver le port du détecteur
    nper:=Segment[indexSeg].nperiph; // y a t-il des périph (recherche det)
    if nper<>0 then
    begin
      n:=0;
      repeat
        k:=Segment[indexSeg].periph[n].OnDevicePort;      // numéro de port sur lequel est le détecteur
        if k=IndexPort then // si le port contenant le det est celui qu'on explore
        begin
          if Segment[indexSeg].periph[nperiph].typ='detector' then
          begin
            detecteur:=Segment[indexSeg].periph[nperiph].adresse;
            if debugBranche then Affiche('Cet aiguillage porte le détecteur '+intToSTR(detecteur),clYellow);
            sBranche:='A'+intToSTR(segment[indexSeg].adresse)+','+intToSTR(detecteur);
          end;
        end;
        inc(n);
      until (n>nper-1) or (k<>-1);
    end;

    if nivdebug=3 then
    begin
      numsegment:=segment[indexSeg].numero;
      NumPort:=segment[indexSeg].port[IndexPort].numero;
      AfficheDebug(intToSTR(i)+' Trouve suivant au= '+intToSTR(numSegment)+' '+intToSTR(NumPort),clOrange);
    end;
    trouve:=trouve_Seg_suivant(indexSeg,indexport,IndexSegSuivant,IndexportSuivant);
    if not(trouve) then
    begin
      s:='Pas de segment suivant à '+intToSTR(numSegment)+' '+intToSTR(NumPort);
      affiche(s,clred);
      afficheDebug(s,clred);
    end;

    if trouve then
    begin
      raz_detect(sdetect);
      numsegment:=segment[indexSegSuivant].numero;
      NumPort:=segment[indexSegSuivant].port[IndexPortSuivant].numero;
      //if debugBranche then
      if NivDebug=3 then AfficheDebug(' Suivant= '+intToSTR(numSegment)+' '+intToSTR(NumPort),clOrange);
      ctype:=segment[indexSegSuivant].typ;

      // est-ce un aiguillage
      if segment_aig(ctype) then
      begin
        AdrAigRencontre:=segment[indexSegSuivant].adresse;
        if debugBranche then Affichedebug('Aiguillage '+intTostr(adrAigRencontre),clyellow);

        if (ctype='dbl_slip_switch') and (segment[indexSegSuivant].adresse2<>0) then
        begin
          // dernier élément rencontré
          adr2:=segment[indexSegSuivant].adresse2;

          derch:=dernier_champ(sBranche);
          if derch[1]='A' then begin derch:=copy(derch,2,length(derch)-1);end;
          val(derch,AdrDer,erreur);

          idx:=index_aig(AdrAigRencontre);
          // trouver de quel côté on va à la TJD
          if (aiguillage[idx].ADroit=AdrDer) or (aiguillage[idx].ADevie=AdrDer) then
              sbranche:=sbranche+',A'+intToSTR(AdrAigRencontre)+',A'+intToSTR(adr2)
          else
          begin
            idx:=index_aig(Adr2);
            if (aiguillage[idx].ADroit=AdrDer) or (aiguillage[idx].ADevie=AdrDer) then
                sbranche:=sbranche+',A'+intToSTR(adr2)+',A'+intToSTR(AdrAigRencontre)
            else
            begin
              s:='TJD '+intToSTR(Adr2)+'/'+intToSTR(adrAigRencontre)+'non résolue en branche '+intToSTR(nbreBranches)+'. Une vérification manuelle est nécessaire.';
              Affiche(s,clred);
              if debugBranche then AfficheDebug(s,clred);
              sbranche:=sbranche+',A??,A??';
            end;
          end;
          //  sbranche:=sbranche+',A'+intToSTR(adrAigRencontre)+',A'+intToSTR(adr2);
          inc(nAig);
          raz_detect(sdetect);
        end
        else
        begin
          sBranche:=sBranche+',A'+intToSTR(AdrAigRencontre);
          //if debugBranche then AfficheDebug('Aiguillage '+intToSTR(segment[indexSegSuivant].adresse),clorange);
          inc(nAig);  // à la première itération on a un aiguillage
          raz_detect(sdetect);
        end;
      end;

      // est-ce un croisement
      if ctype='crossing' then
      begin
        sBranche:=sBranche+',A'+intToSTR(segment[indexSegSuivant].adresse);
        if debugBranche then Affichedebug('croisement',clyellow);
        //if debugBranche then AfficheDebug('Croisement '+intToSTR(segment[indexSegSuivant].adresse),clorange);
        raz_detect(sdetect);
      end;

      if ctype='bumper_stop' then
      begin
        sBranche:=sBranche+',0';
        if NivDebug=3 then Affichedebug('buttoir',clyellow);
        //if debugBranche then AfficheDebug('Buttoir',clorange);
        trouve:=false;
        raz_detect(sdetect);
      end;

      if ctype='turntable' then
      begin
        sBranche:=sBranche+',0';
        if NivDebug=3 then Affichedebug('table tournante',clyellow);
        //if debugBranche then AfficheDebug('Buttoir',clorange);
        trouve:=false;
        raz_detect(sdetect);
      end;

      //else

      begin
        // y a t-il un détecteur?
        NombrePeriph:=segment[indexSegSuivant].nperiph;

        j:=0;detecteur:=0;
        if NombrePeriph<>0 then     // on peut rencontrer des détecteurs non appairés: ex ;  514   522   514   522
        begin
          nb_det:=0;
          repeat
            if segment[indexSegSuivant].periph[j].typ='detector' then
            begin
              detecteur:=segment[indexSegSuivant].periph[j].adresse;
              if detecteur<>0 then
              begin
                if debugBranche then Affichedebug('Détecteur '+inttoStr(detecteur),clyellow);
                // incrémenter le compteur du détecteur rencontré
                inc(nb_det);
                sDetect[nb_det].adresse:=detecteur ;
                // coordonnées du port
                xp:=segment[indexSegSuivant].Port[IndexPortSuivant].x;
                yp:=segment[indexSegSuivant].Port[IndexPortSuivant].y;
                // coordonnées du détecteur
                xd:=segment[indexSegSuivant].periph[j].x;
                yd:=segment[indexSegSuivant].periph[j].y;
                // calculer la distance du détecteur au port
                sDetect[nb_det].distance:=round( sqrt( sqr(xp-xd)+sqr(yp-yd) ));
              end;
            end;
            inc(j);
          until (j>NombrePeriph-1) ;

          if nb_det<>0 then
          begin
            // trier les détecteurs du segment dans l'ordre de la distance de la plus
            // courte à la plus grande au port
            trier(Sdetect,nb_det);

            for j:=1 to nb_det do
            begin
              // vérifier si le détecteur est déja dans la branche
              // nouveau détecteur doublon
              detecteur:=sDetect[j].adresse;
              doublon:=presence_detecteur(sbranche,detecteur);
              if not(doublon) then
              begin
                sbranche:=sbranche+','+intToSTR(detecteur);
                if debugBranche then Affichedebug('Détecteur '+intToSTR(j)+'/'+intToSTR(nb_det)+': pris en compte : '+intToSTR(detecteur),clyellow);
              end
              else
              begin
                if debugBranche then Affichedebug('Détecteur '+intToSTR(j)+'/'+intToSTR(nb_det)+': non pris en compte : '+intToSTR(detecteur),clOrange);
              end;

             { ss:=dernier_champ(sbranche);
              k:=0;
              val(ss,k,erreur);
              if ss<>'' then if not(ss[1] in['0'..'9']) then k:=0;
              // vérifier si le détecteur est déja en fin de branche et que si on est au 2eme élément du tableau, on ne doit pas rencontrer le meme détecteur
              if (k<>detecteur) then
              begin
                  sbranche:=sbranche+','+intToSTR(detecteur);
                  if debugBranche then Affichedebug('Détecteur '+intToSTR(j)+'/'+intToSTR(nb_det)+': pris en compte : '+intToSTR(detecteur),clyellow);
              end; }

              inc(indexElBranche);
            end;
          end;
        end;

        // préparer le suivant , variables : indexSeg et IndexPort
        indexSeg:=indexSegSuivant;

        // trouver le port de l'autre côté du segment
        port:=trouve_port_suivant(IndexSegSuivant,numport);
        trouve_IndexSegPort(Numsegment,port,rien,indexPort);
      end;
    end;
    inc(i);

  until not(trouve) or (nAig=NbreMaxiAigRencontres) or (i>50) or (adrAig=AdrAigRencontre);

  if debugBranche then
  begin
    if not(trouve) then AfficheDebug('Arrêt sur non trouvé suivant ou buttoir ou table',clYellow);
    if (adrAig=AdrAigRencontre) then AfficheDebug('Arret sur bouclage d''aiguillage',clyellow);
    if nAig=NbreMaxiAigRencontres then AfficheDebug('Arret sur aiguillage '+intToSTR(segment[indexSeg].adresse),clYellow);
    if i>50 then AfficheDebug('Arret sur itérations',clyellow);
  end;

  // vérifier si la branche créée est valide ---------------------------------
  // 1: vérifier si le dernier élément est un A ou buttoir
  ss:=dernier_champ(sbranche);
  if ss<>'' then
  begin
    if ss[1]<>'A' then
    begin
      val(ss,i,erreur);
      if i<>0 then
      begin
        if debugBranche then
        begin
          AfficheDebug('La branche '+sbranche,clOrange);
          AfficheDebug('est invalide car le dernier élément n''est ni un aiguillage ni un buttoir',clOrange);
        end;
        exit;  // branche invalide, elle n'est pas prise en compte
      end;
    end;
  end;

  // 2: vérifier si un des détecteurs de la branche existe déja dans les branches précédentes
  trouve:=false;
  if NbreBranches>0 then
  begin
    s:=sbranche;
    repeat
      if s[1]<>'A' then
      begin
        val(s,detecteur,erreur);
        if detecteur<>0 then
        begin
          // balayer les branches
          i:=1;
          repeat
            ss:=branche[i];
            repeat
              if ss[1]<>'A' then
              begin
                val(ss,det2,erreur);
                trouve:=det2=detecteur;
                //if trouve then Affiche('Trouvé detecteur '+IntToSTR(det2)+' en branche '+intToSTR(i),clred);
              end;
              j:=pos(',',ss);
              if j<>0 then delete(ss,1,j);
            until (j=0) or trouve;
            inc(i);
          until (i=NbreBranches+1) or trouve;
        end;
      end;
      k:=pos(',',s);
      if k<>0 then delete(s,1,k);
    until (k=0) or trouve;
  end;
  if trouve then
  begin
    if debugBranche then
    begin
      AfficheDebug('La branche '+sBranche,clorange);
      AfficheDebug('est inutile car un de ses détecteurs '+intToSTR(detecteur)+' existe déjà dans la branche '+intToSTR(i-1),clOrange);
    end;
    exit;
  end;

  // ok
  inc(NbreBranches);
  branche[nbreBranches]:=sBranche;
  if debugBranche then AfficheDebug('Création branche '+sBranche,clwhite);
end;

// crée une branche contenant le détecteur detecteur
procedure cree_branche_det(detecteur : integer);
var i,NumSegment,rien,indexSeg,IndexPeriph,indexPort,IndexSegSuivant,portSuivant,IndexportSuivant,adresse : integer;
    ctype,s : string;
    trouve,fg : boolean;
    label essai_port;
begin
  sbranche:='';
  indexPort:=0; // essayer sur port 0 du détecteur
  fg:=false;
  essai_port:
  if debugBranche then
  begin
    AfficheDebug('Créée branche détecteur '+intToSTR(detecteur)+' sur port '+intToSTR(indexPort),clOrange);
  end;
  trouve:=trouve_IndexSegPortDetecteur(detecteur,indexSeg,indexPeriph);
  if not(trouve) then
  begin
    s:='Erreur 677 : Pas trouvé le segment / port du détecteur '+intToSTR(detecteur);
    AfficheDebug(s,clred);
    Affiche(s,clred);
    sBranche:='';
    exit;
  end;


  numSegment:=Segment[indexSeg].numero;
  ctype:=segment[indexSeg].typ;
  if DebugBranche then AfficheDebug('Le segment porteur du détecteur est le '+intToSTR(numSegment)+' Periph '+intToSTR(Segment[indexSeg].periph[indexperiph].numero)+' '+Ctype,clyellow);
  {
  if segment_aig(ctype) then // cas si c'est déja un aiguillage
  begin
    IndexSegSuivant:=IndexSeg;
    // trouver le port du détecteur
    i:=Segment[indexSeg].periph[indexperiph].OnDevicePort;      // numéro de port sur lequel est le détecteur
    PortSuivant:=Segment[indexSeg].port[i].numero;
    sBranche:='A'+intToSTR(segment[indexSeg].adresse)+','+intToSTR(detecteur);
  end
  else}
  begin
    // parcourir les segments jusqu'au prochain aiguillage
    i:=0;
    repeat
      //if trouve then Affiche(intToSTR(numSegment)+' '+intToSTR(indexPeriph),clyellow);
      inc(i);
      trouve:=trouve_Seg_suivant(indexSeg,indexport,IndexSegSuivant,IndexportSuivant);
      if not(trouve) then
      begin
        s:='Erreur 678 : Pas trouvé d''aiguillage ou de buttoir après le segment / port '+intToSTR(numSegment)+'/'+intToSTR(Segment[indexSeg].port[indexPort].numero);
        AfficheDebug(s,clred);
        Affiche(s,clred);
        sBranche:='';
        exit;
      end;
      numSegment:=Segment[indexSegSuivant].numero;
      portSuivant:=Segment[indexSegSuivant].port[IndexPortSuivant].numero;
      ctype:=segment[IndexSegSuivant].typ;

      if DebugBranche then AfficheDebug('Seg suivant='+intToSTR(numSegment)+' port='+intToSTR(portSuivant)+' '+ctype+' @='+intToSTR(segment[indexSegSuivant].adresse),clYellow);

      if ctype='bumper_stop' then
      begin
        // repartir en sens inverse
        if indexPortSuivant=0 then indexPortSuivant:=1 else indexPortSuivant:=0;
        portSuivant:=Segment[indexSegSuivant].port[IndexPortSuivant].numero;
      end;
      // si on rencontre une table, çà revient dans l'autre sens

      trouve:=segment_aig(ctype);  // est-ce un aiguillage ???

      // prépare suivant
      if not(trouve) then
      begin
        IndexSeg:=IndexSegSuivant;
        port:=trouve_port_suivant(indexSegSuivant,portSuivant);
        trouve_IndexSegPort(Numsegment,port,rien,indexPort);
      end;
    until (i=50) or trouve;

    if not(trouve) then
    begin
      s:='68 : Pas trouvé d''aiguillage à proximité du détecteur '+intToSTR(detecteur);
      AfficheDebug(s,clred);
      Affiche(s,clred);  // et donc essayer sur le port 1 !!
      if fg then
      begin
        S:='Pas trouvé d''aguillage de chaque côté du détecteur '+intToSTR(detecteur);
        AfficheDebug(s,clred);
        Affiche(s,clred);
        exit;
      end;
      indexport:=1;
      fg:=true;
      goto essai_port;
    end;

    //partir de IndexSegSuivant, IndexPort
    adresse:=segment[IndexSegSuivant].adresse;
    sBranche:='A'+intToSTR(Adresse);
  end;

  //convertir le port en index
  IndexPort:=trouve_IndexPort_Segment(IndexSegSuivant,portSuivant);
  if IndexPort=-1 then
  begin
    s:='Pas trouvé index port de '+intToSTR(IndexSegSuivant)+'/'+intToSTR(portSuivant);
    AfficheDebug(s,clred);
    Affiche(s,clred);
    exit;
  end;
  // Crée branche depuis aiguillage
  Cree_branche_aig(IndexSegSuivant,IndexPort,adresse,1) ;  // 1 aiguillage rencontré maxi
end;

// procédure principale de création des branches
procedure creee_branches;
var i,j,k,adresse,detecteur,indexSeg,det2,erreur : integer;
    trouve : boolean;
    c : char;
    s : string;
begin
  // l'origine d'une branche est la pointe d'un aiguillage connecté à un détecteur ----------
  NbreBranches:=0;

  Affiche('Création des branches',clWhite);
  if debugBranche then AfficheDebug('Etape 3.1 création des branches de pointe d''aiguillage à pointe d''aiguillage',clwhite);
  i:=1;
  repeat
    c:=aiguillage[i].APointeB;
    trouve:=(c<>'S') and (c<>'D') and (c<>'P') and (aiguillage[i].modele<>crois) and (aiguillage[i].modele<>tjd);
    // si la pointe de l'aiguillage est <> de S D P, c'est qu'il est connecté à un détecteur
    if trouve then
    begin
      adresse:=aiguillage[i].Adresse;
      detecteur:=aiguillage[i].APointe;
      if debugBranche then
      begin
        AfficheDebug('-------------------------',clyellow);
        AfficheDebug('Début de branche n°'+intToSTR(NbreBranches+1)+' : aiguillage '+intToSTR(adresse)+' Pointe='+intToSTR(detecteur),clyellow);
      end;

      trouve:=trouve_IdSegment_aig(adresse,indexSeg);   // trouve l'aiguillage dans les segments

      if trouve then
      begin
        port:=segment[indexSeg].port[0].numero; // on commence par le port 0 (pointe) de l'aiguillage
        sBranche:='A'+intToSTR(adresse);
        // créée la branche depuis l'aiguillage
        cree_branche_aig(indexSeg,0,adresse,100);  // branche dans sBranche
        if nivdebug=3 then AfficheDebug(sbranche,clwhite);
      end
      else
      begin
        // ne pas afficher de message pour un aiguillage non trouvé en segment s'il s'agit d'un aiguillage créé depuis une bretelle (BDJ)
        if aiguillage[i].AdrCDM=0 then afficheDebug('Erreur 417 : Aig '+intToSTR(adresse)+' non trouvé ',clred);
      end;
    end;
    inc(i); // aiguillage suivant
    trouve:=false;
  until trouve or (i>MaxAiguillage);

  // puis créer les branches depuis les positions déviées ---------------------
  if debugBranche then AfficheDebug('Etape 3.2 création des branches de dévié d''aiguillage',clwhite);
  i:=1;
  repeat
    c:=aiguillage[i].AdevieB;
    trouve:=(c<>'S') and (c<>'D') and (c<>'P') and (aiguillage[i].modele<>crois) and (aiguillage[i].modele<>tjd);
    // si la déviée de l'aiguillage est <> de S D P, c'est qu'il est connecté à un détecteur
    if trouve then
    begin
      adresse:=aiguillage[i].Adresse;
      detecteur:=aiguillage[i].Adevie;
      if debugBranche then AfficheDebug('Début de branche n°'+intToSTR(NbreBranches+1)+' : aiguillage '+intToSTR(adresse)+' dévié='+intToSTR(detecteur),clyellow);

      trouve:=trouve_IdSegment_aig(adresse,indexSeg);
      if not(trouve) then afficheDebug('Erreur 418 : Aig '+intToSTR(adresse)+' non trouvé ',clred);
      //Affiche(IntToSTR(segment[indexSeg].numero),clYellow);

      if trouve then
      begin
        port:=segment[indexSeg].port[2].numero; // on commence par le port 2 cdm (pointe) de l'aiguillage
        sBranche:='A'+intToSTR(adresse);
        cree_branche_aig(indexSeg,2,adresse,100);   // port 2
        if nivdebug=3 then AfficheDebug(sbranche,clwhite);
      end;
    end;
    inc(i); // aiguillage suivant
    trouve:=false;
  until trouve or (i>MaxAiguillage);

  if debugBranche then AfficheDebug('Etape 3.3 Liste de détecteurs absents des branches pour constituer les branches manquantes',clwhite);
  // regarder la liste des détecteurs de CDM qui sont absents des branches--------------------
  for i:=1 to Ndet_cdm do
  begin
    detecteur:=det_CDM[i];
    trouve:=false;
    for j:=1 to NbreBranches do
    begin
      s:=branche[j];
      repeat
        if s[1]<>'A' then
        begin
          val(s,det2,erreur);
          trouve:=trouve or (det2=detecteur);
        end;
        k:=pos(',',s);
        if k<>0 then delete(s,1,k);
      until (k=0) or trouve;
    end;
    if not(trouve) then
    begin
      if debugBranche then
      begin
        AfficheDebug('------------------',clyellow);
        AfficheDebug('Le détecteur '+intToSTR(detecteur)+' est absent des branches, création d''une branche le contenant ',clOrange);
      end;
      cree_branche_det(detecteur) ;
      //if debugBranche then AfficheDebug(sbranche,clWhite);
    end;
  end;

  // recopier le tampon des branches dans le richedit de l'onglet branches de la config
  with formconfig do
  begin
    RichBranche.clear;
    clicListe:=true;
    for i:=1 to NbreBranches do
    begin
      s:=Branche[i];
      RichBranche.Lines.Add(s);
      RE_ColorLine(RichBranche,RichBranche.lines.count-1,ClAqua);
    end;
    With RichBranche do
    begin
      SelStart:=0;
      Perform(EM_SCROLLCARET,0,0);
    end;
  end;
  clicListe:=false;
end;

procedure sauve_ficher_cdm;
var
    i,j,np,npe,ni : integer;
    s : string;
    f : text;
begin
  i:=pos(' ',NomModuleCDM);
  if i<>0 then NomModuleCDM:=copy(NomModuleCDM,i+1,length(NomModuleCDM)-i);

  try
    assign(f,NomModuleCDM);
    rewrite(f);
  except
    Affiche('Impossible de créer le fichier des segments CDM : '+NomModuleCDM,clred);
    exit;
  end;

  Writeln(f,NomModuleCDM);

  writeln(f,'');

  for i:=0 to nseg-1 do
  begin
    np:=segment[i].nport;
    npe:=segment[i].nperiph;
    ni:=segment[i].ninter;
    s:='Segment='+intToSTR(segment[i].numero)+' '+segment[i].typ;
    writeln(f,s);
    s:='Nport='+IntToSTR(segment[i].nport)+' Nperiph='+IntToSTR(segment[i].nperiph)+' Ninter='+IntToSTR(segment[i].ninter);
    writeln(f,s);
    s:='Xmin='+intToSTR(segment[i].XMin)+' Ymin='+intToSTR(segment[i].YMin)+' Xmax='+intToSTR(segment[i].XMax)+' Ymax='+intToSTR(segment[i].Ymax);
    writeln(f,s);
    s:='StartAngle='+intToSTR(segment[i].StartAngle)+' ArcAngle='+intToSTR(segment[i].ArcAngle)+' Rayon='+intToSTR(segment[i].Rayon)+' Radius0='+intToSTR(segment[i].radius0);
    writeln(f,s);
    s:='Angle0='+intToSTR(segment[i].angle0)+' Angle='+intToSTR(segment[i].angle)+' Lengthdev='+intToSTR(segment[i].lengthdev)+' Deltadev0='+intToSTR(segment[i].deltadev0);
    writeln(f,s);
    s:='lXc='+intToSTR(segment[i].lXc)+' lYc='+intToSTR(segment[i].lYc)+' Longueur='+intToSTR(segment[i].longueur)+' longueurDev='+intToSTR(segment[i].longueurDev);
    writeln(f,s);
    s:='DeltaDev='+intToSTR(segment[i].DeltaDev)+' CurveOffset='+intToSTR(segment[i].CurveOffset)+' xc0='+intToSTR(segment[i].xc0)+' yc0='+intToSTR(segment[i].yc0);
    writeln(f,s);
    s:='DeltaDev2='+intToSTR(segment[i].DeltaDev2)+' xc='+intToSTR(segment[i].xc)+' yc='+intToSTR(segment[i].yc);
    writeln(f,s);
    s:='Adresse='+intToSTR(segment[i].adresse)+' Adresse2='+intToSTR(segment[i].adresse2)+' Duree='+intToSTR(segment[i].duree);
    writeln(f,s);

    // ports
    for j:=0 to np-1 do
    begin
      s:='  Port='+intToSTR(segment[i].port[j].numero)+' '+segment[i].port[j].typ+
         ' x='+intToStr(segment[i].port[j].x)+
         ' y='+intToStr(segment[i].port[j].y)+
         ' z='+intToStr(segment[i].port[j].z);
      writeln(f,s);
      s:='  Angle='+intToSTr(segment[i].port[j].angle)+' Local='+intToSTr(segment[i].port[j].local)+' Connecte=';
         if segment[i].port[j].connecte then s:=s+'1' else s:=s+'0';
      writeln(f,s);
      s:='  ConnecteAuPort='+intToSTR(segment[i].port[j].ConnecteAuPort)+' ConnecteAuSegment='+intToSTR(segment[i].port[j].ConnecteAuSeg);
      writeln(f,s);
    end;

    // périphériques
    for j:=0 to npe-1 do
    begin
      s:='  Periph='+intToSTR(segment[i].periph[j].numero)+' Type='+segment[i].periph[j].typ+
         ' Pere='+intToSTR(segment[i].periph[j].pere)+
         ' x='+intToSTR(segment[i].periph[j].x)+
         ' y='+intToSTR(segment[i].periph[j].y)+
         ' z='+intToSTR(segment[i].periph[j].z);
      writeln(f,s);
      s:='  Angle='+intToSTR(segment[i].periph[j].angle)+'  Bright='+intToSTR(segment[i].periph[j].bright)+'  Bdown='+intToSTR(segment[i].periph[j].bdown);
      writeln(f,s);
      s:='  Location='+intToSTR(segment[i].periph[j].location)+'  Adresse='+intToSTR(segment[i].periph[j].adresse)+'  Status='+intToSTR(segment[i].periph[j].Status);
      writeln(f,s);

    end;

    // inter
    for j:=0 to ni-1 do
    begin
      s:='  x='+intToSTR(segment[i].inter[j].x)+' y='+intToSTR(segment[i].inter[j].y)+' z='+intToSTR(segment[i].inter[j].z);
      writeln(f,s);
      s:='  MirrorZ='+intToSTR(segment[i].inter[j].MirrorZ)+' MirrorParent='+intToSTR(segment[i].inter[j].MirrorParent);
      writeln(f,s);
    end;
    writeln(f,'');
  end;
  closefile(f);
  Affiche('Le fichier de segments CDM : '+NomModuleCDM+' a été créé',clYellow);
  formprinc.Affiche_fenetre_CDM.enabled:=true;
  config_modifie:=true;
end;

procedure lit_fichier_segments_cdm;
var
    i,j,k,np,npe,ni : integer;
    s : string;
    f : text;
    fini : boolean;

    function isole(ss : string) : integer;
    var k,erreur : integer;
    begin
      k:=pos(ss,s);
      result:=-1;
      if k<>0 then
      begin
        delete(s,k,length(ss));
        val(s,k,erreur);
        if s[1]='-' then k:=-k;
        delete(s,1,erreur);
        result:=k;
      end;
    end;

    function lire_fichier : string;
    begin
      s:='';
      while not(eof(f)) and not(s<>'') do
      begin
        readln(f,s);
        //Affiche(s,clyellow);
        s:=AnsiLowerCase(s);
      end;
      result:=s;
      fini:=eof(f);
    end;

begin
  xminiCDM:=900000;
  yminiCDM:=900000;
  xmaxiCDM:=-900000;
  ymaxiCDM:=-900000;

  try
    assign(f,NomModuleCDM);
    reset(f);
  except
    Affiche('Impossible d''ouvrir le fichier de segments CDM : '+NomModuleCDM,clred);
    exit;
  end;

  //TformAnalyseCDM.Create(nil);

  i:=0;
  s:=lire_fichier;
  NomModuleCDM:=s;

  while not(eof(f)) do
  begin
    s:=lire_fichier;
    if fini then break;

    setlength(segment,i+1);

    segment[i].numero:=isole('segment=');
    segment[i].typ:=s;

    s:=lire_fichier;
    if fini then break;
    np:=isole('nport=');
    npe:=isole('nperiph=');
    ni:=isole('ninter=');

    segment[i].nport:=np;
    segment[i].nperiph:=npe;
    segment[i].ninter:=ninter;

    s:=lire_fichier;
    if fini then break;
    segment[i].XMin:=isole('xmin=');
    segment[i].YMin:=isole('ymin=');
    segment[i].XMax:=isole('xmax=');
    segment[i].YMax:=isole('ymax=');

    if segment[i].XMin<xminiCDM then xminiCDM:=segment[i].xMin;
    if segment[i].YMin<yminiCDM then yminiCDM:=segment[i].yMin;
    if segment[i].XMax>xmaxiCDM then xmaxiCDM:=segment[i].xMax;
    if segment[i].YMax>ymaxiCDM then ymaxiCDM:=segment[i].yMax;

    s:=lire_fichier;
    if fini then break;
    segment[i].startangle:=isole('startangle=');
    segment[i].arcangle:=isole('arcangle=');
    segment[i].rayon:=isole('rayon=');
    segment[i].radius0:=isole('radius0=');

    s:=lire_fichier;
    if fini then break;
    segment[i].Angle0:=isole('angle0=');
    segment[i].angle:=isole('angle=');
    segment[i].LengthDev:=isole('lengthdev=');
    segment[i].deltaDev0:=isole('deltadev0=');

    s:=lire_fichier;
    if fini then break;
    segment[i].lxc:=isole('lxc=');
    segment[i].lyc:=isole('lyc=');
    segment[i].longueur:=isole('longueur=');
    segment[i].longueurdev:=isole('longueurdev=');

    s:=lire_fichier;
    if fini then break;
    segment[i].deltadev:=isole('deltadev=');
    segment[i].CurveOffset:=isole('CurveOffset=');
    segment[i].xc0:=isole('xc0=');
    segment[i].yc0:=isole('xc0=');

    s:=lire_fichier;
    if fini then break;
    segment[i].deltadev2:=isole('deltadev2=');
    segment[i].xc:=isole('xc=');
    segment[i].yc:=isole('xc=');

    s:=lire_fichier;
    if fini then break;
    segment[i].Adresse:=isole('adresse=');
    segment[i].Adresse2:=isole('adresse2=');
    segment[i].duree:=isole('duree=');

    // port
    if np<>0 then
    begin
      setlength(segment[i].port,np);
      for j:=0 to np-1 do
      begin
        s:=lire_fichier;
        if fini then break;
        segment[i].Port[j].numero:=isole('port=');
        k:=pos(' ',s);
        segment[i].port[j].typ:=copy(s,1,k-1);
        delete(s,1,k);
        segment[i].port[j].x:=isole('x=');
        segment[i].port[j].y:=isole('y=');
        segment[i].port[j].z:=isole('z=');

        s:=lire_fichier;
        if fini then break;
        segment[i].Port[j].angle:=isole('angle=');
        segment[i].port[j].local:=isole('local=');
        segment[i].port[j].connecte:=isole('connecte=')=1;

        s:=lire_fichier;
        if fini then break;
        segment[i].port[j].ConnecteAuPort:=isole('connecteauport=');
        segment[i].port[j].ConnecteAuSeg:=isole('connecteausegment=');
      end;
    end;

    // periphériques
    if npe<>0 then
    begin
      setlength(segment[i].periph,npe);
      for j:=0 to npe-1 do
      begin
        s:=lire_fichier;
        if fini then break;
        segment[i].periph[j].numero:=isole('periph=');
        k:=isole('type');
        k:=pos(' ',s);
        segment[i].periph[j].typ:=copy(s,1,k-1);
        delete(s,1,k);
        segment[i].periph[j].pere:=isole('pere=');
        segment[i].periph[j].x:=isole('x=');
        segment[i].periph[j].y:=isole('y=');
        segment[i].periph[j].z:=isole('z=');
        s:=lire_fichier;
        if fini then break;
        segment[i].periph[j].angle:=isole('angle=');
        segment[i].periph[j].bright:=isole('bright=');
        segment[i].periph[j].bdown:=isole('bdown=');
        s:=lire_fichier;
        segment[i].periph[j].location:=isole('location=');
        segment[i].periph[j].adresse:=isole('adresse=');
        segment[i].periph[j].status:=isole('status=');
      end;
    end;

    if ni<>0 then
    begin
      setlength(segment[i].inter,ni);
      for j:=0 to ni-1 do
      begin
        s:=lire_fichier;
        if fini then break;
        segment[i].inter[j].x:=isole('x=');
        segment[i].inter[j].y:=isole('y=');
        segment[i].inter[j].z:=isole('z=');
        s:=lire_fichier;
        if fini then break;
        segment[i].inter[j].mirrorz:=isole('mirrorz=');
        segment[i].inter[j].mirrorparent:=isole('mirrorparent=');
      end;
    end;
    inc(i);
  end;

  closefile(f);
  nseg:=i;
  formprinc.ButtonAffAnalyseCDM.Visible:=true;
  formprinc.Affiche_fenetre_CDM.enabled:=true;
end;

// trouve l'adresse signaux complexes du croisement de la BDJ d'adresse CDM adresse
function adr_croisement_cdm(adresse : integer) : integer;
var i : integer;
begin
  i:=1;
  result:=0;
  repeat
    if (aig_cdm[i].adrCDM=adresse) and (aig_cdm[i].modele=crois) then
      result:=aig_cdm[i].adresse;
    inc(i);
  until (i>nAig_CDM) ;
end;


// importe la base de données CDM dans la base de données Signaux complexes
// et crée les branches
procedure Importation;
var i,n : integer;
    s: string;
begin
  if MaxAiguillage<>0 then
  begin
    if MessageDlg('Une configuration de réseau existe dans signaux complexes.'+#10+#13+
                  'Si vous importez le réseau CDM, la configuration des aiguillages et des branches '+#10+#13+
                  'de Signaux_complexes sera écrasée.'+#10+#13+
                  'Voulez vous continuer l''importation? (aiguillages & branches)',mtConfirmation,[mbyes,mbNo],0)=mrNo then
      begin
        Affiche('Importation annulée',clOrange);
        exit;
      end;
  end;

  MaxAiguillage:=0;
  n:=0;
  Affiche('Importation des aiguillages et des branches',clWhite);

  // recopier les aiguillages CDM dans signaux_complexes
  for i:=1 to NAig_CDM do
  begin
    Aiguillage[i].adresse:=Aig_CDM[i].adresse;
    tablo_index_aiguillage[aiguillage[i].Adresse]:=i;
    Aiguillage[i].adrtriple:=Aig_CDM[i].adrtriple;
    Aiguillage[i].modele:=Aig_Cdm[i].modele;
    Aiguillage[i].temps:=Aig_cdm[i].temps;
    Aiguillage[i].ADroit:=Aig_Cdm[i].ADroit;
    Aiguillage[i].ADroitB:=Aig_Cdm[i].ADroitB;
    Aiguillage[i].ADevie:=Aig_Cdm[i].ADevie;
    Aiguillage[i].ADevieB:=Aig_Cdm[i].ADevieB;
    Aiguillage[i].APointe:=Aig_Cdm[i].Apointe;
    Aiguillage[i].APointeB:=Aig_Cdm[i].ApointeB;
    Aiguillage[i].DDroit:=Aig_Cdm[i].DDroit;
    Aiguillage[i].DDroitB:=Aig_Cdm[i].DDroitB;
    Aiguillage[i].DDevie:=Aig_CDM[i].DDevie;
    Aiguillage[i].DDevieB:=Aig_CDM[i].DDevieB;
    Aiguillage[i].EtatTJD:=Aig_CDM[i].EtatTJD;
    Aiguillage[i].AdrCDM:=aig_cdm[i].adrCDM;

    Aiguillage[i].posInit:=9;
    aiguillage[i].InversionCDM:=0;
    aiguillage[i].vitesse:=0;
  end;
  MaxAiguillage:=NAig_CDM;
  trier_aig;

  // remplit la list box les aiguillages
  formconfig.ListBoxAig.Clear;
  for i:=1 to MaxAiguillage do
  begin
    s:=encode_aig(i);
    formconfig.ListBoxAig.Items.AddObject(s, Pointer(clRed));
    Aiguillage[i].modifie:=false;
  end;
  FormConfig.ListBoxAig.itemindex:=0;
  AncligneclicAig:=-1;
  ligneclicAig:=-1;

  // créer les branches. Pour créer les branches, les aiguillages doivent être présents et leurs index construits
  NbreBranches:=0;
  creee_branches;

  Affiche('Validation des branches',ClLime);
  valide_branches;
  for i:=1 to NbreBranches do
    compile_branche(Branche[i],i);

  trier_detecteurs;
  Affiche('Importation terminée',clWhite);
  Affiche('Vérification de la cohérence :',clWhite);
  if verif_coherence then Affiche('Configuration cohérente',clLime);
  if debugbranche or debugAnalyse then formDebug.Show;
end;

procedure cree_croisement(i,adresse : integer);
begin
  inc(nSeg);
  inc(DernierSeg);
  Affiche('Création croisement Segment '+intToSTR(DernierSeg),clyellow);
  setlength(segment,nSeg);
  Segment[nSeg-1].nport:=0;
  Segment[nSeg-1].nperiph:=0;
  Segment[nSeg-1].nInter:=0;
  Segment[nSeg-1].adresse:=adresse;
  Segment[nSeg-1].adr_CDM:=adresse;

  Segment[nSeg-1].numero:=DernierSeg;
  Segment[nSeg-1].typ:='crossing';
  Segment[nSeg-1].nport:=4;
  Segment[nSeg-1].nperiph:=0;

  Segment[nSeg-1].XMin:=segment[i].XMin;
  Segment[nSeg-1].YMin:=segment[i].YMin;
  Segment[nSeg-1].XMax:=segment[i].XMax;
  Segment[nSeg-1].YMax:=segment[i].YMax;

  Segment[nSeg-1].StartAngle:=segment[i].StartAngle;
  Segment[nSeg-1].ArcAngle:=segment[i].ArcAngle;
  Segment[nSeg-1].Rayon:=segment[i].rayon;

  Segment[nSeg-1].longueur:=segment[i].longueur;
  Segment[nSeg-1].lengthdev:=segment[i].lengthdev;
  Segment[nSeg-1].DeltaDev:=segment[i].deltadev;
  Segment[nSeg-1].Curveoffset:=segment[i].Curveoffset;

  // créer les 4 ports
  setlength(segment[nSeg-1].port,4);
  inc(DernierSeg);
  Segment[nSeg-1].port[0].numero:=DernierSeg;
  Segment[nSeg-1].port[0].typ:='port';
  Segment[nSeg-1].port[0].local:=0; // port 0
  Segment[nSeg-1].port[0].x:=segment[i].port[2].x;
  Segment[nSeg-1].port[0].y:=segment[i].port[2].y;
  Segment[nSeg-1].port[0].z:=0;
  Segment[nSeg-1].port[0].angle:=0;
  Segment[nSeg-1].port[0].connecte:=true;
  Segment[nSeg-1].port[0].ConnecteAuPort:=990;   // le port 0 est la pointe
  Segment[nSeg-1].port[0].ConnecteAuSeg:=990;

  inc(DernierSeg);
  Segment[nSeg-1].port[1].numero:=DernierSeg;
  Segment[nSeg-1].port[1].typ:='port';
  Segment[nSeg-1].port[1].local:=1;
  Segment[nSeg-1].port[1].x:=segment[i].port[3].x;
  Segment[nSeg-1].port[1].y:=segment[i].port[3].y;
  Segment[nSeg-1].port[1].z:=0;
  Segment[nSeg-1].port[1].angle:=0;
  Segment[nSeg-1].port[1].connecte:=true;
  Segment[nSeg-1].port[1].ConnecteAuPort:=991;   // le port 0 est la pointe
  Segment[nSeg-1].port[1].ConnecteAuSeg:=991;

  inc(DernierSeg);
  Segment[nSeg-1].port[2].numero:=DernierSeg;
  Segment[nSeg-1].port[2].typ:='port';
  Segment[nSeg-1].port[2].local:=2;
  Segment[nSeg-1].port[2].x:=segment[i].port[1].x;
  Segment[nSeg-1].port[2].y:=segment[i].port[1].y;
  Segment[nSeg-1].port[2].z:=0;
  Segment[nSeg-1].port[2].angle:=0;
  Segment[nSeg-1].port[2].connecte:=true;
  Segment[nSeg-1].port[2].ConnecteAuPort:=992;   // le port 0 est la pointe
  Segment[nSeg-1].port[2].ConnecteAuSeg:=992;

  inc(DernierSeg);
  Segment[nSeg-1].port[3].numero:=DernierSeg;
  Segment[nSeg-1].port[3].typ:='port';
  Segment[nSeg-1].port[3].local:=3;
  Segment[nSeg-1].port[3].x:=segment[i].port[0].x;
  Segment[nSeg-1].port[3].y:=segment[i].port[0].y;
  Segment[nSeg-1].port[3].z:=0;
  Segment[nSeg-1].port[3].angle:=0;
  Segment[nSeg-1].port[3].connecte:=true;
  Segment[nSeg-1].port[3].ConnecteAuPort:=993;   // le port 0 est la pointe
  Segment[nSeg-1].port[3].ConnecteAuSeg:=993;

end;

// crée un aiguillage au seglent nSeg, à partir du segment i
procedure cree_aiguillage(i,adresse,AdrCDM : integer);
begin
  inc(nSeg);             //index de segment
  inc(DernierSeg);       //numéro de segment
  Affiche('Création aiguillage Segment '+intToSTR(DernierSeg),clyellow);
  setlength(segment,nSeg);
  Segment[nSeg-1].nport:=0;
  Segment[nSeg-1].nperiph:=0;
  Segment[nSeg-1].nInter:=0;

  Segment[nSeg-1].numero:=DernierSeg;
  Segment[nSeg-1].typ:='turnout';
  Segment[nSeg-1].nport:=3;
  Segment[nSeg-1].nperiph:=0;

  Segment[nSeg-1].XMin:=segment[i].XMin;
  Segment[nSeg-1].YMin:=segment[i].YMin;
  Segment[nSeg-1].XMax:=segment[i].XMax;
  Segment[nSeg-1].YMax:=segment[i].YMax;

  Segment[nSeg-1].adresse:=adresse;
  Segment[nSeg-1].Adr_Cdm:=AdrCDM;
  Segment[nSeg-1].duree:=segment[i].duree;

  // créer les 3 ports
  setlength(segment[nSeg-1].port,3);
  inc(DernierSeg);
  Segment[nSeg-1].port[0].numero:=DernierSeg;
  Segment[nSeg-1].port[0].typ:='port';
  Segment[nSeg-1].port[0].local:=0;
  Segment[nSeg-1].port[0].x:=0;
  Segment[nSeg-1].port[0].y:=0;
  Segment[nSeg-1].port[0].z:=0;
  Segment[nSeg-1].port[0].angle:=0;
  Segment[nSeg-1].port[0].connecte:=true;
  Segment[nSeg-1].port[0].ConnecteAuPort:=990;   // le port 0 est la pointe
  Segment[nSeg-1].port[0].ConnecteAuSeg:=990;

  inc(DernierSeg);
  Segment[nSeg-1].port[1].numero:=DernierSeg;
  Segment[nSeg-1].port[1].typ:='port';
  Segment[nSeg-1].port[1].local:=1;
  Segment[nSeg-1].port[1].x:=0;
  Segment[nSeg-1].port[1].y:=0;
  Segment[nSeg-1].port[1].z:=0;
  Segment[nSeg-1].port[1].angle:=0;
  Segment[nSeg-1].port[1].connecte:=true;
  Segment[nSeg-1].port[1].ConnecteAuPort:=991;   // le port 0 est la pointe
  Segment[nSeg-1].port[1].ConnecteAuSeg:=991;

  inc(DernierSeg);
  Segment[nSeg-1].port[2].numero:=DernierSeg;
  Segment[nSeg-1].port[2].typ:='port';
  Segment[nSeg-1].port[2].local:=2;
  Segment[nSeg-1].port[2].x:=0;
  Segment[nSeg-1].port[2].y:=0;
  Segment[nSeg-1].port[2].z:=0;
  Segment[nSeg-1].port[2].angle:=0;
  Segment[nSeg-1].port[2].connecte:=true;
  Segment[nSeg-1].port[2].ConnecteAuPort:=992;   // le port 0 est la pointe
  Segment[nSeg-1].port[2].ConnecteAuSeg:=992;

end;

// compile le fichier Texte de CDM et l'importe
procedure Compilation;
var s : string;
    nombre,position,adresse,i,j,n,ISegA1,ISegA2,ISegA3,ISegA4,ISegCrois,SegBJD,
    IndexSeg,IndexPort,SegConn,PortConn : integer;
begin
  nombre:=Formprinc.FenRich.Lines.Count;
  if nombre>0 then
  begin
    s:=lowercase(Formprinc.fenRich.Lines[0]);
    if pos('module',s)=0 then
    begin
      Affiche('Pas de module CDM détecté',clred);
      exit;
    end;
  end
  else
  begin
    Affiche('Tampon vide',clred);
    exit;
  end;

  formImportation.showmodal;
  if not(faireImport) then exit;

  ndet_cdm:=0;
  //debugAnalyse:=true;
  //debugBranche:=true;

  if DebugAnalyse or DebugBranche then formDebug.RichDebug.Clear;
  Lignes:=Formprinc.FenRich.Lines;
  nligne:=0;
  nSeg:=0;
  nPort:=0;
  nPeriph:=0;
  nInter:=0;
  DernAdrAig:=0;
  dernierSeg:=0;
  xminiCDM:=0;yMiniCDM:=0;xmaxiCDM:=0;yMaxiCDM:=0;

  NomModuleCDM:=AnsiLowerCase(Lignes[0]+'.cdm');
  formAnalyseCDM.Caption:='Squelette du réseau '+NomModuleCDM;
  formAnalyseCDM.Show;

  Affiche('Compilation en cours',clWhite);
  // on compile: on crée la base de données
  if DebugBranche then AfficheDebug('Compilation des segments, ports, inter et périphériques de CDM rail',ClWhite);
  repeat
    s:=supprime_espaces(lignes[nligne]);
    position:=pos('segment #',s) ; if position=1 then
    begin
      //affiche(s,clred);
      compile_segment;
    end;
    position:=pos('port #',s) ; if position=1 then
    begin
      //affiche(s,clred);
      compile_port;
    end;
    position:=pos('inter:',s) ; if position=1 then
    begin
      //affiche(s,clred);
      compile_inter;
    end;
    position:=pos('periph #',s) ; if position=1 then
    begin
      //affiche(s,clred);
      compile_periph;
    end;
    inc(nligne);
  until (nligne>nombre);
  Affiche('Fin de la compilation des segments',cllime);

  // balayer les segments pour transformer les bjd en créant 1 croisement et 4 aiguillages dans les segments, puis on supprime la bjd des segments
  i:=0;
  repeat
    if segment[i].typ='dbl_cross_over' then
    begin
      adresse:=segment[i].adresse;
      SegBJD:=segment[i].numero;
      s:='La BJD '+intToSTR(adresse)+' a été éclatée en un croisement et 4 aiguillages d''adresse '+intToSTR(DernAdrAig+1)+' a '+intToSTR(DernAdrAig+4);
      Affiche(s,clYellow);
      if DebugAnalyse then AfficheDebug(s,clYellow);

      ISegCrois:=Nseg;
      Cree_croisement(i,adresse);
      inc(DernAdrAig);
      // si la BJD comporte des périphériques, les réintroduire dans le croisement
      n:=segment[i].nperiph;
      if n<>0 then
      begin
        s:='La BJD '+intToSTR(adresse)+' comporte des périphériques, réintégration dans le croisement Segment n°'+intToSTR(Segment[IsegCrois].numero);
        Affiche(s,clyellow);
        if debugAnalyse then AfficheDebug(s,clyellow);
        segment[IsegCrois].nperiph:=n;
        setlength(segment[IsegCrois].Periph,n);
        segment[IsegCrois].periph:=segment[i].periph;
        // mise à jour des champs
        for j:=0 to n do
        begin
          segment[ISegCrois].periph[j].pere:=segment[iSegCrois].numero;
        end;
      end;

      // créer aiguillage 1 (SO) c'est le port 0 de la BJS depuis l'index i des segments
      ISegA1:=Nseg; // dernier index de segment
      cree_aiguillage(i,DernAdrAig,adresse);
      // remplir les coordonnées x y de chaque port
      Segment[iSegA1].port[0].x:=segment[iSegCrois].port[3].x;      // SO : port 0 de la bjs
      Segment[iSegA1].port[0].y:=segment[iSegCrois].port[3].y;
      // port 1 : port droit : milieu
      Segment[iSegA1].port[1].x:=(segment[iSegCrois].port[3].x+segment[iSegCrois].port[0].x) div 2;
      Segment[iSegA1].port[1].y:=(segment[iSegCrois].port[3].y+segment[iSegCrois].port[0].y) div 2;
      // port 2 : port devie : milieu
      Segment[iSegA1].port[2].x:=(segment[iSegCrois].port[3].x+segment[iSegCrois].port[1].x) div 2;
      Segment[iSegA1].port[2].y:=(segment[iSegCrois].port[3].y+segment[iSegCrois].port[1].y) div 2;
      inc(DernAdrAig);

      // créer aiguillage 2 (NO) c'est le port 1 de la BJS depuis l'index i des segments
      ISegA2:=Nseg;
      cree_aiguillage(i,DernAdrAig,adresse);  // créer aiguillage 2 depuis l'index i des segments
      Segment[IsegA2].port[0].x:=segment[iSegCrois].port[2].x;      // NO : port 1 de la bjs
      Segment[IsegA2].port[0].y:=segment[iSegCrois].port[2].y;
      // port 1 : port droit : milieu
      Segment[IsegA2].port[1].x:=(segment[iSegCrois].port[2].x+segment[iSegCrois].port[1].x) div 2;
      Segment[IsegA2].port[1].y:=(segment[iSegCrois].port[2].y+segment[iSegCrois].port[1].y) div 2;
      // port 2 : port devie : milieu
      Segment[IsegA2].port[2].x:=(segment[iSegCrois].port[2].x+segment[iSegCrois].port[0].x) div 2;
      Segment[IsegA2].port[2].y:=(segment[iSegCrois].port[2].y+segment[iSegCrois].port[0].y) div 2;
      inc(DernAdrAig);    

      // créer aiguillage 3 (NE) c'est le port 3 de la BJS depuis l'index i des segments
      ISegA3:=Nseg;
      cree_aiguillage(i,DernAdrAig,adresse);  // créer aiguillage 3 depuis l'index i des segments
      Segment[ISegA3].port[0].x:=segment[iSegCrois].port[1].x;      // NE : port 3 de la bjs
      Segment[ISegA3].port[0].y:=segment[iSegCrois].port[1].y;
      // port 1 : port droit : milieu
      Segment[ISegA3].port[1].x:=(segment[iSegCrois].port[1].x+segment[iSegCrois].port[2].x) div 2;
      Segment[ISegA3].port[1].y:=(segment[iSegCrois].port[1].y+segment[iSegCrois].port[2].y) div 2;
      // port 2 : port devie : milieu
      Segment[ISegA3].port[2].x:=(segment[iSegCrois].port[1].x+segment[iSegCrois].port[3].x) div 2;
      Segment[ISegA3].port[2].y:=(segment[iSegCrois].port[1].y+segment[iSegCrois].port[3].y) div 2;
      inc(DernAdrAig);

      // créer aiguillage 4 (SE) c'est le port 2 de la BJS depuis l'index i des segments
      ISegA4:=Nseg;
      cree_aiguillage(i,DernAdrAig,adresse);  // créer aiguillage 3 depuis l'index i des segments
      Segment[ISegA4].port[0].x:=segment[iSegCrois].port[0].x;      // NE : port 2 de la bjs
      Segment[ISegA4].port[0].y:=segment[iSegCrois].port[0].y;
      // port 1 : port droit : milieu
      Segment[ISegA4].port[1].x:=(segment[iSegCrois].port[0].x+segment[iSegCrois].port[3].x) div 2;
      Segment[ISegA4].port[1].y:=(segment[iSegCrois].port[0].y+segment[iSegCrois].port[3].y) div 2;
      // port 2 : port devie : milieu
      Segment[ISegA4].port[2].x:=(segment[iSegCrois].port[0].x+segment[iSegCrois].port[2].x) div 2;
      Segment[ISegA4].port[2].y:=(segment[iSegCrois].port[0].y+segment[iSegCrois].port[2].y) div 2;
      inc(DernAdrAig);


      // mettre à jour les ports du croisement
      // les indices des ports opposés des croisements sont 0-2 et 1-3
      // croisement port 0 connecté à l'aig 1 port 2 (dévié)
      // attention les ports opposés de la bjd sont 0-3 et 1-2
      segment[ISegCrois].port[0].ConnecteAuPort:=segment[iSegA1].port[2].numero;
      segment[ISegCrois].port[0].ConnecteAuSeg:=segment[ISegA1].numero;



      // croisement port 1 connecté à l'aig 2 port 2 (dévié)
      segment[ISegCrois].port[1].ConnecteAuPort:=segment[iSegA2].port[2].numero;
      segment[ISegCrois].port[1].ConnecteAuSeg:=segment[ISegA2].numero;

      // croisement port 2 connecté à l'aig 3 port 2 (dévié)
      segment[ISegCrois].port[2].ConnecteAuPort:=segment[iSegA3].port[2].numero;
      segment[ISegCrois].port[2].ConnecteAuSeg:=segment[ISegA3].numero;

      // croisement port 3 connecté à l'aig 4 port 2 (dévié)
      segment[ISegCrois].port[3].ConnecteAuPort:=segment[iSegA4].port[2].numero;
      segment[ISegCrois].port[3].ConnecteAuSeg:=segment[ISegA4].numero;


      // aiguillage 1, port 0 (pointe)
      segment[ISegA1].port[0].ConnecteAuPort:=segment[i].port[0].ConnecteAuPort;
      segment[ISegA1].port[0].ConnecteAuSeg:=segment[i].port[0].ConnecteAuSeg;
      // aiguillage 1, port 1 (droit) au port 1 (droit) de l'aig 4
      segment[ISegA1].port[1].ConnecteAuPort:=segment[iSegA4].port[1].numero;
      segment[ISegA1].port[1].ConnecteAuSeg:=segment[ISegA4].numero;
      // aiguillage 1, port 2 (dévié) au port 0 du croisement
      segment[ISegA1].port[2].ConnecteAuPort:=segment[iSegCrois].port[0].numero;
      segment[ISegA1].port[2].ConnecteAuSeg:=segment[ISegCrois].numero;

      // aiguillage 2, port 0 (pointe)
      segment[ISegA2].port[0].ConnecteAuPort:=segment[i].port[1].ConnecteAuPort;;
      segment[ISegA2].port[0].ConnecteAuSeg:=segment[i].port[1].ConnecteAuSeg;
      // aiguillage 2, port 1 (droit) au port 1 (droit) de l'aig 3
      segment[ISegA2].port[1].ConnecteAuPort:=segment[iSegA3].port[1].numero;
      segment[ISegA2].port[1].ConnecteAuSeg:=segment[ISegA3].numero;
      // aiguillage 2, port 2 (dévié) au port 1 du croisement
      segment[ISegA2].port[2].ConnecteAuPort:=segment[iSegCrois].port[1].numero;
      segment[ISegA2].port[2].ConnecteAuSeg:=segment[ISegCrois].numero;

      // aiguillage 3, port 0 (pointe) extérieur
      segment[ISegA3].port[0].ConnecteAuPort:=segment[i].port[3].ConnecteAuPort;
      segment[ISegA3].port[0].ConnecteAuSeg:=segment[i].port[3].ConnecteAuSeg;
      // aiguillage 3, port 1 (droit) au port 1 (droit) de l'aig 2
      segment[ISegA3].port[1].ConnecteAuPort:=segment[iSegA2].port[1].numero;
      segment[ISegA3].port[1].ConnecteAuSeg:=segment[ISegA2].numero;
      // aiguillage 3, port 2 (dévié) au port 2 du croisement
      segment[ISegA3].port[2].ConnecteAuPort:=segment[iSegCrois].port[2].numero;
      segment[ISegA3].port[2].ConnecteAuSeg:=segment[ISegCrois].numero;

      // aiguillage 4, port 0 (pointe)
      segment[ISegA4].port[0].ConnecteAuPort:=segment[i].port[2].ConnecteAuPort;;
      segment[ISegA4].port[0].ConnecteAuSeg:=segment[i].port[2].ConnecteAuSeg;
      // aiguillage 4, port 1 (droit) au port 1 (droit) de l'aig 1
      segment[ISegA4].port[1].ConnecteAuPort:=segment[iSegA1].port[1].numero;
      segment[ISegA4].port[1].ConnecteAuSeg:=segment[ISegA1].numero;
      // aiguillage 4, port 2 (dévié) au port 3 du croisement
      segment[ISegA4].port[2].ConnecteAuPort:=segment[iSegCrois].port[3].numero;
      segment[ISegA4].port[2].ConnecteAuSeg:=segment[ISegCrois].numero;


      if DebugAnalyse then
      begin
        AfficheDebug('Crois : Seg='+intToSTR(segment[IsegCrois].numero),clOrange);
        for j:=0 to 3 do AfficheDebug('port '+intToSTR(j)+'/'+intToSTR(segment[IsegCrois].port[j].numero)+
                 ' connecté au Seg '+intToSTR(segment[IsegCrois].port[j].ConnecteAuSeg)+
                 ' connecté au port '+intToSTR(segment[IsegCrois].port[j].ConnecteAuPort),clYellow);

        AfficheDebug('Aig1 : Seg='+intToSTR(segment[ISegA1].numero),clOrange);
        for j:=0 to 2 do AfficheDebug('port '+intToSTR(j)+'/'+intToSTR(segment[IsegA1].port[j].numero)+
                                 ' connecté au Seg '+intToSTR(segment[ISegA1].port[j].ConnecteAuSeg)+
                                 ' connecté au port '+intToSTR(segment[ISegA1].port[j].ConnecteAuPort),clYellow);

        AfficheDebug('Aig2 : Seg='+intToSTR(segment[ISegA2].numero),clOrange);
        for j:=0 to 2 do AfficheDebug('port '+intToSTR(j)+'/'+intToSTR(segment[IsegA2].port[j].numero)+
                                 ' connecté au Seg '+intToSTR(segment[ISegA2].port[j].ConnecteAuSeg)+
                                 ' connecté au port '+intToSTR(segment[ISegA2].port[j].ConnecteAuPort),clYellow);

        AfficheDebug('Aig3 : Seg='+intToSTR(segment[ISegA3].numero),clOrange);
        for j:=0 to 2 do AfficheDebug('port '+intToSTR(j)+'/'+intToSTR(segment[IsegA3].port[j].numero)+
                                 ' connecté au Seg '+intToSTR(segment[ISegA3].port[j].ConnecteAuSeg)+
                                 ' connecté au port '+intToSTR(segment[ISegA3].port[j].ConnecteAuPort),clYellow);

        AfficheDebug('Aig4 : Seg='+intToSTR(segment[ISegA4].numero),clOrange);
        for j:=0 to 2 do AfficheDebug('port '+intToSTR(j)+'/'+intToSTR(segment[IsegA4].port[j].numero)+
                                 ' connecté au Seg '+intToSTR(segment[ISegA4].port[j].ConnecteAuSeg)+
                                 ' connecté au port '+intToSTR(segment[ISegA4].port[j].ConnecteAuPort),clYellow);
      end;

      // Le port 0 de la bjd est maintenant représenté par le port 0 de l'aiguillage 1
      SegConn:=segment[i].port[0].ConnecteAuSeg;
      PortConn:=segment[i].port[0].ConnecteAuPort;
      if not(trouve_IndexSegPort(SegConn,PortConn,IndexSeg,IndexPort)) then // index de l'élément connecté au port 0 de la bjd
      begin
        s:='Erreur 80 : pas trouvé index Segment/Port de '+intToSTR(SegConn)+'/'+intToSTR(portconn);
        AfficheDebug(s,clred);
        Affiche(s,clred);
        exit;
      end;

      segment[IndexSeg].port[IndexPort].ConnecteAuSeg:=segment[ISegA1].numero;
      segment[IndexSeg].port[IndexPort].ConnecteAuPort:=segment[ISegA1].port[0].numero;

      // Le port 1 de la bjd est maintenant représenté par le port 0 de l'aiguillage 2
      SegConn:=segment[i].port[1].ConnecteAuSeg;
      PortConn:=segment[i].port[1].ConnecteAuPort;
      if not(trouve_IndexSegPort(SegConn,PortConn,IndexSeg,IndexPort)) then // index de l'élément connecté au port 0 de la bjd
      begin
        s:='Erreur 81 : pas trouvé index Segment/Port de '+intToSTR(SegConn)+'/'+intToSTR(portconn);
        AfficheDebug(s,clred);
        Affiche(s,clred);
        exit;
      end;
      segment[IndexSeg].port[IndexPort].ConnecteAuSeg:=segment[ISegA2].numero;
      segment[IndexSeg].port[IndexPort].ConnecteAuPort:=segment[ISegA2].port[0].numero;

      // Le port 2 de la bjd est maintenant représenté par le port 0 de l'aiguillage 4
      SegConn:=segment[i].port[2].ConnecteAuSeg;
      PortConn:=segment[i].port[2].ConnecteAuPort;
      if not(trouve_IndexSegPort(SegConn,PortConn,IndexSeg,IndexPort)) then // index de l'élément connecté au port 0 de la bjd
      begin
        s:='Erreur 82 : pas trouvé index Segment/Port de '+intToSTR(SegConn)+'/'+intToSTR(portconn);
        AfficheDebug(s,clred);
        Affiche(s,clred);
        exit;
      end;
      segment[IndexSeg].port[IndexPort].ConnecteAuSeg:=segment[ISegA4].numero;
      segment[IndexSeg].port[IndexPort].ConnecteAuPort:=segment[ISegA4].port[0].numero;

      // Le port 3 de la bjd est maintenant représenté par le port 0 de l'aiguillage 3
      SegConn:=segment[i].port[3].ConnecteAuSeg;
      PortConn:=segment[i].port[3].ConnecteAuPort;
      if not(trouve_IndexSegPort(SegConn,PortConn,IndexSeg,IndexPort)) then // index de l'élément connecté au port 0 de la bjd
      begin
        s:='Erreur 83 : pas trouvé index Segment/Port de '+intToSTR(SegConn)+'/'+intToSTR(portconn);
        AfficheDebug(s,clred);
        Affiche(s,clred);
        exit;
      end;
      segment[IndexSeg].port[IndexPort].ConnecteAuSeg:=segment[ISegA3].numero;
      segment[IndexSeg].port[IndexPort].ConnecteAuPort:=segment[ISegA3].port[0].numero;

    end;

    inc(i);
  until i>=nSeg;

  // supprimer les segments bjd à voir si 2 bjd adjacentes en index
  i:=0;
  repeat
    if segment[i].typ='dbl_cross_over' then
    begin
      s:='Suppression bjd '+intToSTR(segment[i].adresse)+' des segments';
      Affiche(s,clOrange);
      if DebugAnalyse then AfficheDebug(s,clOrange);
      for j:=i to nseg-2 do segment[j]:=segment[j+1];
      dec(nseg);
    end;
    inc(i);
  until i>=nseg-1;

  Affichage(false);
  Affiche('Nombre de détecteurs='+intToSTR(NDet_cdm),clyellow);
  if debugAnalyse then
  begin
    AfficheDebug('Nombre de détecteurs='+intToSTR(NDet_cdm),clyellow);
    for i:=1 to Ndet_cdm do AfficheDebug(intToSTR(Det_cdm[i]),clYellow);
  end;
  
  formAnalyseCDM.Show;
  formprinc.ButtonAffAnalyseCDM.Visible:=true;
  Affiche('Compilation terminée. Nombre de segments='+intToSTR(nSeg),clWhite);

  remplit_Aig_cdm; //fabrique les aiguillages
  Affiche('Nombre d''aiguillages='+intToSTR(Naig_cdm),clyellow);
  Affiche('Dernière adresse d''aiguillage='+intToSTR(DernAdrAig),clyellow);

  // sauvegarde
  sauve_ficher_cdm;

  // crée les branches
  if nAig_CDM=0 then Affiche('Aucun aiguillage avec adresse, les branches ne peuvent pas être créées',clOrange)
  else
    Importation;
end;


procedure TFormAnalyseCDM.FormResize(Sender: TObject);
begin
  affichage(false);
end;

procedure TFormAnalyseCDM.CheckSegmentsClick(Sender: TObject);
begin
  affichage(false);
end;

procedure TFormAnalyseCDM.CheckConnexionsClick(Sender: TObject);
begin
  Affichage(false);
end;

procedure TFormAnalyseCDM.CheckAdressesClick(Sender: TObject);
begin
  Affichage(false);
end;

procedure TFormAnalyseCDM.TrackBar1Change(Sender: TObject);
begin
  Affichage(false);
end;

procedure TFormAnalyseCDM.ButtonAffPortClick(Sender: TObject);
var i,j,numport,erreur : integer;
begin
  val(editPort.text,numport,erreur);
  if numport<1 then exit;
  trouve_IndexPort(numport,i,j);
  if i<>-1 then
  begin
    Affiche_port(i,j);
    exit;
  end;
  if Index_Segment(numport,i) then
  begin
    Affiche_segment(i);
  end;
end;

procedure TFormAnalyseCDM.FormCreate(Sender: TObject);
begin
  if debug=1 then Affiche('Création fenêtre réseau CDM',clLime);
  IndexClic:=0;
  checkPorts.Checked:=false;
  CheckAdresses.checked:=true;
  LabelPorts.Width:=120;
  LabelPorts.Height:=23;

  ImageCDM.Top:=0;
  ImageCDM.Left:=0;
  formAnalyseCDM.Caption:=NomModuleCDM;

  largeur_voie:=4;

  ImageLoco.Width:=49;
  ImageLoco.Height:=15;

  ImageLoco.Transparent:=true;
  FWICImage:=tbitmap.Create;
  FWICImage.Assign(ImageLoco.Picture.graphic);
  FWICImage.TransparentColor:=clwhite;


  premaff:=true;

  buttonAnime.Visible:=not(diffusion);

  reducX:=1;
  reducY:=1; // évite la division par 0
  
  with FwicImage do
  begin
    largeurTrain:=Width;   // largeur de l'icone du train
    hauteurTrain:=Height;
  end;
  ArcTanHautLargTrain:=ArcTan(HauteurTrain/LargeurTrain);
  if debug=1 then Affiche('Fin création fenetre réseau CDM',clLime);
end;


procedure TFormAnalyseCDM.CheckPortsClick(Sender: TObject);
begin
  Affichage(false);
end;

procedure clic_image;
var pt : Tpoint;
   xSouris,ySouris,x1,y1,x2,y2,i,j,centreX,centrey,rayon,numero: integer;
   StartAngle,StopAngle : double;
   trouve : boolean;
   debug : boolean;
   ctype,s : string;
   canvasI : Tcanvas;
begin
  clic:=true;
  pt:= formAnalyseCDM.ImageCDM.ScreenToClient(Mouse.CursorPos);  // coordonnées par rapport à l'image
  xSouris:=pt.x;
  ySouris:=pt.y;

  canvasI:=FormAnalyseCDM.ImageCDM.Canvas;

  i:=0;
  repeat
    numero:=segment[i].numero;
    debug:=false;

    if debug then Affiche('Segment'+inttoSTR(segment[i].numero),clYellow);
    //Affiche(intToSTR(x1)+' '+intToSTR(y1)+' / '+intToSTR(x2)+' '+intToSTR(y2),clYellow);

    ctype:=Segment[i].typ;

    // aiguillage à 3 ports (turnout)
    if segment_aig(ctype) and (ctype<>'dbl_slip_switch') then
    begin
      x1:=segment[i].port[0].x;
      y1:=segment[i].port[0].y;
      x2:=segment[i].port[1].x;
      y2:=segment[i].port[1].y;
      coords(x1,y1);
      coords(x2,y2);
      trouve:=point_Sur_Segment(Xsouris,Ysouris,x1,y1,x2,y2);
      if trouve then
      begin
        with canvasI do
        begin
          Pen.Width:=largeur_voie;
          pen.Color:=clred;
          MoveTo(x1,y1);
          LineTo(x2,y2);
        end;

        x1:=(x1+x2) div 2;
        y1:=(y1+y2) div 2;
        x2:=segment[i].port[2].x;
        y2:=segment[i].port[2].y;
        coords(x2,y2);
        with canvasI do
        begin
          MoveTo(x1,y1);
          LineTo(x2,y2);
        end;
        if ctype='turnout_3way' then
        begin
          canvasI.moveTo(x1,y1);
          x2:=segment[i].port[3].x;
          y2:=segment[i].port[3].y;
          coords(x2,y2);
          canvasI.LineTo(x2,y2);
        end;

        xAig:=x1;yAig:=y1-5;

        s:=intToSTR(Segment[i].adresse);
        if Segment[i].adresse2<>0 then s:=s+'/'+intToSTR(Segment[i].adresse2);
        formAnalyseCDM.EditAdresse.text:=s;

        s:='Ports 0/1/2 = '+IntToSTR(Segment[i].port[0].numero)+'/'+IntToSTR(Segment[i].port[1].numero)+'/'+
                          IntToSTR(Segment[i].port[2].numero);
        formAnalyseCDM.LabelPorts.Caption:=s;

      end;
    end;

    if (ctype='crossing') or (ctype='dbl_slip_switch') then
    begin
      x1:=segment[i].port[0].x;
      y1:=segment[i].port[0].y;
      x2:=segment[i].port[2].x;
      y2:=segment[i].port[2].y;
      coords(x1,y1);
      coords(x2,y2);
      trouve:=point_Sur_Segment(Xsouris,Ysouris,x1,y1,x2,y2);
      if trouve then
      begin
         s:=intToSTR(Segment[i].adresse);
        if Segment[i].adresse2<>0 then s:=s+'/'+intToSTR(Segment[i].adresse2);
        formAnalyseCDM.EditAdresse.Text:=s;
        s:='Ports 0/1/2/3 = '+IntToSTR(Segment[i].port[0].numero)+'/'+IntToSTR(Segment[i].port[1].numero)+'/'+
                              IntToSTR(Segment[i].port[2].numero)+'/'+IntToSTR(Segment[i].port[3].numero);
        formAnalyseCDM.LabelPorts.Caption:=s;

        CanvasI.Pen.Width:=largeur_voie;
        CanvasI.pen.Color:=clred;

        x1:=segment[i].port[1].x;
        y1:=segment[i].port[1].y;
        x2:=segment[i].port[3].x;
        y2:=segment[i].port[3].y;
        coords(x1,y1);
        coords(x2,y2);
        with canvasI do
        begin
          MoveTo(x1,y1);
          LineTo(x2,y2);
        end;

        x1:=segment[i].port[0].x;
        y1:=segment[i].port[0].y;
        x2:=segment[i].port[2].x;
        y2:=segment[i].port[2].y;
        coords(x1,y1);
        coords(x2,y2);
        with canvasI do
        begin
          MoveTo(x1,y1);
          LineTo(x2,y2);
        end;

        xAig:=x1;yAig:=y1-5;
      end;

    end;

    // le précurve est un type droit!!
    if (ctype='straight') or (ctype='pre_curve') or (ctype='bumper_stop') then
    begin
      x1:=segment[i].port[0].x;
      y1:=segment[i].port[0].y;
      x2:=segment[i].port[1].x;
      y2:=segment[i].port[1].y;
      coords(x1,y1);
      coords(x2,y2);
      trouve:=point_Sur_Segment(Xsouris,Ysouris,x1,y1,x2,y2);
      if trouve then
      begin
        formAnalyseCDM.EditAdresse.text:='';
        s:='Ports 0/1 = '+InttoSTR(Segment[i].port[0].numero)+'/'+IntToSTR(Segment[i].port[1].numero);
        formAnalyseCDM.LabelPorts.Caption:=s;

        with canvasI do
        begin
          Pen.Width:=largeur_voie;
          pen.Color:=clred;
          MoveTo(x1,y1);
          LineTo(x2,y2);
        end;
      end;
    end;

    if (ctype='curve')  or (ctype='arc') then
    begin
      StartAngle:=segment[i].StartAngle/100;
      StopAngle:=segment[i].ArcAngle/100;
      centreX:=segment[i].lXc;
      CentreY:=segment[i].lyc;
      coords(centreX,centreY);

      rayon:=round(Segment[i].Rayon*reducX) div 1000;

      //Affiche('Segment index '+intToSTR(i)+' Numéro='+intToSTR(numero),clOrange);
      if stopAngle<0 then
        canvasI.Pen.color:=clred
         else canvasI.Pen.color:=clOrange;

      trouve:=point_sur_arc(xSouris,ySouris,CentreX,centreY,rayon,StartAngle,StopAngle,debug) ;
      if trouve then
       begin
         FormAnalyseCDM.EditAdresse.text:='';
         s:='Ports 0/1 = '+IntToSTR(Segment[i].port[0].numero)+'/'+IntToSTR(Segment[i].port[1].numero);
         formAnalyseCDM.LabelPorts.Caption:=s;

         if not(debug) then
         begin
           CanvasI.Pen.Width:=largeur_voie;
           CanvasI.pen.Color:=clred;
           D_arc(CanvasI,centreX,centreY,rayon,StartAngle,StopAngle);

         end;
       end;
     end;
    {
    with ImageCDM.canvas do
    begin
      pen.width:=1;
      pen.color:=clred;
      moveTo(x1,y1);LineTo(x2,y2);
      //ellipse(x1-20,y1-20,x2+20,y2+20);
    end;
    }

    if not(trouve) then inc(i);
  until (i>=nSeg) or trouve;

  if trouve then
  begin
    with FormAnalyseCDM do
    begin
      GroupBoxSegment.Caption:='Segment : '+ctype;
      EditSegment.Text:=intToSTR(numero);
      s:='Segment '+IntToSTR(Segment[i].numero)+' '+ctype;
      x1:=Segment[i].adresse;
      if x1<>0 then
      begin
        s:=s+#13+'Adr='+intToSTR(x1);
        if Segment[i].adresse2<>0 then s:=s+'/'+intToSTR(Segment[i].adresse2);
      end;
      for j:=0 to segment[i].nport-1 do
      begin
        s:=s+#13+'Port '+intToSTR(j)+' : '+intToSTR(Segment[i].port[j].numero);
        x1:=Segment[i].port[j].x;
        y1:=Segment[i].port[j].y;
        coords(x1,y1);
        canvasI.TextOut(x1,y1+5,'P'+intToSTR(j)+':'+intToSTR(Segment[i].port[j].numero));
      end;
      Hint:=s;
      showHint:=true;
    end;
    IndexClic:=i;
  end
    else FormAnalyseCDM.hint:='';

  clic:=false;
end;

procedure TFormAnalyseCDM.EditAdresseChange(Sender: TObject);
var adresse,adresse2,erreur,ofs : integer;
    canvas : Tcanvas;
    ctyp,s : string;
    i : integer;
begin
  if clic then exit;
  s:=editAdresse.Text;
  i:=pos('/',s);
  adresse2:=0;
  if i=0 then val(s,adresse,erreur) else
  begin
    val(s,adresse,erreur);
    delete(s,1,erreur);
    val(s,adresse2,erreur);
    erreur:=0;
  end;
  if (adresse<1) or (adresse2<0) then exit;

  if erreur=0 then
  begin
    ofs:=0;
    segment[IndexClic].adresse:=adresse;
    if adresse2<>0 then segment[IndexClic].adresse2:=adresse2;
    if (ctyp='crossing') then ofs:=YcrOffset;
    ctyp:=segment[IndexClic].typ;
    if not(segment_aig(ctyp)) and not(ctyp='crossing') then exit;  // si c'est pas un aiguillage ni un croisement
    if (ctyp='crossing') or (ctyp='dbl_slip_switch') then ofs:=YcrOffset;
    if (ctyp='turnout') or (ctyp='turnout_sym') then ofs:=yTurnoutOffset;
    if (ctyp='turnout_curved') or (ctyp='turnout_curved_2r') then ofs:=YcrOffset;

    canvas:=ImageCDM.Canvas;
    with canvas do
    begin
      Pen.Color:=clLime;
      Brush.Style:=bsSolid;
      brush.Color:=fond_cdm;
    end;
    coords_aff_aig(canvas,indexClic);
    canvas.Brush.Style:=bsClear;
  end;
end;

procedure TFormAnalyseCDM.ImageCDMClick(Sender: TObject);
begin
  clic_image;
end;

procedure TFormAnalyseCDM.FormMouseWheel(Sender: TObject;Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint;var Handled: Boolean);
var i : integer;
begin
  i:=TrackBar1.Position;

  if WheelDelta>0 then
  begin
    if (i<=Zmini) then
    begin
      TrackBar1.Position:=Zmini;
      exit;
    end;
    dec(i);
  end
  else
  begin
    if (i>=ZMaxi) then
    begin
      TrackBar1.Position:=Zmaxi;
      exit;
    end;
    inc(i);
  end;

  TrackBar1.Position:=i;
  Affichage(false);

  // positionner la trackbar zoom

  {FormTCO.TrackBarZoom.Position:=i;
  calcul_cellules;
  Affiche_TCO;
  SelectionAffichee:=false;}
end;

// trouve les index du segment qui contiennent le détecteur Adresse
// renvoie dans index les index, et dans periph les numéros de périphériques dans lesquels ils se trouvent
function index_segment_det(Adresse : integer;var TabloDetSeg : TdetSeg; nombre : integer) : boolean;
var i,n,ip,itablo : integer;
begin
  //trouver les deux segments du détecteurs
  if adresse=0 then
  begin
    nombre:=0;
    result:=false;
    exit;
  end;
  for i:=1 to 10 do
  begin
    TabloDetSeg[i].index:=-1;
    TabloDetSeg[i].periph:=-1;
  end;

  i:=0;
  itablo:=0;
  repeat
    n:=segment[i].nperiph;
    if n<>0 then
    begin
      for ip:=0 to n-1 do
      begin
        if segment[i].periph[ip].typ='detector' then
        begin
          if segment[i].periph[ip].adresse=adresse then
          begin
            inc(itablo);
            if itablo>10 then
            begin
              AfficheDebug('Erreur 857 : Nbre périphériques>10 Segment '+intToSTR(segment[i].numero),clred);
              exit;
            end;
            TabloDetSeg[itablo].index:=i;
            TabloDetSeg[itablo].periph:=ip;
          end;
        end;
      end;
    end;
    inc(i);
  until (i>nseg-1);
  nombre:=itablo;
  result:=itablo>0;
end;



// allume la zone du détecteur "adresse"
procedure dessine_det(adresse : integer);
var p,centreX,CentreY,rayon,i,index,x,y,x1,y1,x2,y2,x3,y3,x4,y4,np,NindexR,NumPort,IndexportSuiv,NumSeg,indexport,
    xs,ys,nombre,ind1,ind2,per1,per2,IndexSegBumper,PortBumper,port : integer;
    StartAngle,stopangle,startDegres,StopDegres : double;
    trouve,ts,tn,tp : boolean;
    ctyp,s : string;
    canvas : Tcanvas;
    circuit_det : array[1..20] of record
                     index : integer;  // index des segments du circuit des détecteurs
                     port  : integer;  // pour les premiers et derniers index, index du périphérique, sinon du port
                     end;
    tabloDetSeg : TdetSeg;
begin
  if adresse=0 then exit;
  canvas:=FormAnalyseCDM.ImageCDM.canvas;
  Canvas.pen.color:=clred;
  canvas.Pen.width:=largeur_voie;

  // trouve la liste des détecteurs, résultat dans tabloDetSeg, nombre
  index_segment_det(adresse,tabloDetSeg,nombre);
  circuit_det[1].index:=tabloDetSeg[1].index;

   ind1:=tabloDetSeg[1].index;
   ind2:=tabloDetSeg[2].index;
   per1:=tabloDetSeg[1].periph;
   per2:=tabloDetSeg[2].periph;

  //Affiche('Trouvé détecteur sur le segment '+inttoSTR(segment[i1].numero),clYellow);

  // cas ou il y a plus d'un détecteur sur le même segment
  // pour l'instant on ne traite que 2 détecteurs (identiques) par segment maxi
  if ind1=ind2 then
  begin
    circuit_det[1].port:=tabloDetSeg[1].periph; //index du périph1
    circuit_det[1].index:=tabloDetSeg[1].index;
    circuit_det[2].port:=tabloDetSeg[2].periph; //index du périph2
    circuit_det[2].index:=tabloDetSeg[2].index;
    ctyp:=segment[ind1].typ;
    if (ctyp='straight') or (ctyp='pre_curve') or (ctyp='bumper_stop')  then
    begin
      x:=segment[ind1].periph[per1].x;
      y:=segment[ind1].periph[per1].y;
      coords(x,y);
      canvas.moveto(x,y);

      x:=segment[ind1].periph[per2].x;
      y:=segment[ind1].periph[per2].y;
      coords(x,y);
      Canvas.LineTo(x,y);
    end;

    if (ctyp='curve') or (ctyp='arc') then
    begin
      rayon:=segment[ind1].Rayon;
      StartAngle:=segment[ind1].StartAngle/100;
      StopAngle:=segment[ind1].ArcAngle/100;
      centreX:=segment[ind1].lxc;
      CentreY:=segment[ind1].lyc;
      Canvas.pen.color:=clred;
      x1:=segment[ind1].periph[per1].x;
      y1:=segment[ind1].periph[per1].y;
      x2:=segment[ind1].periph[per2].x;
      y2:=segment[ind1].periph[per2].y;
      canvas.pen.Color:=clred;
      arc_xy(canvas,centreX,centreY,rayon,x1,y1,x2,y2);

      coords(centreX,centreY);
      {coords(x2,y2);
      canvas.MoveTo(centreY,centreY);
      canvas.LineTo(x2,y2);}
    end;

  end

  else
  begin
    // aller dans un sens pour tenter de trouver le détecteur homologue
    port:=0;
    trouve:=false;
    IndexSegBumper:=-1;
    repeat
      i:=1;
      index:=ind1;
      //Affiche('Sens port '+intToSTR(port),clred);
      //Affiche(IntToSTR(segment[index].numero),clred);
      indexport:=port;
      circuit_det[i].port:=port; //index du port
      circuit_det[i].index:=ind1; //index du segment
      i:=2;

      repeat
        ts:=trouve_seg_suivant(Index,indexport,Suivant,IndexportSuiv);  // si ts=faux, possible sur un buttoir car pas de suivant
        index:=Suivant;
        indexport:=IndexPortSuiv;
        numseg:=segment[index].numero;
        ctyp:=segment[index].typ;
        if (ctyp='bumper_stop') or (ctyp='turntable') then
        begin
          IndexSegBumper:=index;
          PortBumper:=indexport;
        end;
        if ts then
        begin
          //Affiche(intToSTR(numseg),clred);
          circuit_det[i].port:=indexport; //index du port
          circuit_det[i].index:=index;
          indexport:=1-indexport;  // inverser le port
          tn:=indexport>=0;    // si pas tn c'est que c'est un segment à plus de 3 ports
          np:=segment[index].nperiph;
          if np<>0 then
          begin
            p:=0;
            repeat
              if segment[index].periph[p].typ='detector' then trouve:=segment[index].periph[p].adresse=adresse;
              inc(p);
            until (p>np-1) or trouve;
          end;
          inc(i);
        end;
      until trouve or (i>10) or not(tn) or not(ts);  // boucle de segment
      if not(trouve) then
      begin
        inc(port); // tester le port suivant (changement de sens)
        index:=ind2;
      end;
    until (port=2) or trouve; // boucle de port

    if not(trouve) then
    begin
      if IndexSegBumper=-1 then begin AfficheDebug('Msg 187 : pas trouvé de route pour le détecteur '+intToSTR(adresse),clOrange) ; exit; end;
      begin
        // repartir du buttoir vers le détecteur
        IndexPort:=PortBumper;
        per1:=port;
        i:=1;
        index:=IndexSegBumper;
        circuit_det[i].index:=IndexSegBumper;
        circuit_det[i].port:=PortBumper;
        i:=2;
        repeat
          ts:=trouve_seg_suivant(index,Indexport,Suivant,IndexportSuiv);
          if not(ts) then begin AfficheDebug('Erreur 102 - Pas de suivant Segment '+intToSTR(segment[Index].numero),clred);exit;end;
          index:=Suivant;
          indexport:=IndexPortSuiv;
          numseg:=segment[index].numero;
          //Affiche(intToSTR(numseg),clred);
          circuit_det[i].port:=indexport; //index du port
          circuit_det[i].index:=index;
          indexport:=1-indexport;  // inverser le port
          tn:=indexport>=0;
          if not(tn) then begin AfficheDebug('Erreur 103 - Port destination négatif - Segment '+intToSTR(segment[IndexSegBumper].numero),clred);exit;end;

          np:=segment[index].nperiph;
          if np<>0 then
          begin
            p:=0;
            repeat
              if segment[index].periph[p].typ='detector' then trouve:=segment[index].periph[p].adresse=adresse;
              inc(p);
            until (p>np-1) or trouve;
          end;
          inc(i);
        until trouve or (i>10);
        if not(trouve) then begin AfficheDebug('Erreur 104 - Detecteur '+intToSTR(adresse)+' non trouvé car trop distant',clred);exit;end;
        per2:=p-1;
      end;
    end;
    NindexR:=i-1;

    {
    for i:=1 to NindexR do
    begin
      index:=circuit_det[i].index;
      s:=intToSTR(segment[index].numero)+
         ' StartAngle='+IntToSTR(segment[index].StartAngle div 100)+
         ' ArcAngle='+IntToSTR(segment[index].ArcAngle div 100);
      Affiche(s,clWhite);
    end;              }

    // le circuit des segments est trouvé dans le tableau circuit_det
    if trouve then
    begin
      // départ sur détecteur
      index:=circuit_det[1].index;
      ctyp:=segment[index].typ;
      if (ctyp='curve') or (ctyp='arc') then
      begin
        rayon:=segment[index].Rayon;
        StartAngle:=segment[index].StartAngle/100;
        StopAngle:=segment[index].ArcAngle/100;
        centreX:=segment[index].lxc;
        CentreY:=segment[index].lyc;
        x:=segment[index].periph[per1].x;
        y:=segment[index].periph[per1].y;
        // déterminer la destination
        // trouve le port connecté au segment suivant
        tp:=index_port_connecte(index,circuit_det[2].index,indexPort);
        // trouver les coordonnées su x,y sur lequel est connecté le port
        xs:=Segment[index].port[IndexPort].x;
        ys:=Segment[index].port[IndexPort].y;
        // donc tracer un arc de (x,y) détecteur à (xs,ys) port
        arc_xy(canvas,centreX,centreY,rayon,x,y,xs,ys);
      end;
      if (ctyp='straight') or (ctyp='pre_curve') or (ctyp='bumper_stop')  then
      begin
        if ctyp='bumper_stop' then
        begin
          // faire le raccordement sur le port non connecté
          if segment[index].port[0].connecte then p:=1 else p:=0;
          x:=segment[index].port[p].x;
          y:=segment[index].port[p].y;
        end
        else
        begin
          x:=segment[index].periph[per1].x;
          y:=segment[index].periph[per1].y;
        end;

        coords(x,y);
        canvas.moveto(x,y);

        x:=segment[index].port[circuit_det[1].port].x;
        y:=segment[index].port[circuit_det[1].port].y;
        coords(x,y);
        Canvas.LineTo(x,y);
      end;

      for i:=2 to NindexR-1 do
      begin
        index:=circuit_det[i].index;
        //Affiche('Route de index Seg='+inttoSTR(segment[index].numero),clYellow);

        ctyp:=segment[index].typ;
        if (ctyp='curve') or (ctyp='arc') then
        begin
          rayon:=segment[index].Rayon;
          StartAngle:=segment[index].StartAngle/100;
          StopAngle:=segment[index].ArcAngle/100;
          centreX:=segment[index].lxc;
          CentreY:=segment[index].lyc;
          angle_cdm(canvas,centreX,CentreY,startangle,stopangle,rayon);
        end;

        if (ctyp='straight') or (ctyp='pre_curve') or (ctyp='bumper_stop')  then
        begin
          x:=segment[index].port[0].x;
          y:=segment[index].port[0].y;
          coords(x,y);
          canvas.moveto(x,y);

          x:=segment[index].port[1].x;
          y:=segment[index].port[1].y;
          coords(x,y);
          Canvas.LineTo(x,y);
        end;
      end;
      // dernier élément
      index:=circuit_det[NindexR].index;
      ctyp:=segment[index].typ;
      if (ctyp='curve') or (ctyp='arc') then
      begin
        rayon:=segment[index].Rayon;
        StartAngle:=segment[index].StartAngle/100;
        StopAngle:=segment[index].ArcAngle/100;
        centreX:=segment[index].lxc;
        CentreY:=segment[index].lyc;
        Canvas.pen.color:=clred;
        x:=segment[index].periph[per2].x;
        y:=segment[index].periph[per2].y;
        // déterminer la destination
        // trouve le port connecté au segment précédent
        tp:=index_port_connecte(index,circuit_det[NindexR-1].index,indexPort);
        // trouver les coordonnées su x,y sur lequel est connecté le port
        xs:=Segment[index].port[IndexPort].x;
        ys:=Segment[index].port[IndexPort].y;
        // donc tracer un arc de (x,y) (détecteur) à (xs,ys) port
        arc_xy(canvas,centreX,centreY,rayon,x,y,xs,ys);
      end;
      if (ctyp='straight') or (ctyp='pre_curve') or (ctyp='bumper_stop')  then
      begin
        x:=segment[index].periph[per2].x;
        y:=segment[index].periph[per2].y;
        coords(x,y);
        canvas.moveto(x,y);

        x:=segment[index].port[circuit_det[nindexr].port].x;
        y:=segment[index].port[circuit_det[nindexr].port].y;
        coords(x,y);
        Canvas.LineTo(x,y);
      end;
    end;
  end;
end;

procedure TFormAnalyseCDM.ButtonAffDetClick(Sender: TObject);
var i,erreur : integer;
begin
  val(EditDetecteur.text,i,erreur);
  if i<1 then exit;
  if erreur=0 then dessine_det(i);
end;

procedure TFormAnalyseCDM.CheckColorationDiffClick(Sender: TObject);
begin
  coloration_diff:=CheckColorationDiff.checked;
  Affichage(false);
end;

procedure TFormAnalyseCDM.ButtonAllumeTsClick(Sender: TObject);
var i,j,p,np : integer;
begin
  for i:=0 to NSeg-1 do
  begin
    np:=segment[i].nperiph; // nombre de périphériques
    for j:=0 to np-1 do
    begin
      if segment[i].periph[j].typ='detector' then
      begin
        p:=segment[i].periph[j].adresse;
        if p<>0 then dessine_det(p);
      end;
    end;
  end;
end;

procedure TFormAnalyseCDM.ButtonImprimeClick(Sender: TObject);
begin
  affichage(true);
  affichage(false); //pour remettre les variables globales de dimensionnement qui sont différentes de l'impression
end;

procedure TFormAnalyseCDM.ImageCDMMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var Xs,Ys,i,xt,yt : integer;
    trouve : boolean;
begin
  xs:=x;ys:=y;
  inv_coords(xs,ys);
  caption:=NomModuleCDM+' '+intToSTR(xs)+' '+intToSTr(ys)+' / '+intToSTR(x)+' '+intToSTR(y);
  i:=1;

  // afficher le hint du train
  repeat
    xt:=trains[i].x;
    yt:=trains[i].y;
    trouve:=(abs(xt-x)<50) and (abs(yt-y)<50);
    if not(trouve) then inc(i);
  until (i>Ntrains) or trouve;
  if trouve then
  begin
    ImageCDM.Hint:=trains[i].nom_train+' @='+intToSTR(trains[i].adresse);
    ImageCDM.showHint:=true;
  end
  else ImageCDM.showhint:=false;
end;

procedure TFormAnalyseCDM.ButtonAnimeClick(Sender: TObject);
var zom,ang : single;
    x,y : integer;
begin
  x:=50;y:=50;
  zom:=1.2;
  ang:=0;

  repeat
    peindre(1,x,y,zom);

    //peindre(2,500-x,y,0,zom);
    //zom:=zom+0.005;
    x:=x+10;
    y:=y+3;
    ang:=ang+0.02;
    zom:=zom+0.01;
    sleep(40);
    Application.processMessages;
  until x>800;

  //until x>500;


end;



end.





