Wie geht ihr mit Rundungsfehlern beim Zeichnen um?

  • Allgemein

Es gibt 2 Antworten in diesem Thema. Der letzte Beitrag () ist von Bartosz.

    Wie geht ihr mit Rundungsfehlern beim Zeichnen um?

    Moin.

    Ich habe hier mehrere Datumsangaben. Ich muss sie euch leider anonymisiert zeigen. Diese Datumsangaben stellen Beginn und Ende eines Prozesses dar. Hieraus werden Zeitspannen (TimeSpan) gemacht. Diese TimeSpans werden untereinander und entlang der chronologischen Reihenfolge in einer PictureBox gezeichnet. Trotz Verwendung von
    Math.Round() (statt nur mittels CInt() abzuschneiden) ergeben sich Pixelfehler von ±1 Pixel, so dass es so aussehen könnte, als wenn 2 Prozesse gerade so eben zeitgleich laufen – obwohl der nächste Prozess definitiv später angefangen hat als der vorherige! Was macht ihr bei so etwas? Ich habe mir in die Dokumentation geschrieben: „Da das Programm die Zeitspannen auf die Bildbreite (in Pixeln) verteilen muss, kann ein Rundungsfehler von ± 1 Pixel auftreten.“

    Meine PictureBox füllt die ganze Formbreite aus. Das heißt, PictureBox1 beginnt auf der Form bei {0,0} und hat die Breite von 1902 und eine Höhe von 400 Pixeln.
    Der Zeitstrahl beginnt am linken Bildrand mit dem Datum [denkt euch bitte ein Datum, das noch vor dem allerersten Prozess ist] und endet am rechten Bildrand mit dem Datum 22.03.2021. Die PictureBox-Breite ist somit der gesamte betrachtete Zeitraum, und somit 1902 Pixel und 100%.

    Die Rechnung:
    • mehrere Datumsangaben für Beginn und Ende
    • aus denen wird jeweils eine TimeSpan
    • es wird berechnet, wie viel Prozent der gesamten Spanne diese kleine Spanne groß ist
    • Es wird auch berechnet, ab welchem Pixel das Rechteck zu sehen ist.
    Ich gebe euch ein Beispiel für 1 Prozess.
    Meine Klasse Zeichnung

    VB.NET-Quellcode

    1. Public Class Zeichnung
    2. Private ReadOnly Start_dieser_Zeichnung As Date = #08/01/2010# '01.08.2010 'Beispiel :-)
    3. Private Start_Prozess_I As Date = #01/01/2021#
    4. Private Ende_Prozess_I As Date = #02/28/2021#
    5. Private Ende_dieser_Zeichnung As Date 'heute
    6. Public Liste_mit_prozentualen_Zeitspannen As New List(Of Double)
    7. Public Liste_mit_Farben As New List(Of Color) From {
    8. Color.FromArgb(255, 166, 0), 'Orange
    9. Color.FromArgb(255, 255, 0), 'Gelb
    10. Color.FromArgb(98, 255, 0), 'Hellgrün
    11. Color.FromArgb(0, 194, 0), 'Mittelgrün
    12. Color.FromArgb(0, 255, 221), 'Cyan
    13. Color.FromArgb(0, 215, 255), 'Hellblau
    14. Color.FromArgb(0, 140, 255), 'Normalblau
    15. Color.FromArgb(144, 0, 255), 'Lila
    16. Color.FromArgb(255, 0, 191), 'Rosa
    17. Color.FromArgb(255, 0, 81), 'Magenta
    18. Color.FromArgb(178, 184, 123), 'Gelb-Grau
    19. Color.FromArgb(255, 255, 255), _ 'Weiß
    20. Color.Firebrick
    21. }
    22. Public Liste_mit_Startpunkten As New List(Of Integer)
    23. Public Sub Berechne_Zeitspannen_und_Relationen()
    24. Liste_mit_prozentualen_Zeitspannen.Clear()
    25. Ende_dieser_Zeichnung = Date.Now
    26. Dim Spanne_dieser_Zeichnung As TimeSpan = Ende_dieser_Zeichnung - Start_dieser_Zeichnung
    27. Dim Spanne_ProzessI As TimeSpan = Ende_Prozess_I - Start_Prozess_I
    28. Dim Prozent_ProzessI As Double = Spanne_ProzessI.TotalDays * 100.0 / Spanne_dieser_Zeichnung.TotalDays
    29. Liste_mit_prozentualen_Zeitspannen.Add(Prozent_ProzessI)
    30. '====================================
    31. Dim t As TimeSpan
    32. Dim t1 As Integer
    33. t = Start_Prozess_I - Start_dieser_Zeichnung
    34. t1 = CInt(Math.Round(t.TotalDays * Form_Main.PictureBox1.Width / Spanne_dieser_Zeichnung.TotalDays, 0))
    35. Liste_mit_Startpunkten.Add(t1)
    36. End Sub
    37. End Class


    in Form_Main

    VB.NET-Quellcode

    1. Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
    2. If e.Graphics IsNot Nothing AndAlso Z1.Liste_mit_prozentualen_Zeitspannen IsNot Nothing AndAlso Z1.Liste_mit_prozentualen_Zeitspannen.Count > 0 Then
    3. e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias
    4. e.Graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality
    5. e.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality
    6. e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear
    7. Using IMG As New Bitmap(PictureBox1.Width, PictureBox1.Height)
    8. Using G As Graphics = Graphics.FromImage(IMG)
    9. G.FillRectangle(Brushes.Black, New Rectangle(0, 0, PictureBox1.Width, PictureBox1.Height))
    10. Dim recta As Rectangle
    11. For Each member As Double In Z1.Liste_mit_prozentualen_Zeitspannen
    12. recta = New Rectangle(Z1.Liste_mit_Startpunkten(i),
    13. y_aktuell_Start,
    14. CInt(Math.Round(Z1.Liste_mit_prozentualen_Zeitspannen(i) * PictureBox1.Width / 100.0, 0)),
    15. Hoehe_des_Rechtecks)
    16. Using myBrush As SolidBrush = New SolidBrush(Z1.Liste_mit_Farben(i))
    17. e.Graphics.FillRectangle(myBrush, recta)
    18. G.FillRectangle(myBrush, recta)
    19. End Using
    20. y_aktuell_Start += 33
    21. i += 1
    22. Next
    23. IMG.Save("meinPfad\1.png", Imaging.ImageFormat.Png)
    24. End Using
    25. End Using
    26. End If
    27. End Sub


    Die Bilder im Anhang stammen nicht aus dem ↑ Beispielcode.
    Bilder
    • ungezoomt.png

      558 Byte, 271×122, 178 mal angesehen
    • Mit Smartphone gezoomt - Kopie.jpg

      3,29 MB, 4.032×3.024, 73 mal angesehen

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Bartosz“ ()

    Hi

    Was Du versuchen könntest, anstelle in Integer, in Single zu rechnen. Fast alle Grafikoperationen können auch Single verarbeiten. Also anstelle von Rectangle, RectangleF verwenden usw.

    Anstelle von

    VB.NET-Quellcode

    1. G.FillRectangle(Brushes.Black, New Rectangle(0, 0, PictureBox1.Width, PictureBox1.Height))

    kannst auch

    VB.NET-Quellcode

    1. G.Clear(Color.Black)

    verwenden um die gesamte Fläche mit Schwarz zu löschen/ zu überzeichnen.
    Mfg -Franky-