none
Graphics Rendering RRS feed

  • Frage

  • Hallo,
    ich habe eine Problem mit dem Rendering von System.Drawing.Graphics.

    Mit diesem Code zeichne ich einen Graphen.

    Public Function CalcCurve(ByVal Width As Integer, ByVal MaxSegmentLength As Integer, ByVal MaxY As Integer) As System.Array
    
        Dim SegmentLength As Integer        'Enthält die momentan berechnete Segmentlänge
        Dim SegmentY As Integer           'Enthält die Höhe (Y-Wert) des aktuellen Segments
        Dim WidthCounter As Integer = 1       'Zählt die schon berechnete Anzahl an X-Punkten (Auf der X-Skala)
        Dim FromSegmentCounter As Integer = 1    'Enthält den vorherigen Wert von WidthCounter. Dient dazu, den Zuweisungsbereich an das Array "Arr" zu optimieren
        Dim Arr(Width - 1) As Integer        'Enthält die Kurve in einem Array
    
        Randomize()
    
        Do
    
          SegmentLength = Int(Rnd() * MaxSegmentLength)
    
          FromSegmentCounter = WidthCounter
          WidthCounter += SegmentLength
    
          If WidthCounter >= Width Then
    
            SegmentLength -= (WidthCounter - Width)
            WidthCounter = Width
    
          End If
    
          SegmentY = Int(Rnd() * MaxY)
    
          For i = FromSegmentCounter To WidthCounter
    
            Arr(i - 1) = SegmentY
    
          Next
    
          If WidthCounter >= Width Then Exit Do
    
        Loop
    
        Return Arr
    
      End Function

    Diese Funktion gibt eine Art gezackte Kurve aus.

    Bei erscheinen der Form soll sie gezeichnet werden.

     Private Sub Form_Main_Shown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shown
    
        Dim gh As Graphics = Me.CreateGraphics
    
        gh.Clear(Me.BackColor)
    
        Dim Curve As Array = CalcCurve(1500, MaxSegmentLength, MaxY)
    
        For i = 0 To Curve.Length - 2
    
          gh.DrawLine(Pens.Black, i, Curve(i), i + 1, Curve(i + 1))
    
        Next
    
    End Sub

    Soweit alles wunderbar.

    Doch wenn ich versuche eine zweite Kurve zu zeichnen, so wird der Zwischenraum der beiden Kurven komisch gefüllt, obwohl
    ich nur die Methode DrawLines verwende und auch nicht FillPath benutze.

    Hoffe auf Antworten

    Gruß VauBe

    Samstag, 3. Juli 2010 17:25

Antworten

  • Hallo VauBe,

    Frank hat bereits einige Aspekte angesprochen. Einige weitere Dinge dazu:
    Du solltest Dein Projekt mit Option Strict On erstellen.
    Denn gerade bei Grafik, wo viel gerechnet wird, hat das schnell einen großen Einfluß.
    Und so solltest Du anstatt System.Array mit dem richtigen Typ - Integer() - arbeiten.

    Ein weiterer Aspekt ist das Nutzen der Möglichkeiten von GDI+.
    Für eine Einführung siehe Grafik und Zeichnen in Windows Forms
    und einiges weitere findest Du bei Bob Powell

    In Deinem Falle kann man das Ganze "hübscher" aussehen lassen,
    wenn man den SmoothingMode und InterpolationMode nutzt.
    Damit dies GDI+ richtig ausnutzen kann empfiehlt sich der Einsatz
    der DrawLines Methode - womit es nebenbei auch schneller werden dürfte.

    Eine Alternative Implementation, wo ich die Kurve in eine eigene Klasse verpackt habe:

     

    ' Empfohlen: Auf Projektebene bzw. in unter Extras->Optionen einstellen
    Option Strict On
    Option Explicit On
    Option Infer On
    
    Public Class CurveForm
      Const MaxSegmentLength As Integer = 20
      Dim Curves As New List(Of Curve)
    
      Public Sub New()
        InitializeComponent()
    
        Dim MaxY As Integer = Me.ClientSize.Height
    
        Curves.Add(Curve.Create(750, MaxSegmentLength, MaxY, Pens.Red))
        Curves.Add(Curve.Create(1000, MaxSegmentLength, MaxY * 2, Pens.Blue))
        Curves.Add(Curve.Create(Me.ClientSize.Width, MaxSegmentLength, MaxY, Pens.Green))
      End Sub
    
    
      Private Sub CurveForm_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
        e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
        e.Graphics.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
        For Each curve In Me.Curves
          curve.Paint(e.Graphics)
        Next
      End Sub
    End Class
    
    Public Class Curve
      Shared ReadOnly rnd As New Random()
    
      Private _points As Point()
      Private _pen As Pen
    
      Public Sub New(ByVal pen As Pen, ByVal points As Point())
        Me._pen = pen
        Me._points = points
      End Sub
    
      Public Shared Function Create( _
        ByVal Width As Integer, _
        ByVal MaxSegmentLength As Integer, _
        ByVal MaxY As Integer,
        ByVal pen As Pen) As Curve
        ' Alle Punkte zunächst als Auflistung
        Dim points As New List(Of Point)
    
        Dim point As Point
        Dim lastPoint As Point = point.Empty
    
        'points.Add(lastPoint) ' 0,0
        Do
          point.X = lastPoint.X + rnd.Next(MaxSegmentLength)
          If (point.X > Width) Then
            point.X = Width
          End If
          point.Y = rnd.Next(MaxY)
    
          points.Add(point)
          lastPoint = point
        Loop Until point.X >= Width
    
        Return New Curve(pen, points.ToArray)
      End Function
    
      Public Sub Paint(ByVal g As Graphics)
        g.DrawLines(Me._pen, Me._points)
      End Sub
    End Class
    Gruß Elmar

     

    • Als Antwort markiert MAKisBACK Sonntag, 4. Juli 2010 13:20
    Sonntag, 4. Juli 2010 08:40
    Beantworter

Alle Antworten

  • Hallo,

    Doch wenn ich versuche eine zweite Kurve zu zeichnen, so wird der Zwischenraum der beiden Kurven komisch gefüllt, obwohl ich nur die Methode DrawLines verwende und auch nicht FillPath benutze.

    ok, es käme darauf an, wie Du das "zweite Kurve zeichnen" implementiert hast. Wenn es so wie unten implementiert ist, sollte es ~normal keinen Zwischenraum geben, richtig.  
    • Nur mit Form.Shown-Ereignis zu arbeiten würde Probleme beim Resizing der Form oder Verschieben der Form (z.B. über den Rand heraus) ergeben. Deswegen setzt man das normal über das Paint-Ereignis der Form um. Dadurch kannst Du die Graphis-Instanz auch gleich den PaintEventArgs entnehmen und benötigst kein Dispose() am Ende.

    Trotzdem, wenn alles normal implementiert wäre, sollte das zweite Zeichnen der "Kurve" ansich auch funktionieren. Etwa:

      Private Sub Form_Main_Shown(ByVal sender As Object, _
        ByVal e As System.EventArgs) Handles Me.Shown
    
        Dim gh As Graphics = Me.CreateGraphics
    
        'gh.Clear(Me.BackColor) 
    
        Dim Curve As Array = CalcCurve(1500, 150, 100) ' Erste
        For i = 0 To Curve.Length - 2
          gh.DrawLine(Pens.Black, i, Curve(i), i + 1, Curve(i + 1))
        Next
    
        Curve = CalcCurve(1500, 80, 80) ' Zweite
        For i = 0 To Curve.Length - 2
          gh.DrawLine(Pens.Blue, i, Curve(i), i + 1, Curve(i + 1))
        Next
    
        gh.Dispose() ' Solltest man hier noch zufügen
    
      End Sub

    Vielleicht ist noch mehr Code mit bestimmten EventHandlern verbunden, den wir hier nicht sehen können. Reduziere ansonsten (ggf.) Deinen Code und schaue auf mögliche Handler, die zusätzlich ausgeführt werden könnten. 

     


    ciao Frank
    Sonntag, 4. Juli 2010 07:01
  • Hallo VauBe,

    Frank hat bereits einige Aspekte angesprochen. Einige weitere Dinge dazu:
    Du solltest Dein Projekt mit Option Strict On erstellen.
    Denn gerade bei Grafik, wo viel gerechnet wird, hat das schnell einen großen Einfluß.
    Und so solltest Du anstatt System.Array mit dem richtigen Typ - Integer() - arbeiten.

    Ein weiterer Aspekt ist das Nutzen der Möglichkeiten von GDI+.
    Für eine Einführung siehe Grafik und Zeichnen in Windows Forms
    und einiges weitere findest Du bei Bob Powell

    In Deinem Falle kann man das Ganze "hübscher" aussehen lassen,
    wenn man den SmoothingMode und InterpolationMode nutzt.
    Damit dies GDI+ richtig ausnutzen kann empfiehlt sich der Einsatz
    der DrawLines Methode - womit es nebenbei auch schneller werden dürfte.

    Eine Alternative Implementation, wo ich die Kurve in eine eigene Klasse verpackt habe:

     

    ' Empfohlen: Auf Projektebene bzw. in unter Extras->Optionen einstellen
    Option Strict On
    Option Explicit On
    Option Infer On
    
    Public Class CurveForm
      Const MaxSegmentLength As Integer = 20
      Dim Curves As New List(Of Curve)
    
      Public Sub New()
        InitializeComponent()
    
        Dim MaxY As Integer = Me.ClientSize.Height
    
        Curves.Add(Curve.Create(750, MaxSegmentLength, MaxY, Pens.Red))
        Curves.Add(Curve.Create(1000, MaxSegmentLength, MaxY * 2, Pens.Blue))
        Curves.Add(Curve.Create(Me.ClientSize.Width, MaxSegmentLength, MaxY, Pens.Green))
      End Sub
    
    
      Private Sub CurveForm_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
        e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
        e.Graphics.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
        For Each curve In Me.Curves
          curve.Paint(e.Graphics)
        Next
      End Sub
    End Class
    
    Public Class Curve
      Shared ReadOnly rnd As New Random()
    
      Private _points As Point()
      Private _pen As Pen
    
      Public Sub New(ByVal pen As Pen, ByVal points As Point())
        Me._pen = pen
        Me._points = points
      End Sub
    
      Public Shared Function Create( _
        ByVal Width As Integer, _
        ByVal MaxSegmentLength As Integer, _
        ByVal MaxY As Integer,
        ByVal pen As Pen) As Curve
        ' Alle Punkte zunächst als Auflistung
        Dim points As New List(Of Point)
    
        Dim point As Point
        Dim lastPoint As Point = point.Empty
    
        'points.Add(lastPoint) ' 0,0
        Do
          point.X = lastPoint.X + rnd.Next(MaxSegmentLength)
          If (point.X > Width) Then
            point.X = Width
          End If
          point.Y = rnd.Next(MaxY)
    
          points.Add(point)
          lastPoint = point
        Loop Until point.X >= Width
    
        Return New Curve(pen, points.ToArray)
      End Function
    
      Public Sub Paint(ByVal g As Graphics)
        g.DrawLines(Me._pen, Me._points)
      End Sub
    End Class
    Gruß Elmar

     

    • Als Antwort markiert MAKisBACK Sonntag, 4. Juli 2010 13:20
    Sonntag, 4. Juli 2010 08:40
    Beantworter
  • Hallo VauBe,

    Doch wenn ich versuche eine zweite Kurve zu zeichnen, so wird der Zwischenraum der beiden Kurven komisch gefüllt

    Wir sollten uns zunächst über das unterhalten, was Du als "komisch" bezeichnest. Inwiefern komisch? Ist das, was Du als komisch bezeichnest, reproduzierbar? Hat es immer dieselbe Form? Kannst Du Monitor- und Grafikkarteninterferenzen ausschließen? Verwendest Du eine HatchBrush/TextureBrush irgendwo in deinem Painting-Code? BufferedGraphics? Multithreading?

    Gruß
    Marcel

    Sonntag, 4. Juli 2010 11:38
  • Hallo nochmal,

    Vielen Dank für die Vorschläge. Elmars Lösung hat geklappt. Wenn ich DrawLines benutze, malt er auf einmal doch 2 unabhängige Linien (ohne komische Füllung).

     

    Gruß VauBe

    Sonntag, 4. Juli 2010 13:19
  • Hallo,

    das von Dir beobachte "Zusammenkleben" könnte daraus resultieren,
    dass sich die Linien bei Dir häufiger überschnitten haben, je nachdem
    wie bei Dir MaxY, MaxSegmentLength eingestellt waren.

    Ich habe u. a. den Zufallsgenerator als Statisch deklariert
    um eine bessere Streuung zu haben und System.Random
    anstatt des Zufallsgenerators von Visual Basic verwendet.

    Wobei bei meinen Versuchen mit Deinem Code das Problem
    so nicht aufgetreten war.

    Gruß Elmar

    Sonntag, 4. Juli 2010 17:20
    Beantworter
  • Hallo,

    Wenn ich DrawLines benutze, malt er auf einmal doch 2 unabhängige Linien (ohne komische Füllung).

    es liegt nicht an DrawLines (im Gegensatz zu Deinem DrawLine).
    Alle hier geposteten Lösungen "klappen" ja hier. Interessant wäre deswegen nur für spätere Benutzer, was Dein Fehler in Wirklichkeit war. Gut ist zwar das (von mir im ersten Posting angesprochen Paint-Ereignis), wo Elmar dann mal ein Beispiel draus gemacht hat. Dennoch - wie gesagt - es funktioniert auch mit Deinem Shown-Ereignis (mit den von mir angegebenen Beschränkungen). Insofern ist die Ursache und Lösung der Usergemeinde sicher noch nicht klar.
     


    ciao Frank
    Sonntag, 4. Juli 2010 18:26