Limit rotational direction

BlitzMax Forums/BlitzMax Programming/Limit rotational direction

Shagwana(Posted 2005) [#1]
Right, i have three varibles ...
Speed:Float    'Speed to change directions at (could be anything)
Current:Float  'Current direction (0 to 360)
Wanted:Float   'Wanted direction (0 to 360)

After a little bit of code, i have worked out this varibles value...
iTurnDir:Int '=-1 anti-clockwise, 0 = no turn needed, 1 = clockwise


What i want to do is add the speed to the current direction (or subtract, depending on iTurnDir) to make it reach the wanted direction.

Now my question is, does anyone have a efficent and elegent solution to limit the current direction to the wanted direction with out over stepping it?

Say for example, the solution needs to be able to deal with;

1. speed=10 current=10 wanted=200 iTurnDir=1 <- current will = 20
2. speed=100 current=300 wanted=10 iTurnDir=1 <- current will = wanted
3. speed=100 current=10 wanted=300 iTurnDir=-1 <- current will = wanted

Now lets see what you lot can come up with :)


Tom(Posted 2005) [#2]


Any good?


Will(Posted 2005) [#3]
I remember writing something like this, it was surprisingly difficult. I was making a tank turret rotate towards the target by the closest method possible - this is the code, you can adapt it. Trust me, it takes ages to work this crap out - at least for someone as much of an amateur as myself. I hope people aren't about to ridicule me - I don't want them to look bad :P

Function matchAngle(c:craftController, TargetAngle#)
		c.c.rot = hMath.fixAngle(c.c.rot)  'Makes angle between 0 and 360
		TargetAngle = hMath.fixAngle(TargetAngle)  'Makes angle between 0 and 360
		
		'Rotates Ship Quickest Way to Appropriate Angle
		'c.c is the ship
		'c.c.rot = the ships current rotation
		'c.turning is its intention to turn in a positive or negative direction
		If c.c.rot <> TargetAngle Then
			If Abs(c.c.rot - TargetAngle) < Abs(c.c.rotSpeed * .5 * DeltaT) Then 
				c.c.rotSpeed = c.c.rotSpeed * .5
			Else
				If c.c.rot - TargetAngle > 180 Then
					c.turning = 1
				Else
					If TargetAngle - c.c.rot > 180 Then
						c.turning = -1
					Else
						If c.c.rot > TargetAngle Then
							c.turning = -1
						End If
						If c.c.rot < TargetAngle Then
							c.turning = 1
						End If
					End If
				End If
			End If
		End If

	End Function



Shagwana(Posted 2005) [#4]
Tom, yours brakes with ...
Local angle# = 300
Local target# = 10
Local speed# = 60


Will, thats almost there, done that part myself. Thats how to calculate the "iTurnDir" value. Alos an aid when adding it to current direction as you can see.



Strict
Rem
'0 to 360
Local fRotation_Wanted:Float=90.0
Local fRotation_Current:Float=0.0
Local fRotation_Speed:Float=10.0


'Decide on direction from current to wanted
Local iTurnDir:Int=0   'Presume no direction is needed

If fRotation_Current<>fRotation_Wanted    'Is a direction needed?
  'Need to rotate it towards
  If fRotation_Current>180
    'Upper half
    If fRotation_Wanted>=(fRotation_Current-180) And (fRotation_Wanted<fRotation_Current)
      'Wanted direction is lower!
      iTurnDir=-1
      Else
      'Wanted direction must be higher (cause its not lower!)
      iTurnDir=1
      EndIf
    Else 
    'Lower half
    If (fRotation_Wanted>=fRotation_Current) And fRotation_Wanted<(fRotation_Current+180)
      'Wanted direction is higher
      iTurnDir=1
      Else
      'Wanted direction must be lower (cause its not higher)
      iTurnDir=-1
      EndIf
    EndIf
  EndIf
  
                       
'Add in the rotation towards wanted
fRotation_Current = fRotation_Current + (Float(iTurnDir)*fRotation_Speed)    
'Limit current to 0 to 360
fRotation_Current = fRotation_Current Mod 360.0
If fRotation_Current < 0 Then fRotation_Current :+360 

'*** now check to see if we didt overstep wanted ***


Im just after a smart solution to the limit part of the problem!


Shagwana(Posted 2005) [#5]
Well i think i have solved it myself ...



Strict

'0 to 360
Global fRotation_Current:Float
Global fRotation_Wanted:Float
Global fRotation_Speed:Float




'Force overstep into wrap around
fRotation_Current:Float=330.0
fRotation_Wanted:Float=310.0
fRotation_Speed:Float=7.0







Global iTurnDir:Int


'Testcode
Print " "
Print ">.  "+fRotation_Current+" -> "+fRotation_Wanted

Local iCount:Int=0
While (fRotation_Current<>fRotation_Wanted) And (iCount<100)

  CalcDir()
  AddAndLimit()

  iCount:+1
  Print iCount+".  "+fRotation_Current+" -> "+fRotation_Wanted+" "+iTurnDir

  Wend


'Decide on direction from current to wanted
Function CalcDir()
  iTurnDir:Int=0     'Presume its no direction
  If fRotation_Current<>fRotation_Wanted    'Is a direction needed?
    'Need to rotate it towards
    If fRotation_Current>180
      'Upper half
      If fRotation_Wanted>=(fRotation_Current-180) And (fRotation_Wanted<fRotation_Current)
        'Wanted direction is lower!
        iTurnDir=-1
        Else
        'Wanted direction must be higher (cause its not lower!)
        iTurnDir=1
        EndIf
      Else 
      'Lower half
      If (fRotation_Wanted>=fRotation_Current) And fRotation_Wanted<(fRotation_Current+180)
        'Wanted direction is higher
        iTurnDir=1
        Else
        'Wanted direction must be lower (cause its not higher)
        iTurnDir=-1
        EndIf
      EndIf
    EndIf
  End Function
  

Function AddAndLimit()

  If fRotation_Current<>fRotation_Wanted
    'Movement needed ...

    If iTurnDir=1
      'Clockwise
      fRotation_Current = fRotation_Current + fRotation_Speed

      If fRotation_Wanted<180.0 And fRotation_Current>180.0
        'Unwrap the 360 limit
        If fRotation_Current>=(fRotation_Wanted+360.0) Then fRotation_Current=(fRotation_Wanted+360.0)
        Else
        'Simple as its in range
        If fRotation_Current>=fRotation_Wanted Then fRotation_Current=fRotation_Wanted
        EndIf
      Else
      'Anti-clockwise
      fRotation_Current = fRotation_Current - fRotation_Speed

      If fRotation_Wanted>180.0 And fRotation_Current<180.0 
        'Unwrap the 360 limit

        If fRotation_Current<=(fRotation_Wanted-360.0) Then fRotation_Current=(fRotation_Wanted-360.0)
        Else
        'Simple as its in range
        If fRotation_Current<=fRotation_Wanted Then fRotation_Current=fRotation_Wanted
        EndIf


      EndIf

    'Limit current to 0 to 360
    fRotation_Current = fRotation_Current Mod 360.0
    If fRotation_Current < 0 Then fRotation_Current :+360 

    EndIf
   
  End Function



Posted the code, so that i might be usefull to others ...


Shagwana(Posted 2005) [#6]
Well upon further investigation, the code i posted above does not do whats required.

However, i have another idea to pull off what i want.. stay tuned!


Shagwana(Posted 2005) [#7]
Well i have sorted it this time ...




Strict

'0 to 360
Global fRotation_Current:Float
Global fRotation_Wanted:Float
Global fRotation_Speed:Float




'Force overstep into wrap around
fRotation_Current:Float=10.0
fRotation_Wanted:Float=300.0
fRotation_Speed:Float=15.0







Global iTurnDir:Int
Global fTurnAngle:Float

'Testcode
Print " "
Print ">.  "+fRotation_Current+" -> "+fRotation_Wanted

Local iCount:Int=0
While (fRotation_Current<>fRotation_Wanted) And (iCount<100)

  CalcDir()

  iCount:+1
  Print iCount+".  "+fRotation_Current+" -> "+fRotation_Wanted+" "+iTurnDir

  Wend


'Decide on direction from current to wanted
Function CalcDir()
  iTurnDir:Int=0     'Presume its no direction
  If fRotation_Current<>fRotation_Wanted    'Is a direction needed?
    'Need to rotate it towards
    If fRotation_Current>180
      'Upper half
      If fRotation_Wanted>=(fRotation_Current-180) And (fRotation_Wanted<fRotation_Current)
        'Wanted direction is lower!
        iTurnDir=-1
        Else
        'Wanted direction must be higher (cause its not lower!)
        iTurnDir=1
        EndIf
      Else 
      'Lower half
      If (fRotation_Wanted>=fRotation_Current) And fRotation_Wanted<(fRotation_Current+180)
        'Wanted direction is higher
        iTurnDir=1
        Else
        'Wanted direction must be lower (cause its not higher)
        iTurnDir=-1
        EndIf
      EndIf
    EndIf


  Local fTurnSpeed:Float = fRotation_Speed
  If iTurnDir<>0

    'Calculate how many angles inbetween
    fTurnAngle=Abs(fRotation_Wanted-fRotation_Current)
    If fTurnAngle>180.0
      'Need to correct it
      fTurnAngle:-180           'Put it into the correct half
      fTurnAngle=180-fTurnAngle 'Invert it
      EndIf

    'Limit turning speed to no bigger then the angle we need to go
    If fTurnSpeed>fTurnAngle Then fTurnSpeed=fTurnAngle
    EndIf

  'Rotate it
  fRotation_Current = fRotation_Current + (fTurnSpeed * Float(iTurnDir))
  'Limit current to 0 to 360
  fRotation_Current = fRotation_Current Mod 360.0
  If fRotation_Current < 0 Then fRotation_Current :+360 

  End Function



There must be a million different ways to do this!


GW(Posted 2005) [#8]
Sorry to drudge up this old thread, I recently ran into this issue and thought i would post the solution I found for those in the future.

direction :+ sgn(direction-(sgn(direction-target)*180)-target)*1

direction is the current angle and target is the desired angle. the *1 at the end is the stepping value.

(I found this code on the net but alas i cant remember where to give credit)