dcsimg
Results 1 to 6 of 6

Thread: [RESOLVED] Getting rid of lines drawn under other lines

  1. #1

    Thread Starter
    Junior Member
    Join Date
    Sep 2013
    Posts
    22

    Resolved [RESOLVED] Getting rid of lines drawn under other lines

    I'm trying to copy that microsoft screen saver with the moving line on a black background, and I've reached a barrier.
    Perhaps it's just best you look at my code before I try to explain myself, it could get confusing.
    Code:
    Public Class Form1
    
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Randomize()
            g = Me.CreateGraphics
    
            While y1c = y2c
                y1c = 3 + CInt(7 * Rnd()) 'makes endpoint location change values different
            End While
    
            While x1c = x2c
                x1c = 3 + CInt(7 * Rnd())
            End While
    
            Me.BackColor = Color.Black
    
            Timer.Start()
    
        End Sub
    
        Dim i1 As Integer = CInt(255 * Rnd()) 'argb values
        Dim i2 As Integer = CInt(255 * Rnd())
        Dim i3 As Integer = CInt(255 * Rnd())
    
        Dim g As Graphics
    
        Dim x1 As Integer = CInt(Rnd(Me.Width)) 'line endpoint location values
        Dim y1 As Integer = CInt(Rnd(Me.Height))
        Dim x2 As Integer = CInt(Rnd(Me.Width))
        Dim y2 As Integer = CInt(Rnd(Me.Height))
    
        Dim x1c As Integer = 3 + CInt(7 * Rnd()) 'line endpoint location change values
        Dim y1c As Integer = 3 + CInt(7 * Rnd())
        Dim x2c As Integer = 3 + CInt(7 * Rnd())
        Dim y2c As Integer = 3 + CInt(7 * Rnd())
    
        Private Sub Timer_Tick(sender As Object, e As EventArgs) Handles Timer.Tick
    
            i1 = i1 + 1
            i2 = i2 + 2
            i3 = i3 + 3
            If i1 > 255 Then i1 = 1
            If i2 > 254 Then i2 = 1
            If i3 > 253 Then i3 = 1
    
            x1 = x1 + x1c
            If x1 > Me.Width Or x1 < 0 Then x1c = x1c * -1
            y1 = y1 + y1c
            If y1 > Me.Height Or y1 < 0 Then y1c = y1c * -1
    
            x2 = x2 + x2c
            If x2 > Me.Width Or x2 < 0 Then x2c = x2c * -1
            y2 = y2 + y2c
            If y2 > Me.Height Or y2 < 0 Then y2c = y2c * -1
    
            g.DrawLine(New Pen(Color.FromArgb(i1, i2, i3), 1), New Point(x1, y1), New Point(x2, y2))
    
        End Sub
    End Class
    I just need help with getting rid of the lines that it has drawn after ~15 lines have been drawn after those *EFFICIENTLY*, so the screen doesn't feel so cluttered after a moment of it running.
    Thanks!
    Last edited by DinosaurusRex; May 31st, 2014 at 09:21 PM.

  2. #2
    eXtreme Programmer .paul.'s Avatar
    Join Date
    May 2007
    Location
    Chelmsford UK
    Posts
    22,594

    Re: Getting rid of lines drawn under other lines

    do your drawing in the form's paint event. the painteventargs parameter contains a graphics object that you'd use instead of creategraphics

  3. #3
    eXtreme Programmer .paul.'s Avatar
    Join Date
    May 2007
    Location
    Chelmsford UK
    Posts
    22,594

    Re: Getting rid of lines drawn under other lines

    also Rnd and Randomize are legacy code. use the Random class instead:

    http://msdn.microsoft.com/en-us/libr...em.random.aspx

  4. #4
    .NUT jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    103,476

    Re: Getting rid of lines drawn under other lines

    Quote Originally Posted by .paul. View Post
    do your drawing in the form's paint event. the painteventargs parameter contains a graphics object that you'd use instead of creategraphics
    I have to agree with .paul. here. You should NEVER call CreateGraphics yourself. You'll see it in many GDI+ examples because it's quick and dirty but those are just examples. Examples often use a simplified setup in order to demonstrate what they're really about, which is the actual drawing in this case.

    Controls regularly raise their Paint event and, on that event, some or all of the control's surface is erased and repainted. One reason that you must do your drawing on the Paint event is because anything you draw elsewhere might be erased. It's also that erasing that you use to get rid of any old drawing that you no longer want. Here are the basic steps you should follow when using GDI+ drawing.

    1. Declare one or more member variables to store the data that describes your drawing.
    2. Update the data stored in those variables when the drawing needs to change.
    3a. If the entire control has or may have changed, call its Refresh method to erase and repaint the entire surface.
    3b. If only some of the control has or may have changed, call its Invalidate method one or more times to erase that area or areas and then call its Update method to repaint the same.
    4. Handle the Paint method of the control and do ALL your drawing there. remember that the old drawing has already been erased so you don't need to do that manually. Read the data from your member variables and do the drawing using the Graphics object provided by the event.

    With regards to points 3a and 3b, actually painting the pixels to the screen is the slowest part of the process so that should be kept to a minimum. For that reason, it's generally preferable to call Invalidate and Update rather than Refresh unless it really is all or most of the control's surface that has or may have changed. It's quicker to do some calculations to determine the area that has or may have changed in most cases than to repaint the entire control.

    Follow the CodeBank link in my signature below and you'll find three separated threads on GDI+ Drawing. The Simple Drawing Program is the first that you should check out.

  5. #5
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    5,622

    Re: Getting rid of lines drawn under other lines

    While the points about how you should do your drawing are valid, they don't answer the question asked.
    The way you're drawing now is drawing directly to the screen area of the form, thus you have temporary persistence that lasts only as long as something doesn't invalidate that area and cause the form to redraw itself.
    This temporary persistence is what makes your existing code "work", because the lines are accumulating on the screen.
    In order to "erase" some of the lines, but keep the rest, you are going to have to keep track of the lines drawn, so you can clear the area and redraw the lines you want to keep. Generally, this would involve an array, or array like structure (i.e. a List) to hold the history of the line information so you can redraw all the lines you want to display.

    As an example, I've followed your code's logic, but I've replaced some of the type's used with other types to do the same thing, for example, instead of two integers to define the X,Y coordinate of a point, I used a Point structure, which contains X and Y properties. And, instead of X and Y change values, used a Size structure which contains properties Width and Height.
    You can update a point's position (both its X,Y values) by adding a Size object to the point.
    So, that changes the code a bit, but the logic is still what you had, just adjusted to suit the different types.
    A user defined structure was added to keep track of the attributes of a line itself, i.e its startpoint, endpoint, and color.
    An array of that structure can keep track of however many lines you want to display.
    When a paint event occurs, the drawing surface has already been reinitialized to its beginning state for the area invalidated. So, in your case, for the default invalidation, the form is cleared to black and you are passed a graphics object to use to do your drawing. The code has to loop through the history array and redraw all the lines to be displayed.
    Before running the code, change the MaxLines value (currently set to 30) to draw fewer or more lines on the screen before the older ones are "erased".

    p.s. For extra entertainment, after you see how the code draws lines, change the lines to ellipses, using the following line:
    e.Graphics.DrawEllipse(colorPen, .pnt1.X, .pnt1.Y, Math.Abs(.pnt2.X - .pnt1.X), Math.Abs(.pnt2.Y - .pnt1.Y))
    (or do both line and ellipses).
    Code:
    Public Class Form1
      Dim rand As New Random
    
      Dim i1 As Integer = rand.Next(256) 'argb values
      Dim i2 As Integer = rand.Next(256)
      Dim i3 As Integer = rand.Next(256)
    
      Dim MaxLines As Integer = 30   'Maximum number of lines to display simultaneously
      Dim currentLine As Integer     'Current line being updated in our array of lines
    
      Private Structure LineType   'A structure to hold information for one line
        Public pnt1 As Point       'The start point of the line
        Public pnt2 As Point       'The end point of the line 
        Public colr As Color       'the color of the line
      End Structure
    
      Dim newLine As LineType      'A single line that is updated to move around the screen
      Dim lines(MaxLines - 1) As LineType  'An array of lines to maintain a history of where the single line has been located
    
      Dim pnt1c, pnt2c As Size   'We can add a "Size" to a Point to update the X,Y values of the point
      Dim colorPen As New Pen(Brushes.Black)  'A pen used to draw the line (update the color of the pen to when drawing)
    
      Private Sub Form1_Load_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.DoubleBuffered = True  'To prevent flashing because we're now redrawing the whole form in the paint event
    
        With newLine  'Initial the endpoints of the line
          .pnt1.X = rand.Next(Me.Width) 'line endpoint location values
          .pnt1.Y = rand.Next(Me.Height)
          .pnt2.X = rand.Next(Me.Width) 'line endpoint location values
          .pnt2.Y = rand.Next(Me.Height)
        End With
        lines(0) = newLine   'Store the line in the first entry of lines
    
        pnt1c.Width = 3 + rand.Next(8)   'Initial the endpoint location change values
        pnt1c.Height = 3 + rand.Next(8)
        pnt2c.Width = 3 + rand.Next(8)
        pnt2c.Height = 3 + rand.Next(8)
    
        While pnt1c.Height = pnt2c.Height
          pnt1c.Height = 3 + rand.Next(8) 'makes endpoint location change values different
        End While
    
        While pnt1c.Width = pnt2c.Width
          pnt1c.Width = 3 + rand.Next(8) 'makes endpoint location change values different
        End While
    
        Me.BackColor = Color.Black
        Timer1.Interval = 10          'Anything less than 16 will probably be a maximum of 64hz (15.625 ms) 
        Timer1.Start()
    
      End Sub
    
      Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        i1 = i1 + 1                 'Update the color values
        i2 = i2 + 2
        i3 = i3 + 3
        If i1 > 255 Then i1 = 1
        If i2 > 254 Then i2 = 1
        If i3 > 253 Then i3 = 1
    
        newLine.pnt1 += pnt1c       'update the startpoint(x,y) of the line
        newLine.pnt2 += pnt2c       'update the endpoint(x,y) of the line
    
        With newLine.pnt1           'Check the first point's X value to keep in bounds
          If .X > Me.Width Then
            .X = Me.Width
            pnt1c.Width *= -1
          ElseIf .X < 0 Then
            .X = 0
            pnt1c.Width *= -1
          End If
    
          If .Y > Me.Height Then    'Check the first point's Y value to keep in bounds
            .Y = Me.Height
            pnt1c.Height *= -1
          ElseIf .Y < 0 Then
            .Y = 0
            pnt1c.Height *= -1
          End If
        End With
    
        With newLine.pnt2
          If .X > Me.Width Then     'Check the second point's X value to keep in bounds
            .X = Me.Width           'Prevent getting stuck if form size changed
            pnt2c.Width *= -1       'Change direction
          ElseIf .X < 0 Then
            .X = 0
            pnt2c.Width *= -1
          End If
    
          If .Y > Me.Height Then    'Check the second point's Y value to keep in bounds
            .Y = Me.Height
            pnt2c.Height *= -1
          ElseIf .Y < 0 Then
            .Y = 0
            pnt2c.Height *= -1
          End If
        End With
    
        newLine.colr = Color.FromArgb(i1, i2, i3)     'Update the line's color value
        currentLine = (currentLine + 1) Mod MaxLines  'Advance (cycle) the pointer into the history array
        lines(currentLine) = newLine                  'Save the updated line position in the history array
        Invalidate()                                  'Update the form's display
      End Sub
    
      Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
        For i As Integer = 0 To lines.Length - 1        'For each line in the history array
          With lines(i)
            colorPen.Color = .colr                      '  Set the pen's color to this lines color   
            e.Graphics.DrawLine(colorPen, .pnt1, .pnt2) '  Draw the line
          End With
        Next
      End Sub
    End Class
    Try the code above first to see how it works for comparison to the code below.

    Now, an alternative to redrawing all the lines is to maintain persistence by using a bitmap in memory to draw to. Then in the paint event, you just draw that bitmap to the form. The main drawback is that you have the same issue. How to get rid of older lines. You could use the method above, and clear the bitmap and redraw the lines, but that would not have any real advantage, in this case, to what was done above.
    But, you could go for a different, and some may say neater/cooler method to get rid of older lines and that is to let them fade out by continually filling the bitmap with a low alpha value backcolor. Each time the low alpha color is drawn, it merges with the existing colors in the bitmap, which "fades" the color slowly down to that alpha level.
    If we use that technique, we don't have to keep track of the lines, because we never clear the bitmap so don't have to redraw the lines we want to keep. But the moving line will leave a trail, that fades over time. Because the fade can't fade to a solid background color, the lines are never fully erased, so you have a faint ghosting left in the background.

    As an example of that, the following code is a modification of your code that creates a bitmap, draws in it and copies it to the form in the paint event.
    Because of the larger area being filled continually, this takes more time than the code above.
    Also, this code is closer to your original, so doesn't take into consideration what happens if the user resizes the form, which can cause the program to get line stuck outside the boundary of the form.
    The bitmap is made large so if the form is expanded, the line drawing can extend into the new area "exposed", but the size checks are not there, as they are in the above code, to bring the coordinates back in bounds if the form is resized smaller and catches an endpoint off the form.
    Because of the large bitmap drawing, the update rate (time taken to draw is longer) is slower.
    Code:
    Public Class Form1
      Dim rand As New Random
      Dim bmp As New Bitmap(Screen.PrimaryScreen.WorkingArea.Width, Screen.PrimaryScreen.WorkingArea.Height)
      Dim gBmp As Graphics = Graphics.FromImage(bmp)
    
      Dim i1 As Integer = rand.Next(256) 'argb values
      Dim i2 As Integer = rand.Next(256)
      Dim i3 As Integer = rand.Next(256)
    
      Dim x1 As Integer = rand.Next(Me.Width) 'line endpoint location values
      Dim y1 As Integer = rand.Next(Me.Height)
      Dim x2 As Integer = rand.Next(Me.Width)
      Dim y2 As Integer = rand.Next(Me.Height)
    
      Dim x1c As Integer = 3 + rand.Next(8) 'line endpoint location change values
      Dim y1c As Integer = 3 + rand.Next(8)
      Dim x2c As Integer = 3 + rand.Next(8)
      Dim y2c As Integer = 3 + rand.Next(8)
    
      Dim fadeBrush As New SolidBrush(Color.FromArgb(8, 0, 0, 0))
    
      Private Sub Form1_Load_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.DoubleBuffered = True
        While y1c = y2c
          y1c = 3 + rand.Next(8) 'makes endpoint location change values different
        End While
    
        While x1c = x2c
          x1c = 3 + rand.Next(8)
        End While
    
        Me.BackColor = Color.Black
        Timer1.Interval = 10
        Timer1.Start()
    
      End Sub
    
      Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        i1 = i1 + 1
        i2 = i2 + 2
        i3 = i3 + 3
        If i1 > 255 Then i1 = 1
        If i2 > 254 Then i2 = 1
        If i3 > 253 Then i3 = 1
    
        x1 = x1 + x1c
        If x1 > Me.Width Or x1 < 0 Then x1c = x1c * -1
        y1 = y1 + y1c
        If y1 > Me.Height Or y1 < 0 Then y1c = y1c * -1
    
        x2 = x2 + x2c
        If x2 > Me.Width Or x2 < 0 Then x2c = x2c * -1
        y2 = y2 + y2c
        If y2 > Me.Height Or y2 < 0 Then y2c = y2c * -1
    
        gBmp.DrawLine(New Pen(Color.FromArgb(i1, i2, i3), 1), New Point(x1, y1), New Point(x2, y2))
        gBmp.FillRectangle(fadeBrush, ClientRectangle)
        Invalidate(ClientRectangle)
      End Sub
    
      Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
        e.Graphics.DrawImage(bmp, 0, 0)
      End Sub
    End Class
    Last edited by passel; Jun 1st, 2014 at 02:36 AM.

  6. #6

    Thread Starter
    Junior Member
    Join Date
    Sep 2013
    Posts
    22

    Re: Getting rid of lines drawn under other lines

    Very detailed and well thought out response with great practices too! Thanks a ton, this was very helpful

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Featured


Click Here to Expand Forum to Full Width