dcsimg
Results 1 to 18 of 18
  1. #1

    Thread Starter
    New Member
    Join Date
    Aug 2014
    Posts
    11

    Powerpacks shape control location data type

    Hi,

    I'm new to VB and I need help on drawing a rectangle on a picture box in runtime.

    Code:
    Public Sub plotPoles(ByVal x As PointF)
            Dim canvas As New ShapeContainer
            Dim pole As New RectangleShape
            canvas.Parent = picInput
            pole.Parent = canvas
            pole.Size = New System.Drawing.Size(5, 5)
            pole.BorderColor = Color.Red
            pole.FillStyle = FillStyle.DiagonalCross
            pole.Location = New System.Drawing.Point(x.X, y.Y)
            End Sub
    It seems that the shape control location function only accepts Point data type, however the coordinates on my picture box is of PointF data type. How do I draw by using PointF location?

    Thanks

  2. #2
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    9,329

    Re: Powerpacks shape control location data type

    I would suggest not using the RectangleShape and use GDI+ instead. Here is an example:
    Code:
    Public Sub plotPoles(ByVal x As PointF)
        'Create a new instance of a bitmap, this is what we'll draw on
        Dim b As Bitmap = New Bitmap(picInput.Width, picInput.Height)
    
        'Create a new instance of a graphics object, this is how we'll draw
        Using g As Graphics = Graphics.FromImage(b)
            'Create a new instance of a red pen(by default the width is 1)
            Using rPen As Pen = New Pen(Color.Red)
                'Draw a rectangle with the PointF parameter and with a size of (5, 5)
                g.DrawRectangle(rPen, x.X, x.Y, 5.0, 5.0)
            End Using 'Dispose of pen
    
            'Save the graphics
            g.Save()
        End Using 'Dispose of graphics
    
        'Set the image of the picturebox to the bitmap
        picInput.Image = b
    End Sub
    Edit - I'd also suggest renaming your X parameter to something a bit more meaningful, especially since X is a property of PointF. My suggestion would be to rename it to something like rectangleLocation or rectanglePoint.
    Last edited by dday9; Sep 10th, 2014 at 01:02 PM. Reason: Added comments

  3. #3

    Thread Starter
    New Member
    Join Date
    Aug 2014
    Posts
    11

    Re: Powerpacks shape control location data type

    Quote Originally Posted by dday9 View Post
    I would suggest not using the RectangleShape and use GDI+ instead. Here is an example:
    Code:
    Public Sub plotPoles(ByVal x As PointF)
        'Create a new instance of a bitmap, this is what we'll draw on
        Dim b As Bitmap = New Bitmap(picInput.Width, picInput.Height)
    
        'Create a new instance of a graphics object, this is how we'll draw
        Using g As Graphics = Graphics.FromImage(b)
            'Create a new instance of a red pen(by default the width is 1)
            Using rPen As Pen = New Pen(Color.Red)
                'Draw a rectangle with the PointF parameter and with a size of (5, 5)
                g.DrawRectangle(rPen, x.X, x.Y, 5.0, 5.0)
            End Using 'Dispose of pen
    
            'Save the graphics
            g.Save()
        End Using 'Dispose of graphics
    
        'Set the image of the picturebox to the bitmap
        picInput.Image = b
    End Sub
    Edit - I'd also suggest renaming your X parameter to something a bit more meaningful, especially since X is a property of PointF. My suggestion would be to rename it to something like rectangleLocation or rectanglePoint.
    Hi dday9,

    Thanks for your reply.

    The reason I use shape control instead of the GDI+ is because I want to be able to drag and move the object after placing it (and the object follows the cursor when dragging). From what I understand, this will be difficult to be implemented with GDI+ right? I dont know how to do it with GDI+, that's why I went to the direction of shape control.

    Maybe if you know can you give me a direction? Your help is much appreciated.

    Thank you

  4. #4
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    9,329

    Re: Powerpacks shape control location data type

    Yeah, I suppose that would be a bit more difficult because GDI+ shapes are not objects in windows form applications. Not impossible, just harder. If that's the case though, the location of the RectangleShape will always be a Point and not a PointF.

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

    Re: Powerpacks shape control location data type

    Just change that line.
    pole.Location = New System.Drawing.Point(x.X, y.Y)

    If you have Option Strict on, if you hover the mouse over the error in the code and then click on the error dropdown that appears it will usually suggest how to fix the problem, e.g.
    "Replace 'pf.x' with 'CInt(pf.x)'
    and that is usually a hot link that will apply the fix for you if you click on it.

  6. #6
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    9,329

    Re: Powerpacks shape control location data type

    Quote Originally Posted by passel View Post
    Just change that line.
    pole.Location = New System.Drawing.Point(x.X, y.Y)

    If you have Option Strict on, if you click on the dropdown on the error it will usually suggest how to fix the problem, e.g.
    "Replace 'pf.x' with 'CInt(pf.x)'
    and that is usually a hot link that will apply the fix for you if you click on it.
    That's assuming that the OP doesn't need the higher precision that the PointF offers, but considering that the OP has the parameters as a PointF, I'm assuming that the OP needs that higher precision. Otherwise just pass a point as the parameter because that's essentially what you're doing whenever you convert the PointF to a Point.

  7. #7
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    4,445

    Re: Powerpacks shape control location data type

    If he is using a shape for the rectangle, it doesn't matter how precise the PointF is, the shape can only be drawn at Integral intervals.
    The pointF comes into play when you're using matrix transforms, and I haven't used shape controls, but I would assume they are not subject to GDI+ transforms.

  8. #8
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    9,329

    Re: Powerpacks shape control location data type

    They aren't, that's why I suggested using GDI+ because otherwise you can't get the precision with a RectangleShape. That was point in post #4 and I was just reiterating it in post #6.

  9. #9
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    4,445

    Re: Powerpacks shape control location data type

    Quote Originally Posted by zqkhor View Post
    Hi dday9,

    Thanks for your reply.

    The reason I use shape control instead of the GDI+ is because I want to be able to drag and move the object after placing it (and the object follows the cursor when dragging). From what I understand, this will be difficult to be implemented with GDI+ right? I dont know how to do it with GDI+, that's why I went to the direction of shape control.

    Maybe if you know can you give me a direction? Your help is much appreciated.

    Thank you
    How many rectangles do you want to drag and draw. It isn't really that hard to do in GDI+. If you keep an array of rectangles that you want to use, you can check if the mouse location when you click is in one of those rectangles, and as you move the mouse, you just update the rectangle's location relative to the mouse location and call Invalidate to redraw (cause the paint event).
    I'll attach a project I wrote for someone as an example, but I don't see it already on this forum. It uses a rectangle to specify a portion of an image to be selected on one form and magnified on another form.
    You can drag the rectangle around by clicking anywhere with the left mouse button and dragging.
    You can resize the rectangle by clicking with the right mouse button in the rectangle and dragging.

    For a more involved example, where I drag multiple rectangles that are drawn with card images in them, then you can check out this post, but if you're new to VB, you may not want to wade into that pool just yet. Besides, it is code that was ported from VB3 (written around 1995), so isn't object oriented in the least, but is not a bad example of one way of drawing and dragging a bunch of images around without dragging controls around that hold the images.
    Attached Files Attached Files

  10. #10

    Thread Starter
    New Member
    Join Date
    Aug 2014
    Posts
    11

    Re: Powerpacks shape control location data type

    Quote Originally Posted by dday9 View Post
    That's assuming that the OP doesn't need the higher precision that the PointF offers, but considering that the OP has the parameters as a PointF, I'm assuming that the OP needs that higher precision. Otherwise just pass a point as the parameter because that's essentially what you're doing whenever you convert the PointF to a Point.
    Yes, I want the shape to be drawn at the precise location. I didn't know that the shape control is restricted to only Point parameter, that's why I posted this question hoping that there will be a solution, since shape controls will be easier to work with?(I suppose so)

    Quote Originally Posted by passel View Post
    How many rectangles do you want to drag and draw. It isn't really that hard to do in GDI+. If you keep an array of rectangles that you want to use, you can check if the mouse location when you click is in one of those rectangles, and as you move the mouse, you just update the rectangle's location relative to the mouse location and call Invalidate to redraw (cause the paint event).
    I'll attach a project I wrote for someone as an example, but I don't see it already on this forum. It uses a rectangle to specify a portion of an image to be selected on one form and magnified on another form.
    You can drag the rectangle around by clicking anywhere with the left mouse button and dragging.
    You can resize the rectangle by clicking with the right mouse button in the rectangle and dragging.

    For a more involved example, where I drag multiple rectangles that are drawn with card images in them, then you can check out this post, but if you're new to VB, you may not want to wade into that pool just yet. Besides, it is code that was ported from VB3 (written around 1995), so isn't object oriented in the least, but is not a bad example of one way of drawing and dragging a bunch of images around without dragging controls around that hold the images.
    It will be restricted to about 6 rectangles. I've already did like what you said, keeping all the locations of rectangle in arraylist. Right now I have this code in my picturebox paint event to load up the X and Y axis when the program starts:
    Code:
    Private Sub picInput_Paint(sender As Object, e As PaintEventArgs) Handles picInput.Paint
            Dim x, y As Single
    
            With e.Graphics
                .SmoothingMode = SmoothingMode.AntiAlias
                .ScaleTransform(scaleRatio, scaleRatio)
                .TranslateTransform(xTranslate, yTranslate)
                .DrawString("Im", New Font("Ariel", 0.3), Brushes.Black, 0.3, -4)
                .DrawString("Re", New Font("Ariel", 0.3), Brushes.Black, 5.5, -0.75)
    
                ' Draw axes.
                Using p As New Pen(Color.Gray, 0)
                    .DrawLine(p, -6, 0, 6, 0)
                    For x = -5 To 5 Step 1
                        .DrawLine(p, x, -0.1F, x, 0.1F)
                    Next x
    
                    .DrawLine(p, 0, -4, 0, 4)
                    For y = -3 To 3 Step 1
                        .DrawLine(p, -0.1F, y, 0.1F, y)
                    Next y
    
                    'Labels ticks
                    For x = -5 To -1 Step 1
                        .DrawString(x.ToString, New Font("Ariel", 0.2), Brushes.Black, x - 0.15, 0.3)
                    Next x
    
                    For x = 1 To 5 Step 1
                        .DrawString(x.ToString, New Font("Ariel", 0.2), Brushes.Black, x - 0.1, 0.3)
                    Next x
    
                    For y = -3 To -1 Step 1
                        .DrawString(y.ToString, New Font("Ariel", 0.2), Brushes.Black, -0.4, -y - 0.13)
                    Next y
    
                    For y = 1 To 3 Step 1
                        .DrawString(y.ToString, New Font("Ariel", 0.2), Brushes.Black, -0.4, -y - 0.13)
                    Next y
    
                End Using
    
            End With
        End Sub
    So when the user starts plotting on the picturebox, all the locations will be saved in arraylist. If the user drags/moves a particular rectangle, I want the other rectangles to remain intact. Can I set a certain area (border) to Invalidate? Or should I Invalidate the picturebox and then redraw the other rectangles (using the locations stored in arraylist) on every MouseMove? I'm not sure whether that would cause the animation to appear laggish.

    Anyway thanks for posting your example project, maybe the answer I'm looking for is in there, I haven't have time to look at it yet though. I will be busy in the next few days so I will come back again soon. Thanks for the help guys, you guys are really helpful for a newbie like me

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

    Re: Powerpacks shape control location data type

    Quote Originally Posted by zqkhor View Post
    Yes, I want the shape to be drawn at the precise location. I didn't know that the shape control is restricted to only Point parameter, that's why I posted this question hoping that there will be a solution, since shape controls will be easier to work with?(I suppose so)
    Graphics.DrawRectangle and FillReectangle are a bit of an oddity because practically all the other graphics methods accept floating point arguments. There is a simple solution to this if you want to retain the accuracy. The Graphics.DrawRectangles method (with an s) does accept RectangleFs, so you could use it to draw a single RectangleF like this:
    Code:
    e.Graphics.DrawRectangles(Pens.Blue, {myRectangleF})
    The curly braces turn the single RectangleF into a one-element array of RectangleFs, just to keep the syntax happy. Since you have six rectangles to draw you might find it convenient to use DrawRectangles for the collection anyway.


    Quote Originally Posted by zqkhor View Post
    So when the user starts plotting on the picturebox, all the locations will be saved in arraylist. If the user drags/moves a particular rectangle, I want the other rectangles to remain intact. Can I set a certain area (border) to Invalidate? Or should I Invalidate the picturebox and then redraw the other rectangles (using the locations stored in arraylist) on every MouseMove? I'm not sure whether that would cause the animation to appear laggish.
    In MouseMove, it's best to use Invalidate(oldRectangle) to cause repainting of the position before the move, and then Invalidate(newRectangle) to cause repainting in the new position. This will be more efficient than repainting the whole control display each time. Sometimes the results are improved by using Rectangle.Inflate to enlarge the invalidated rectangles by a few pixels.

    You could try adding Update after the Invalidate statements, which causes immediate repainting of the invalidated areas (rather like Refresh, which forces immediate repainting of the whole control) instead of putting them in a queue . But don't do this unless it really shows an improvement, because like Refresh, Update could itself cause lag when dragging an image. It forces repainting every time the MouseMove event fires, which can be more often than necessary to allow a smooth movement.

    By the way, if you are concerned about performance you should avoid using the rather outdated ArrayList collection and use a generic collection like List(Of Rectangle). The generics are more efficient (because they don't require conversion from Object to a specific Type) and they have a much more useful range of methods.

    BB

  12. #12
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    4,445

    Re: Powerpacks shape control location data type

    All great advice from BB.
    However, I had already added some code around your paint event to serve as a new example, and didn't implement any of those suggestions in the example as it stands.
    I did in the "this post" link (last paragraph in post #9) of the card game, since there were a larger number of rectangles with images being drawn, invalidate an area around where the card was and where the card moved to, in the rapid animation portion of the code to restrict how much of the screen was redrawn.
    But in this example since there are only a few rectangles, I didn't bother.
    Also, as BB pointed out, you can't pass a RectangleF to DrawRectangle, but you can pass four singles defining X,Y,Width and Height, which is what I did.
    You could pass the array to FillRectangles and DrawRectangles, but if you might want to have different color rectangles, then you will probably have to draw them in a loop so you can change brushes and pens.

    I did change the paint code you provided since you are creating new Fonts many, many times, in your loops, and not disposing of any of them. Since you only use two fonts (at this point), my preference would be to create them once outside the paint event and reuse them, but in this example, just changed it to Create the font with Using, use it how ever many times necessary, and then dispose of it (End Using) within the paint event.

    Just for the purpose of example, went ahead and added a button to define a 1x1 rectangle (rectangleF) at the origin of your graph, from a fixed array of seven rectangles.
    You can drag the rectangle around with the left mouse button, and resize it by dragging on it with the right mouse button.
    If you drag on the graph area (not on a rectangle), then the graph is moved around (left mouse), or resized (right mouse).

    I used a function to convert mouse coordinates into graph coordinates, but you could also use a copy of the graphics matrix (or an inverse version of the matrix) used to do the drawing as another way to convert between the two scales, but that is usually more useful when you have an array of points to convert between the two coordinate systems.
    I'm attaching the project, but also will post the modified code here in case someone wants a quick look at the code but not bother downloading the project.
    Code:
    Imports System.Drawing.Drawing2D
    
    Public Class Form1
      Private scaleRatio As Single = 1.0
      Private xTranslate, yTranslate As Single
      Private rects(6) As RectangleF
      Private numOfRects As Integer
      Dim fillBrush As New SolidBrush(Color.FromArgb(32, 0, 0, 0)) 'mostly transparent black
      Dim activeRectangle As Integer
    
      Private Sub picInput_MouseDown(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles picInput.MouseDown
        Dim locationf As PointF = devToPicPoint(e.Location)
        activeRectangle = -1
        For i = rects.Count - 1 To 0 Step -1   'we draw in forward order (bottom to top), so check hit in reverse order (top to bottom) 
          If rects(i).Contains(locationf) Then
            activeRectangle = i                'Found the topmost rectangle, so 
            Exit For                           'exit the search
          End If
        Next
      End Sub
    
      Private Function devToPicPoint(pin As Point) As PointF
        'Convert a point to a pointf, for instance, converting from Picturebox mouse coordinates, to Graph's scale coordinates
        Dim x As Single = pin.X 'change incoming integer values to single
        Dim y As Single = pin.Y
        x /= scaleRatio         'adjust for the current scaling
        y /= scaleRatio
        x -= xTranslate         'offset by the current translation
        y -= yTranslate
        Return New PointF(x, y) 'return a PointF
      End Function
    
      Private Sub picInput_MouseMove(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles picInput.MouseMove
        Static lastLocationF As PointF 'track the last point in graph coordinates (for moving and drawing)
        Static lastELoc As Point       'track the last point in picturebox coordinates (for changing scale)
    
        Dim locationf As PointF = devToPicPoint(e.Location)  'translate the mouse position into graph coordinates
    
        If e.Button = Windows.Forms.MouseButtons.Left Then             'If dragging with left mouse button
          If activeRectangle >= 0 Then                                   'If we clicked on a rectangle
            rects(activeRectangle).X += (locationf.X - lastLocationF.X)  '  move the rectangle
            rects(activeRectangle).Y += (locationf.Y - lastLocationF.Y)
            picInput.Invalidate()
            lastLocationF = locationf
          Else                                                            'click is on the graph, not a rectangle
            xTranslate = xTranslate + (locationf.X - lastLocationF.X)      'move the graph
            yTranslate = yTranslate + (locationf.Y - lastLocationF.Y)
            picInput.Invalidate()
          End If
    
        ElseIf e.Button = Windows.Forms.MouseButtons.Right Then          'If dragging with Right mouse Button
          If activeRectangle >= 0 Then                                   '  If dragging on a rectangle
            'don't allow rectangle size smaller than 0.1
            rects(activeRectangle).Width = Math.Max(rects(activeRectangle).Width + (locationf.X - lastLocationF.X), 0.1!)
            rects(activeRectangle).Height = Math.Max(rects(activeRectangle).Height + (locationf.Y - lastLocationF.Y), 0.1!)
            picInput.Invalidate()
            lastLocationF = locationf
          Else                                                          'click is on the graph, not a rectangle
            'let's change the scaling of the graph when dragging left and right
            If lastLocationF <> locationf Then         'if the mouse has moved in X
              Dim currentRatio As Single = scaleRatio  '  Save this so we can "Zoom" around the translated center
              scaleRatio *= CSng((e.X / lastELoc.X))     'Multiple by the ratio (mouse position / last mouse position)
              xTranslate *= (currentRatio / scaleRatio)  'adjust the translated center to
              yTranslate *= (currentRatio / scaleRatio)  'maintain it at the same display location when we scale
              picInput.Invalidate()
              lastELoc = e.Location
            End If
          End If
        Else
          lastLocationF = locationf  'for moving object in the scale
          lastELoc = e.Location      'for changing scale (so matches mouse movement)
        End If
      End Sub
    
      Private Sub picInput_Paint(sender As Object, e As PaintEventArgs) Handles picInput.Paint
        Dim x, y As Single
    
        With e.Graphics
    
          .SmoothingMode = SmoothingMode.AntiAlias
          .ScaleTransform(scaleRatio, scaleRatio)
          .TranslateTransform(xTranslate, yTranslate)
          Using fnt As New Font("Ariel", 0.3)
            .DrawString("Im", fnt, Brushes.Black, 0.3, -4)
            .DrawString("Re", fnt, Brushes.Black, 5.5, -0.75)
          End Using
          ' Draw axes.
          Using p As New Pen(Color.Gray, 0)
            .DrawLine(p, -6, 0, 6, 0)
            For x = -5 To 5 Step 1
              .DrawLine(p, x, -0.1F, x, 0.1F)
            Next x
    
            .DrawLine(p, 0, -4, 0, 4)
            For y = -3 To 3 Step 1
              .DrawLine(p, -0.1F, y, 0.1F, y)
            Next y
    
            'Labels ticks
            Using fnt As New Font("Ariel", 0.2)
              For x = -5 To -1 Step 1
                .DrawString(x.ToString, fnt, Brushes.Black, x - 0.15!, 0.3!)
              Next x
    
              For x = 1 To 5 Step 1
                .DrawString(x.ToString, fnt, Brushes.Black, x - 0.1!, 0.3!)
              Next x
    
              For y = -3 To -1 Step 1
                .DrawString(y.ToString, fnt, Brushes.Black, -0.4!, -y - 0.13!)
              Next y
    
              For y = 1 To 3 Step 1
                .DrawString(y.ToString, fnt, Brushes.Black, -0.4!, -y - 0.13!)
              Next y
            End Using
    
            For i As Integer = 0 To numOfRects - 1
              .FillRectangle(fillBrush, rects(i))
              With rects(i)
                e.Graphics.DrawRectangle(p, .X, .Y, .Width, .Height)
              End With
            Next
          End Using
        End With
      End Sub
    
      Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        scaleRatio = 32.0!
        xTranslate = (picInput.ClientSize.Width / 2.0!) / scaleRatio
        yTranslate = (picInput.ClientSize.Height / 2.0!) / scaleRatio
      End Sub
    
      Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        If numOfRects < rects.Count Then
          numOfRects += 1
          rects(numOfRects - 1) = New RectangleF(0.0!, 0.0!, 1.0!, 1.0!)
          picInput.Invalidate()
        End If
      End Sub
    End Class
    p.s. Looking back at the code, "New RectangleF(0.0!, 0.0!, 1.0!, 1.0!)", looks like I'm really excited about my numbers. I have Option Strict on so have to designate the literals as Singles, as opposed to the default Doubles. But using "!" is actually old school BASIC, and you can look less excited by doing a global search and replace to replace the "!" with "F", so the snippet should look like "New RectangleF(0.0F, 0.0F, 1.0F, 1.0F)", which would be more recognizable to others familiar with other .Net languages.
    Attached Images Attached Images  
    Attached Files Attached Files
    Last edited by passel; Sep 12th, 2014 at 08:23 PM.

  13. #13

    Thread Starter
    New Member
    Join Date
    Aug 2014
    Posts
    11

    Re: Powerpacks shape control location data type

    Hey sorry for the late reply, I have been busy recently. Thank you for spending time and putting in effort to help me again, I really appreciate that!

    Actually my project is suppose to plot diagonal crosses ("X") and circles ("O") on the graph (not rectangles, I tried to use rectangle cos there's this diagonal cross shape style in the shape controls,now I've switched back to graphics). I used your example as a reference and did some changes to my code, but the drag and move feature is not working completely (it is able to redraw at new position at MouseUp event, but it does not follow my mouse during MouseMove event).

    Are you able to tell what is wrong with my code?
    Code:
    Imports System.Drawing.Drawing2D
    
    Public Class Form1
        Public picCursor, oldPos As PointF
        Public scaleRatio As Integer = 35
        Public xTranslate As Integer = 7, yTranslate As Integer = 5
        Public selectedIndex As Integer
        Public complex As Boolean = False, poleSelected As Boolean = False, zeroSelected As Boolean = False, above As Boolean = False, below As Boolean = False
        Public polesArrayX, polesArrayY, zerosArrayX, zerosArrayY, polesStatus, zerosStatus As New ArrayList
        
        Private Sub picInput_Paint(sender As Object, e As PaintEventArgs) Handles picInput.Paint
            Dim x, y As Single
            Dim i As Integer = 0, j As Integer = 0
    
            With e.Graphics
                .SmoothingMode = SmoothingMode.AntiAlias
                .ScaleTransform(scaleRatio, scaleRatio)
                .TranslateTransform(xTranslate, yTranslate)
    
                ' Draw axes.
                Using p As New Pen(Color.Gray, 0)
                    .DrawLine(p, -6, 0, 6, 0)
                    For x = -5 To 5 Step 1
                        .DrawLine(p, x, -0.1F, x, 0.1F)
                    Next x
    
                    .DrawLine(p, 0, -4, 0, 4)
                    For y = -3 To 3 Step 1
                        .DrawLine(p, -0.1F, y, 0.1F, y)
                    Next y
                End Using
    
                'Labels ticks
                Using fnt As New Font("Ariel", 0.2)
                    For x = -5 To -1 Step 1
                        .DrawString(x.ToString, fnt, Brushes.Black, x - 0.15!, 0.3!)
                    Next x
    
                    For x = 1 To 5 Step 1
                        .DrawString(x.ToString, fnt, Brushes.Black, x - 0.1!, 0.3!)
                    Next x
    
                    For y = -3 To -1 Step 1
                        .DrawString(y.ToString, fnt, Brushes.Black, -0.4!, -y - 0.13!)
                    Next y
    
                    For y = 1 To 3 Step 1
                        .DrawString(y.ToString, fnt, Brushes.Black, -0.4!, -y - 0.13!)
                    Next y
                End Using
    
                'Labels axes
                Using fnt As New Font("Ariel", 0.3)
                    .DrawString("Im", fnt, Brushes.Black, 0.3, -4)
                    .DrawString("Re", fnt, Brushes.Black, 5.5, -0.75)
                End Using
            End With
            updatePlots()
        End Sub
    
        Private Sub picInput_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles picInput.MouseMove
            'Converts Picturebox mouse position to Graph coordinates
            picCursor.X = Math.Round(e.X / scaleRatio - xTranslate, 2, MidpointRounding.AwayFromZero)
            picCursor.Y = -Math.Round(e.Y / scaleRatio - yTranslate, 2, MidpointRounding.AwayFromZero)
    
            'Displays cursor coordinates
            lblXValue.Text = picCursor.X.ToString()
            lblYValue.Text = picCursor.Y.ToString()
    
            If e.Button = Windows.Forms.MouseButtons.Left Then
                Dim newPos As PointF = picCursor
                If poleSelected = True Then 'Checks whether pole "X" or zero "O" is selected
                    polesArrayX.Item(selectedIndex) += (newPos.X - oldPos.X)
                    polesArrayY.Item(selectedIndex) += (newPos.Y - oldPos.Y)
                    picInput.Invalidate()
                ElseIf zeroSelected = True Then
                    zerosArrayX.Item(selectedIndex) += (newPos.X - oldPos.X)
                    zerosArrayY.Item(selectedIndex) += (newPos.Y - oldPos.Y)
                    picInput.Invalidate()
                End If
            End If
        End Sub
      
     Private Sub picInput_MouseDown(sender As Object, e As MouseEventArgs) Handles picInput.MouseDown
                oldPos = picCursor 'Captures the original mouse position
                For i = polesArrayX.Count To 1 Step -1 'Performs poles hit test
                    If listPoles.Items.Contains(i & ". (" & picCursor.X & " , " & picCursor.Y & ")") Then
                        selectedIndex = i - 1
                        poleSelected = True
                        listPoles.SetSelected(selectedIndex, True) 'Highlights the selected poles on listbox
                    End If 
               Next
        End Sub
    
    
    Private Sub picInput_MouseUp(sender As Object, e As MouseEventArgs) Handles picInput.MouseUp
           If poleSelected = True Then
                 polesArrayX.Item(selectedIndex) = picCursor.X
                 polesArrayY.Item(selectedIndex) = picCursor.Y
                 poleSelected = False
          End If
        picInput.Refresh()
        updatePlots()
    End Sub
    
    Public Sub updatePlots()
            Dim i As Integer = 0, j As Integer = 0
    
            For Each pole As Decimal In polesArrayX
                DrawPoles(toPoint(pole, polesArrayY.Item(i)))
                i += 1
            Next
    
            For Each zero As Decimal In zerosArrayX
                DrawZeros(toPoint(zero, zerosArrayY.Item(j)))
                j += 1
            Next
        End Sub
    
    Public Sub DrawPoles(ByVal point As PointF)
            Dim p As New System.Drawing.Pen(Color.Red, 1)
            Dim g As System.Drawing.Graphics
            g = picInput.CreateGraphics
            g.DrawLine(p, point.X - 5, point.Y + 5, point.X + 5, point.Y - 5)
            g.DrawLine(p, point.X + 5, point.Y + 5, point.X - 5, point.Y - 5)
            g.Dispose()
        End Sub
    
    'Converts Graph coordinates to Picturebox position (for drawing purpose)
        Public Function toPoint(ByVal value As PointF) As PointF
            value.X = (value.X + xTranslate) * scaleRatio
            If complex = True Then
                value.Y = (value.Y + yTranslate) * scaleRatio
            Else
                value.Y = (-value.Y + yTranslate) * scaleRatio
            End If
            Return value
        End Function
    Well these codes are only part of the whole program which contributes to the drag and move feature (to make things simple). I have attached the complete codes just in case you need it to see clearer. I tried to use Invalidate instead of Refresh during MouseUp event but it didn't redraw the plot at new location (not sure why), so I use Refresh instead.plot.zipplot.zip

  14. #14
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    4,445

    Re: Powerpacks shape control location data type

    I can't try out the project in full at the moment, because I'll need to get to a machine with vb.net 2012 on it, since I don't want to take the time to try to manually back the 2012 project into a 2010 project, but this line jumps out.
    picCursor.Y = -Math.Round(e.Y / scaleRatio - yTranslate, 2, MidpointRounding.AwayFromZero)

    I don't think you want the - sign on the front of that expression.
    I know that your graph is showing that Y axis is positive in the Up direction, but your actually drawing coordinates are still Y positive going down.
    I thought about mentioning that before but didn't because I assumed since you were able to draw the tick marks and axis values correctly by negating the Y coordinate you understood what was going on.
    To draw something at a Y value of -2 in graph coordinates, you draw it at a Y value of 2 in GDI+ world coordinates.
    So, picCursor.Y should be in the GDI+ world coordinates.
    But, then further on where you display the coordinates
    lblYValue.Text = picCursor.Y.ToString()
    that is a place you would want to display in graph coordinates, so you would negate the Y there (not tested)
    lblYValue.Text = (-picCursor.Y).ToString()

    You could change the ScaleTransform to use a negative Y scaling to flip the Y coordinates, but the problem there is if you draw text, and other objects, they will be drawn inverted as well.
    It is usually easiest to keep the drawing in the default +,+ scaling, and just invert the Y values of your data when you plot and display coordinate values.

  15. #15
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    4,445

    Re: Powerpacks shape control location data type

    After looking at the whole code and running on another machine, the negation at that point may not be a problem.
    But you're not doing the drawing the way it should be done.
    All the drawing should be done in the paint event, using the graphics object passed (e.graphics). Pictureboxes are doubled buffered by default, and that graphics object will draw in the backing buffer, which will get flushed to the screen at the end of the paint event.
    You're creating a graphics object in your draw routine, and using that object will not draw in the backbuffer, but will draw directly to the screen area of the picturebox. When you finish the paint event, the flushing of the backbuffer to the screen will wipeout what you just drew, which is probably why you don't see the zeros and Xs when dragging in the picturebox.
    It's also a pain the way you're doing your hit testing. Having to select the exact pixel to select the symbol is not very friendly, but I'm assuming you want to fix the bigger issues fist, then look at providing a larger hit test target.
    Unfortuanately, I'm really backed up this week at work, so need to be focusing on that pretty much full time whenever I'm on the computer, so can play around with the code to try to make any suggested changes.

  16. #16

    Thread Starter
    New Member
    Join Date
    Aug 2014
    Posts
    11

    Re: Powerpacks shape control location data type

    Quote Originally Posted by passel View Post
    After looking at the whole code and running on another machine, the negation at that point may not be a problem.
    But you're not doing the drawing the way it should be done.
    All the drawing should be done in the paint event, using the graphics object passed (e.graphics). Pictureboxes are doubled buffered by default, and that graphics object will draw in the backing buffer, which will get flushed to the screen at the end of the paint event.
    You're creating a graphics object in your draw routine, and using that object will not draw in the backbuffer, but will draw directly to the screen area of the picturebox. When you finish the paint event, the flushing of the backbuffer to the screen will wipeout what you just drew, which is probably why you don't see the zeros and Xs when dragging in the picturebox.
    It's also a pain the way you're doing your hit testing. Having to select the exact pixel to select the symbol is not very friendly, but I'm assuming you want to fix the bigger issues fist, then look at providing a larger hit test target.
    Unfortuanately, I'm really backed up this week at work, so need to be focusing on that pretty much full time whenever I'm on the computer, so can play around with the code to try to make any suggested changes.
    Thank you for your suggestion once again.

    I do understand that all drawings not in the paint event will be wiped out when I invalidate/refresh the picturebox. That's why I added the updatePlots() function at the end of the paint event, to draw all the plots in the paint event. But unfortunately it isn't working now, I will try to research more on the backbuffering of picturebox in the next few days.

    Yes you're right, I will work on the hit testing issue that you've mentioned after getting this drag and move feature done.

  17. #17
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    4,445

    Re: Powerpacks shape control location data type

    It is important you draw at the right time, i.e. in the paint event, but it is also important where you draw, i.e. what graphics object you use.
    You added calls to the drawing in the paint event, but you are not using the e.Graphics graphics object in those drawing routines, so you are drawing to a different context (i.e. using a graphics object you created), and that drawing will be wiped out.
    It would be common to pass the Paint Event's graphics object to the drawing routine so that it can be used to draw in the same context.
    Code:
    '...
            updatePlots(e.Graphics)  'pass the Paint event's graphics object to the drawing routines
    '...
    
    Public Sub updatePlots(g as Graphics)
            Dim i As Integer = 0, j As Integer = 0
    
            For Each pole As Decimal In polesArrayX
                DrawPoles(g, toPoint(pole, polesArrayY.Item(i)))  'pass the graphics context on to the specific drawing routines
                i += 1
            Next
    
            For Each zero As Decimal In zerosArrayX
                DrawZeros(g, toPoint(zero, zerosArrayY.Item(j)))  'pass the graphics context on to the specific drawing routines
                j += 1
            Next
        End Sub
    
    Public Sub DrawPoles(ByVal g as Graphics, ByVal point As PointF)
            Dim p As New System.Drawing.Pen(Color.Red, 0)                     'Use 0 for size so the pen is always the minimum size, i.e. 1 pixel, regardless of scale.
    '        Dim g As System.Drawing.Graphics
    '        g = picInput.CreateGraphics
            g.DrawLine(p, point.X - 5, point.Y + 5, point.X + 5, point.Y - 5)  'need to adjust these 5's to fit the scale of your graph, 5/35 i.e. .142857.
            g.DrawLine(p, point.X + 5, point.Y + 5, point.X - 5, point.Y - 5)
    '        g.Dispose()             'Don't dispose the Paint event's graphics object
        End Sub
    
    'Converts Graph coordinates to Picturebox position (for drawing purpose)
        Public Function toPoint(ByVal value As PointF) As PointF
            value.X = (value.X + xTranslate)  ' * scaleRatio         'Don't need to multiply by scaleRatio since you are drawing in the graph's coordinate system
            If complex = True Then
                value.Y = (value.Y + yTranslate) ' * scaleRatio
            Else
                value.Y = (-value.Y + yTranslate)  ' * scaleRatio
            End If
            Return value
        End Function
    I didn't see the circle drawing in your code posted above, but I assume similar adjustment need to be done there.
    The other calls you may have elsewhere to the draw routines need to be commented out, and instead your arrays updated and the paint event triggered again (by calling invalidate or refresh) to draw the updates.

    I only modified the code visible above that you posted in post 13, so is not tested. I'm on the machine without vb 2012 again, so there may be errors in my post, but should give you the general idea of what needs to be done, your existing code.

  18. #18

    Thread Starter
    New Member
    Join Date
    Aug 2014
    Posts
    11

    Re: Powerpacks shape control location data type

    Quote Originally Posted by passel View Post
    It is important you draw at the right time, i.e. in the paint event, but it is also important where you draw, i.e. what graphics object you use.
    You added calls to the drawing in the paint event, but you are not using the e.Graphics graphics object in those drawing routines, so you are drawing to a different context (i.e. using a graphics object you created), and that drawing will be wiped out.
    It would be common to pass the Paint Event's graphics object to the drawing routine so that it can be used to draw in the same context.
    Code:
    '...
            updatePlots(e.Graphics)  'pass the Paint event's graphics object to the drawing routines
    '...
    
    Public Sub updatePlots(g as Graphics)
            Dim i As Integer = 0, j As Integer = 0
    
            For Each pole As Decimal In polesArrayX
                DrawPoles(g, toPoint(pole, polesArrayY.Item(i)))  'pass the graphics context on to the specific drawing routines
                i += 1
            Next
    
            For Each zero As Decimal In zerosArrayX
                DrawZeros(g, toPoint(zero, zerosArrayY.Item(j)))  'pass the graphics context on to the specific drawing routines
                j += 1
            Next
        End Sub
    
    Public Sub DrawPoles(ByVal g as Graphics, ByVal point As PointF)
            Dim p As New System.Drawing.Pen(Color.Red, 0)                     'Use 0 for size so the pen is always the minimum size, i.e. 1 pixel, regardless of scale.
    '        Dim g As System.Drawing.Graphics
    '        g = picInput.CreateGraphics
            g.DrawLine(p, point.X - 5, point.Y + 5, point.X + 5, point.Y - 5)  'need to adjust these 5's to fit the scale of your graph, 5/35 i.e. .142857.
            g.DrawLine(p, point.X + 5, point.Y + 5, point.X - 5, point.Y - 5)
    '        g.Dispose()             'Don't dispose the Paint event's graphics object
        End Sub
    
    'Converts Graph coordinates to Picturebox position (for drawing purpose)
        Public Function toPoint(ByVal value As PointF) As PointF
            value.X = (value.X + xTranslate)  ' * scaleRatio         'Don't need to multiply by scaleRatio since you are drawing in the graph's coordinate system
            If complex = True Then
                value.Y = (value.Y + yTranslate) ' * scaleRatio
            Else
                value.Y = (-value.Y + yTranslate)  ' * scaleRatio
            End If
            Return value
        End Function
    I didn't see the circle drawing in your code posted above, but I assume similar adjustment need to be done there.
    The other calls you may have elsewhere to the draw routines need to be commented out, and instead your arrays updated and the paint event triggered again (by calling invalidate or refresh) to draw the updates.

    I only modified the code visible above that you posted in post 13, so is not tested. I'm on the machine without vb 2012 again, so there may be errors in my post, but should give you the general idea of what needs to be done, your existing code.
    Tested and works like a charm! Thank you so much, problem resolved

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


×
We have made updates to our Privacy Policy to reflect the implementation of the General Data Protection Regulation.