Rotate(), math.sin(), Degrees and Radians - am I missing something?

I want to rotate a sprite.

In a mesh I can specify a rotation (in radians, anticlockwise) as the fifth parameter in addRect. Is there an equivalent with sprite? I don’t think there is but maybe I’m missing something.

Instead, the code below gets the right answer, though I’m a little confused as to the need to take the negative of the angle. Also, is there a reason why the angles rotate anticlockwise rather than clockwise (I’m not aware of a coding convention either way, but if there is one I’d like to know). Finally @Simeon is there any reason for the use of degrees rather than radians in the rotate() function?

Code taken from step 8 of my snake tutorial


-- Snake tutorial
-- by West

--8. Add movement

function setup()
    displayMode(FULLSCREEN)
    supportedOrientations(LANDSCAPE_ANY) 
    x=300
    y=500    
    angle=90 
    turning=2
    speed=2.5 --a variable to hold the current speed of the snake
end

function draw()
    background(40, 40, 50)
    if CurrentTouch.state==BEGAN or CurrentTouch.state==MOVING then
        if CurrentTouch.x<WIDTH/2 then 
            angle = angle + turning --use the turning variable
        else 
            angle = angle - turning --use the turning variable
        end
    end

--change the x and y coordinates based on the current direction. The direction needs to be resolved into its x and y components using simple trigonometry.  The variable speed will be used to control the magnitude of the components.

--math.sin is the sine function - I assume you know what this is - you should have learned trigonometry at school. If you are still at school - ask your teacher.
--in codea, the trig functions require the angles to be provided in radians not degrees.  Fortunately there is an in built function to convert from degrees to radians called math.rad().  Again I will assume you know what radians are, but if you don't either use the function blindly or find out!

--Finally, the angle is negative.  I'm not sure why but reckon it's linked back to the rotate function being anticlockwise
x = x + speed*math.sin(math.rad(-angle))
y = y + speed*math.cos(math.rad(-angle))

    pushMatrix()
        translate(x,y)
        rotate(angle) 
        sprite("Space Art:Part Green Hull 4",0,0) 
    popMatrix()
end

@West I don’t think you’re missing anything. Functions are created depending on the programmer who originally wrote them. There could have been different people writing the routines and they never coordinated the code. Or it could have been the same programmer, but the routines were written several week or months apart and he never thought about what was written before. Our job is to figure out how the functions work and write code based on that. The original routines aren’t going to be changed no matter what we say or ask.

@West - suppose you are drawing on paper, and need to draw a pic at an angle in the top right corner. To make it easy, you pull the paper towards you (translate) and turn the paper (rotate) so you can draw it vertically instead of at an angle. Then you put the paper back the way it was (pushMatrix).

Why is the initial rotation negative? Suppose you want the pic at an angle to the right. Think of the sequence of actions.

  1. Rotate one way (rotate)
  2. Draw the pic (sprite)
  3. Rotate the other way (pushMatrix) to go back to te original position

To get the pic tilted to the right, step 3 has to be a right hand rotation, ie after you’ve drawn the pic, you rotate to the right. This means the initial rotation has to be to the left.

And if that’s still not clear, just try actually doing it with a sheet of paper and pencil!

PS so it has nothing at all to do with rotation being anti clockwise… It can be any way you want

PPS to make things a little confusing, if you ever rotate a physics object using its angle property, you don’t make it negative, just use it as it is. That must mean that the Box2D engine makes it negative for you, behind the scenes.

It’s because your sprite and your user disagree about which way around it is.

You start with the sprite rotated at 90 degrees. This ensures that it starts pointing along the negative x axis. If you do nothing, you want this state of affairs to continue: namely, that the sprite continues moving along the negative x axis. So the equation of motion should be:

pos = pos + speed * vec2(-1,0)

Now if it turns a bit, you want to adjust the direction by the turn. So you want to do:

pos = pos + speed * vec2(-1,0):rotate(angle)

The angle here is the angle that the user thinks the sprite has turned through. But this is NOT the angle in your code. Rather, it is the relative angle: the difference between the total angle and the initial angle. So you want:

pos = pos + speed * vec2(-1,0):rotate(angle - initialAngle)
       = pos + speed * vec2(-1,0):rotate(angle - 90)

Now vec2(-1,0):rotate(angle - 90) is -(cos(angle - 90), sin(angle - 90)). Using the sum and difference formulae for trig, we get:

cos(angle - 90) = cos(angle)cos(-90) - sin(angle)sin(-90) = sin(angle)
sin(angle - 90) = sin(angle)cos(-90) + cos(angle)sin(-90) = -cos(angle)

So the correct formula is:

x = x - speed * sin(angle)
y = y + speed * cos(angle)

which is what you have (since cos(angle) = cos(-angle) and sin(-angle) = - sin(angle)).

Now, I think that this is slightly confusing. So what I would do is define another variable spriteAngle which is the angle needed to get the sprite rotated so that it is aligned with the x axis. This would be -90. This makes the sprite and the user agree on the frame of reference for the sprite. Then angle becomes the rotation with respect to the x axis, as it should be, and you can use the proper formulae:

x = x + speed * cos(angle)
y = y + speed * sin(angle)

To get the sprite facing backwards initially, set angle = 180 in setup.

In draw, as well as changing the formulae above, you either put:

rotate(angle)
rotate(spriteAngle)

or just

rotate(angle + spriteAngle)

I prefer the first one pedagogically, but the second makes for simpler code.

Further remarks:

  1. Although a mathematician to the core, I think that the use of radians in programming is daft. Everything should be in degrees. The reason is that you are far more likely to want to say “rotate everything by one third of a full turn” than “rotate everything by 0.31831 of a full turn”. The reason we have degrees is that 360 is a highly divisible number and lots of useful fractions of it turn out to be integers. Integers are better for programming than floats, so we should use them wherever possible. Until you get to calculus, radians are a luxury.

  2. Angles rotate anticlockwise by convention. This is the convention throughout mathematics and there is no area that I know of which uses the other convention. It is an arbitrary choice, but when you measure from the positive x-axis then it makes sense to go towards the positive y-axis first. A clock, on the other hand, goes from the positive y-axis to the positive x-axis. So you could say that the disagreement stems from different choices of where one begins. Unlike matrices, this seems to be one area where programmers have followed mathematicians. Fortunately.

@Andrew_Stacey - awesome point 2 is the confirmation I was after. After mulling it over last night I remembered that angles were measured from the positive x axis and rotated anticlockwise but I was getting hung up on rotating it like the hands on a clock.

Also cheers for the clarity on the sprite rotation.

Thanks too to @dave1707 and @Ignatz for their input