# Understanding rotations - any info for non-physicists? :)

Please take a moment to consider if this thread is worth bumping.

## Recommended Posts

Hi, I'm sure this question has been asked before, but I haven't found a straightforward explanation yet lol.

The problem:

In short, there is no clear-cut, plain English explanation of how the rotation API works.  It appears tohave been developed by an angry mathematician, seeking vengeance on all programmers who don't know their roots.  LOL - really?  Quatermion sounds like a planet from a science fiction sim, Euler sounds like that kid that had a day off (wait that was Bueler), and Radian sounds like something nuclear.  The LSL Wiki and Wikipedia explanations may as well be written in Japanese.  I've learned entire languages faster than I've been able to grasp this stuff.  I can code, but I'm no rocket scientist (yet). :D

What I'm looking for

An explanation of the basic concepts behind the mysteries of the SL rotation API.  My goal is to reach a point where I at least have a clue.  Rotation doesn't seem to be the only API that uses some of this random lingo (particle systems and object movement being others) but I can kinda get by for now.  But if I can somehow decrypt the documentation, to where it makes sense to the average programmer, then I'll really be able to take my skills to the next level.  Then it gets interesting. :)

An example

On every developer forum I've ever visited, example code is usually encouraged, so here's one of my most recent projects.  It's simple enough conceptually, a chair that can rotate on 90-degree angles on command.  I copied four rotations and wrote a function to switch between them.  Then I wrote a listener (probably going to be a HUD down the road) and idk what the heck is wrong with it, but anyway here's the code:

```rotation r1 = <0.00000, 0.00000, 0.00000, 1.00000>;
rotation r2 = <0.00000, 0.00000, 0.70711, 0.70711>;
rotation r3 = <-0.00000, 0.00000, -1.00000, 0.00000>;
rotation r4 = <-0.00000, 0.00000, -0.70711, 0.70711>;

RotateLeft()
{
if (llGetLocalRot() == r1)
{
llSetLocalRot(r2);
return;
}
else if (llGetLocalRot() == r2)
{
llSetLocalRot(r3);
return;
}
else if (llGetLocalRot() == r3)
{
llSetLocalRot(r4);
return;
}
else if (llGetLocalRot() == r4)
{
llSetLocalRot(r1);
return;
}
else
{
// llOwnerSay((string)llGetLocalRot());
}
}

default
{
/*
This sets the "sit target"
The sit target is the coordinates ("vector" data type) and rotation (another SL specific data type).
A friend mentioned you can also do animations with this, but I'm not going there yet.
*/
state_entry()
{
llSitTarget(<0.0, 0.0, 0.5>, ZERO_ROTATION);
llSetLocalRot(r1);
}

changed(integer change)
{
if (change & CHANGED_LINK)  // A random bit of bitwise math I don't get yet
{
key av = llAvatarOnSitTarget();     // Here's where it gets interesting; it detects whether the avatar is on the object's sit target
if (av)     // on sit
{
llListen(7, "", NULL_KEY, "");
}
else        // on stand
{
llResetScript();
}
}
}

listen(integer channel, string avName, key avKey, string message)
{
if (channel == 7 && message == "l")
RotateLeft();
}
}```

So when I call the rotate function, it works the first time (rotates 90 degrees to the left), but the second time it does nothing.  Now if I understood rotation beyond copy & paste, I could debug this no problem.  But as it is, I'm left scratching my head and trying very hard not to pull my hair out.  :))

So many questions... like:

• What is this mysterious Fourth Dimension used in rotations?  I think it has something to do with Planet Quatermion. Z.  :)
• How the heck did it get 0.70711?  I literally laughed out loud at that... what the function? :D
• From what I managed to understand from the LSL wiki, Radian is some kind of alternative to degrees, and one the developers of SL preferred for some reason... how do those work?

Seriously, I know this is a huge post and probably sounds more like a rant than anything (and I apologize for that), so I truly appreciate your taking the time to read through this.  This is one of those few situations where it seems like the more I read, the less I understand.  I never took quantum physics and barely passed algebra, so while I understand and respect that programming was oriignally based on math, I'm totally and completely lost.  So any (comprehensible) information on the subject would be awesome!  I've been over the LSL wiki and Wikipedia and plenty of random Googling, but I don't know where else to start on this.  Thanks in advance for your time. :)

##### Share on other sites

Rotations can be confusing, so you are not alone.  They are not advanced math, though, so they can be mastered by mortals.  A radian is still the same as you were taught in high school. So the angular sweep all the way around a circle is still 360 degrees or 2*PI radians. That means that one radian is pretty close to 57.296 degrees.  If any of that is only a foggy recollection, you can look in Wikipedia or any of a ton of web sites for "radian".

You didn't encounter quaternions in high school, though, and they are harder to explain.  Forunately, however, almost nobody in SL thinks about quaternions directly.  The only reason that LSL uses them is that they get around some very nasty problems when we are dealing with rotations in 3D that can occur when an object is aligned with two of the global axes. (Google "gimbal lock" if you are interested).  When we are scripting, most of us let the system use quaternions and then translate them to a more familiar form with llRot2Euler (or the other way with llEuler2Rot).

Now, the main reason that your script isn't working is that you are telling it to turn your object to specific fixed rotations, not to new rotations relative to where they are at the moment.  For example, if I tell a script to rotate my object with the command

it will always rotate it precisely to face due north, which is 90 degrees from ZERO_ROTATION, or due east.  If I want to turn it 90 degrees from where it is every time I issue the command, I need to write

which will always start from where the object is currently facing (llGetRot() ) and then turn 90 degrees.

Note that in either case, since 90 degrees is the same as PI/2, I could have written the rotation itself as

llEuler2Rot(<0.0,0.0,PI/2>)

Now, as a secondary issue, you have discovered llGetLocalRot and llSetLocalRot, which are used with rotations relative to the object itself, rather than rotations relative to the global grid.  When applied to the root prim of an object, neither of those is particularly useful, because they amount to the same thing as llGetRot and llSetRot.  They only make sense if you are using them on child prims.  And that's where most beginning scripters start to get headaches.

I suggest spending a lot of time studying http://wiki.secondlife.com/wiki/Rotation and http://wiki.secondlife.com/wiki/User:Void_Singer/Rotations and then spending a much longer time experimenting.  The headaches don't go away, but as the landscape becomes more familiar you will develop your own 3D sense, and rotations will become manageable.

EDIT:  Totally apart from the business of what quaternions and rotations are, note that when you define a variable as global, you can't include a calculation in the definition.  That is, you cannot write

rotation r1 = llEuler2Rot(<0.0,0.0,PI/2>);

and put that up top in your script.  You'll have to write

rotation r1;

and then do the

r1 = llEuler2Rot(<0.0,0.0,PI/2>);

part in one of your events, like state_entry.

• 1
##### Share on other sites

OMGosh Thank you!!!

I'm definitely down for some reading and experimenting - that's kind of my process for learning new APIs anyway, plus SL scripting is a blast most of the time - but it helps when I know what the heck the documentation is talking about.  When you explained what a radian is, then it started making sense (thanks also for the info on "local" rotation btw).  So for the script above I came up with a couple functions (in case someone with the same question comes by this post):

`/*Rotates an object by degrees (no messing with radians and quatermions and whatever alien encryption algorithms are involved in making SL work under the hood. :DParameters:    x (float): Rotation in degrees along x-axis    y (float): Rotation in degrees along y-axis    z (float): Rotation in degrees along z-axisNotes:    - To rotate in the opposite direction, use negative numbers.    - It also worked with a linked object*/Rotate(float x, float y, float z){    rotation rot = llGetRot();          // Get the object's current rotation in its encrypted form    vector euler = llRot2Euler(rot);    // Convert the rotation to "radians"    euler *= RAD_TO_DEG;                // Convert this to degrees    euler += <x, y, z>;                 // Add the user-specified degrees to the variable    euler *= DEG_TO_RAD;                // Convert degrees back to "radians"    rot =  llEuler2Rot(euler);          // Convert the euler back to a rotation "quarternion" (IMO same as encryptiing it LOL)    llSetRot(rot);                    // Apply the updated rotation to the prim   }/*This sets the rotation (not relative to its current position)Parameters are the same as above.*/SetRotation(float x, float y, float z){    llSetRot(llEuler2Rot(<x, y, z> * DEG_TO_RAD));}`

Anyway, thanks again for helping me get going in the right direction.

##### Share on other sites

Well, don't thank me yet.  The road ahead is filled with potholes.  :smileytongue:

Not only that, but I have probably mislead you.  Your script might actually have worked, except that you made the mistake of trusting the universe.  When you set a rotation, it is quite reasonable to expect that it will be exactly where you set it when you look again.  Unfortunately, the universe is plagued with round off errors.  So, if you tell a script to rotate your prim 90.0 degrees, it may actually rotate it 90.000001 degrees.  Then if you test later and say

`if (llGetRot() == <0.0,0.0,90.0*DEG_TO_RAD> ){    // Do something}`

it's not likely to do it, because the current rotation is not <0.0,0.0,90.0*DEG_TO_RAD> .  The lesson is never to test whether a float is exactly what you expect it to be.  Always test whether it is within a range, as in

`vector TestRot = llRot2Euler( llGetRot() )* RAD_TO_DEG;if (llFabs( TestRot.z - 90.0 ) < 0.01)  // That is, if the rotation in Z is within 0.01 degrees of being 90.0 ....{    // Do something}`

Or avoid the problem altogether by not testing, as in

`integer gCount;list Rots = [<0.00000, 0.00000, 0.00000, 1.00000>,<0.00000, 0.00000, 0.70711, 0.70711>,<-0.00000, 0.00000, -1.00000, 0.00000>,<-0.00000, 0.00000, -0.70711, 0.70711>];RotateLeft(){    ++gCount;    llSetRot( llList2Rot(Rots,gCount%4) );}default{    state_entry()    {        llSitTarget(<0.0, 0.0, 0.5>, ZERO_ROTATION);        llSetRot(r1);    }        changed(integer change)    {        if (change & CHANGED_LINK)        {            key av = llAvatarOnSitTarget();            if (av)     // on sit            {                llSay(0,"Hello, Avatar!");            }            else        // on stand            {                llResetScript();            }        }    }        touch_start(integer num)    {        if (llAvatarOnSitTarget())        {            RotateLeft();        }    }}`

which steps methodically through your four listed rotations, one at a time, each time you click on the object with an avatar seated on it.

If that's not confusing enough, I don't know what is. :smileywink:

##### Share on other sites

In addition to Rolig's excellent suggestions, I would recommend two tutorials over the road at SL Universe.   I won't provide the links, since posts with links to SLU tend to vanish, but find SL Universe, and then the Tutorials subforum of the Development Discussion and Support section, and look for two articles by Grandma Bates: Basic Introduction to Rotations and Introduction to Rotations with Translations.

They are both very accessible and I certainly find them helpful.