Mario Peters hat geschrieben: Mo 28. Jul 2025, 14:10
Bei mir, Lazarus 3.8 mit FPC 3.2.2 64 Bit sehe ich beide Ellipsenbögen exakt abgeschlossen.
Ich hatte ja geschrieben, dass du das mit einer Lazarus-Release-Version auf Windows nicht so siehst wie in meinem Screenshot mit Laz/main, wo der Bug behoben ist.
Mario Peters hat geschrieben: Mo 28. Jul 2025, 14:10
Sollte dies der Fehler sein, frage ich mal wie Windows das in seinem API macht?
Gar nicht. Das Windows-API kennt nur die Arc-Version mit den SX,SY,EX,EY-Parametern (linkes Bild im Screenshot). Entsprechend kennt auch Delphi nur diese Version.
Mario Peters hat geschrieben: Mo 28. Jul 2025, 14:10
Und wie macht linux das, der XServer?
Die gtk und qt-Widgetsets kennen wiederum nur die Arc-Version mit den Winkel-Argumenten, wobei, wie immer wieder betont, diese keine geometrischen Winkel sind, sondern die Parameter phi in den Ellipsengleichungen (x = a * cos(phi), y = b * sin(phi)). In den Kommentaren zu den intern benötigten Routinen in der GraphMath-Unit sind sie als "eccentric angles" bezeichnet - dieser Begriff ist in
https://byjus.com/jee/eccentric-angle-a ... n-ellipse/ erläutert.
Jetzt wird's kompliziert - das habe ich gerade selbst erst gemerkt, als ich mein Testprogramm aus dem vorigen Post unter Linux habe laufen lassen (so wie Mathias, bevor ich das posten konnte). Da es unter Linux die SX-SY-EX-EY-Variante der Arc-Prozedur nicht nativ gibt, hat ein früherer Lazarus-Entwickler die Winkelvariante für Lazarus neu programmiert (genauso wie er umgekehrt in Windows die nativ nicht vorhandene Winkel-Variante nachgebaut hat). Dabei hat er sich auf die für die jeweils vorhandene Winkel-Definition verlassen, und so sind für Windows und Linux jeweils verschiedenes Verhalten entstanden. Der Unterschied in Windows ist in Laz/Main behoben. Aber wie man in Mathias' Screenshot von Linux sieht, hat man nun ein anderes Verhalten bei den dort nachgebauten SX-SY-EX-EY-Varianten: Während bei Windows im linken Bild der Bogen an der durch den geometrischen Winkel definierten Linie beginnt, erfolgt dieser bei Linux früher, genau dort, wo er bei der Winkel-Variante (rechtes Bild) liegt.
Mario Peters hat geschrieben: Mo 28. Jul 2025, 14:10
da wäre dann wuch eine eigene vom System unabhängige Funktion schön.
Das ist nach obiger Bemerkung jetzt ganz vertrackt. Die Grundfrage ist: Welches Verhalten ist richtig?
Bei Fragen nach "richtig" ode "falsch" ist oft die Antwort: "So wie es Delphi macht".
In unserem Fall kennt Delphi nur die SX-SY-EX-EY-Variante - dieses Verhalten (unter Windows) ist also "richtig". Die entsprechende Linux-Variante, die diesem widerspricht, ist somit "falsch".
Und was ist mit der anderen Arc-Routine, der mit den Winkelargumenten? Unter Windows erstreckt sich dieser Bogen unter den Lazarus-Release-Versionen, so wie du eben auch geschrieben hast, zwischen den Schnittpunkten der Ellipse mit den Richtungslinien der SX-SY-EX-EY-Variante. Ist das richtig? In diesem Fall gibt es keinen Vergleich mit Delphi, das diese Routine gar nicht hat. Ich meine, dieses Verhalten ist wenig sinnvoll. Zum einen: Wieso sollte man dafür eine eigene Routine spendieren, wenn das Verhalten schon in der SX-SY-EX-EY-Variante enthalten ist? Zum anderen: Nehmen wir zwei Kreis-Bögen, der eine verläuft zwischen 0 und 45° und der andere zwischen 45 und 90°, beide sind gleich lang. Nun stauchen wir den vertikalen Radius. Weil die 45°-Linie weiterhin beide Bögen trennt, wird der obere Bogen zwischen 45 und 90° immer kürzer im Vergleich zum unteren Bogen zwischen 0 und 45°. Das kann man so akzeptieren, hat aber den Nachteil, dass, wenn der Bogen Bestandteil eines Pie-Charts ist, dann werden die Anteile der Tortenstücke verzerrt.
Und jetzt zu Linux: Dort beginnt und endet der Bogen nicht an den Schnittpunkten mit den Winkellinien, sondern an den Punkten, die man erhält, wenn man die genannten Winkel in die Ellipsengleichung einsetzt: xStart = a * cos(startWinkel), yStart = b*sin(startwinkel), und genauso für den Endwinkel.
Daher bin ich der Überzeugung, dass das Verhalten in den Lazarus-Release-Versionen für Linux richtig und für Windows fehlerhaft ist.
Mit dieser Erkenntnis kann man, die Arc-Routine mit den Winkelargumenten in Windows fixen (das ist in der Trunk-Version von Lazarus schon geschehen), und genauso muss man die Arc-Routine in Linux/mac mit den SX-SY-EX-EY Argumenten noch reparieren.
Unabhängig von der Lazarus-Version, kann man sich aber auch eigene "universelle" Arc-Routinen schreiben, die in allen "Welten" gleich funktionieren. Dabei kann man für die Routine mit den SX-SY-EX-EY-Argumenten davon ausgehen, dass die Windows-Version richtig ist und muss für die anderen Widgetsets nachkorrigieren:
Code: Alles auswählen
uses
GraphMath;
procedure UniversalArc(Canvas: TCanvas; Left, Top, Right, Bottom: Integer; SX, SY, EX, EY: Integer);
{$IFNDEF LCLWin32}
var
startAngle: extended = 0.0;
endAngle: extended = 0.0;
sweepAngle: extended = 0.0;
sinAngle, cosAngle: Extended;
a, b: Integer;
{$ENDIF}
begin
{$IFDEF LCLWin32}
Canvas.Arc(Left, Top, Right, Bottom, SX, SY, EX, EY); // Das ist unter Windows richtig
{$ELSE}
a := Right - Left;
b := Bottom - Top;
Coords2Angles(Left, Top, a, b, SX, SY, EX, EY, startAngle, sweepAngle);
startAngle := DegToRad(startAngle / 16);
sweepAngle := DegToRad(sweepAngle / 16);
endAngle := startAngle + sweepAngle;
SinCos(startAngle, sinAngle, cosAngle);
startAngle := RadToDeg(ArcTan2(a*sinAngle, b*cosAngle));
SinCos(endAngle, sinAngle, cosAngle);
endAngle := RadToDeg(ArcTan2(a*sinAngle, b*cosAngle));
if endAngle < 0 then endAngle := endAngle + 360;
sweepAngle := endAngle - startAngle;
Canvas.Arc(Left, Top, Right, Bottom, round(startAngle*16), round(sweepAngle*16));
{$ENDIF}
end;
Und bei der Routine mit den Winkel-Argumenten kann man davon ausgehen, dass die Nicht-Windows-Version richtig ist und man muss für Windows korrigieren:
Code: Alles auswählen
// In Laz/main bereits in Unit GraphMath enthalten.
procedure EllipseParams2Coords(X, Y, Width, Height: Integer;
t1, t2: extended; out SX, SY, EX, EY: Integer);
var
sin_t1, cos_t1, sin_t2, cos_t2: Extended;
a, b: Double;
begin
SinCos(t1, sin_t1, cos_t1);
SinCos(t2, sin_t2, cos_t2);
a := Width/2;
b := Height/2;
SX := X + round(a + cos_t1 * a);
SY := Y + round(b - sin_t1 * b);
EX := X + round(a + cos_t2 * a);
EY := Y + round(b - sin_t2 * b);
end;
procedure UniversalArc(canvas: TCanvas; Left, Top, Right, Bottom: Integer;
StartAngle16, SweepAngle16: Integer);
{$IFDEF LCLWin32}
var
a, b, SX, SY, EX, EY: Integer;
tStart, tEnd: Double;
{$ENDIF}
begin
{$IFDEF LCLWin32}
a := Right - Left;
b := Bottom - Top;
tStart := DegToRad(StartAngle16 / 16);
tEnd := DegToRad((StartAngle16 + SweepAngle16) / 16);
EllipseParams2Coords(Left, Top, a, b, tStart, tEnd, SX, SY, EX, EY);
Canvas.Arc(Left, Top, Right, Bottom, SX, SY, EX, EY);
{$ELSE}
Canvas.Arc(Left, Top, Right, Bottom, StartAngle16, SweepAngle16); // Das ist unter Linux/Mac richtig
{$ENDIF}
end;