|
-
May 31st, 2014, 09:14 PM
#1
Thread Starter
Junior Member
[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.
-
May 31st, 2014, 09:38 PM
#2
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
- Coding Examples:
- Features:
- Online Games:
- Compiled Games:
-
May 31st, 2014, 09:41 PM
#3
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
- Coding Examples:
- Features:
- Online Games:
- Compiled Games:
-
May 31st, 2014, 10:05 PM
#4
Re: Getting rid of lines drawn under other lines
 Originally Posted by .paul.
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.
-
Jun 1st, 2014, 02:18 AM
#5
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.
-
Jun 1st, 2014, 03:40 PM
#6
Thread Starter
Junior Member
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
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|