Bjr à vous,

Je poste ici un petit 'composant' Lazarus permettant de faire du dessin avec une approche 'Liste d'affichage', en utilisant le système de coordonnées orthonormal classique (et non les coordonnées écran)
C'est un TFrame contenant notamment un TPaintBox. Il utilise la bibliothèque BGRABitmap et est sous licence CC0.

Nom : TCdrDrawingContextGCS.png
Affichages : 419
Taille : 1,28 Mo

Par rapport aux approches que je connais, cette solution utilise une liste d'affichage basée sur un TStringList dont la propriété indexée .Objects[] pointe sur les objets à dessiner et les commandes à envoyer au tampon de dessin.

Avantages de cette approche:
- Le programme appelant envoie des commandes de dessin au composant
- Solution élégante
- La liste d'affichage peut être
* modifiée a posteriori
* réutilisée pour générer du SVG
* sauvegardée

Inconvénients constatés:
- Une certaine lenteur (certainement liée aux tests ci-dessous)

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 
// on dessine ici: exécution de la liste d'affichage
    Nb := getNbEntities();
    if (Nb > 0) then
    begin
      for i := 0 to Nb - 1 do
      begin
        MyEntity := FListOfEntities.Objects[i];
        if (MyEntity is TDGCSegment)    then QDrawSegment(MyEntity as TDGCSegment);     // Temps de reconstruction: 00:00:00.140 // as TDGCSegment);
        if (MyEntity is TDGCCircle)     then QDrawCircle(MyEntity as TDGCCircle);       //                                       // as TDGCCircle);
        if (MyEntity is TDGCTriangle)   then QDrawTriangle(MyEntity as TDGCTriangle);   //                                       // as TDGCTriangle);
        if (MyEntity is TDGCPolyline)   then QDrawPolyline(MyEntity as TDGCPolyline);
        if (MyEntity is TDGCPolygon)    then QDrawPolygon(MyEntity as TDGCPolygon);
        if (MyEntity is TDGCPen)        then QSetPenStyle(MyEntity as TDGCPen);
        if (MyEntity is TDGCBrush)      then QSetBrushStyle(MyEntity as TDGCBrush);
      end;
    end;
Le code de l'unité principale

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
 
unit cadredrawinggcscontext;
// Cadre générique de dessin 2D en coordonnées GCS
// indépendant de GHTopo
// Doit remplacer à terme les contextes d'affichage des diagrammes d'orientation et d'altimétrie
// ainsi que le visualisateur de la carte du frontal BDD 6 Minutes
// Ceci est aussi un exercice sur l'approche 'Listes d'Affichage'
// Avantages:
// - Solution élégante
// - Souplesse du TStringList
//
 
{$mode delphi}
{$DEFINE GHTOPO_INDEPENDANT}
{.$UNDEF GHTOPO_INDEPENDANT}
 
interface
 
uses
  {$IFNDEF GHTOPO_INDEPENDANT}
  StructuresDonnees,
  Common,
  {$ENDIF}
  Graphics,
  BGRABitmap,
  Dialogs,
  Classes, SysUtils, Forms, Controls, ExtCtrls, StdCtrls;
 
{$IFDEF GHTOPO_INDEPENDANT}
type TPoint2Df = record
  X:  double;
  Y:  double;
end;
function MakeTPoint2Df(const QX, QY: double): TPoint2Df;
{$ENDIF}
 
{$DEFINE USE_TSTRINGLIST}
{.$UNDEF USE_TSTRINGLIST}
 
// Préfixe TDGC : (D)rawing (G)HTopo (C)ontext
// pour différencier
type TDGCPen = class
  Couleur : TColor;
  Opacity : byte;
  Width   : integer;
  Style   : TPenStyle;
end;
type TDGCBrush = class
  Couleur : TColor;
  Opacity : byte;
  Style   : TBrushStyle;
end;
type TDGCPolyLineGon = class
 private
   FName: string;
   FDoFlush: boolean;
 public
   ListeVertex: array of TPoint;
   property Name   : string read FName write FName;
   property DoFlush: boolean read FDoFlush write FDoFlush;
 
   constructor Create(const QName: string; const QDoFlush: boolean);
   destructor Destroy();
 
   function AddVertex(const V: TPoint): boolean;
   function GetVertex(const Idx: integer): TPoint;
   function GetNbVertex(): integer;
 
end;
 
type TDGCPolyline = class(TDGCPolyLineGon)
end;
 
type TDGCPolygon = class(TDGCPolyLineGon)
end;
 
 
type  TDGCTriangle = class
  private
    FP1: TPoint;
    FP2: TPoint;
    FP3: TPoint;
 
  public
    constructor Create(const P1, P2, P3: TPoint);
    property P1: TPoint read FP1 write FP1;
    property P2: TPoint read FP2 write FP2;
    property P3: TPoint read FP3 write FP3;
end;
 
type TDGCCircle = class
  private
    FCentre: TPoint;
    FRayon1: integer;
    FRayon2: integer;
 
 
  public
    constructor Create(const QCentre: TPoint; const QR1, QR2: integer);
    property Centre: TPoint  read FCentre write FCentre;
    property Rayon1: integer read FRayon1 write FRayon1;
    property Rayon2: integer read FRayon2 write FRayon2;
end;
type TDGCSegment = class
  private
    FP1: TPoint;
    FP2: TPoint;
 
  public
    constructor Create(const P1, P2: TPoint);
    property P1: TPoint read FP1 write FP1;
    property P2: TPoint read FP2 write FP2;
end;
 
type
 
{ TCdrDrawingContextGCS }
 
 TCdrDrawingContextGCS = class(TFrame)
    Label1: TLabel;
    Vue: TPaintBox;
    pnlVue: TPanel;
    procedure VuePaint(Sender: TObject);
  strict private
    FLastError: string;
    FTmpBuffer: TBGRABitmap;
    // couleurs et styles: brosse, crayon
    FBackGroundColor  : TColor;
    FBackGroundOpacity: byte;
    // gestion des zooms
    //FZC1, FZC2    : TPoint2Df;
    //FZP1, FZP2    : TPoint;
    FRegionCMini  : TPoint2Df;
    FRegionCMaxi  : TPoint2Df;
    // paramètres de vues
    FRappHLVue      : double;
    FRappScrReal    : double;
    FInvRappScrReal : double;
    // statut du dessin
    FDessinReady    : boolean;
    FDessinEnCours  : boolean;
    // liste des objets à tracer
    {$IFDEF USE_TSTRINGLIST}
    FListOfEntities: TStringList;
    {$ELSE}
    FListOfEntities: array of TObject;
    {$ENDIF USE_TSTRINGLIST}
 
    // Objets temporaires
    FTemporaryPolyLineGon: TDGCPolyLineGon;
    FMakingPolyLineGon: boolean;
 
 
 
    function QAddObject(const QMsg: string; const QObj: TObject): boolean;
  private
    function GetCoordsMonde(const PP: TPoint): TPoint2Df;
    function GetCoordsPlan(const PM: TPoint2Df): TPoint;
    function GetRYMaxi(): double;
    procedure RedessinVue();
    function getNbEntities(): integer;
    procedure ViderListeAffichage();
    // /!\ Les objets doivent être ici créés par l'appelant
    function AddPolyLine(const P: TDGCPolyline; const QName: string; const DoFlush: boolean): boolean;
    function AddPolygon(const P: TDGCPolygon; const QName: string; const DoFlush: boolean): boolean;
  public
    function  Initialiser(const X1, Y1, X2, Y2: double): boolean;
    procedure Finaliser();
    procedure ClearDrawing();
    // définition des styles de crayon et brosses
    procedure SetBackgroundColor(const C: TColor; const QOpacity: byte = 255);
    procedure SetPenColorStyle(const C: TColor; const QOpacity: byte = 255; const QWidth: integer = 0; const QStyle: TPenStyle = psSolid);
    procedure SetBrushColorStyle(const C: TColor; const QOpacity: byte = 255; const QStyle: TBrushStyle = bsSolid);
 
    procedure SetViewLimits(const X1, Y1, X2, Y2: double; const DebugTag: string); overload;
    procedure SetViewLimits(const QCoinBasGauche, QCointHautDroit: TPoint2Df; const DebugTag: string); overload;
 
 
    function  AddLine(const X1, Y1, X2, Y2: double; const QName: string = 'Line'; const DoFlush: boolean = false): boolean;
    function  AddCircle(const X1, Y1, R1, R2: double; const QName: string = 'Circle'; const DoFlush: boolean = false): boolean;
    function  AddTriangle(const X1, Y1, X2, Y2, X3, Y3: double; const QName: string = 'Triangle'; const DoFlush: boolean = false): boolean;
 
    function  BeginPolyline(const QName: string='PolyLine'; const QDoFlush: boolean=false): boolean;
    procedure AddVertex(const QX, QY: double);
    procedure EndPolyline();
     function BeginPolygon(const QName: string='Polygone'; const QDoFlush: boolean=false): boolean;
    procedure EndPolygon();
    // procedure SetListCapacity(const N: integer); // Inutile: pas de gain de temps significatif
    procedure BeginDrawing();
    procedure EndDrawing();
    procedure Flush();
 
    function  GetLastError(): string;
  end;
 
implementation
 
{$R *.lfm}
 
{$IFDEF GHTOPO_INDEPENDANT}
procedure pass; inline; // analogue à l'instruction 'pass' du Python
begin
  ; // ne fait rien
end;
function MakeTPoint2Df(const QX, QY: double): TPoint2Df;
begin
  Result.X := QX;
  Result.Y := QY;
end;
procedure Swap(var V1, V2: Double); overload;
var Tmp: Double;
begin
  Tmp  := V1;
  V1   := V2;
  V2   := Tmp;
end;
 
{ TDGCPolyLineGon }
 
constructor TDGCPolyLineGon.Create(const QName: string; const QDoFlush: boolean);
begin
  inherited Create;
  FName    := QName;
  FDoFlush := QDoFlush;
  SetLength(ListeVertex, 0);
end;
 
destructor TDGCPolyLineGon.Destroy();
begin
  SetLength(ListeVertex, 0);
  inherited Destroy;
end;
 
function TDGCPolyLineGon.AddVertex(const V: TPoint): boolean;
var
  n: Integer;
begin
  result := false;
  n := Length(ListeVertex);
  SetLength(ListeVertex, n + 1);
  ListeVertex[n] := V;
end;
 
function TDGCPolyLineGon.GetVertex(const Idx: integer): TPoint;
begin
  Result := ListeVertex[Idx];
end;
 
function TDGCPolyLineGon.GetNbVertex(): integer;
begin
  Result := length(ListeVertex);
end;
 
{ TDGCTriangle }
 
constructor TDGCTriangle.Create(const P1, P2, P3: TPoint);
begin
  inherited Create;
  FP1 := P1;
  FP2 := P2;
  FP3 := P3;
end;
 
{ TCircle }
 
constructor TDGCCircle.Create(const QCentre: TPoint; const QR1, QR2: integer);
begin
  inherited Create;
  FCentre := QCentre;
  FRayon1 := QR1;
  FRayon2 := QR2;
 
end;
 
{ TSegment }
 
constructor TDGCSegment.Create(const P1, P2: TPoint);
begin
  inherited Create;
  FP1 := P1;
  FP2 := P2;
end;
//******************************************************************************
{$ENDIF}
function TCdrDrawingContextGCS.Initialiser(const X1, Y1, X2, Y2: double): boolean;
begin
  Result := false;
  FLastError := '';
  SetBackgroundColor(clWhite);
  FDessinReady    := false;
  FDessinEnCours  := False;
  FMakingPolyLineGon := false;
  {$IFDEF USE_TSTRINGLIST}
  FListOfEntities := TStringList.Create;
  try
    FListOfEntities.OwnsObjects := True; // les objets membres seront détruits par la liste
  {$ELSE}
  try
  {$ENDIF USE_TSTRINGLIST}
    ViderListeAffichage();
    self.SetViewLimits(X1, Y1, X2, Y2, 'Initialiser()');
    Label1.Caption:= format('%f, %f -> %f, %f', [FRegionCMini.X, FRegionCMini.Y, FRegionCMaxi.X, FRegionCMaxi.Y]);
    FDessinReady := True;
    result := True;
    Vue.Invalidate;
  except
 
  end;
end;
procedure TCdrDrawingContextGCS.Finaliser();
begin
  ViderListeAffichage();
  pass;
end;
 
procedure TCdrDrawingContextGCS.ClearDrawing();
begin
  ViderListeAffichage();
  vue.Invalidate;
end;
 
function TCdrDrawingContextGCS.QAddObject(const QMsg: string; const QObj: TObject): boolean;
var
  Nb: Integer;
begin
 
  {$IFDEF USE_TSTRINGLIST}
  Result := (FListOfEntities.AddObject(QMsg, QObj) > 0);
  {$ELSE}
  Result := false;
  try
  Nb := Length(FListOfEntities);
  SetLength(FListOfEntities, Nb + 1);
  FListOfEntities[Nb] := QObj;
  Result := True;
  except
  end;
  {$ENDIF USE_TSTRINGLIST}
end;
 
 
procedure TCdrDrawingContextGCS.SetBackgroundColor(const C: TColor; const QOpacity: byte = 255);
begin
  FBackGroundColor   := C;
  FBackGroundOpacity := QOpacity;
end;
 
procedure TCdrDrawingContextGCS.SetPenColorStyle(const C: TColor; const QOpacity: byte; const QWidth: integer; const QStyle: TPenStyle);
var
  MyPen: TDGCPen;
begin
  MyPen := TDGCPen.Create;
  MyPen.Couleur := C;
  MyPen.Opacity := QOpacity;
  MyPen.Width   := QWidth;
  QAddObject('Command', MyPen);
end;
 
procedure TCdrDrawingContextGCS.SetBrushColorStyle(const C: TColor; const QOpacity: byte; const QStyle: TBrushStyle = bsSolid);
var
  MyBrush: TDGCBrush;
begin
  MyBrush := TDGCBrush.Create;
 
  MyBrush.Couleur  := C;
  MyBrush.Opacity  := QOpacity;
  MyBrush.Style    := QStyle;
  QAddObject('Command', MyBrush);
end;
 
 
procedure TCdrDrawingContextGCS.RedessinVue();
var
  R: TRect;
  MyEntity: TObject;
  Nb, i: Integer;
  T0, T1: TDateTime;
  HH, MM, SS, MS: word;
  // dessin des objets
  procedure QSetPenStyle(const E: TDGCPen);
  begin
    FTmpBuffer.CanvasBGRA.Pen.Color    := E.Couleur;
    FTmpBuffer.CanvasBGRA.Pen.Opacity  := E.Opacity;
    FTmpBuffer.CanvasBGRA.Pen.Width    := E.Width;
    FTmpBuffer.CanvasBGRA.Pen.Style    := E.Style;
  end;
  procedure QSetBrushStyle(const E: TDGCBrush);
  begin
    FTmpBuffer.CanvasBGRA.Brush.Color    := E.Couleur;
    FTmpBuffer.CanvasBGRA.Brush.Opacity  := E.Opacity;
    FTmpBuffer.CanvasBGRA.Brush.Style    := E.Style;
  end;
  procedure QDrawSegment(const E: TDGCSegment);
  begin
    FTmpBuffer.CanvasBGRA.MoveTo(E.P1.x, E.P1.y);
    FTmpBuffer.CanvasBGRA.LineTo(E.P2.x, E.P2.y);
  end;
  procedure QDrawCircle(const E: TDGCCircle);
  begin
    FTmpBuffer.CanvasBGRA.EllipseC(E.FCentre.X, E.Centre.Y, E.Rayon1, E.Rayon2);
  end;
  procedure QDrawTriangle(const E: TDGCTriangle);
  begin
    FTmpBuffer.CanvasBGRA.Polygon([E.P1, E.P2, E.P3]);
  end;
  procedure QDrawPolyline(const E: TDGCPolyline);
  begin
    FTmpBuffer.CanvasBGRA.Polygon(E.ListeVertex);
  end;
  procedure QDrawPolygon(const E: TDGCPolygon);
  begin
    FTmpBuffer.CanvasBGRA.Polygon(E.ListeVertex);
  end;
begin
  if (not FDessinReady) then Exit;
  if (FDessinEnCours) then Exit; // FDessinEnCours est un flag pour protéger la reconstruction du dessin
  FDessinEnCours := True;
  FTmpBuffer := TBGRABitmap.Create(vue.Width, vue.Height);
  try
    T0 := Now();
    R.Left   := Vue.Left;
    R.Top    := Vue.Top;
    R.Bottom := Vue.Top  + Vue.Height;
    R.Right  := Vue.Left + Vue.Width;
    FTmpBuffer.CanvasBGRA.Brush.Color := FBackGroundColor;
    FTmpBuffer.CanvasBGRA.Brush.Opacity := 255;
    FTmpBuffer.CanvasBGRA.Brush.Style   := bsSolid;
    FTmpBuffer.CanvasBGRA.FillRect(R);
    // on dessine ici: exécution de la liste d'affichage
    Nb := getNbEntities();
    if (Nb > 0) then
    begin
      for i := 0 to Nb - 1 do
      begin
        {$IFDEF USE_TSTRINGLIST}
        MyEntity := FListOfEntities.Objects[i];
        {$ELSE}
        MyEntity := FListOfEntities[i];
        {$ENDIF USE_TSTRINGLIST}
 
        if (MyEntity is TDGCSegment)    then QDrawSegment(MyEntity as TDGCSegment);     // Temps de reconstruction: 00:00:00.140 // as TDGCSegment);
        if (MyEntity is TDGCCircle)     then QDrawCircle(MyEntity as TDGCCircle);       //                                       // as TDGCCircle);
        if (MyEntity is TDGCTriangle)   then QDrawTriangle(MyEntity as TDGCTriangle);   //                                       // as TDGCTriangle);
        if (MyEntity is TDGCPolyline)   then QDrawPolyline(MyEntity as TDGCPolyline);
        if (MyEntity is TDGCPolygon)    then QDrawPolygon(MyEntity as TDGCPolygon);
        if (MyEntity is TDGCPen)        then QSetPenStyle(MyEntity as TDGCPen);
        if (MyEntity is TDGCBrush)      then QSetBrushStyle(MyEntity as TDGCBrush);
      end;
    end;
    T1 := Now();
    FTmpBuffer.Draw(vue.Canvas, 0, 0, True);
    DecodeTime(t1-t0, HH, MM, SS, MS);
    Label1.Caption:= format('%f, %f -> %f, %f - %d entites - Temps: %.2d:%.2d:%.2d.%.3d', [FRegionCMini.X, FRegionCMini.Y, FRegionCMaxi.X, FRegionCMaxi.Y, Nb, HH, MM, SS, MS]);
  finally
    FreeAndNil(FTmpBuffer);
    FDessinEnCours := False;
  end;
end;
 
function TCdrDrawingContextGCS.getNbEntities(): integer;
begin
  {$IFDEF USE_TSTRINGLIST}
  Result := FListOfEntities.Count;
  {$ELSE}
  Result := length(FListOfEntities);
  {$ENDIF USE_TSTRINGLIST}
 
 
end;
 
procedure TCdrDrawingContextGCS.ViderListeAffichage();
var
  Nb, i: Integer;
begin
  {$IFDEF USE_TSTRINGLIST}
  Nb := FListOfEntities.Count;
  FListOfEntities.Clear;
  {$ELSE}
  Nb := Length(FListOfEntities);
  for i := 0 to Nb - 1 do
  begin
    FreeAndNil(FListOfEntities[i]);
  end;
  SetLength(FListOfEntities, 0);
  {$ENDIF USE_TSTRINGLIST}
 
  //FListOfEntities.capacity := 4000;
end;
 
 
 
function TCdrDrawingContextGCS.GetCoordsPlan(const PM: TPoint2Df): TPoint;
begin
  Result.X := Round((PM.X - FRegionCMini.X) * FRappScrReal);
  Result.Y := Round((FRegionCMaxi.Y - PM.Y) * FRappScrReal);
end;
 
procedure TCdrDrawingContextGCS.VuePaint(Sender: TObject);
begin
  RedessinVue();
end;
 
function TCdrDrawingContextGCS.GetCoordsMonde(const PP: TPoint): TPoint2Df;
begin
  Result.X :=  FInvRappScrReal * PP.X + FRegionCMini.X;
  Result.Y := -FInvRappScrReal * PP.Y + FRegionCMaxi.Y;
end;
// cette fonction retourne d'autres paramètres
function TCdrDrawingContextGCS.GetRYMaxi(): double;
var
  qdx: Double;
begin
  qdx := FRegionCMaxi.X - FRegionCMini.X;
  // calcul du rapport Hauteur/largeur de vue
  FRappHLVue := Vue.Height / Vue.Width;
  // calcul du rapport Ecran/Réel
  FRappScrReal := Vue.Width / qdx;
  FInvRappScrReal := 1 / FRappScrReal;
  // calcul de la hauteur de visualisation
  Result := FRegionCMini.Y + qdx * FRappHLVue;
end;
 
 
 
 
procedure TCdrDrawingContextGCS.SetViewLimits(const X1, Y1, X2, Y2: double; const DebugTag: string);
const
  Epsilon = 1e-2;
var
  qX1, qX2, qY1, qY2: double;
  d1, d2: double;
begin
  qX1 := X1;
  qY1 := Y1;
  qX2 := X2;
  qY2 := Y2;
 
  d1 := qX2 - qX1;
  d2 := qY2 - qY1;
  // si zone trop étroite, abandonner
  if (Abs(d1) < Epsilon) or (Abs(d2) < Epsilon) then
    Exit;
  // échanger de manière à rendre indifférent le sens du rectangle de sélection
  if (qX2 < qX1) then Swap(qX1, qX2);
  if (qY2 < qY1) then Swap(qY1, qY2);
  FRegionCMini := MakeTPoint2Df(qX1, qY1);
  FRegionCMaxi := MakeTPoint2Df(qX2, qY2);
  // Redéfinition de la hauteur maxi
  FRegionCMaxi.Y := GetRYMaxi;
end;
procedure TCdrDrawingContextGCS.SetViewLimits(const QCoinBasGauche, QCointHautDroit: TPoint2Df; const DebugTag: string);
begin
  SetViewLimits(QCoinBasGauche.X, QCoinBasGauche.Y, QCointHautDroit.X, QCointHautDroit.Y, DebugTag);
end;
// liste d'affichage pour les objets: Ajout des éléments
function TCdrDrawingContextGCS.AddLine(const X1, Y1, X2, Y2: double; const QName: string = 'Line'; const DoFlush: boolean = false): boolean;
var
  MySegment: TDGCSegment;
  PP1, PP2: TPoint;
begin
  //if (not FDessinReady) then Exit;
  PP1 := GetCoordsPlan(MakeTPoint2Df(X1, Y1));
  PP2 := GetCoordsPlan(MakeTPoint2Df(X2, Y2));
  MySegment := TDGCSegment.Create(PP1, PP2);
  Result := QAddObject(QName, MySegment);
  //if (DoFlush) then Vue.Invalidate;
end;
function TCdrDrawingContextGCS.AddCircle(const X1, Y1, R1, R2: double; const QName: string = 'Circle'; const DoFlush: boolean = false): boolean;
var
  MyCircle: TDGCCircle;
  PP1, PP2: TPoint;
begin
  //if (not FDessinReady) then Exit;
  PP1 := GetCoordsPlan(MakeTPoint2Df(X1, Y1));
  PP2 := GetCoordsPlan(MakeTPoint2Df(X1 + R1, Y1 + R2));
  MyCircle := TDGCCircle.Create(PP1, PP2.X - PP1.X, PP2.Y - PP1.Y);
  Result := QAddObject(QName, MyCircle);
  //if (DoFlush) then Vue.Invalidate;
end;
function TCdrDrawingContextGCS.AddTriangle(const X1, Y1, X2, Y2, X3, Y3: double; const QName: string = 'Triangle'; const DoFlush: boolean = false): boolean;
var
  MyTriangle: TDGCTriangle;
  PP1, PP2, PP3: TPoint;
begin
  if (not FDessinReady) then Exit;
  PP1 := GetCoordsPlan(MakeTPoint2Df(X1, Y1));
  PP2 := GetCoordsPlan(MakeTPoint2Df(X2, Y2));
  PP3 := GetCoordsPlan(MakeTPoint2Df(X3, Y3));
  MyTriangle := TDGCTriangle.Create(PP1, PP2, PP3);
  Result := QAddObject(QName, MyTriangle);
  //if (DoFlush) then Vue.Invalidate;
end;
// /!\ Ici, l'objet TDGCPolyLineGon doit être créé par l'appelant
function TCdrDrawingContextGCS.AddPolyLine(const P: TDGCPolyline; const QName: string; const DoFlush: boolean): boolean;
begin
  //if (not FDessinReady) then Exit;
  Result := QAddObject(QName, P);
  //if (DoFlush) then Vue.Invalidate;
end;
function TCdrDrawingContextGCS.AddPolygon(const P: TDGCPolygon; const QName: string; const DoFlush: boolean): boolean;
begin
  //if (not FDessinReady) then Exit;
  Result := QAddObject(QName, P);
  //if (DoFlush) then Vue.Invalidate;
end;
 
procedure TCdrDrawingContextGCS.BeginDrawing();
begin
  pass; //FProcBeginDrawingCalled := true;
end;
procedure TCdrDrawingContextGCS.EndDrawing();
begin
  pass; //FProcBeginDrawingCalled := false;
end;
 
// Ajout de polyligne/polygone
procedure TCdrDrawingContextGCS.AddVertex(const QX, QY: double);
var
  PP: TPoint;
begin
  if (not FMakingPolyLineGon) then
  begin
    FLastError := format('%s.AddVertex(%f, %f) sans %s.BeginPolyline()', [ClassName, QX, QY, ClassName]);
    Exit;
  end;
  if (not assigned(FTemporaryPolyLineGon)) then
  begin
    FLastError := format('Champ %s.FTemporaryPolyLineGon non initialisé', [ClassName]);
    Exit;
  end;
  PP := GetCoordsPlan(MakeTPoint2Df(QX, QY));
  FTemporaryPolyLineGon.AddVertex(PP);
end;
function TCdrDrawingContextGCS.BeginPolyline(const QName: string = 'PolyLine'; const QDoFlush: boolean = false): boolean;
begin
  result := false;
  FTemporaryPolyLineGon := TDGCPolyLineGon.Create(QName, QDoFlush);
  try
    FMakingPolyLineGon := True;
    Result := True;
  except
  end;
end;
 
 
 
procedure TCdrDrawingContextGCS.EndPolyline();
begin
 
end;
function TCdrDrawingContextGCS.BeginPolygon(const QName: string = 'Polygone'; const QDoFlush: boolean = false): boolean;
begin
  result := false;
  if (FMakingPolyLineGon) then
  begin
    FLastError := 'Polygone précédent non clôturé par EndPolygon()';
    Exit;
  end;
  FTemporaryPolyLineGon := TDGCPolyLineGon.Create(QName, QDoFlush);
  try
    FMakingPolyLineGon := True;
    Result := True;
  except
  end;
end;
 
 
 
procedure TCdrDrawingContextGCS.EndPolygon();
var
  MyPolygon : TDGCPolygon;
  i, Nb: Integer;
begin
  try
    if (not FMakingPolyLineGon) then
    begin
      FLastError := format('%s.EndPolygon() sans %s.BeginPolygon()', [ClassName, ClassName]);
      Exit;
    end;
    Nb := FTemporaryPolyLineGon.GetNbVertex();
    if (Nb < 3) then
    begin
      FLastError := Format('Polygone "%s" invalide, %d sommets', [FTemporaryPolyLineGon.Name, FTemporaryPolyLineGon.GetNbVertex()]);
      exit;
    end;
    MyPolygon := TDGCPolygon.Create(FTemporaryPolyLineGon.Name, FTemporaryPolyLineGon.DoFlush);
    for i := 0 to Nb - 1 do MyPolygon.AddVertex(FTemporaryPolyLineGon.GetVertex(i));
 
    // et on ajoute
    AddPolygon(MyPolygon, FTemporaryPolyLineGon.Name, FTemporaryPolyLineGon.DoFlush);
 
    FreeAndNil(FTemporaryPolyLineGon);
    // avant de désarmer le flag FMakingPolyLineGon
    FMakingPolyLineGon := false;
 
  except
 
  end;
end;
 
// Rafraichit l'affichage
procedure TCdrDrawingContextGCS.Flush();
begin
  Vue.Invalidate;
end;
 
function TCdrDrawingContextGCS.GetLastError(): string;
begin
  Result := FLastError;
end;
 
end.
Pour attribution CC0, remarques, suggestions

Le code du LFM
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
 
object CdrDrawingContextGCS: TCdrDrawingContextGCS
  Left = 0
  Height = 452
  Top = 0
  Width = 542
  Align = alClient
  ClientHeight = 452
  ClientWidth = 542
  ParentFont = False
  TabOrder = 0
  DesignLeft = 603
  DesignTop = 463
  object pnlVue: TPanel
    Left = 0
    Height = 428
    Top = 24
    Width = 542
    Anchors = [akTop, akLeft, akRight, akBottom]
    BevelOuter = bvLowered
    ClientHeight = 428
    ClientWidth = 542
    DoubleBuffered = True
    ParentDoubleBuffered = False
    TabOrder = 0
    object Vue: TPaintBox
      Left = 1
      Height = 426
      Top = 1
      Width = 540
      Align = alClient
      OnPaint = VuePaint
    end
  end
  object Label1: TLabel
    Left = 4
    Height = 13
    Top = 0
    Width = 31
    Caption = 'Label1'
    ParentColor = False
  end
end
Exemple d'utilisation:

- Créer un projet vierge avec un TForm
- Sauvegarder
- Copier les fichiers *.pas du 'composant' dans le dossier du projet
- Déposer un TPanel dans lequel on posera le TCdrDrawingContextGCS
- Déposer un TButton et y associer le code suivant

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
 
procedure TForm1.Button1Click(Sender: TObject);
var
FFF: string;
begin 
  //  Initialise avec une plage X allant de -100 à +100 m, une plage Y allant de -101 à +101 m
  CdrDrawingContextGCS1.Initialiser(-100.0, -101.0, 100.0, 101.0);
  // Prépare le dessin
  CdrDrawingContextGCS1.BeginDrawing();  
  // Définit les attributs de crayon (en fait, empile une commande dans la liste d'affichage)
  CdrDrawingContextGCS1.SetPenColorStyle(clRed, 255, 0);
  // Ajoute 666 lignes aléatoires
  for i := 0 to 666 do
   CdrDrawingContextGCS1.AddLine(-100 * Random, -100 * Random, 100 * Random, 100 * Random);
  // Définit crayon et brosse (empile dans la liste d'affichage)
  CdrDrawingContextGCS1.SetPenColorStyle(clGreen, 255, 0);
  CdrDrawingContextGCS1.SetBrushColorStyle(clGreen, 128, bsClear);
  // Ajoute 666 ellipses
  for i := 0 to 666 do
    CdrDrawingContextGCS1.AddCircle(-100 * Random, -100 * Random, 100 * Random, 100 * Random);         
  CdrDrawingContextGCS1.SetPenColorStyle(clBlue, 255, 0);
  CdrDrawingContextGCS1.SetBrushColorStyle(clGreen, 128, bsSolid);
  // Ajoute 666 triangles
  for i := 0 to 666 do
    CdrDrawingContextGCS1.AddTriangle(-100 * Random, -100 * Random, -100 * Random, 100 * Random, -100 * Random, 100 * Random);
 
  // Ajoute deux polygones
  CdrDrawingContextGCS1.SetPenColorStyle(clRed, 255, 3);
  CdrDrawingContextGCS1.SetBrushColorStyle(clYellow, 68, bsSolid);
  CdrDrawingContextGCS1.BeginPolygon('Polygone1');
    CdrDrawingContextGCS1.AddVertex(-80, -90);
    CdrDrawingContextGCS1.AddVertex( 80, -70);
    CdrDrawingContextGCS1.AddVertex( 12, 65);
  CdrDrawingContextGCS1.EndPolygon();
 
  CdrDrawingContextGCS1.SetPenColorStyle(clBlue, 255, 2);
  CdrDrawingContextGCS1.SetBrushColorStyle(clFuchsia, 32, bsSolid);
 
  CdrDrawingContextGCS1.BeginPolygon('Polygone2');
    CdrDrawingContextGCS1.AddVertex(10, 10);
    CdrDrawingContextGCS1.AddVertex(80, 0);
    CdrDrawingContextGCS1.AddVertex(89, 69);
    CdrDrawingContextGCS1.AddVertex(40, 47);
    CdrDrawingContextGCS1.AddVertex(-5, 39);
  CdrDrawingContextGCS1.EndPolygon();
 
 
 // Après ces opérations, rien n'est encore dessiné. 
 // La fonction suivante Flush() exécute la liste d'affichage et trace le dessin
 CdrDrawingContextGCS1.Flush(); 
// récupération et affichage du dernier message d'erreur
  FFF := CdrDrawingContextGCS1.GetLastError();
  if (FFF <> '') then ShowMessage(FFF);            
end;