Results 1 to 6 of 6

Thread: Plotting points and accounting for resize

  1. #1

    Thread Starter
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    11,753

    Plotting points and accounting for resize

    I'm trying to plot points on a hexagon where the numbers are drawn in the middle of the line of the hexagon, sort of like this:
    Name:  Hexagon.jpg
Views: 679
Size:  96.3 KB

    I'm using visual basic.net 2010 and using a Queue to store the list of points(as well as a separate queue to store the numbers) and right now I'm trying something like this:

    vb.net Code:
    1. pts.Clear()
    2.  
    3.         Dim upper_left As New Point(10, CInt(Me.Height / 2) - CInt(Me.Height / 10))
    4.         Dim upper_right As New Point(Me.Width - 10, CInt(Me.Height / 2) - CInt(Me.Height / 10))
    5.         Dim middle_left As New Point(5, CInt(Me.Height / 2))
    6.         Dim middle_right As New Point(Me.Width - 5, CInt(Me.Height / 2))
    7.         Dim lower_left As New Point(10, Me.Height - CInt(Me.Height / 2) + CInt(Me.Height / 10))
    8.         Dim lower_right As New Point(Me.Width - 10, Me.Height - CInt(Me.Height / 2) + CInt(Me.Height / 10))
    9.  
    10.         pts.Enqueue(upper_left)
    11.         pts.Enqueue(upper_right)
    12.         pts.Enqueue(middle_left)
    13.         pts.Enqueue(middle_right)
    14.         pts.Enqueue(lower_left)
    15.         pts.Enqueue(lower_right)

    My issue is that the numbers are plotted out all crazy. I know it's nothing to do with how my coding is, I'm almost positive I'm not doing the math right to plot the points. How would I set out the location of the numbers and deal with resizing? By the way this is how the numbers currently look plotted:
    Name:  hexagon.jpg
Views: 259
Size:  4.0 KB
    "Code is like humor. When you have to explain it, it is bad." - Cory House
    VbLessons | Code Tags | Sword of Fury - Jameram

  2. #2
    Only Slightly Obsessive jemidiah's Avatar
    Join Date
    Apr 2002
    Posts
    2,431

    Re: Plotting points and accounting for resize

    It's hard to say what's wrong when you haven't posted much of your actual code, especially since the problem seems to be the edge labels but you've shown no edge label code. Still, it's probably easiest to just use some basic trig rather than hand-tuning the constants to get points and numbers to appear where you want them to. Imagine your hexagon lies on a circle. Your label 3 lies at 0 degrees, 4 at 360/6 = 60 degrees, 5 at 120 degrees, etc. (The angle conventions I've used are consistent with trig in the VB.NET context where larger y-values are lower on screen.) Similarly your vertices lie at angles 30 degrees, 90 degrees, 150 degrees, etc.

    The basic trig is this: given a center point (x0, y0), a radius r, and an angle t, the point (x0+r*cos(t), y0+r*sin(t)) is at distance r away from (x0, y0) and at angle t measured using the conventions above. For instance, if the center of your hexagon were at (100, 100) on-screen and you used r=50, the bottom vertex of the hexagon would be at (100+50*cos(pi/2), 100+50*sin(pi/2)) = (100, 150) [here I've converted from 90 degrees to pi/2 radians; to convert from degrees to radians, multiply by 2*pi/360 == pi/180].

    Given that, I'd say the easiest way to figure out your vertex and label locations is to (i) pick a center point; (ii) pick a circle radius; (iii) use the formula above to find the six vertexes at angles pi/6, 3pi/6, 5pi/6, 7pi/6, 9pi/6, 11pi/6 and draw lines between consecutive ones; (iv) use the formula above to find the six label locations at angles 0, pi/3, 2pi/3, 3pi/3, 4pi/3, 5pi/3--I would also scale back my r value so the edge labels are inside the hexagon. You can do it all by hand but it's an error-prone mess that's hard to maintain, and there's this really simple solution anyway.

    P.S. If you want, you can use (x0+r1*cos(t), y0+r2*sin(t)) instead. This puts you on an ellipse rather than a circle, but it hardly matters. You just have to pick r1 and r2 instead of just r. This would be useful if you have to resize while not maintaining the original aspect ratio--you could, for instance, take (x0, y0) to be the center of your form, r1 = half the width of your form, r2 = half the height of your form. Tweak till you like how things look.
    The time you enjoy wasting is not wasted time.
    Bertrand Russell

    <- Remember to rate posts you find helpful.

  3. #3

    Thread Starter
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    11,753

    Re: Plotting points and accounting for resize

    Well see I don't use any labels or anything, it's all drawn. Here is the full class in case you wanted to see it:
    Code:
    Option Strict On
    Option Explicit On
    Public Class Hexagon
        Inherits Windows.Forms.Control
    
        Private r As New Random
    
        Private nums As New Queue(Of Integer)
        Public ReadOnly Property Numbers() As Queue(Of Integer)
            Get
                Return nums
            End Get
        End Property
    
        Private pts As New Queue(Of Point)
        Public ReadOnly Property NumberLocations() As Queue(Of Point)
            Get
                Return pts
            End Get
        End Property
    
        Private Sub SetLocation()
            pts.Clear()
    
            Dim upper_left As New Point(10, CInt(Me.Height / 2) - CInt(Me.Height / 10))
            Dim upper_right As New Point(Me.Width - 10, CInt(Me.Height / 2) - CInt(Me.Height / 10))
            Dim middle_left As New Point(5, CInt(Me.Height / 2))
            Dim middle_right As New Point(Me.Width - 5, CInt(Me.Height / 2))
            Dim lower_left As New Point(10, Me.Height - CInt(Me.Height / 2) + CInt(Me.Height / 10))
            Dim lower_right As New Point(Me.Width - 10, Me.Height - CInt(Me.Height / 2) + CInt(Me.Height / 10))
    
            pts.Enqueue(upper_left)
            pts.Enqueue(upper_right)
            pts.Enqueue(middle_right)
            pts.Enqueue(lower_right)
            pts.Enqueue(lower_left)
            pts.Enqueue(middle_left)
    
            Me.Refresh()
    
        End Sub
    
        Friend Sub RotateLeft()
            nums.Enqueue(nums.Dequeue)
            pts.Enqueue(pts.Dequeue)
    
            Call SetLocation()
        End Sub
    
        Friend Sub RotateRight()
            For i As Integer = 0 To nums.Count - 2
                nums.Enqueue(nums.Dequeue)
                pts.Enqueue(pts.Dequeue)
            Next
    
            Call SetLocation()
        End Sub
    
        Friend Sub Scramble()
            nums.Clear()
    
            Dim exclusive_numbers() As Integer = Enumerable.Range(0, 6).OrderBy(Function(n) r.Next(7)).ToArray
    
            For Each itm As Integer In exclusive_numbers
                nums.Enqueue(itm + 1)
            Next
    
            Call SetLocation()
    
        End Sub
    
        Protected Overrides Sub OnPaint(e As System.Windows.Forms.PaintEventArgs)
            MyBase.OnPaint(e)
    
            'The points for the hexagon
            Dim hex_pts() As Point = { _
                New Point(CInt(Me.Width / 2), 0), _
                New Point(Me.Width, CInt(Me.Height / 2) - CInt(Me.Height / 4)), _
                New Point(Me.Width, CInt(Me.Height / 2) + CInt(Me.Height / 4)), _
                New Point(CInt(Me.Width / 2), Me.Height), _
                New Point(0, CInt(Me.Height / 2) + CInt(Me.Height / 4)), _
                New Point(0, CInt(Me.Height / 2) - CInt(Me.Height / 4)) _
            }
    
            'Draw the hexagon with a darker boarder
            e.Graphics.DrawPolygon(New Pen(Brushes.Black, 3), hex_pts)
    
            'Setup the region
            Dim path As New Drawing2D.GraphicsPath
            path.AddPolygon(hex_pts)
            Me.Region = New Region(path)
    
    
            'Draw the numbers
            For i As Integer = 0 To nums.Count - 1
                e.Graphics.DrawString(nums(i).ToString, Me.Font, Brushes.Black, pts(i))
            Next
    
        End Sub
    
        Private Sub Hexagon_Resize(sender As Object, e As System.EventArgs) Handles Me.Resize
            Call SetLocation()
        End Sub
    
        Public Sub New()
            Call Scramble()
        End Sub
    End Class
    I like the idea of using an ellipse to plot out the points. I'm going to work something up and post it back here.
    "Code is like humor. When you have to explain it, it is bad." - Cory House
    VbLessons | Code Tags | Sword of Fury - Jameram

  4. #4

    Thread Starter
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    11,753

    Re: Plotting points and accounting for resize

    Well this is what I've come up with after taking your suggestion and it's almost right. This is how I've adjusted plotting the points:
    Code:
        Private Sub SetLocation()
            pts.Clear()
    
            'Dim upper_left As New Point(10, CInt(Me.Height / 2) - CInt(Me.Height / 10))
            'Dim upper_right As New Point(Me.Width - 10, CInt(Me.Height / 2) - CInt(Me.Height / 10))
            'Dim middle_left As New Point(5, CInt(Me.Height / 2))
            'Dim middle_right As New Point(Me.Width - 5, CInt(Me.Height / 2))
            'Dim lower_left As New Point(10, Me.Height - CInt(Me.Height / 2) + CInt(Me.Height / 10))
            'Dim lower_right As New Point(Me.Width - 10, Me.Height - CInt(Me.Height / 2) + CInt(Me.Height / 10))
    
            'pts.Enqueue(upper_left)
            'pts.Enqueue(upper_right)
            'pts.Enqueue(middle_right)
            'pts.Enqueue(lower_right)
            'pts.Enqueue(lower_left)
            'pts.Enqueue(middle_left)
    
            'Me.Refresh()
    
    
            Dim center As New Point(CInt(Me.Width / 2), CInt(Me.Height / 2))
            Dim radius As Double = Me.Width / 2 - 10
            Dim angles(5) As Integer
            Dim vertices(5) As Point
    
            For i As Integer = 0 To 5
                angles(i) = CInt(i * Math.PI / 3)
    
                vertices(i) = New Point(CInt(center.X + radius * Math.Cos(angles(i))), CInt(center.Y + radius * Math.Sin(angles(i))))
    
                pts.Enqueue(vertices(i))
            Next
    
            Me.Refresh()
    
    
        End Sub
    And this is how the hexagon looks at different sizes:
    Name:  sizes hexagon.png
Views: 338
Size:  7.2 KB
    "Code is like humor. When you have to explain it, it is bad." - Cory House
    VbLessons | Code Tags | Sword of Fury - Jameram

  5. #5
    Only Slightly Obsessive jemidiah's Avatar
    Join Date
    Apr 2002
    Posts
    2,431

    Re: Plotting points and accounting for resize

    Is there a reason you're using "angles(i) = CInt(i * Math.PI / 3)" instead of "angles(i) = i * Math.PI / 3" (with angles() a floating point array)? Decimals really matter for radian angles and shouldn't be cut off. It turns out that pi/3 = 1.047..., so apparently by blind luck this doesn't actually matter much for this exact case (and pretty much only this case).

    I'm a bit confused at your number drawing code. Is it still
    Code:
            'Draw the numbers
            For i As Integer = 0 To nums.Count - 1
                e.Graphics.DrawString(nums(i).ToString, Me.Font, Brushes.Black, pts(i))
            Next
    ?

    I can't reconcile this with the sample output. For what it's worth, I expected you to create a second array like pts but containing the locations of the numbers. (Oh, to be clear, "label" in my last post didn't necessarily mean a Label control. Using one seems needlessly overcomplicated here.) You would create this array in virtually the same way as pts, except the angles would be i * Math.PI /3 + Math.PI/6. Also, DrawString takes in the upper left corner of the desired output, whereas you want to give it the center. You should maybe look around to see if there's another method that can be given the center, or if you can make your own.
    The time you enjoy wasting is not wasted time.
    Bertrand Russell

    <- Remember to rate posts you find helpful.

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

    Re: Plotting points and accounting for resize

    Hi dday,
    You might be interested in a function I wrote to return a regular polygon as an array of pointFs, based on ellipse geometry:
    Code:
    Function RegularPolygon(ByVal bounds As RectangleF, ByVal rotation As Single, ByVal vertices As Integer) As PointF()
    	If vertices < 3 Then vertices = 3
    	Dim a As Single = bounds.Width / 2.0F 'major semi axis
    	Dim b As Single = bounds.Height / 2.0F 'minor semi axis
    	Dim x As Single = bounds.X + a 'centre x
    	Dim y As Single = bounds.Y + b 'centre y
    	Dim beta As Double = -rotation * (Math.PI / 180.0F)
    	Dim sinBeta As Double = Math.Sin(beta)
    	Dim cosBeta As Double = Math.Cos(beta)
    	Dim pts(vertices - 1) As PointF
    	For i As Integer = 0 To vertices - 1
    		Dim alpha As Double = i * Math.PI * 2.0F / vertices
    		Dim sinAlpha As Double = Math.Sin(alpha)
    		Dim cosAlpha As Double = Math.Cos(alpha)
    		pts(i).X = CSng(x + a * cosAlpha * cosBeta - b * sinAlpha * sinBeta)
    		pts(i).Y = CSng(y + a * cosAlpha * sinBeta + b * sinAlpha * cosBeta)
    	Next
    	Return pts
    End Function
    The bounds rectangle determines the size and shape, as in DrawEllipse etc. By default the first point is on the right of the polygon centre, but you can change that by setting a non-zero value for rotation (in degrees). The integer vertices will of course take value 6 for a hexagon. Here's an example of code in the Paint event of a PictureBox, with a NumericUpdown to set the number of vertices:
    Code:
    	e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
    	Dim r As Rectangle = PictureBox1.ClientRectangle
    	r.Inflate(-10, -10)
    	Dim vertices() As PointF = RegularPolygon(r, 0, CInt(NumericUpDown1.Value))
    	e.Graphics.DrawPolygon(Pens.Blue, vertices)
    	For Each p As PointF In vertices
    		e.Graphics.FillEllipse(Brushes.Red, p.X - 3, p.Y - 3, 6, 6)
    		e.Graphics.DrawEllipse(Pens.Black, p.X - 3, p.Y - 3, 6, 6)
    	Next
    To play with this:
    1. invalidate the PictureBox in NumericUpDown.Value changed and in PictureBox.SizeChanged.
    2. anchor the PictureBox to all sides of the form to resize it easily.

    EDIT: To anyone trying the above code. I just discovered that the rotation doesn't work quite as intended when the bounding rectangle isn't square. I want to rotate the starting point around the polygon centre without changing the horizontal and vertical size. At the moment it rotates the whole polygon, which I don't need the function to do because it could be done with Graphics transforms. I'm sure it won't be hard to fix but I'll have to think it through.

    BB

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