From 7b9621f2346a3fcaa63696367e5521c9f21668d4 Mon Sep 17 00:00:00 2001 From: wp_xyz Date: Fri, 11 Jul 2025 00:42:00 +0200 Subject: [PATCH] LCL/Graphics: Fix win32 widgetset Arc method overload with angle arguments to behave like in all other widgetsets. Issue #41748. --- components/lazutils/graphmath.pp | 38 ++++++++++++++++++++++++++++ lcl/interfaces/win32/win32winapi.inc | 6 ++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/components/lazutils/graphmath.pp b/components/lazutils/graphmath.pp index bf7ff79c2a..bb20facf8d 100644 --- a/components/lazutils/graphmath.pp +++ b/components/lazutils/graphmath.pp @@ -72,6 +72,9 @@ function Distance(const Pt, SP, EP : TFloatPoint) : Extended; overload; function EccentricAngle(const PT : TPoint; const Rect : TRect) : Extended; +procedure EllipseParams2Coords(X, Y, Width, Height: Integer; + Angle1, Angle2: Integer; var SX, SY, EX, EY: Integer); + function EllipseRadialLength(const Rect : TRect; EccentricAngle : Extended) : Longint; function EllipsePolygon(const aRect: TRect): TPointArray; @@ -340,6 +343,41 @@ begin EY := EP.Y; end; +{------------------------------------------------------------------------------- + Method: EllipseParams2Coords + Params: x, y, width, height, angle1, angle2, sx, sy, ex, ey + Returns: Nothing + + In parametric form, the ellipse equation for every point (x, y) along the + perimeter is + x = a * cos (t), y = b * sin(t) + where a and b are the major and minor half-axes of the ellipse, and t is + an "angle" parameter ranging between 0 and 2*pi. + + This procedure used by the Windows Arc method calculates the start and end + points, (SX, SY) and (EX, EY), respectively, of an arc beginning at + parameter t = angle1 and ending at parameter t = angle1 + angle2. + But note that angle1 and angle2 are expressed as multiples of 1/16 degree. + Positive values of the angles are in counter-clockwise, negative values in + clockwise direction. + Zero degrees is at 3'o clock position. +-------------------------------------------------------------------------------} +procedure EllipseParams2Coords(X, Y, Width, Height: Integer; + Angle1, Angle2: Integer; var SX, SY, EX, EY: Integer); +var + sinAngle1, cosAngle1, sinAngle2, cosAngle2: Extended; + a, b: Integer; +begin + SinCos(DegToRad(Angle1/16), sinAngle1, cosAngle1); + SinCos(DegToRad((Angle1 + Angle2)/16), sinAngle2, cosAngle2); + a := Width div 2; + b := Height div 2; + SX := X + a + round(cosAngle1 * a); + SY := Y + b - round(sinAngle1 * b); + EX := X + a + round(cosAngle2 * a); + EY := Y + b - round(sinAngle2 * b); +end; + {------------------------------------------------------------------------------ Method: Arc2Bezier Params: X, Y, Width, Height, Angle1, Angle2, Rotation, Points, Count diff --git a/lcl/interfaces/win32/win32winapi.inc b/lcl/interfaces/win32/win32winapi.inc index da900a0c9e..a7ef01846f 100644 --- a/lcl/interfaces/win32/win32winapi.inc +++ b/lcl/interfaces/win32/win32winapi.inc @@ -46,6 +46,10 @@ circle equals 5760 (16*360). Positive values of Angle and AngleLength mean counter-clockwise while negative values mean clockwise direction. Zero degrees is at the 3'o clock position. + + But note that due to the elliptic eccetricity these angles are not the + same as "real" polar angles, but only correspond to the parameter t in the + ellipse equations: x = a * cos(t); y = b * sin(t) ------------------------------------------------------------------------------} function TWin32WidgetSet.Arc(DC: HDC; Left, Top, Right, Bottom, Angle16Deg, Angle16DegLength: Integer): Boolean; var @@ -58,7 +62,7 @@ begin if Angle16DegLength < 0 then OldArcDirection := Windows.SetArcDirection(DC, AD_CLOCKWISE) else OldArcDirection := Windows.SetArcDirection(DC, AD_COUNTERCLOCKWISE); - Angles2Coords(Left, Top, Right - Left, Bottom - Top, Angle16Deg, Angle16DegLength, SX, SY, EX, EY); + EllipseParam2Coords(Left, Top, Right - Left, Bottom - Top, Angle16Deg, Angle16DegLength, SX, SY, EX, EY); Result := Boolean(Windows.Arc(DC, Left, Top, Right, Bottom, SX, SY, EX, EY)); // Revert the arc direction to the previous value Windows.SetArcDirection(DC, OldArcDirection);