Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Delphi Canvas - gleichschenkliches gefülltes Dreieck (https://www.delphipraxis.net/185320-canvas-gleichschenkliches-gefuelltes-dreieck.html)

stahli 30. Mai 2015 19:03

Canvas - gleichschenkliches gefülltes Dreieck
 
Liste der Anhänge anzeigen (Anzahl: 1)
Ich zeichne auf einen Canvas ein Dreieck.


Delphi-Quellcode:
procedure TTest.PaintBackground(aGui: TsoGui; aBitmap: TBitmap;
  aRect: TRect);
const
  Size = 5;
var
  A: TPoint;
  B: TPoint;
  C: TPoint;
  X, Y, S: Integer;
begin
  ...

  aBitmap.Canvas.Brush.Color := clGray;
  aBitmap.Canvas.Pen.Color := clBlack;

  X := aRect.Left + (aRect.Width div 2);
  Y := aRect.Top + (aRect.Height div 2);
  S := (Size div 2);
 
  ...
 
  A := Point(X - S, Y - S);
  B := Point(X + S, Y - S);
  C := Point(X, Y + S);

  ...

  aBitmap.Canvas.Polygon([A, B, C]);
end;
Das Dreieck ist aber nicht symmetrisch.
Zuerst lege ich einen Mittelpunkt fest und dann die Entfernung um den Mittelpunkt.
Es wird von LinksOben nach RechtsOben, dann nach UntenMitte gezogen und dann zurück nach LinksOben.

Nach meinem Verständnis sollte das Dreieck damit symmetrisch aussehen, was es aber nicht ist.

Muss man das irgendwie anders lösen?

Helmi 30. Mai 2015 19:30

AW: Canvas - gleichschenkliches gefülltes Dreieck
 
kann es sein, dass du Rundungsfehler hast?

bei
Delphi-Quellcode:
const
   Size = 5;
   X, Y, S: Integer;
begin
   ...
   S := (Size div 2);
   ...
end;
würde normalerweis 2,5 rauskommen, was aber nicht in einen Integer passt.

Vielleicht hilft es, wenn du die Berechnungen mit Floats machst und dann auf Integer rundest für die Canvas-Befehle

stahli 30. Mai 2015 19:36

AW: Canvas - gleichschenkliches gefülltes Dreieck
 
"Div" rundet direkt ab. Es kommt also 2 raus.
Vom Mittelpunkt aus gesehen hat das Dreieck jeweils einen Abstand von 2 Pixel.
Das sollte also so passen.

Das Problem liegt wohl bei der unterschiedlichen Rundung der Windows-API bei den einzelnen Linien.
Ich schätze mal dass man damit leben muss oder sonst die Linien einzeln berechnen muss.

Aber vielleicht weiß ja jemand noch etwas...

Helmi 30. Mai 2015 20:00

AW: Canvas - gleichschenkliches gefülltes Dreieck
 
mmh - ich hab das grad nachgestellt
es scheint wirklich ein Zeichnenproblem zu sein.
von C nach B scheint es um ein Pixel zu weit links zu sein

Perlsau 30. Mai 2015 20:06

AW: Canvas - gleichschenkliches gefülltes Dreieck
 
  • Ein Kreis mit dem Durchmesser eines Pixels ist ein Punkt. Geht nicht anders auf einem digitalen System.
  • Ein Kreis mit einem Durchmesser von zwei Pixeln ist ein Quadrat. Geht nicht anders auf einem digitalen System.
  • Ein Kreis mit dem Durchmesser von 100 Pixeln mag zwar für das menschliche Auge wie ein Kreis aussehen, ist aber in Wirklichkeit ein Vieleck. Ein gleichschenkliges Dreieck ist ein Kreis mit lediglich drei Knotenpunten. Nimmst du 5 Punkte, hast du ein Fünfeck. Nimmst du 30 Punkte, kannst du die Ecken des 30-Ecks kaum noch erkennen. Nimmst du 100 oder mehr Punkte, ist das für dich ein Kreis, technisch gesehen aber noch immer ein 100-Eck.

Du kannst eine diagonale Linie nicht mit gleichviel Pixeln abbilden wie ein horizontale oder eine vertikale Linie. Machst du dein Dreieck größer, wird dieser Eindruck verwischt. Bei vier Pixeln Grundfläche gibt es keinen echten Mittelpunkt mehr für die Spitze des Dreiecks.

stahli 30. Mai 2015 20:12

AW: Canvas - gleichschenkliches gefülltes Dreieck
 
Ja das ist schon richtig.
Man könnte aber die rechte Linie genau so runden wie die linke (also spiegeln).

Natürlich weiß die Polygon-Funktion nicht, dass man das als Betrachter als sinnvoll erachten würde.

Vielleicht wird eine Linie links+unten nach rechts+oben auch generell anders auf- und abgerundet als in der anderen Richtung.


Also ich kann akzeptieren wenn man sagt, "die Standard-Polygon-Funktion kann diesen Sonderfall nicht explizit behandeln", auch wenn das zu einem besseren Ergebnis führen würde.
So wird es wohl liegen...

Perlsau 30. Mai 2015 20:18

AW: Canvas - gleichschenkliches gefülltes Dreieck
 
Du kannst ja mal mit dieser Procedure rumspielen.

himitsu 30. Mai 2015 21:26

AW: Canvas - gleichschenkliches gefülltes Dreieck
 
Bei LineTo fehlt ja auch das letzte Pixel, aber ich hätte gedacht, daß somit nur beim letzen Punkt einer PolyLine das selbe Problem auftritt und es sich bei einem Polygon aufhebt, da dort der letzte Punkt eh dort ist, wo sich schon der erste Punkt hingemalt hatte. :gruebel:

himitsu 30. Mai 2015 21:44

AW: Canvas - gleichschenkliches gefülltes Dreieck
 
Liste der Anhänge anzeigen (Anzahl: 2)
Hallo, dein Dreieck ist vollkommen korrekt, auch wenn es nicht so aussieht.
Wenn man sich nur mal die Endpunkte noch mit einzeichnet, dann fällt es bestimmt besser auf, daß es wirklich stimmt.

Delphi-Quellcode:
procedure TForm5.FormPaint(Sender: TObject);
var
  X, Y, S, I, J, K: Integer;
  A: array[0..2] of TPoint;
begin
  X := 20;
  Y := 20;
  S := 3;

  A[0] := Point(X - S, Y - S);
  A[1] := Point(X + S, Y - S);
  A[2] := Point(X,    Y + S {+ 1});

  for I := Low(A) to High(A) do
    for J := -1 to +1 do
      for K := -1 to +1 do
        Canvas.Pixels[A[I].X + J, A[I].Y + K] := clRed;

  Canvas.Pen.Color := clBlue;
  Canvas.Polygon(A);
end;
Die Zeichenfunktion der rechten Linie ist natürlich andersrum. (von oben nach unten ... von unten nach oben)
Links von oben aus jeweils Zwei runter und dann Eines nach rechts.
Und Rechts jeweils Zwei nach oben und Eines nach rechts.

Passt also ganz genau dazu, wie du es auch zeichnen wolltest. :angle:
Bei einer geraden Anzahl (siehe Auskommentiertes) passt es wohl besser zusammen, für diesen Fall der 2:1-Steigung = 2x.

Popov 30. Mai 2015 23:18

AW: Canvas - gleichschenkliches gefülltes Dreieck
 
@stahli

Zu dem Problem. Das liegt in dem Verständnis was eine Canvas-Linie ist. Um die zu verstehen muss man ein Schritt zurück treten. Denkt man nicht darüber nach was eine Canvas-Linie eigentlich ist, denkt man vorschnell, dass es eine mit einem 1-Pixel gezeichnete Linie ist, von Punkt A nach Punkt B. Ist sie aber nicht.

Im Grunde ist es eine mit einem Pinsel der Stärke x-Pixel gezeichnete Linie. Und Pinsel der Breite x ist immer in der Form berechnet. Intern mit Nachkommastellen. Guck was Perlsau oben über den Kreis geschrieben hat. Jeder Pixel ist im Grunde ein berechneter Kreis. Der kleinste Kreis ist ein Pixel breit. Er sieht quadratisch aus, ist aber ein berechneter Kreis. D. h. am Anfang und Ende eine Linie, selbst wenn sie ein Pixel breit ist, ist ein Halbkreis. Ist der Pinsel 1-Pixel breit, sieht der Halbkreis wie ein Quadrat aus, ist aber ein Halbkreis.

Und nun muss man einen Schritt zurück treten und sich angucken was das alles ist. So rechteckig das für uns auch aussieht, es ist alles mit Nachkommastellen berechnet. Du hast von Punkt A nach Punkt B 5-Pixel weit gezeichnet. Intern kann die Position B aber ein Wert mit Dezimalstellen sein.

stahli 30. Mai 2015 23:40

AW: Canvas - gleichschenkliches gefülltes Dreieck
 
Liste der Anhänge anzeigen (Anzahl: 1)
Ja, das ist schon klar. Dass der Canvas Pixel darstellt ist ja bekannt.

Ich hätte mir gewünscht, dass das Dreieck so aussieht, wie im Anhang.
Das wäre ja auch nicht unlogisch.

Ok, dann muss man das vermutlich explizit als Anforderung berücksichtigen.


@himitsu

Dein Beispiel sieht gut aus. Aber größere Dreiecke können auch besser dargestellt werden.

Popov 31. Mai 2015 00:22

AW: Canvas - gleichschenkliches gefülltes Dreieck
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von stahli (Beitrag 1303673)
Ja, das ist schon klar. Dass der Canvas Pixel darstellt ist ja bekannt.

Das ist das was du verstanden hast? Hm, wo waren Pixel das Thema? Das mit dem Pinsel hast du aber verstanden?

Hier noch mal was ich meinte. Dieses Dreieck (siehe Anhang) besteht aus zwei übereignender liegenden Dreiecken. Beide haben die gleichen Koordinate, nur hat das eine einen Pinsel der Breit 4 Pixel, das andere einen Pinsel der Breite 1 Pixel.

Sie sind nicht symmetrisch, obwohl die Koordinate identisch sind. Also wenn du verstanden hast, dass ich dir sagen wollte, dass Canvas Pixel darstellen kann, dann weiß ich was du verstanden hast.


//Edit:
Andere Grafik eingefügt. Sie zeigt besser was ich meine.

himitsu 31. Mai 2015 00:25

AW: Canvas - gleichschenkliches gefülltes Dreieck
 
Nichtmal "austricksen" lässt sich die Zeichenroutine. :stupid:
Delphi-Quellcode:
  Canvas.MoveTo(A[0].X, A[0].Y);
  Canvas.LineTo(A[1].X, A[1].Y);
  Canvas.LineTo(A[2].X, A[2].Y);

  Canvas.MoveTo(A[0].X, A[0].Y);
  Canvas.LineTo(A[2].X, A[2].Y);
Da bleibt dann wohl nur selber malen. (Pixel für Pixel)

Luckie 31. Mai 2015 00:39

AW: Canvas - gleichschenkliches gefülltes Dreieck
 
Oder um es mal laienhaft auszudrücken: Bei eine schrägen Linie mit der Stärke ein Pixel springen die Pixel eben etwas hin und her. Bei einer längeren und oder breiteren Linie fällt es nicht so stark auf, wenn man nicht gerade mit der Nase vorm Bildschirm hängt. Um den Effekt etwas abzuschwächen gibt es die Kantenglättung oder Antialiasing. Das funktioniert aber nur wenn die Linie entsprechen breit ist. Bei der Stärke von einem Pixel kann die Kantenglättung auch nichts mehr reißen.

Aber die Grafik ist doch nur 5x5 Pixel groß. Da kann man doch die Pixel einzeln setzen. Wenn du mehrere Grafiken zeichnen willst, dann hinterleg sie als "Zeichenvorschrift" in einer entsprechenden Datenstruktur. Oder binde entsprechende Bitmaps als Ressource ein.

Popov 31. Mai 2015 01:09

AW: Canvas - gleichschenkliches gefülltes Dreieck
 
Liste der Anhänge anzeigen (Anzahl: 1)
Ich hab mal gezeichnet:

stahli 31. Mai 2015 10:05

AW: Canvas - gleichschenkliches gefülltes Dreieck
 
Ich danke Euch.

Die Pinselstärke habe ich mal gedanklich ausgenommen, da die hier nicht relevant ist.

Letztlich gibt es Algorithmen für Linien, die ein Pixel (das rechnerisch bei 3,5 liegen würde) auf 3 oder auf 4 setzen.
Das ist logisch.

Dass es mir als Betrachter lieber wäre das untere auf 3 und das obere auf 4 zu setzen, kann die Polygon-Funktion ja nicht wissen.

Dazu müsste ich eben eine GleichschenklichesDreieck-Funktion schreiben oder benutzen (oder fixe Bitmaps oder Pfade).

Insofern schließe ich mich hier Luckie komplett an.


Ich war nur zunächst von dem etwas unschönen Ergebnis überrascht und dachte, ich müsste vielleicht noch einen bestimmten Endpunkt festlegen oder so. Oder dass ein anderer Fehler vorlag.

Jetzt sehe ich, dass "mein spezielles" Wunschpolygon mit der Standardfunktion so nicht zu erreichen ist.
Also lasse ich es so oder entscheide mich irgendwann für eine genauere Lösung.

himitsu 31. Mai 2015 11:18

AW: Canvas - gleichschenkliches gefülltes Dreieck
 
Der Algo müsste praktisch einen Mittelpunkt berechnen und von diesem Punkt aus gesehn alle Punkte eher aufrunden, als abzurunden. (mit Bankers-Round würde das ganze noch unvorhersehbarer und sieht gleich anders aus, nur weil man das Bild um 1-2 Pixel verschiebt)

Popov 31. Mai 2015 11:31

AW: Canvas - gleichschenkliches gefülltes Dreieck
 
Zitat:

Zitat von stahli (Beitrag 1303696)
Die Pinselstärke habe ich mal gedanklich ausgenommen, da die hier nicht relevant ist.

Indirekt schon. Das ist eigentlich das was ich versuche die ganze Zeit rüberzubringen. Wäre LineTo eine Funktion die immer eine 1-Pixel breite Linie zeichnet, wäre die Interne Berechnung der Line evtl. anders. Vermutlich würde man dann einer anderen Berechnungslogik folgen und schräge Linien wären symmetrisch. Aber LineTo ist eine Funktion die immer mit einem Pinsel der Stärke X zeichnet, bei der die Breite 1-Pixel nur der kleinste Wert ist.

Dejan Vu 1. Jun 2015 07:06

AW: Canvas - gleichschenkliches gefülltes Dreieck
 
Mit dem Bresenham-Algorithmus wäre das vermutlich nicht passiert. Der wird aber wohl nicht angewandt, weil es schnellere Algorithmen gibt, aber sicher bin ich mir hier nicht. Die Vorgehensweise bei dem Algorithmus ist jedenfalls so, das alle Linien zunächst auf den Bereich (x>0, y>0, Steigung < 1) normalisiert werden und dadurch eigentlich gleiche Linien herauskommen müssten.

http://de.wikipedia.org/wiki/Bresenham-Algorithmus

Grundsätzlich ist es ja so:
Um eine Linie von (0,0) nach (1,2) zu zeichnen kann man so vorgehen
(0,0)-(1,1)-(1,2): Erst schräg nach oben dann nach rechts
(0,0)-(0,1)-(1,2): Erst nach rechts, dann nach schräg oben

Beide Pfade sind komplett gleichwertig. Wenn ein Algorithmus nun für eine Linie (0,0)-(1,2) die Variante #1 wählt, aber von (1,2)-(0,0) die Variante #2 hätte man schon eine Asymmetrie.

Nicht umsonst ist das Rendern von Fonts im Pixelbereich so schwer und gelingt kaum so, das die Buchstaben dann noch symmetrisch sind. Es geht halbwegs, aber dazu sind die einzelnen Buchstaben mit so.g. 'Hints' versehen, die dem Renderer z.B. sagen, das bei dem Dreicke die linke und rechte Schräge Linie symmetrisch sein sollen, und dann kann er das entsprechend rendern.

Ob intern in der Canvas-Paint.Methode mit Nachkommastellen gearbeitet wird, wage ich zu bezweifeln.

stahli 1. Jun 2015 08:16

AW: Canvas - gleichschenkliches gefülltes Dreieck
 
Danke. Das klingt plausibel und ist (auch für mich) verständlich. :-)


Alle Zeitangaben in WEZ +1. Es ist jetzt 08:18 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz