2
// This unit is part of the GLScene Engine https://github.com/glscene
5
Implements a basic Canvas-like interface over for OpenGL.
6
This class can be used for generic OpenGL applications and has no dependencies
7
to the GLScene core units (only to base units).
10
10/11/12 - PW - Added CPP compatibility: changed vector arrays to records,
11
Replaced direct access to PenAlpha property with GetPenAlpha method
12
05/02/11 - Yar - Now PenColor setter always direct set color
13
03/10/10 - Yar - Added RoundRect (thanks eric129)
14
21/09/10 - Yar - Added Arc, ArcTo (thanks µAlexx)
15
03/09/10 - Yar - Added FillRectGradient, FillEllipseGradient (thanks µAlexx)
16
23/08/10 - Yar - Replaced OpenGL1x functions to OpenGLAdapter
17
04/04/10 - Yar - Fixes after GLState revision
18
07/11/09 - DaStr - Some cosmetic fixes. Overloaded TGLCanvas.EllipseBB(),
19
TGLCanvas.Ellipse(), TGLCanvas.FillEllipse()
20
31/07/07 - DaStr - Added missing StopPrimitive call to TGLCanvas.FillRect
21
(Bugtracker ID = 1775528)
22
06/06/07 - DaStr - Removed ConvertColorVector and ConvertWinColor (now in GLColor.pas)
23
Added GLColor to uses (BugtrackerID = 1732211)
24
Removed TColor declaration (now in GLCrossPlatform.pas)
25
02/08/04 - LR, YHC - BCB corrections: used record instead array
26
Replaced direct access of some properties by a getter and a setter
27
08/07/04 - LR - Replace Graphics and TPoint by GLCrossPlatform for Linux
28
13/01/04 - EG - Polyline/Polygon fix
29
07/05/03 - EG - SetPenWidth now correctly stops the primitive
30
08/01/03 - EG - StopPrimitive now public
31
09/12/02 - EG - Now properly disables fog
32
20/11/02 - EG - Now uses Types/Windows TPoint (D5 & D6 tested only)
33
01/10/02 - EG - Added Polygon & Polyline
34
04/03/02 - EG - Added FrameRect and FillRect
35
31/01/02 - EG - Texture3D/CubeMap only disabled if supported
36
24/01/02 - EG - Added PenAlpha
37
19/01/02 - EG - Creation
56
TArcDirection = (adCounterClockWise, adClockWise);
60
{ A simple Canvas-like interface for OpenGL.
61
This class implements a small "shell" for 2D operations in OpenGL,
62
it operates over the current OpenGL context and provides methods
63
for drawing lines, ellipses and points.
64
This class is typically used by creating an instance, using it for drawing,
65
and freeing the instance. When drawing (0, 0) is the top left corner.
66
All coordinates are internally maintained with floating point precision.
67
Several states are cached and it is of primary importance not to invoke
68
OpenGL directly throughout the life of an instance (at the cost of
69
unespected behaviour). }
73
FBufferSizeX, FBufferSizeY: Integer;
75
FLastPrimitive: Integer;
76
FCurrentPos: TAffineVector;
79
FCurrentPenColorVector: TVector;
80
FArcDirection: TArcDirection;
83
procedure BackupOpenGLStates;
85
procedure StartPrimitive(const primitiveType: Integer);
87
procedure EllipseVertices(x, y, xRadius, yRadius: Single);
89
procedure SetPenColor(const val: TColor);
91
function GetPenAlpha: Single;
92
procedure SetPenAlpha(const val: Single);
93
procedure SetPenWidth(const val: Integer);
95
procedure SwapSingle(pX, pY: PSingle);
96
procedure NormalizePoint(const x1, y1, x2, y2: Single;
97
const x, y: Single; pX, pY: PSingle);
99
procedure DrawArc(x1, y1, x2, y2, x3, y3, x4, y4: Single;
100
UpdateCurrentPos: Boolean); overload;
101
procedure DrawArc(x1, y1, x2, y2: Single;
102
AngleBegin, AngleEnd: Single;
103
UpdateCurrentPos: Boolean); overload;
106
constructor Create(bufferSizeX, bufferSizeY: Integer;
107
const baseTransform: TMatrix); overload;
108
constructor Create(bufferSizeX, bufferSizeY: Integer); overload;
109
destructor Destroy; override;
111
{ Stops the current internal primitive.
112
This function is invoked automatically by TGLCanvas when changeing
113
primitives, you should directly call if you want to render your
114
own stuff intertwined with TGLCanvas drawings. In that case, call
115
it before your own OpenGL calls. }
116
procedure StopPrimitive;
118
{ Inverts the orientation of the Y Axis.
119
If (0, 0) was in the top left corner, it will move to the bottom
120
left corner or vice-versa. }
121
procedure InvertYAxis;
123
property CanvasSizeX: Integer read FBufferSizeX;
124
property CanvasSizeY: Integer read FBufferSizeY;
126
{ Current Pen Color. }
127
property PenColor: TColor read FPenColor write SetPenColor;
128
{ Current Pen Alpha channel (from 0.0 to 1.0) }
129
property PenAlpha : Single read GetPenAlpha write SetPenAlpha;
130
{ Current Pen Width. }
131
property PenWidth: Integer read FPenWidth write SetPenWidth;
133
{ Updates the current position (absolute coords). }
134
procedure MoveTo(const x, y: Integer); overload;
135
procedure MoveTo(const x, y: Single); overload;
136
{ Updates the current position (relative coords). }
137
procedure MoveToRel(const x, y: Integer); overload;
138
procedure MoveToRel(const x, y: Single); overload;
140
{ Draws a line from current position to given coordinate.
141
Current position is updated. }
142
procedure LineTo(const x, y: Integer); overload;
143
procedure LineTo(const x, y: Single); overload;
144
procedure LineToRel(const x, y: Integer); overload;
145
procedure LineToRel(const x, y: Single); overload;
146
{ Draws a line from (x1, y1) to (x2, y2).
147
The current position is NOT updated. }
148
procedure Line(const x1, y1, x2, y2: Integer); overload;
149
procedure Line(const x1, y1, x2, y2: Single); overload;
151
{ Draws the set of lines defined by connecting the points.
152
Similar to invoking MoveTo on the first point, then LineTo
153
on all the following points. }
154
procedure Polyline(const points: array of TGLPoint);
155
{ Similar to Polyline but also connects the last point to the first. }
156
procedure Polygon(const points: array of TGLPoint);
158
{ Plots a pixel at given coordinate.
159
PenWidth affects pixel size.
160
The current position is NOT updated. }
161
procedure PlotPixel(const x, y: Integer); overload;
162
procedure PlotPixel(const x, y: Single); overload;
164
{ Draw the (x1,y1)-(x2, y2) rectangle's frame (border). }
165
procedure FrameRect(const x1, y1, x2, y2: Integer); overload;
166
procedure FrameRect(const x1, y1, x2, y2: Single); overload;
168
{ Draw the (x1,y1)-(x2, y2) rectangle (filled with PenColor). }
169
procedure FillRect(const x1, y1, x2, y2: Integer); overload;
170
procedure FillRect(const x1, y1, x2, y2: Single); overload;
172
{ Draw the (x1,y1)-(x2, y2) rectangle (filled with given gradient's color). }
173
procedure FillRectGradient(const x1, y1, x2, y2: Single;
174
const x1y1Color, x2y1Color, x2y2Color, x1y2Color: TColorVector); overload;
175
procedure FillRectGradient(const x1, y1, x2, y2: Integer;
176
const x1y1Color, x2y1Color, x2y2Color, x1y2Color: TColorVector); overload;
178
{ Draws an ellipse with (x1,y1)-(x2, y2) bounding rectangle. }
179
procedure EllipseBB(const x1, y1, x2, y2: Integer); overload;
180
procedure EllipseBB(const x1, y1, x2, y2: Single); overload;
182
{ Draws and ellipse centered at (x, y) with given radiuses. }
183
procedure Ellipse(const x, y: Integer; const xRadius, yRadius: Single);
185
procedure Ellipse(const x, y: Single; const xRadius, yRadius: Single);
187
procedure Ellipse(const x, y: Single; const Radius: Single); overload;
189
{ Draw a filled ellipse. }
190
procedure FillEllipse(const x, y: Integer; const xRadius, yRadius: Single);
192
procedure FillEllipse(const x, y: Single; const xRadius, yRadius: Single);
195
procedure FillEllipse(const x, y: Single; const Radius: Single); overload;
197
{ Draw a filled gradient ellipse.
198
OpenGL will use the last PenColor and PenAlpha as the center color and do gradient to edge of ellipse using the edgeColor parameter. }
199
procedure FillEllipseGradient(const x, y, xRadius, yRadius: Single;
200
const edgeColor: TColorVector); overload;
201
procedure FillEllipseGradient(const x, y: Integer;
202
const xRadius, yRadius: Integer; const edgeColor: TColorVector); overload;
203
procedure FillEllipseGradient(const x, y, Radius: Single;
204
const edgeColor: TColorVector); overload;
205
{ Draw an elliptical arc.
206
The points (x1, y1) and (x2, y2) specify the bounding rectangle.
207
An ellipse formed by the specified bounding rectangle defines the curve of the arc.
208
The arc extends in the current drawing direction from the point where it intersects the radial from the center of the bounding rectangle to the (x3, y3) point.
209
The arc ends where it intersects the radial from the center of the bounding rectangle to the (x4, y4) point.
210
If the starting point and ending point are the same, a complete ellipse is drawn.
211
Use the ArcDirection property to get and set the current drawing direction for a device context.
212
The default drawing direction is counterclockwise. }
213
procedure Arc(const x1, y1, x2, y2, x3, y3, x4, y4: Integer); overload;
214
procedure Arc(const x1, y1, x2, y2, x3, y3, x4, y4: Single); overload;
215
procedure Arc(const x1, y1, x2, y2: Single; AngleBegin,
216
AngleEnd: Single); overload;
218
{ Same as Arc but update the current position. }
219
procedure ArcTo(const x1, y1, x2, y2, x3, y3, x4, y4: Integer); overload;
220
procedure ArcTo(const x1, y1, x2, y2, x3, y3, x4, y4: Single); overload;
221
procedure ArcTo(const x1, y1, x2, y2: Single; AngleBegin,
222
AngleEnd: Single); overload;
224
procedure RoundRect(const x1, y1, x2, y2, xr, yr: Integer); overload;
225
procedure RoundRect(const x1, y1, x2, y2, xr, yr: Single); overload;
228
property ArcDirection: TArcDirection read FArcDirection
232
//-------------------------------------------------------------
233
//-------------------------------------------------------------
234
//-------------------------------------------------------------
236
//-------------------------------------------------------------
237
//-------------------------------------------------------------
238
//-------------------------------------------------------------
246
cNoPrimitive = MaxInt;
250
// ------------------
251
// ------------------ TGLCanvas ------------------
252
// ------------------
257
constructor TGLCanvas.Create(bufferSizeX, bufferSizeY: Integer;
258
const baseTransform: TMatrix);
262
FBufferSizeX := bufferSizeX;
263
FBufferSizeY := bufferSizeY;
265
GL.MatrixMode(GL_PROJECTION);
267
PM := CreateOrthoMatrix(0, bufferSizeX, bufferSizeY, 0, -1, 1);
270
GL.MatrixMode(GL_MODELVIEW);
272
GL.LoadMatrixf(@baseTransform);
276
FLastPrimitive := cNoPrimitive;
277
FArcDirection := adCounterClockWise;
283
constructor TGLCanvas.Create(bufferSizeX, bufferSizeY: Integer);
285
Create(bufferSizeX, bufferSizeY, IdentityHmgMatrix);
291
destructor TGLCanvas.Destroy;
295
GL.MatrixMode(GL_PROJECTION);
298
GL.MatrixMode(GL_MODELVIEW);
305
procedure TGLCanvas.BackupOpenGLStates;
307
with CurrentGLContext.GLStates do
312
Disable(stColorMaterial);
313
Disable(stDepthTest);
314
Disable(stLineSmooth);
315
Disable(stLineStipple);
316
Disable(stPointSmooth);
318
SetBlendFunc(bfSrcAlpha, bfOneMinusSrcAlpha);
320
// Setup and backup pen stuff
321
FPenColor := clBlack;
322
SetVector(FCurrentPenColorVector, NullHmgPoint);
323
GL.Color4fv(@FCurrentPenColorVector);
333
procedure TGLCanvas.StartPrimitive(const primitiveType: Integer);
335
if primitiveType <> FLastPrimitive then
337
if FLastPrimitive <> cNoPrimitive then
339
if primitiveType <> cNoPrimitive then
340
GL.Begin_(primitiveType);
341
FLastPrimitive := primitiveType;
348
procedure TGLCanvas.StopPrimitive;
350
StartPrimitive(cNoPrimitive);
356
procedure TGLCanvas.InvertYAxis;
360
mat := IdentityHmgMatrix;
362
mat.V[3].V[1] := FBufferSizeY;
363
GL.MultMatrixf(@mat);
369
procedure TGLCanvas.SetPenColor(const val: TColor);
371
SetVector(FCurrentPenColorVector, ConvertWinColor(val,
372
FCurrentPenColorVector.V[3]));
374
GL.Color4fv(@FCurrentPenColorVector);
380
procedure TGLCanvas.SetPenAlpha(const val: Single);
382
FCurrentPenColorVector.V[3] := val;
383
GL.Color4fv(@FCurrentPenColorVector);
389
procedure TGLCanvas.SetPenWidth(const val: Integer);
393
if val <> FPenWidth then
394
with CurrentGLContext.GLStates do
406
procedure TGLCanvas.MoveTo(const x, y: Integer);
408
FCurrentPos.V[0] := x;
409
FCurrentPos.V[1] := y;
415
procedure TGLCanvas.MoveTo(const x, y: Single);
417
FCurrentPos.V[0] := x;
418
FCurrentPos.V[1] := y;
424
procedure TGLCanvas.MoveToRel(const x, y: Integer);
426
FCurrentPos.V[0] := FCurrentPos.V[0] + x;
427
FCurrentPos.V[1] := FCurrentPos.V[1] + y;
433
procedure TGLCanvas.MoveToRel(const x, y: Single);
435
FCurrentPos.V[0] := FCurrentPos.V[0] + x;
436
FCurrentPos.V[1] := FCurrentPos.V[1] + y;
442
procedure TGLCanvas.LineTo(const x, y: Integer);
444
StartPrimitive(GL_LINES);
445
GL.Vertex2fv(@FCurrentPos);
447
GL.Vertex2fv(@FCurrentPos);
453
procedure TGLCanvas.LineTo(const x, y: Single);
455
StartPrimitive(GL_LINES);
456
GL.Vertex2fv(@FCurrentPos);
458
GL.Vertex2fv(@FCurrentPos);
464
procedure TGLCanvas.LineToRel(const x, y: Integer);
466
LineTo(FCurrentPos.V[0] + x, FCurrentPos.V[1] + y);
472
procedure TGLCanvas.LineToRel(const x, y: Single);
474
LineTo(FCurrentPos.V[0] + x, FCurrentPos.V[1] + y);
480
procedure TGLCanvas.Line(const x1, y1, x2, y2: Integer);
482
StartPrimitive(GL_LINES);
490
procedure TGLCanvas.Line(const x1, y1, x2, y2: Single);
492
StartPrimitive(GL_LINES);
500
procedure TGLCanvas.Polyline(const points: array of TGLPoint);
507
StartPrimitive(GL_LINE_STRIP);
508
GL.Vertex2iv(@points[Low(points)]);
509
for i := Low(points) + 1 to High(points) do
510
GL.Vertex2iv(@points[i]);
518
procedure TGLCanvas.Polygon(const points: array of TGLPoint);
525
StartPrimitive(GL_LINE_LOOP);
526
GL.Vertex2iv(@points[Low(points)]);
527
for i := Low(points) + 1 to High(points) do
528
GL.Vertex2iv(@points[i]);
536
procedure TGLCanvas.PlotPixel(const x, y: Integer);
538
StartPrimitive(GL_POINTS);
545
procedure TGLCanvas.PlotPixel(const x, y: Single);
547
StartPrimitive(GL_POINTS);
551
// FrameRect (integer)
554
procedure TGLCanvas.FrameRect(const x1, y1, x2, y2: Integer);
556
StartPrimitive(GL_LINE_LOOP);
567
procedure TGLCanvas.FrameRect(const x1, y1, x2, y2: Single);
569
StartPrimitive(GL_LINE_LOOP);
577
function TGLCanvas.GetPenAlpha: Single;
579
Result := FCurrentPenColorVector.V[3];
585
procedure TGLCanvas.FillRect(const x1, y1, x2, y2: Integer);
587
StartPrimitive(GL_QUADS);
598
procedure TGLCanvas.FillRect(const x1, y1, x2, y2: Single);
600
StartPrimitive(GL_QUADS);
611
procedure TGLCanvas.EllipseVertices(x, y, xRadius, yRadius: Single);
616
n := Round(MaxFloat(xRadius, yRadius) * 0.1) + 5;
620
PrepareSinCosCache(s, c, 0, 90);
621
ScaleFloatArray(s, yRadius);
622
ScaleFloatArray(c, xRadius);
623
// first quadrant (top right)
625
GL.Vertex2f(x + c[i], y - s[i]);
626
// second quadrant (top left)
627
for i := n - 1 downto 0 do
628
GL.Vertex2f(x - c[i], y - s[i]);
629
// third quadrant (bottom left)
631
GL.Vertex2f(x - c[i], y + s[i]);
632
// fourth quadrant (bottom right)
633
for i := n - 1 downto 0 do
634
GL.Vertex2f(x + c[i], y + s[i]);
640
procedure TGLCanvas.EllipseBB(const x1, y1, x2, y2: Integer);
642
Ellipse((x1 + x2) * 0.5, (y1 + y2) * 0.5, Abs(x2 - x1) * 0.5, Abs(y2 - y1) *
649
procedure TGLCanvas.EllipseBB(const x1, y1, x2, y2: Single);
651
Ellipse((x1 + x2) * 0.5, (y1 + y2) * 0.5, Abs(x2 - x1) * 0.5, Abs(y2 - y1) *
658
procedure TGLCanvas.Ellipse(const x, y: Single; const Radius: Single);
660
Ellipse(x, y, Radius, Radius);
666
procedure TGLCanvas.Ellipse(const x, y: Integer; const xRadius, yRadius:
673
Ellipse(sx, sy, xRadius, yRadius);
679
procedure TGLCanvas.Ellipse(const x, y: Single; const xRadius, yRadius: Single);
681
StartPrimitive(GL_LINE_STRIP);
682
EllipseVertices(x, y, xRadius, yRadius);
689
procedure TGLCanvas.FillEllipse(const x, y: Integer; const xRadius, yRadius:
692
StartPrimitive(GL_TRIANGLE_FAN);
693
GL.Vertex2f(x, y); // not really necessary, but may help with memory stride
694
EllipseVertices(x, y, xRadius, yRadius);
701
procedure TGLCanvas.FillEllipse(const x, y, xRadius, yRadius: Single);
703
StartPrimitive(GL_TRIANGLE_FAN);
704
GL.Vertex2f(x, y); // not really necessary, but may help with memory stride
705
EllipseVertices(x, y, xRadius, yRadius);
712
procedure TGLCanvas.FillEllipse(const x, y, Radius: Single);
714
FillEllipse(x, y, Radius, Radius);
720
procedure TGLCanvas.FillRectGradient(const x1, y1, x2, y2: Single;
721
const x1y1Color, x2y1Color, x2y2Color, x1y2Color: TColorVector);
723
StartPrimitive(GL_QUADS);
724
GL.Color4f(x1y1Color.V[0], x1y1Color.V[1], x1y1Color.V[2], x1y1Color.V[3]);
726
GL.Color4f(x2y1Color.V[0], x2y1Color.V[1], x2y1Color.V[2], x2y1Color.V[3]);
728
GL.Color4f(x2y2Color.V[0], x2y2Color.V[1], x2y2Color.V[2], x2y2Color.V[3]);
730
GL.Color4f(x1y2Color.V[0], x1y2Color.V[1], x1y2Color.V[2], x1y2Color.V[3]);
735
GL.Color4fv(@FCurrentPenColorVector);
741
procedure TGLCanvas.FillRectGradient(const x1, y1, x2, y2: Integer;
742
const x1y1Color, x2y1Color, x2y2Color, x1y2Color: TColorVector);
744
StartPrimitive(GL_QUADS);
745
GL.Color4f(x1y1Color.V[0], x1y1Color.V[1], x1y1Color.V[2], x1y1Color.V[3]);
747
GL.Color4f(x2y1Color.V[0], x2y1Color.V[1], x2y1Color.V[2], x2y1Color.V[3]);
749
GL.Color4f(x2y2Color.V[0], x2y2Color.V[1], x2y2Color.V[2], x2y2Color.V[3]);
751
GL.Color4f(x1y2Color.V[0], x1y2Color.V[1], x1y2Color.V[2], x1y2Color.V[3]);
756
GL.Color4fv(@FCurrentPenColorVector);
759
// FillEllipseGradient (integer)
762
procedure TGLCanvas.FillEllipseGradient(const x, y: Integer; const xRadius, yRadius: Integer; const edgeColor: TColorVector);
764
StartPrimitive(GL_TRIANGLE_FAN);
766
// the center will use the last set PenColor and PenAlpha
767
GL.Vertex2f(x, y); // really necessary now :)
769
// then openGL will do a gradient from the center to the edge using the edgeColor
770
GL.Color4f(edgeColor.V[0], edgeColor.V[1], edgeColor.V[2], edgeColor.V[3]);
771
EllipseVertices(x, y, xRadius, yRadius);
775
GL.Color4fv(@FCurrentPenColorVector);
778
// FillEllipseGradient (single)
781
procedure TGLCanvas.FillEllipseGradient(const x, y, xRadius, yRadius: Single; const edgeColor: TColorVector);
783
StartPrimitive(GL_TRIANGLE_FAN);
784
GL.Vertex2f(x, y); // really necessary now :)
785
GL.Color4f(edgeColor.V[0], edgeColor.V[1], edgeColor.V[2], edgeColor.V[3]);
786
EllipseVertices(x, y, xRadius, yRadius);
790
GL.Color4fv(@FCurrentPenColorVector);
793
// FillEllipseGradient (single)
796
procedure TGLCanvas.FillEllipseGradient(const x, y, Radius: Single; const edgeColor: TColorVector);
798
FillEllipseGradient(x, y, Radius, Radius, edgeColor);
804
procedure TGLCanvas.Arc(const x1, y1, x2, y2, x3, y3, x4, y4: Integer);
806
DrawArc(x1, y1, x2, y2, x3, y3, x4, y4, False);
809
procedure TGLCanvas.Arc(const x1, y1, x2, y2, x3, y3, x4, y4: Single);
811
DrawArc(x1, y1, x2, y2, x3, y3, x4, y4, False);
814
procedure TGLCanvas.Arc(const x1, y1, x2, y2: Single; AngleBegin, AngleEnd: Single);
816
DrawArc(x1, y1, x2, y2, AngleBegin, AngleEnd, False);
822
procedure TGLCanvas.ArcTo(const x1, y1, x2, y2, x3, y3, x4, y4: Integer);
824
DrawArc(x1, y1, x2, y2, x3, y3, x4, y4, True);
827
procedure TGLCanvas.ArcTo(const x1, y1, x2, y2, x3, y3, x4, y4: Single);
829
DrawArc(x1, y1, x2, y2, x3, y3, x4, y4, True);
832
procedure TGLCanvas.ArcTo(const x1, y1, x2, y2: Single; AngleBegin, AngleEnd: Single);
834
DrawArc(x1, y1, x2, y2, AngleBegin, AngleEnd, True);
837
procedure TGLCanvas.RoundRect(const x1, y1, x2, y2, xr, yr: Integer);
839
x2r, y2r, x, y: integer;
845
Arc(x, y1, x + x2r, y1 + y2r, pi3on2, pi);
846
Line(x1, y1 + yr, x1, y - yr);
847
Arc(x, y, x + x2r, y - y2r, pi, pion2);
848
Line(x + xr, y2, x2 - xr, y2);
849
Arc(x2, y, x2 - x2r, y - y2r, pion2, 0);
850
Line(x2, y1 + yr, x2, y - yr);
851
Arc(x2, y1, x2 - x2r, y1 + y2r, 0, pi3on2);
852
Line(x + xr, y1, x2 - xr, y1);
855
procedure TGLCanvas.RoundRect(const x1, y1, x2, y2, xr, yr: Single);
857
x2r, y2r, x, y: Single;
863
Arc(x, y1, x + x2r, y1 + y2r, pi3on2, pi);
864
Line(x1, y1 + yr, x1, y - yr);
865
Arc(x, y, x + x2r, y - y2r, pi, pion2);
866
Line(x + xr, y2, x2 - xr, y2);
867
Arc(x2, y, x2 - x2r, y - y2r, pion2, 0);
868
Line(x2, y1 + yr, x2, y - yr);
869
Arc(x2, y1, x2 - x2r, y1 + y2r, 0, pi3on2);
870
Line(x + xr, y1, x2 - xr, y1);
877
// wrapper from "ByPoints" methode
879
procedure TGLCanvas.DrawArc(x1, y1, x2, y2, x3, y3, x4, y4: Single; UpdateCurrentPos: Boolean);
882
AngleBegin, AngleEnd: Single;
885
SwapSingle(@x1, @x2);
887
SwapSingle(@y1, @y2);
889
NormalizePoint(x1, y1, x2, y2, x3, y3, @x, @y);
890
AngleBegin := ArcTan2(y, x);
892
NormalizePoint(x1, y1, x2, y2, x4, y4, @x, @y);
893
AngleEnd := ArcTan2(y, x);
895
DrawArc(x1, y1, x2, y2, AngleBegin, AngleEnd, UpdateCurrentPos);
900
procedure TGLCanvas.DrawArc(x1, y1, x2, y2: Single; AngleBegin, AngleEnd: Single; UpdateCurrentPos: Boolean);
902
Xc, Yc, Rx, Ry, x, y, s, c: Single;
903
AngleCurrent, AngleDiff, AngleStep: Single;
905
// check that our box is well set (as the original Arc function do)
907
SwapSingle(@x1, @x2);
909
SwapSingle(@y1, @y2);
911
if (x1 = x2) or (y1 = y2) then
914
Xc := (x1 + x2) * 0.5;
915
Yc := (y1 + y2) * 0.5;
917
Rx := Abs(x2 - x1) * 0.5;
918
Ry := Abs(y2 - y1) * 0.5;
920
// if ClockWise then swap AngleBegin and AngleEnd to simulate it.
921
if FArcDirection = adClockWise then
923
AngleCurrent := AngleBegin;
924
AngleBegin := AngleEnd;
925
AngleEnd := AngleCurrent;
928
if (AngleEnd >= AngleBegin) then
929
begin // if end sup to begin, remove 2*Pi (360°)
930
AngleEnd := AngleEnd - 2 * Pi;
933
AngleDiff := Abs(AngleEnd - AngleBegin); // the amount radian to travel
934
AngleStep := AngleDiff / Round(MaxFloat(Rx, Ry) * 0.1 + 5); // granulity of drawing, not too much, not too less
936
AngleCurrent := AngleBegin;
938
StartPrimitive(GL_LINE_STRIP);
939
while AngleCurrent >= AngleBegin - AngleDiff do
941
SinCos(AngleCurrent, s, c);
947
AngleCurrent := AngleCurrent - AngleStep; // always step down, rotate only one way to draw it
950
SinCos(AngleEnd, s, c);
958
if UpdateCurrentPos then
959
MoveTo(x, y); //FCurrentPos := CurrentPos;
964
procedure TGLCanvas.NormalizePoint(const x1, y1, x2, y2: Single; const x, y: Single; pX, pY: PSingle);
966
pX^ := (x - x1) / (x2 - x1) * 2.0 - 1.0;
967
pY^ := (y - y1) / (y2 - y1) * 2.0 - 1.0;
970
procedure TGLCanvas.SwapSingle(pX, pY: PSingle);