Results 1 to 14 of 14

Thread: [RESOLVED] Point rotation with reference to zero?

  1. #1

    Thread Starter
    Addicted Member
    Join Date
    Feb 2015
    Posts
    156

    Resolved [RESOLVED] Point rotation with reference to zero?

    Hi all, hope you had a good weekend . I should be able to do this but for some reason not wrapping my mind around it. The following code rotates a given point around an origin, usually the center. Proven, straightforward math.

    Code:
    Private Function RotatePoint(ByRef pointToRotate As POINTF, ByVal angle As Single, ByRef Origin As POINTF) As POINTF
            
            Const pi As Single = 3.141593
            
            Dim RadAngle As Double
            Dim X As Double
            Dim Y As Double
            Dim StartAngle As Double
            Dim EndAngle As Double
            Dim Radius As Double
            Dim NewX As Single
            Dim NewY As Single
                       
            '- Convert degrees to Radians
            RadAngle = 2 * pi * angle / 360
            '- Find angle between origin and point to rotate
            X = pointToRotate.X - (Origin.X)
            Y = pointToRotate.Y - (Origin.Y)
            If X = 0 Then X = 0.00001
            StartAngle = Atn(Y / X)
            '- Add the desired angle with which to rotate
            EndAngle = StartAngle + RadAngle
            '- Calculate Radius
            Radius = X / Cos(StartAngle)
            '- Find location of point after rotation
            NewX = CSng(Radius * Cos(EndAngle))
            NewY = CSng(Radius * Sin(EndAngle))
            '- Return new point position
            RotatePoint.X = NewX + Origin.X
            RotatePoint.Y = NewY + Origin.Y
           
    
        End Function

    Works as it should but with one caveat for certain applications - the desired rotation angle is always added to the previous angle, so that each time this is called and a new angle(0-360 degrees) passed, it will keep adding the angles together and go nuts if you want to, say, use it for rotation with a slider control.

    Is it possible to modify this so as to always reference rotation from zero? Then one could simply set the desired angle of rotation to a number from 0 to 360, every time.

    I've been experimenting for the last few days but not getting my desired result . I'm fairly proficient with basic trig functions but this one has me baffled! . any help much appreciated.

  2. #2
    PowerPoster
    Join Date
    Nov 2017
    Posts
    3,138

    Re: Point rotation with reference to zero?

    You seem to have left out the critical piece of the puzzle, which is how this function is called.

    If you are doing something like this pseudocode:

    Code:
    'p is the POINTF to be rotated
    slider_change()
      p = RotatePoint(p, slider.value, origin)
      'Do something with p
    Then of course the next time the slider changes, p will be moved relative to the last move. If so, you probably need a separate variable:

    Code:
    'p is the POINTF to be rotated
    'rotated is the POINTF result of the rotation
    slider_change()
      rotated = RotatePoint(p, slider.value, origin)
      'Do something with rotated
    Or you could keep track of the last slider.value and calculate the delta (the difference between the current value and the last value) and use that as the angle that is passed to RotatePoint.
    Last edited by OptionBase1; Jul 5th, 2021 at 03:06 PM.

  3. #3

    Thread Starter
    Addicted Member
    Join Date
    Feb 2015
    Posts
    156

    Re: Point rotation with reference to zero?

    Oops, such is true. Here is how it is called in my application, rotating (4) points of a GDI+ path:

    Code:
    '- Convert bounding path points to pointf structures
        For i = 0 To 3
         rtPts(i).X = nPathPts(0, i)
         rtPts(i).Y = nPathPts(1, i)
        Next
        '- Perform rotation
        For i = 0 To 3
         rtDPts(i) = RotatePoint(rtPts(i), rAngle, cNTR) '- rAngle is the desired angle of rotation around cNTR (center). rAngle value(0-360) is set by a slider.
        Next
            
        For i = 0 To 3
         nPathPts(0, i) = rtDPts(i).X
         nPathPts(1, i) = rtDPts(i).Y
        Next

    It works but goes crazy and rotates super fast when the slider is moved because rtPts(i) are moved relative to the previous value rather than a fixed value....

  4. #4
    PowerPoster
    Join Date
    Nov 2017
    Posts
    3,138

    Re: Point rotation with reference to zero?

    Right, you are doing a convoluted version of my first example, whereby the point object that you are doing things with has been overwritten by the rotated version.

    So do the suggestion I made at the end of my last post. Rather than passing rAngle (the slider value), pass the difference between the current slider value and the previous slider value, which means you will have to create an appropriate variable to store the previous slider value.

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

    Re: Point rotation with reference to zero?

    You can do as OptionBase1 says, and add a delta (the delta can be positive or negative, so you're technically adding or subtracting a small angle to the current angle), but I prefer to keep the original points unmodified and return a new array of points.
    There are two reasons for this.

    The first is what you mentioned, being able to rotate directly to any angle because you always have the original points at the "zero" angle and you rotate them into a new array at the angle you want.

    The second reason is that you're dealing with floating point numbers and the calculations are almost never an exact representation of the results of the calculation. For instance, when you rotate the points once, the resulting values are likely to be slightly off. If you rotate those points again, the results will be slightly off from that rotation, so you're accumulating the errors from each rotation.

    If you rotate by 1 degree 10 times, then rotate by -1 degree 10 times, the points you have now should be the original points, but they won't be. They will be slightly off (probably not enough to notice... you can probably rotate thousands of times before the errors can grow enough to be an issue).

    But if you never modify the original points, then you're always just doing one rotation away from your original points so you never accumulate any floating point calculation resolution errors.
    "Anyone can do any amount of work, provided it isn't the work he is supposed to be doing at that moment" Robert Benchley, 1930

  6. #6

    Thread Starter
    Addicted Member
    Join Date
    Feb 2015
    Posts
    156

    Re: Point rotation with reference to zero?

    Quote Originally Posted by passel View Post
    You can do as OptionBase1 says, and add a delta (the delta can be positive or negative, so you're technically adding or subtracting a small angle to the current angle), but I prefer to keep the original points unmodified and return a new array of points.
    There are two reasons for this.

    The first is what you mentioned, being able to rotate directly to any angle because you always have the original points at the "zero" angle and you rotate them into a new array at the angle you want.

    The second reason is that you're dealing with floating point numbers and the calculations are almost never an exact representation of the results of the calculation. For instance, when you rotate the points once, the resulting values are likely to be slightly off. If you rotate those points again, the results will be slightly off from that rotation, so you're accumulating the errors from each rotation.

    If you rotate by 1 degree 10 times, then rotate by -1 degree 10 times, the points you have now should be the original points, but they won't be. They will be slightly off (probably not enough to notice... you can probably rotate thousands of times before the errors can grow enough to be an issue).

    But if you never modify the original points, then you're always just doing one rotation away from your original points so you never accumulate any floating point calculation resolution errors.
    Exactly right and I think that i'm going to try to modify my code accordingly. Not exactly sure how to leave the original points unmodified but I'm off to experiment .

    Thanks for help guys.

  7. #7

    Thread Starter
    Addicted Member
    Join Date
    Feb 2015
    Posts
    156

    Re: Point rotation with reference to zero?

    Well gosh guys, I'm just not getting it . I understand the concept which Passel presented, and this is what I would like to do i.e. leave the origin points unmodified, and return them in a new array.

    I've tried various ways with my code from above but still can't achieve the desired result. The reason that I need for the rotation to be always zero based and to accept angles fro 0-360 without adding is because the rectangle rotation is being done with a "grab handle". I've posted code of the grab handle to show what I'm doing. Place it into a form and run:

    Code:
    Option Explicit
    Const PI As Single = 3.141593
    Const PIDIV2 As Single = PI / 2
    Dim cX As Single
    Dim cY As Single
    
    Private Sub Form_Load()
    
     With Form1
      .AutoRedraw = -1
      .ScaleMode = 3
      .Caption = "Hold left mouse key & drag to rotate"
     End With
    
     '- Define center position
     cX = Form1.ScaleWidth / 2
     cY = Form1.ScaleHeight / 2
     
     '- Center marker
     Me.Circle (cX, cY), 7, vbRed
        
    End Sub
    
    Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
    
    '- Position & show radial line.
     Me.Line (cX, cY)-(X, Y)
    '- Draw "Handle"
     Me.Circle (X, Y), 7, vbGreen
    End Sub
    
    Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    
        Dim Radians As Single
        Dim Degrees As Single
        Dim hCX As Integer
      
      If (Button = 1) Then
      
       Me.Cls
       '- Center marker
       Me.Circle (cX, cY), 7, vbRed
       '- "Handle"
       Me.Circle (X, Y), 7, vbGreen
       '- Position & show radial line.
       Me.Line (cX, cY)-(X, Y)
     
       '- Convert X,Y coordinates into Radians using a helper function
       Radians = ATan2(Y - cY, X - cX)
    '- Convert returned Radians into degrees (0-360).
    '- Zero degrees is at 12 o'clock
    Degrees = Round((Radians / PI * 180) + 90)
    '- Display rotation angle in degrees
    Caption = CStr(Degrees) & " Degrees"
    
    End If
    
    End Sub
    Private Function ATan2(ByVal DY As Single, ByVal DX As Single) As Single
    '- Helper function: Converts X,Y coordinates into Radians
      If DX <> 0 Then
        ATan2 = Atn(DY / DX) - (Sgn(DX) - 1) * PIDIV2
      Else
        ATan2 = PIDIV2 * Sgn(DY)
      End If
    End Function

    So as one moves the grab handle, the rectangle should follow using my point rotation code from posts 1 & 3 above to rotate the points. Just like in MS Word .


    EDIT: I'm actually rotating certain GDI+ path points, and it is necessary to do it this way per the code from post #s 1 & 3 because the points may not always be symmetrical i.e not a perfect rectangle.


    Any help/thoughts very much appreciated. Have a great weekend!
    Last edited by SomeYguy; Jul 10th, 2021 at 02:00 PM.

  8. #8
    PowerPoster
    Join Date
    Nov 2017
    Posts
    3,138

    Re: Point rotation with reference to zero?

    I mean, speaking in generalities, if you don't want to modify the original values, then make sure your code doesn't modify the original values, right?

    If you still are using the code from post #3, and assuming that code runs more than once, then you are still modifying your original points. It isn't clear from that contextless code which variable contains your "original" points, but you are essentially doing this:

    Code:
    c = a
    b = rotate(c)
    a = b
    I trust you see that at the end of that code, a is equal to b, which is equal to the rotation of c. But then the next time that code is ran, c is overwritten by the contents of a, which is the rotation of c.

    Can't help further. Good luck.

  9. #9

    Thread Starter
    Addicted Member
    Join Date
    Feb 2015
    Posts
    156

    Re: Point rotation with reference to zero?

    Thanks OB1, no need to be condescending. Sometimes even a very experienced coder may not see the forest for the trees. I'll get it working eventually I'm sure.....

  10. #10
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,582

    Re: Point rotation with reference to zero?

    I don't see where you've declared the original path or set it up, but basically just store the original points at the same scope and modify your code you showed in post #3 to reference those original points.
    Code:
    '- Convert bounding path points to pointf structures
        For i = 0 To 3
         rtPts(i).X = origPathPts(0, i)    'we never modify the origPathPts in the rotation, 
         rtPts(i).Y = origPathPts(1, i)    'only if we modify the shape should they be changed
        Next
        '- Perform rotation
        For i = 0 To 3
         rtDPts(i) = RotatePoint(rtPts(i), rAngle, cNTR) '- rAngle is the desired angle of rotation around cNTR (center). rAngle value(0-360) is set by a slider.
        Next
            
        For i = 0 To 3
         nPathPts(0, i) = rtDPts(i).X    'copy the rotated original points
         nPathPts(1, i) = rtDPts(i).Y    'into the path that is displayed
        Next
    Last edited by passel; Jul 12th, 2021 at 08:17 AM.
    "Anyone can do any amount of work, provided it isn't the work he is supposed to be doing at that moment" Robert Benchley, 1930

  11. #11

    Thread Starter
    Addicted Member
    Join Date
    Feb 2015
    Posts
    156

    Re: Point rotation with reference to zero?

    Quote Originally Posted by passel View Post
    I don't see where you've declared the original path or set it up, but basically just store the original points at the same scope and modify your code you showed in post #3 to reference those original points.
    Okay, now I understand. Don't know why I didn't get it because I had to write something similar for a horizontal flipping function awhile back.

    Here is the whole sub, modified so as not to alter the original points(I think ), and showing how the original points are obtained. And with more commenting.

    Code:
    Private Sub RotateItem(ByVal rAngle As Integer)
    
      Dim nL As Long
      Dim nT As Long
      Dim nOrigPts() As Single
      Dim nRtPts() As Single
      Dim rtPts(0 To 3) As POINTF ', RtdPts(0 To 3) As POINTF
      Dim cNTR As POINTF
      Dim prAngle As Integer
      
      Dim l As Long, t As Long, R As Long, b As Long
      
      Dim i As Integer
      
      '- In case no object selected
      On Local Error GoTo SelERR
      
      '- Get bounding rectangle in order to calculate center
      DrawingObjects(itmSelected).GetBoundingRect l, t, R, b
         
         cNTR.X = CSng((l + R) / 2)
         cNTR.Y = CSng((t + b) / 2)
          
        '- Get current object bounding points.
        '- These are returned in a two-dimensional array of singles.
        '- (0,x) are x values, (1,x) are Y values
          DrawingObjects(itmSelected).GetBoundingPoints nOrigPts() '- Original points
        
        '- Copy original points into a new variable
        nRtPts() = nOrigPts()
                   
        '- Convert bounding path points to pointf structures
        For i = 0 To 3
         rtPts(i).X = nRtPts(0, i)
         rtPts(i).Y = nRtPts(1, i)
        Next
        '- Perform rotation
        For i = 0 To 3
         rtPts(i) = RotatePoint(rtPts(i), rAngle, cNTR)
        Next
        '- Convert point structures back to points
        For i = 0 To 3
         nRtPts(0, i) = rtPts(i).X
         nRtPts(1, i) = rtPts(i).Y
        Next
        
        '- Set dest path back to same coordinates with rotation
        DrawingObjects(itmSelected).SetPathDest_Points nRtPts()
        '- Draw
        DrawSelection DrawingObjects(itmSelected)
        DrawGraphics
        Exit Sub
      
        '- Update angle readout
        With lblReadout(0)
           .Caption = "Angle: " & rAngle & Chr(176)
           .Refresh
        End With
                
       
    
    SelERR:
    
    End Sub

    But the issue persists I'm afraid - When the RotatePoint function is called with the rAngle value set to a value from 0-359 which is returned by the "grab handle" in post #7, the returned screen object goes nuts and rotates many times quickly with just a slight movement of the grab handle.

    Again, thank you so much to anybody helping with this, it is very much appreciated.


    EDIT: Wonder if maybe the issue is in the RotatePoint function (post #1)? Maybe not a fault per se but that function calculates the previous angle at each call and then adds the desired angle to it which will just keep multiplying I think. So say the previous angle was 45, and now we get a value of 80 from the grab handle. Rather than rotating to 80 degrees as desired, the rotation will be at 125. So then a new value of, say, 90 from the grab handle will produce rotation at 125+90 = 215 instead of the desired 90 etc. etc. so it goes nuts.....
    Last edited by SomeYguy; Jul 12th, 2021 at 01:04 PM.

  12. #12
    PowerPoster
    Join Date
    Nov 2017
    Posts
    3,138

    Re: Point rotation with reference to zero?

    The fact that all of these arrays of points are local variables inside of this method you posted is a bit disconcerting.

    I don't know the specifics of what exactly this line of code is doing, but I would imagine if you debug your code and set a breakpoint on this line you would likely find that the contents of nOrigPts are not staying static. In other words, they are almost certainly changing as stuff is rotated.

    Code:
    DrawingObjects(itmSelected).GetBoundingPoints nOrigPts() '- Original points
    If I were you I would declare nOrigPts as a form level variable, add appropriate code to store values in it only when needed (in other words, populate that array once with the original values), and get rid of any code anywhere else that modifies that array.

    If that doesn't work, then if I were you I would create a brand new project with only the simplest example of what you are trying to accomplish, and get your concept working there first. Perhaps there are other unmentioned interactions that are in play here that none of us have any insight into.

    Good luck.

  13. #13

    Thread Starter
    Addicted Member
    Join Date
    Feb 2015
    Posts
    156

    Re: Point rotation with reference to zero?

    Quote Originally Posted by OptionBase1 View Post
    The fact that all of these arrays of points are local variables inside of this method you posted is a bit disconcerting.

    I don't know the specifics of what exactly this line of code is doing, but I would imagine if you debug your code and set a breakpoint on this line you would likely find that the contents of nOrigPts are not staying static. In other words, they are almost certainly changing as stuff is rotated.
    Could very well be and I'll modify accordingly. I actually did create a much smaller, separate test project to help work this out and am doing some of the testing there.

    I originally wrote the rotate code using a simple GDI+ transformation matrix (see here) and that method works great except that after rotation I can't retrieve the rotated points and therefore can't correctly position the sizing handles for the user to resize objects on the fly.

    Anyway, I'll try as you suggest. Thanks again

  14. #14

    Thread Starter
    Addicted Member
    Join Date
    Feb 2015
    Posts
    156

    Re: [RESOLVED] Point rotation with reference to zero?

    Resolved. Thanks folks for help & advice .

    I was doing everything right per suggestions here. The problem persisted but the fix turned out to be simple.

    My program uses the mouse for rotation. What I was not doing was storing values for the original point location and angle in the MouseDown event. So as was said, those values had no fixed reference and were overwritten on each call of the rotation code.

    Don't know why I didn't think of that earlier.....

    Now for a bit of code cleanup and it is good to go.

    Thanks again guys.

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