Page 1 of 2 12 LastLast
Results 1 to 40 of 42

Thread: Very Simple Drawing Program

  1. #1

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,299

    Very Simple Drawing Program

    C# version here, courtesy of ForumAccount.

    The following code can be used as the basis for a simple drawing program. It assumes that you have a PictureBox on your form named PictureBox1. The Save and Clear methods can be called from wherever you like. The most likely candidates would be the Click event handlers of either Buttons or menu items. I wrote this in VB 2005. For earlier versions you'd have to use an ArrayList instead of a List(Of Line) or else derive your own LineCollection class from CollectionBase.
    VB.NET Code:
    1. Public Class Form1
    2.  
    3.     'The lines that have been drawn but not saved.
    4.     Private lines As New List(Of Line)
    5.  
    6.     'The start point of the line currently being drawn.
    7.     Private start As Point
    8.  
    9.     Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    10.         'Place a blank image in the PictureBox control.
    11.         Me.PictureBox1.Image = New Bitmap(Me.PictureBox1.Width, Me.PictureBox1.Height)
    12.     End Sub
    13.  
    14.     Private Sub PictureBox1_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDown
    15.         'Remember the point at which the line started.
    16.         Me.start = e.Location
    17.     End Sub
    18.  
    19.     Private Sub PictureBox1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseUp
    20.         'Remember the point at which the line ended.
    21.         Dim [end] As Point = e.Location
    22.  
    23.         'Add the new line to the list.
    24.         Me.lines.Add(New Line(Me.start, [end]))
    25.  
    26.         Dim area As New Rectangle(Math.Min(Me.start.X, [end].X), _
    27.                                   Math.Min(Me.start.Y, [end].Y), _
    28.                                   Math.Abs(Me.start.X - [end].X), _
    29.                                   Math.Abs(Me.start.Y - [end].Y))
    30.  
    31.         'Inflate the rectangle by 1 pixel in each direction to ensure every changed pixel will be redrawn.
    32.         area.Inflate(1, 1)
    33.  
    34.         'Force the control to repaint so the new line is drawn.
    35.         Me.PictureBox1.Invalidate(area)
    36.         Me.PictureBox1.Update()
    37.     End Sub
    38.  
    39.     Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
    40.         'Draw each line on the control.
    41.         Me.DrawLines(e.Graphics)
    42.     End Sub
    43.  
    44.     Private Sub Save()
    45.         'Create a Graphics object from the Image in the PictureBox.
    46.         Using g As Graphics = Graphics.FromImage(Me.PictureBox1.Image)
    47.             'Draw each line on the image to make them permanent.
    48.             Me.DrawLines(g)
    49.         End Using
    50.  
    51.         'Clear the temporary lines that were just saved.
    52.         Me.Clear()
    53.     End Sub
    54.  
    55.     Private Sub Clear()
    56.         'Clear all unsaved lines.
    57.         Me.lines.Clear()
    58.  
    59.         'Force the control to repaint so the lines are removed.
    60.         Me.PictureBox1.Refresh()
    61.     End Sub
    62.  
    63.     Private Sub DrawLines(ByVal g As Graphics)
    64.         For Each line As Line In Me.lines
    65.             g.DrawLine(Pens.Black, line.Start, line.End)
    66.         Next line
    67.     End Sub
    68.  
    69. End Class
    70.  
    71. Public Class Line
    72.  
    73.     'The line's start point.
    74.     Private _start As Point
    75.  
    76.     'The line's end point.
    77.     Private _end As Point
    78.  
    79.     'The line's start point.
    80.     Public Property Start() As Point
    81.         Get
    82.             Return Me._start
    83.         End Get
    84.         Set(ByVal value As Point)
    85.             Me._start = value
    86.         End Set
    87.     End Property
    88.  
    89.     'The line's end point.
    90.     Public Property [End]() As Point
    91.         Get
    92.             Return Me._end
    93.         End Get
    94.         Set(ByVal value As Point)
    95.             Me._end = value
    96.         End Set
    97.     End Property
    98.  
    99.     Public Sub New()
    100.         Me.New(Point.Empty, Point.Empty)
    101.     End Sub
    102.  
    103.     Public Sub New(ByVal start As Point, ByVal [end] As Point)
    104.         Me._start = start
    105.         Me._end = [end]
    106.     End Sub
    107.  
    108. End Class
    I strongly recommend that you now go down and read post #17 for some explanation.
    Last edited by jmcilhinney; Oct 12th, 2017 at 08:08 PM. Reason: Added call to Update after Invalidate to force redraw.

  2. #2
    Fanatic Member uniquegodwin's Avatar
    Join Date
    Jul 2005
    Location
    Chennai,India
    Posts
    694

    Re: Very Simple Drawing Program

    Nice tool Jm
    Godwin

    Help someone else with what someone helped you!

  3. #3
    New Member
    Join Date
    Nov 2006
    Posts
    2

    Re: Very Simple Drawing Program

    Excellent post and thanks, i have succesfully modified this to work under the cf which is my aim but im looking for a free hand tool. ie in full .net 2.0 click and draw. This is a line application and does this very well. Is it easy to modify this so that it will be a free hand tool and if so where?
    VB Code:
    1. g.DrawLine(pens, line.Start.X, line.Start.Y, line.End.X, line.End.Y)
    chnage to the above for cf as it doesnt suppoert .location

  4. #4

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,299

    Re: Very Simple Drawing Program

    Quote Originally Posted by jameswilson
    Excellent post and thanks, i have succesfully modified this to work under the cf which is my aim but im looking for a free hand tool. ie in full .net 2.0 click and draw. This is a line application and does this very well. Is it easy to modify this so that it will be a free hand tool and if so where?
    VB Code:
    1. g.DrawLine(pens, line.Start.X, line.Start.Y, line.End.X, line.End.Y)
    chnage to the above for cf as it doesnt suppoert .location
    I've never tried freehand drawing myself but this was the third result returned by a Google search for freehand drawing ".net".

  5. #5
    Fanatic Member Crash893's Avatar
    Join Date
    Dec 2005
    Posts
    930

    Re: Very Simple Drawing Program

    I have been playing around with this for a few days now

    How would i draw a rectangle rather than line?

  6. #6

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,299

    Re: Very Simple Drawing Program

    Quote Originally Posted by Crash893
    I have been playing around with this for a few days now

    How would i draw a rectangle rather than line?
    If you call the DrawLine method of a Graphics object to draw a line, how would you guess you draw a rectangle?

  7. #7
    Fanatic Member Crash893's Avatar
    Join Date
    Dec 2005
    Posts
    930

    Re: Very Simple Drawing Program

    sorry that did seem a little vague


    here is what i have played around with so far


    vb Code:
    1. Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
    2.  
    3.         ' Create pen.
    4.         Dim RedPen As New Pen(Color.Red, 3)
    5.  
    6.         ' Create rectangle.
    7.         Dim rect As New Rectangle(0, 0, 200, 200)
    8.         e.Graphics.DrawRectangle(RedPen, rect)
    9.  
    10.  
    11.         'Draw each line on the control.
    12.         For Each line As Line In Me.lines
    13.             e.Graphics.DrawLine(Pens.Black, line.Start, line.End)
    14.  
    15.         Next line
    16.  
    17.     End Sub

    the result of which is that it draws a rectangle in the upper right hand side of the screen.

    im still a little unsure where the cordnates are read in from the mouse clicks

  8. #8

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,299

    Re: Very Simple Drawing Program

    As you can see from my code I create a Line object from two Points that are read in on the MouseDown and MouseUp events. Instead of a collection of Line objects you would have a collection of Rectangle objects, or maybe just one Rectangle object. You can easily create a Rectangle from two Points. Two Points provides your maximum and minumum X values and Y values. What else do you need for a Rectangle? Then you just pass that Rectangle object to DrawRectangle on the Paint event.

  9. #9
    Junior Member
    Join Date
    Apr 2007
    Location
    Oslo, Norway
    Posts
    23

    Re: Very Simple Drawing Program

    Yet another easy example for a beginner like me.

    The only think I haven't figured out is this code:

    vb.net Code:
    1. 'Remember the point at which the line ended.
    2. Dim [end] As Point = e.Location

    Why is the variable end inside brackets?

  10. #10

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,299

    Re: Very Simple Drawing Program

    Quote Originally Posted by Odin3655
    Yet another easy example for a beginner like me.

    The only think I haven't figured out is this code:

    vb.net Code:
    1. 'Remember the point at which the line ended.
    2. Dim [end] As Point = e.Location

    Why is the variable end inside brackets?
    'End' is a reserved word in VB.NET. Just like in SQL, if you want to use a reserved word as an identifier you have to enclose it in brackets. If you don't then it will be interpreted as a reserved word and a syntax error will result. Note that the brackets are not part of the identifier, but just indicate that the enclosed value IS an identifier. You can use brackets on any and all identifiers if you want, but they only serve a purpose on reserved words.

  11. #11
    New Member
    Join Date
    Mar 2008
    Posts
    1

    Re: Very Simple Drawing Program

    Is there a way to erase lines you have drawn ?

  12. #12

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,299

    Re: Very Simple Drawing Program

    Quote Originally Posted by Zboy
    Is there a way to erase lines you have drawn ?
    What gets drawn is whatever's in the List, so if you don't want a line drawn any more then you need to remove it from the List.

  13. #13
    Frenzied Member
    Join Date
    Jan 2008
    Posts
    1,754

    Re: Very Simple Drawing Program

    How exactly would i make it freedraw?

    You can just draw your mouse around and draw instead of having to release the button on your mouse (mouse up).

  14. #14
    Frenzied Member
    Join Date
    Jan 2008
    Posts
    1,754

    Re: Very Simple Drawing Program

    Quote Originally Posted by noahssite
    How exactly would i make it freedraw?

    You can just draw your mouse around and draw instead of having to release the button on your mouse (mouse up).
    Whell after modifing your code i came up with this:

    vb.net Code:
    1. Public Class Form1
    2.  
    3.     'The lines that have been drawn but not saved.
    4.     Private lines As New List(Of Line)
    5.  
    6.     'The start point of the line currently being drawn.
    7.     Private start As Point
    8.     Dim [end] As Point
    9.  
    10.     Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    11.         'Place a blank image in the PictureBox control.
    12.         Me.PictureBox1.Image = New Bitmap(Me.PictureBox1.Width, Me.PictureBox1.Height)
    13.     End Sub
    14.  
    15.     Private Sub PictureBox1_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDown
    16.         'Remember the point at which the line started.
    17.         Me.start = e.Location
    18.  
    19.     End Sub
    20.  
    21.     Private Sub PictureBox1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseMove
    22.         If e.Button = Windows.Forms.MouseButtons.Left Then
    23.                         Me.start = e.Location
    24.             [end] = New Point(e.Location.X + 1, e.Location.Y + 1)
    25.  
    26.             'Add the new line to the list.
    27.             Me.lines.Add(New Line(Me.start, [end]))
    28.  
    29.             Dim area As New Rectangle(Math.Min(Me.start.X, [end].X), _
    30.                                       Math.Min(Me.start.Y, [end].Y), _
    31.                                       Math.Abs(Me.start.X - [end].X), _
    32.                                       Math.Abs(Me.start.Y - [end].Y))
    33.  
    34.             'Inflate the rectangle by 1 pixel in each direction to ensure every changed pixel will be redrawn.
    35.             area.Inflate(1, 1)
    36.  
    37.             'Force the control to repaint so the new line is drawn.
    38.             Me.PictureBox1.Invalidate(area)
    39.             Me.PictureBox1.Update()
    40.         End If
    41.     End Sub
    42.  
    43.     Private Sub PictureBox1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseUp
    44.  
    45.     End Sub
    46.  
    47.     Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
    48.         'Draw each line on the control.
    49.         Me.DrawLines(e.Graphics)
    50.     End Sub
    51.  
    52.     Private Sub Save()
    53.         'Create a Graphics object from the Image in the PictureBox.
    54.         Using g As Graphics = Graphics.FromImage(Me.PictureBox1.Image)
    55.             'Draw each line on the image to make them permanent.
    56.             Me.DrawLines(g)
    57.         End Using
    58.  
    59.         'Clear the temporary lines that were just saved.
    60.         Me.Clear()
    61.     End Sub
    62.  
    63.     Private Sub Clear()
    64.         'Clear all unsaved lines.
    65.         Me.lines.Clear()
    66.  
    67.         'Force the control to repaint so the lines are removed.
    68.         Me.PictureBox1.Refresh()
    69.     End Sub
    70.  
    71.     Private Sub DrawLines(ByVal g As Graphics)
    72.         For Each line As Line In Me.lines
    73.             g.DrawLine(Pens.Black, line.Start, line.End)
    74.         Next line
    75.     End Sub
    76.  
    77. End Class
    78.  
    79. Public Class Line
    80.  
    81.     'The line's start point.
    82.     Private _start As Point
    83.  
    84.     'The line's end point.
    85.     Private _end As Point
    86.  
    87.     'The line's start point.
    88.     Public Property Start() As Point
    89.         Get
    90.             Return Me._start
    91.         End Get
    92.         Set(ByVal value As Point)
    93.             Me._start = value
    94.         End Set
    95.     End Property
    96.  
    97.     'The line's end point.
    98.     Public Property [End]() As Point
    99.         Get
    100.             Return Me._end
    101.         End Get
    102.         Set(ByVal value As Point)
    103.             Me._end = value
    104.         End Set
    105.     End Property
    106.  
    107.     Public Sub New()
    108.         Me.New(Point.Empty, Point.Empty)
    109.     End Sub
    110.  
    111.     Public Sub New(ByVal start As Point, ByVal [end] As Point)
    112.         Me._start = start
    113.         Me._end = [end]
    114.     End Sub
    115. End Class

    I modified mouse_move/down/up. Though what i did works, it looks bad if you move your mouse really fast.
    Really i just moved the code around in the mouse_up sub.

  15. #15

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,299

    Re: Very Simple Drawing Program

    Quote Originally Posted by noahssite
    How exactly would i make it freedraw?

    You can just draw your mouse around and draw instead of having to release the button on your mouse (mouse up).
    No freehand drawing tools handle fast mouse movements all that well. You can either call DrawEllipse repeatedly and leave gaps between the dots, or else call DrawLine repeatedly and end up with a slightly less curvy curve. Paint.NET and MS Paint both take the latter approach.

    I do plan to extend this example soon to include freehand drawing, rectangles and ellipses, so stay tuned.

  16. #16
    Frenzied Member
    Join Date
    Jan 2008
    Posts
    1,754

    Re: Very Simple Drawing Program

    So how would i call it repeatidly? Where would i put it in your code?

  17. #17

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,299

    Re: Very Simple Drawing Program

    I thought that I would add a bit of explanation to this to explain why things are done the way they are.

    The way GDI+ works, controls get repainted on screen over and over again, sometimes many times in quick succession. On each of these occasions the control's Paint event is raised. You need to use the Paint event of the control to perform your own drawing or else, the next time the control is repainted, your drawing will just disappear as the control paints over it.

    When you're creating a custom control you should override the OnPaint method and perform your drawing there. When you're designing a form or user control and you want to add custom drawing to a child control you handle that control's Paint event, then perform your drawing in the event handler.

    You may have seen, or heard people talk about, controls flickering on screen. That's caused by repainting large areas of the control repeatedly and happens because painting on screen is actually quite a slow process. As such, you should try to do as little of it as possible.

    When you make a change that will affect the drawing on the control you need to force it to repaint. The easiest way to do this is to call the control's Refresh method. This will cause the control to repaint its entire area. Sometimes this is necessary. Note that, in my code above, I call Refresh in the Clear method. That's because the lines being cleared could be anywhere on the control.

    More often than not, though, you will make a change that will only affect a certain area of a control. In that case, rather than calling Refresh, you should call Invalidate and Update. It should be noted that Refresh actually calls Invalidate and Update internally. It calls Invalidate with no parameters though, so it invalidates the entire control. When you know that you've only changed a specific area you should specify that area when calling Invalidate, by passing a Rectangle or Region. In my code above, notice that I call Invalidate in the MouseUp event handler and I pass the smallest Rectangle that contains the line to be drawn.

    Now, here's something that often trips people up. I then call Update, which forces the control to repaint, thereby raising the Paint event. In the Paint event handler, the code will draw over the entire area of the control. There's no need for you to worry about what area has been invalidated and what has not. You just draw everything over the entire control. This creates a picture in memory of your drawing. The system then determines what parts of that picture will actually get painted to the screen, based on what parts of the control have been invalidated. Your code in the Paint event handler is executed very quickly. It's the actual painting of pixels that is slow, so it's that part that needs to be kept to a minimum.

    Note that it is often worth writing relatively complex code to determine the smallest area possible to invalidate because even that complex calculation will take less time than it would to paint the extra pixels that your calculation excludes. As such, it may even have proven to be more efficient in my code to create a Region that was a tighter fit to the line being drawn than a Rectangle that, obviously, will have a fair bit of area in it not occupied by the line. I didn't do that because I was just trying to illustrate a point, but in applications that do a lot of drawing and repainting it would likely be worthwhile.

  18. #18
    Frenzied Member
    Join Date
    Jan 2008
    Posts
    1,754

    Re: Very Simple Drawing Program

    Thankyou jmc although i said i understand perfectly in my thread i only understood that code and such... now i understand GDI which will come in handy..

    So a program like MSPaint would override the OnPaint method?

    But MSPaint doesnt flicker at all and you can draw freely all over.. would that be because it would only invalidate a small region?

  19. #19

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,299

    Re: Very Simple Drawing Program

    Quote Originally Posted by noahssite
    Thankyou jmc although i said i understand perfectly in my thread i only understood that code and such... now i understand GDI which will come in handy..

    So a program like MSPaint would override the OnPaint method?

    But MSPaint doesnt flicker at all and you can draw freely all over.. would that be because it would only invalidate a small region?
    Paint isn't a .NET application so it wouldn't use this technique, but if you were to create a Paint-like program (which this is a very simple version of) then yes, you would always invalidate the smallest area possible to minimise the area that gets repainted and thereby minimise the chance of flicker. You can also use double-buffering on the form to reduce flicker even more.

  20. #20
    Lively Member
    Join Date
    Jan 2010
    Posts
    106

    Re: Very Simple Drawing Program

    how would i be able to place the drawing with a (real) image in the picturebox and be able to save over the loaded image? (save both image and drawing?)

  21. #21

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,299

    Re: Very Simple Drawing Program

    Quote Originally Posted by LoGiCAnti View Post
    how would i be able to place the drawing with a (real) image in the picturebox and be able to save over the loaded image? (save both image and drawing?)
    The whole purpose of this thread is to illustrate how to do exactly that. In my example I use a blank Image. If you don't want to use a blank Image then don't use a blank Image. Use some other Image instead.

  22. #22
    Registered User
    Join Date
    Feb 2014
    Posts
    1

    Re: Very Simple Drawing Program

    nc jmcilhinney

    but i wonder can you save it in database after your draw in picturebox???? tnx

  23. #23

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,299

    Re: Very Simple Drawing Program

    Quote Originally Posted by [F]ufu View Post
    nc jmcilhinney

    but i wonder can you save it in database after your draw in picturebox???? tnx
    An Image is an Image, regardless of how it's created. You would save an Image created like this the same way as you would any other. Follow the CodeBank link in my signature below and check out my thread on Saving Images To Databases.

  24. #24
    New Member
    Join Date
    Mar 2014
    Posts
    2

    Re: Very Simple Drawing Program

    I know it has been a long time since anything was added to this discussion, but I can't think of a better place to put this...

    I used your "simple drawing" example to solve a problem I had with creating images in a picturebox. The application is working very well and I thank you for that.

    Now I would like to save the contents of the picturebox to a disk file. It looks like I should be able to add a line in your Save routine like:

    Me.Picturebox1.Image.Save(<path and filename>, System.Drawing.Imaging.ImageFormat.Jpeg)

    and call the routine from a button click, but the image comes out blank. Can you tell me what I am missing? How would I save the contents of the picturebox to disk?

  25. #25

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,299

    Re: Very Simple Drawing Program

    Quote Originally Posted by rinkley View Post
    I know it has been a long time since anything was added to this discussion, but I can't think of a better place to put this...

    I used your "simple drawing" example to solve a problem I had with creating images in a picturebox. The application is working very well and I thank you for that.

    Now I would like to save the contents of the picturebox to a disk file. It looks like I should be able to add a line in your Save routine like:

    Me.Picturebox1.Image.Save(<path and filename>, System.Drawing.Imaging.ImageFormat.Jpeg)

    and call the routine from a button click, but the image comes out blank. Can you tell me what I am missing? How would I save the contents of the picturebox to disk?
    I just did a quick test and saw a similar result, although I'm not sure why to be honest. I'll have to do a bit of investigation and get back to you on that.

  26. #26
    New Member
    Join Date
    Mar 2014
    Posts
    2

    Re: Very Simple Drawing Program

    Thank you for your quick reply. I am honored.
    After more investigation, I found the solution. I don't know enough about what I am doing to explain why it works, but it turns out that adding Me.Picturebox1.Refresh immediately before saving the image to disk takes care of the problem.

  27. #27
    Fanatic Member
    Join Date
    Sep 2002
    Posts
    527

    Re: Very Simple Drawing Program

    Hi all,

    I'd like to through in a little extra challenge to this nice little vectorial drawing example app. In this current example, it is not possible to move, modify or delete drawn objects (line, rectangle, ellipse, whatever...). Although being able to modify these objects would imply lots of extra code, the first step is to add some code that will allow users to 'select' drawn objects. That's what I'm interested in.

    I seriously gave a lot of thoughts about that, and so far what I came up with is not simple, nor very performant I'm afraid. The source of the problem is to define an efficient way to find out if the mouse cursor if over a drawn object, and which one. Unless the GDI+ has some feature I don't know about the addresses just that (which may be possible, given the fact I'm just starting with VB.NET...).

    Any thoughts?

    Thanks!
    Don't ask why, just reboot!

  28. #28
    Frenzied Member Gruff's Avatar
    Join Date
    Jan 2014
    Location
    Scappoose Oregon USA
    Posts
    1,293

    Re: Very Simple Drawing Program

    Hope you don't mind JM.

    Alain:

    Selections routines entail searching through your list(of clsDrawingObjects)
    For line objects you generally want to use a closest point perpendicular to the line formula.
    Search within a predetermined selection tolerance. You also have to determine if the selection
    is within the length of the line.

    If you implement real world drawing units and zoom and pan the selection tolerance needs to be translated into screen coordinates so that that selection distance to the object always appears the same.

    Every drawing object type will need it's own selection formula.

    In short this is typically not a trivial thing to do.

    If you want to pursue this further here is a sub-routine that calculates the closest perpendicular distant from a cursor point to a line. The OnPt parameter is the intersection point on the line selected.
    With this you can check if the OnPt is inside the line endpoints.

    The hypotnuse function is not provided, but should not be difficult to create.

    Code:
      Public Function Closest_Dist(ByVal Pt As PointF, ByVal sPt As PointF, ByVal ePt As PointF, ByRef OnPt As PointF) As Single
        'Task:  Given a line(sPt, ePt) and a point(Pt)
        '       Determine the closest distance between the line and the point.
    
        '       Although the line is defined by a pair of x,y points, this function makes no
        '       such distinction. The line is assumed infinite in length.
    
        'Step 1 Check if the line is vertical or horizontal.  Solve if true.
        'Step 2 Determine a line perpendicular to the existing line and through the point.
        'Step 3 Determine the intersection point of both lines.
    
        Dim Sab, Scd, Iab, Icd, XDist, YDist, Dist As Single
    
        If IsEqual(sPt.X, ePt.X) Then
          Dist = Math.Abs(sPt.X - Pt.X) 'Line is vertical
          OnPt.X = sPt.X 'Line is vertical
          OnPt.Y = Pt.Y
        ElseIf IsEqual(sPt.Y, ePt.Y) Then
          Dist = Math.Abs(sPt.Y - Pt.Y) 'Line is horizontal
          OnPt.X = Pt.X 'Line is horizontal
          OnPt.Y = sPt.Y
        Else
          Sab = (sPt.Y - ePt.Y) / (sPt.X - ePt.X)  'Slope of the existing line
          Iab = sPt.Y - (Sab * sPt.X)        'Y intercept of the existing line
    
          Scd = -(1 / Sab)             'Slope of the calculated the line (Inverse of existing.)
          Icd = Pt.Y - (Scd * Pt.X)          'Y intercept of calculated line
    
          OnPt.X = CSng((Icd - Iab) / (Sab - Scd)) 'Calculate Intersection point of both lines.
          OnPt.Y = CSng((Sab * OnPt.X) + Iab)
    
          XDist = Math.Abs(OnPt.X - Pt.X)            'Calculate X and Y Spacing
          YDist = Math.Abs(OnPt.Y - Pt.Y)
    
          'Find the true distance (Hypotnuse of Xdist and Ydist).
          Dist = CSng(Hypotnuse(XDist, YDist)) 'Hypotnuse of Xdist and Ydist.
        End If
    
        Return Dist
      End Function
    Last edited by Gruff; Jul 26th, 2014 at 03:04 PM.
    Burn the land and boil the sea
    You can't take the sky from me


    ~T

  29. #29
    Fanatic Member
    Join Date
    Sep 2002
    Posts
    527

    Re: Very Simple Drawing Program

    Quote Originally Posted by Gruff View Post
    In short this is typically not a trivial thing to do.
    I couldn't agree more!

    I was working on such an issue years ago, and didn't find a viable solution until I put this aside (it was just for fun, outside of my work). I tried the implement the proceedure you described, and wasn't able to do so it worked efficiently.

    I had a conversation with a coworker (in a past job) who implemented such a thing. Without getting into details, he explained that every object on a drawing had it's phantom collection of small rectangles that were overlapping every drawings (which were all extensions of the polyline object). Then when a user clicked somewhere, the process had to check if he cliked within a rectangle, and if so then select the associated drawing object. If I remember well, an important part of the process was to store the coordinates of the recangles in some sort of hash table (or something), in order to avoid evaluating each and every rectangle to see if the user clicked in it. But since we didn't get into details (which I regret we didn't today!), I'm stuck reinventing the well (and asking you guys if you wish to play with me at the same time! )

    Here's an small drawing (done with MS Paint, so bare with me...) illustrating the phantom rectangle thing.

    Name:  Vectorial Drawings.jpg
Views: 7735
Size:  10.6 KB

    The downside about this method is that you have to recode every GDI drawing function, in order to use the polyline instead to make lines, rectangles, circles and stuff...


    EDIT: Thanks for your code Gruff. I was typing as you edited your post. I'll check it out.
    Don't ask why, just reboot!

  30. #30
    Frenzied Member Gruff's Avatar
    Join Date
    Jan 2014
    Location
    Scappoose Oregon USA
    Posts
    1,293

    Re: Very Simple Drawing Program

    Circles are actually easier to detect than lines.
    You derive the Distance of the cursor location to the circle center point.
    Code:
      Dist = Hypotnuse(DeltaX, DeltaY)
      Gap = ABS(Dist-Radius)
      if Gap <= SelectionTol then
        'Add entity to selection list here
      End If
    Burn the land and boil the sea
    You can't take the sky from me


    ~T

  31. #31
    PowerPoster boops boops's Avatar
    Join Date
    Nov 2008
    Location
    Holland/France
    Posts
    3,201

    Re: Very Simple Drawing Program

    Hi Alain,

    to do hit testing on a graphic line, use a Drawing2D.GraphicsPath. In other words, instead of drawing the rectangle, arc etc. with commands like Graphics.DrawArc, do something like this:
    Code:
    'delcare the GraphicsPath object:
    Private gp As New Drawing2D.GraphicsPath
    
    'build the path:
    gp.AddArc(ellipseRectangle, startAngle, sweepAngle)
    
    'draw the path e.g. in the Paint event handler:
    e.Graphics.DrawPath(pens.Black, gp)
    The point of this is that you can use the GraphicsPath.IsVisible(point) and GraphicsPath.IsOutlineVisible(point, pen) methods for hit testing. IsVisible tests if the point is contained in a closed path e.g. an ellipse. IsOutlineVisible tests if the point is approximately on the line. The pen argument is used to specify the thickness of the hit testing area along the line, plus some other details; the pen colour is irrelevant).

    The advantage of these methods is that they work for any kind of drawing. Anything you can draw in a Graphcs statement (except DrawImage) you can also add to a GraphicsPath. So you can do your hit testing in a few statements on lines, polylines, rectangles, ellipses, arcs, Bézier curves, cubic splines, typographic text and a few other things too; plus any combination of these into a single figure. Try doing that with mathematical formulas.

    The hit testing probably boils down to something like the tiny rectangles you illustrated or formulas like Gruff's at a lower level. But there is no need to do it yourself.

    By the way, all this is going way of topic from the Very Simple Drawing Program JMcIlhinney offered to help beginners. If you have further questions, it would be better to post them either to the VB.Net Forum or the Games and Graphics forum.

    BB

  32. #32
    Fanatic Member
    Join Date
    Sep 2002
    Posts
    527

    Re: Very Simple Drawing Program

    Hi Boops,

    Thanks a lot for letting me know about the GraphicsPath class (or namespace, I'm not sure which word to use, still learning .NET.). This is obviously the way to go to get the result I'm after.

    And yes, you're right, my question was making a 'very simple application' quite a bit more complex. Thus I should have asked the question in the standard VB.NET forum. Will do for any further inquiery about this.

    And sorry for not getting back earlier, I'm on vacation!

    Thanks again, and have a great day!
    Don't ask why, just reboot!

  33. #33
    New Member
    Join Date
    Dec 2022
    Posts
    8

    Red face Re: Very Simple Drawing Program

    Quote Originally Posted by jmcilhinney View Post
    C# version here, courtesy of ForumAccount.

    The following code can be used as the basis for a simple drawing program. It assumes that you have a PictureBox on your form named PictureBox1. The Save and Clear methods can be called from wherever you like. The most likely candidates would be the Click event handlers of either Buttons or menu items. I wrote this in VB 2005. For earlier versions you'd have to use an ArrayList instead of a List(Of Line) or else derive your own LineCollection class from CollectionBase.
    VB.NET Code:
    1. Public Class Form1
    2.  
    3.     'The lines that have been drawn but not saved.
    4.     Private lines As New List(Of Line)
    5.  
    6.     'The start point of the line currently being drawn.
    7.     Private start As Point
    8.  
    9.     Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    10.         'Place a blank image in the PictureBox control.
    11.         Me.PictureBox1.Image = New Bitmap(Me.PictureBox1.Width, Me.PictureBox1.Height)
    12.     End Sub
    13.  
    14.     Private Sub PictureBox1_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDown
    15.         'Remember the point at which the line started.
    16.         Me.start = e.Location
    17.     End Sub
    18.  
    19.     Private Sub PictureBox1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseUp
    20.         'Remember the point at which the line ended.
    21.         Dim [end] As Point = e.Location
    22.  
    23.         'Add the new line to the list.
    24.         Me.lines.Add(New Line(Me.start, [end]))
    25.  
    26.         Dim area As New Rectangle(Math.Min(Me.start.X, [end].X), _
    27.                                   Math.Min(Me.start.Y, [end].Y), _
    28.                                   Math.Abs(Me.start.X - [end].X), _
    29.                                   Math.Abs(Me.start.Y - [end].Y))
    30.  
    31.         'Inflate the rectangle by 1 pixel in each direction to ensure every changed pixel will be redrawn.
    32.         area.Inflate(1, 1)
    33.  
    34.         'Force the control to repaint so the new line is drawn.
    35.         Me.PictureBox1.Invalidate(area)
    36.         Me.PictureBox1.Update()
    37.     End Sub
    38.  
    39.     Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
    40.         'Draw each line on the control.
    41.         Me.DrawLines(e.Graphics)
    42.     End Sub
    43.  
    44.     Private Sub Save()
    45.         'Create a Graphics object from the Image in the PictureBox.
    46.         Using g As Graphics = Graphics.FromImage(Me.PictureBox1.Image)
    47.             'Draw each line on the image to make them permanent.
    48.             Me.DrawLines(g)
    49.         End Using
    50.  
    51.         'Clear the temporary lines that were just saved.
    52.         Me.Clear()
    53.     End Sub
    54.  
    55.     Private Sub Clear()
    56.         'Clear all unsaved lines.
    57.         Me.lines.Clear()
    58.  
    59.         'Force the control to repaint so the lines are removed.
    60.         Me.PictureBox1.Refresh()
    61.     End Sub
    62.  
    63.     Private Sub DrawLines(ByVal g As Graphics)
    64.         For Each line As Line In Me.lines
    65.             g.DrawLine(Pens.Black, line.Start, line.End)
    66.         Next line
    67.     End Sub
    68.  
    69. End Class
    70.  
    71. Public Class Line
    72.  
    73.     'The line's start point.
    74.     Private _start As Point
    75.  
    76.     'The line's end point.
    77.     Private _end As Point
    78.  
    79.     'The line's start point.
    80.     Public Property Start() As Point
    81.         Get
    82.             Return Me._start
    83.         End Get
    84.         Set(ByVal value As Point)
    85.             Me._start = value
    86.         End Set
    87.     End Property
    88.  
    89.     'The line's end point.
    90.     Public Property [End]() As Point
    91.         Get
    92.             Return Me._end
    93.         End Get
    94.         Set(ByVal value As Point)
    95.             Me._end = value
    96.         End Set
    97.     End Property
    98.  
    99.     Public Sub New()
    100.         Me.New(Point.Empty, Point.Empty)
    101.     End Sub
    102.  
    103.     Public Sub New(ByVal start As Point, ByVal [end] As Point)
    104.         Me._start = start
    105.         Me._end = [end]
    106.     End Sub
    107.  
    108. End Class
    I strongly recommend that you now go down and read post #17 for some explanation.
    Finally I found this! I have a question about save image in picturebox.
    I loaded an image in picturebox and drew afew lines on picturebox. now I want to save the image and drawn lines in a path.
    if use this code:
    Code:
    picturebox1.image.save(PATH, Imaging.ImageFormat)
    only saves the image that loaded in picturebox without drawn lines

    Please guide me and please don't say "check out my thread on Saving Images To Databases"! Because I read all of that but didn't get answer. thnx

  34. #34

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,299

    Re: Very Simple Drawing Program

    @user20, my example demonstrates what you need to do. What part do you not understand? Have you actually tested this code in the debugger to see how it works, or are you assuming that it should just work without any effort on your part. You need to make an effort here. I wrote this code to demonstrate the principle so that people like you could see, understand it and then apply it themselves. I see no evidence that you've tried to understand it. If you have a specific question about my code, I'm happy to answer it. If you're just going to ignore the example and expect me to write your code for you, you'll be disappointed.

  35. #35
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: Very Simple Drawing Program

    Quote Originally Posted by user20 View Post
    if use this code:
    Code:
    picturebox1.image.save(PATH, Imaging.ImageFormat)
    only saves the image that loaded in picturebox without drawn lines
    What you are looking for is literally in the code you are quoting in the Save procedure

    Code:
        Private Sub Save()
            'Create a Graphics object from the Image in the PictureBox.
            Using g As Graphics = Graphics.FromImage(Me.PictureBox1.Image)
                'Draw each line on the image to make them permanent.
                Me.DrawLines(g)
            End Using
     
            'Clear the temporary lines that were just saved.
            Me.Clear()
        End Sub
    Call this procedure to "save" the lines buffered up till now inside the PictureBox1.Image before saving this PictureBox1.Image in a file using your (confusingly named) PATH variable the way you do.

    cheers,
    </wqw>

  36. #36
    New Member
    Join Date
    Dec 2022
    Posts
    8

    Re: Very Simple Drawing Program

    Thanks guys for the help
    I add two button in form for zoom in and zoom out.
    Code:
       Private Sub Button_zoom_out_Click(sender As Object, e As EventArgs) Handles Button2.Click
    
            PictureBox1.Width += Convert.ToInt32(PictureBox1.Width * -50 / 1000)
            PictureBox1.Height += Convert.ToInt32(PictureBox1.Height * -50 / 1000)
            PictureBox1.SizeMode = PictureBoxSizeMode.StretchImage
    
            For Each line As Line In lines
                line.Start = New Point(Convert.ToInt32(line.Start.X * (1 - 50 / 1000)), Convert.ToInt32(line.Start.Y * (1 - 50 / 1000)))
                line.End = New Point(Convert.ToInt32(line.End.X * (1 - 50 / 1000)), Convert.ToInt32(line.End.Y * (1 - 50 / 1000)))
            Next
        End Sub
    
        Private Sub Button_zoom_in_Click(sender As Object, e As EventArgs) Handles Button2.Click
    
            PictureBox1.Width += Convert.ToInt32(PictureBox1.Width * 50 / 1000)
            PictureBox1.Height += Convert.ToInt32(PictureBox1.Height * 50 / 1000)
            PictureBox1.SizeMode = PictureBoxSizeMode.StretchImage
    
            For Each line As Line In lines
                line.Start = New Point(Math.Round(line.Start.X * (1 + 50 / 1000)), Convert.ToInt32(line.Start.Y * (1 + 50 / 1000)))
                line.End = New Point(Convert.ToInt32(line.End.X * (1 + 50 / 1000)), Convert.ToInt32(line.End.Y * (1 + 50 / 1000)))
            Next
        End Sub
    After zoom in or zoom out the image, if we save it, the lines will be moved. picturebox.sizemode is AutoSize .
    Attachment 186416

  37. #37

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,299

    Re: Very Simple Drawing Program

    Quote Originally Posted by user20 View Post
    picturebox.sizemode is AutoSize .
    Except it's not, because you're changing it to StretchImage in the code you just posted. If the Image is not displayed with its original size or aspect ratio or its origin is not coincident with the control then you can't just do the same drawing on both and expect it to look the same. You would need to work out the maths required to translate from one to the other.

  38. #38
    New Member
    Join Date
    Dec 2022
    Posts
    8

    Re: Very Simple Drawing Program

    Quote Originally Posted by jmcilhinney View Post
    Except it's not, because you're changing it to StretchImage in the code you just posted. If the Image is not displayed with its original size or aspect ratio or its origin is not coincident with the control then you can't just do the same drawing on both and expect it to look the same. You would need to work out the maths required to translate from one to the other.
    I change Sizemode of picturebox to StretchImage But the problem was not solved. Please If my codes are not correct , edit them for me.

  39. #39

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,299

    Re: Very Simple Drawing Program

    Quote Originally Posted by user20 View Post
    I change Sizemode of picturebox to StretchImage But the problem was not solved.
    Why would that solve the problem when I just told you that it's the cause?
    Quote Originally Posted by user20 View Post
    Please If my codes are not correct , edit them for me.
    No. Code doesn't just appear out of thin air. You work out the logic first, then you write code to implement the logic. If you could specify what the logic was and then specify exactly where you're having trouble writing code to implement it then I might provide that code. You're e4xpecting others to work out the logic to implement and the code to implement it though. I'm not here to do that. I've told you what you need to do: work out the maths needed to translate from one coordinate system, i.e. the PictureBox, to another coordinate system, i.e. the Image. That's not a programming problem so you don't need to have programming experience to solve it. You just need a basic understanding of maths and the will to make the effort. Until I see that effort, i will not be contributing further.

  40. #40
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: Very Simple Drawing Program

    Quote Originally Posted by user20 View Post
    I change Sizemode of picturebox to StretchImage But the problem was not solved. Please If my codes are not correct , edit them for me.
    Your codes are completely wrong. You'll have to start over.

    First try to implement zooming in/out the image alone without the additional lines drawn upon it.

    Can you implement saving zoomed images based on some scale variable e.g. scale = 0.5 for half the size, scale = 2.0 for double the original size?

    cheers,
    </wqw>

Page 1 of 2 12 LastLast

Posting Permissions

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



Click Here to Expand Forum to Full Width