Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Additive Rotation Hell(p)

Winter Ventura
Eclectic Randomness
Join date: 18 Jul 2006
Posts: 2,579
02-13-2009 10:59
Okay so here's the situation. I had this store full of vendors. These vendors were all carefully arranged about the store.. and then came the day when I decided that I wanted to replace my old vendors with newer ones.

I made this little script, it's nothing fancy..

#1. drop in new prim
CODE

integer channel = 65;

default
{
state_entry()
{
llListen(channel, "", NULL_KEY, "");
llSay(0, "Waiting for target data");
}

listen(integer channel, string name, key id, string message)
{
if (llGetOwnerKey(id) != llGetOwner()) return;
llSay(0, "Target location received");
list commands = llParseString2List(message, ["|"], []);
vector targetPos = (vector)llList2String(commands, 0);
rotation targetRot = (rotation)llList2String(commands, 1);
vector targetScale = (vector)llList2String(commands, 2);

while (llGetPos() != targetPos) llSetPos(targetPos);

llSetRot(targetRot);
llSay(0, "Position reached, cleaning up.");
llSay(channel, "Okay, I'm here now, you can go.");
llRemoveInventory(llGetScriptName());
}
}


#2. drop in old prim
CODE

integer channel = 65;

default
{
state_entry()
{
llListen(channel, "", NULL_KEY, "");
llRegionSay(channel, (string)llGetPos() + "|" + (string)llGetRot() + "|" + (string)llGetScale());
}

listen(integer channel, string name, key id, string message)
{
if (llGetOwnerKey(id) != llGetOwner()) return;
if (message == "Okay, I'm here now, you can go.") llDie();
}
}


The principle is simple enough.. tell the new prim, the location and rotation of the old prim, then have it go there.. and when it gets there, delete the old one.

Now here's the problem.

One of these times, I decided to change the "base orientation" of the vendor. While I originally decided that the prim's orientation should be <0,0,0>... I played with taper, and decided that it would be cooler to have the vendor's orientation be <0,90,0>

Now... you'd think this would be a simple prospect.. but niether multiplying, nor dividing, nor even adding or subtracting the two rotations seems to generate the desired result.

After casting the various rotations back and forth into Eulers and Quats, I can't seem to find anything that doesn't come up with a garbage result. I finally figured out what I THINK is the root of the problem.

<0.00, -90.00, 0.00>
+
<0.00, 0.00, 45.00>

results in
<0.00, -90.00, 45.00>

not the
<90.00, -45.00, 90.00>
that I actually need.

So, what is the magic trick, to do
old rotation + offset rotation = desired rotation?
_____________________

● Inworld Store: http://slurl.eclectic-randomness.com
● Website: http://www.eclectic-randomness.com
● Twitter: @WinterVentura
Taff Nouvelle
Virtual Business Owners
Join date: 4 Sep 2006
Posts: 216
02-13-2009 11:06
have you tried

CODE


llGetPos() * llGetRot()

Winter Ventura
Eclectic Randomness
Join date: 18 Jul 2006
Posts: 2,579
02-13-2009 11:10
This is the result of placing the follwoing script into the <0,0,45> (old) prim.
CODE

default
{
state_entry()
{
llSay(0, (string)(llGetPos() * llGetRot()));
}
}


[11:08] Object: <137.10470, 170.77280, 27.40294>

How does that relate to any of the numbers above?
_____________________

● Inworld Store: http://slurl.eclectic-randomness.com
● Website: http://www.eclectic-randomness.com
● Twitter: @WinterVentura
Taff Nouvelle
Virtual Business Owners
Join date: 4 Sep 2006
Posts: 216
02-13-2009 11:17
this the line I use in my rezzers to rez at the correct orientation.

CODE

llRezAtRoot("Object", llGetPos() + <1,1,0> * llGetRot(), ZERO_VECTOR,llGetRot(),1);

Argent Stonecutter
Emergency Mustelid
Join date: 20 Sep 2005
Posts: 20,263
02-13-2009 11:38
The thing is you don't add rotations like that. You have to:

(1) convert the rotation from degrees to radians.
(2) convert the rotation from euler to quaternion format.
(3) multiply, not add.

So if you have:

vector r1 = <0.00, -90.00, 0.00>;
vector r2 = <0.00, 0.00, 45.00>;

These are not "rotations" in SL, they're vectors. A rotation is actually four numbers, like <0,0,0,1>. This is a quarternion... in LSL the type of a quaternion is "rotation". So...

You need to do:

rotation R1 = llEuler2Rot(r1 * DEG_TO_RAD);
rotation R2 = llEuler2Rot(r2 * DEG_TO_RAD);

Now you can apply both rotations (multiply them):

rotation NEW = R2 * R1;

And get a vector in degrees back, if you want to look at it:

vector new = llRot2Euler(NEW) * RAD_TO_DEG;
_____________________
Argent Stonecutter - http://globalcausalityviolation.blogspot.com/

"And now I'm going to show you something really cool."

Skyhook Station - http://xrl.us/skyhook23
Coonspiracy Store - http://xrl.us/coonstore
Winter Ventura
Eclectic Randomness
Join date: 18 Jul 2006
Posts: 2,579
02-13-2009 11:53
From: Argent Stonecutter
The thing is you don't add rotations like that.


That's the problem though.. that SHOULD work.... but it doesn't. Here's what I did...

CODE

default
{
state_entry()
{
vector r1 = <0.00, -90.00, 0.00>; // new vendor's offset
vector r2 = <0.00, 0.00, 45.00>; // rotation of old vendor

// These are not "rotations" in SL, they're vectors. A rotation is
// actually four numbers, like <0,0,0,1>. This is a quarternion...
// in LSL the type of a quaternion is "rotation". So...

// You need to do:

rotation R1 = llEuler2Rot(r1 * DEG_TO_RAD);
rotation R2 = llEuler2Rot(r2 * DEG_TO_RAD);

// Now you can apply both rotations (multiply them):

rotation NEW = R2 * R1;

// And get a vector in degrees back, if you want to look at it:

vector new = llRot2Euler(NEW) * RAD_TO_DEG;

llSay(0, (string)new);
}
}

result:
[11:51] Object: <0.00000, -90.00000, 45.00000>


This is the exact scenario that pointed out the problem.. I followed "all the conventional wisdom".. and I got a result that very blatantly is just the addition of two vectors.
<0, -90, 0> + <0, 0, 45> = <0, -90, 45>



Desired result:
<90.00000, -45.00000, 90.00000>

Obviously, we're not there yet.
_____________________

● Inworld Store: http://slurl.eclectic-randomness.com
● Website: http://www.eclectic-randomness.com
● Twitter: @WinterVentura
Argent Stonecutter
Emergency Mustelid
Join date: 20 Sep 2005
Posts: 20,263
02-13-2009 12:17
You happen to hit a special case. A euler form means "apply each of these three rotations to a ZERO_ROTATION (<0,0,0,1>;)". If you happen to have two eulers that are simple rotations in a single axis and you're applying them in the right order (Z, then Y, then X), the result will be the same as adding them. However, that's not the general case.

In addition, there are combinations of rotations in Euler space that are equivalent.

So, now, let's step back a bit.

Why do you expect that taking an object oriented <0,0,45> and rotating it -90 degrees in the Y axis would leave it oriented <90, -45, 90>?

Alternatively, are you sure the operations you have described are the ones you want to perform?
_____________________
Argent Stonecutter - http://globalcausalityviolation.blogspot.com/

"And now I'm going to show you something really cool."

Skyhook Station - http://xrl.us/skyhook23
Coonspiracy Store - http://xrl.us/coonstore
Hewee Zetkin
Registered User
Join date: 20 Jul 2006
Posts: 2,702
02-13-2009 12:21
SOMETIMES it will happen to work out that the multiplication of two rotations will result in a Euler angle that is the same as the sum of the two rotations' Euler angles, but it is not GENERALLY the case. In fact, I suggest against using Euler angles at all. If you are really thinking of a rotation as an angle about an axis, use the function llAxisAngle2Rot(). For example, your -90 degree rotation about the z-axis can be expressed as any of:

CODE

llAxisAngle2Rot(<0.0, 0.0, 1.0>, -PI_BY_TWO);
llAxisAngle2Rot(<0.0, 0.0, 1.0>, -90.0*DEG_TO_RAD);
llAxisAngle2Rot(<0.0, 0.0, -1.0>, PI_BY_TWO);
llAxisAngle2Rot(<0.0, 0.0, -1.0>, 90.0*DEG_TO_RAD);


With the benefit that it doesn't produce the misleading notion you can just willy-nilly combine rotations about different axes to get an intuitive and useful result.

Right. Did I mention I hate Euler angles, and LL for including them in their GUI and API?
Winter Ventura
Eclectic Randomness
Join date: 18 Jul 2006
Posts: 2,579
02-13-2009 12:29
Some more experimentation.. with some really queer results.

CODE

vector adjustmentRotDeg = <0.00, -90.00, 0.00>; // new vendor's offset
vector targetRotDeg = <0.00, 0.00, 45.00>; // rotation of old vendor

rotation adjustmentRotQuat = llEuler2Rot(adjustmentRotDeg * DEG_TO_RAD);
rotation targetRotQuat = llEuler2Rot(targetRotDeg * DEG_TO_RAD);

rotation newRotQuat = targetRotQuat * adjustmentRotQuat;
vector newRotDeg = llRot2Euler(newRotQuat) * RAD_TO_DEG;

llSay(0, (string)newRotDeg);


Incorrect result.. [12:19] Object: <0.00000, -90.00000, 45.00000>

CODE

vector adjustmentRotDeg = <0.00, -90.00, 0.00>; // new vendor's offset
vector targetRotDeg = <0.00, 0.00, 45.00>; // rotation of old vendor

rotation adjustmentRotQuat = llEuler2Rot(adjustmentRotDeg * DEG_TO_RAD);
rotation targetRotQuat = llEuler2Rot(targetRotDeg * DEG_TO_RAD);

rotation newRotQuat = adjustmentRotQuat * targetRotQuat;
vector newRotDeg = llRot2Euler(newRotQuat) * RAD_TO_DEG;

llSay(0, (string)newRotDeg);


Correct Result.. [12:24] Object: <90.00001, -45.00000, 90.00001>

Here's the queer bit..

rotation quatA;
rotation quatB;

quatA * quatB != quatB * quatA

Why, in the world, would quaternions violate one of the most basic tenets of mathematics? (A*B = B*A)
_____________________

● Inworld Store: http://slurl.eclectic-randomness.com
● Website: http://www.eclectic-randomness.com
● Twitter: @WinterVentura
Taff Nouvelle
Virtual Business Owners
Join date: 4 Sep 2006
Posts: 216
02-13-2009 12:30
ok, now the real question, why is it that

rotation result =quat1*quat2;

gives a different result to

rotation result =quat2*quat1;

that was the actual problem in the script.
Argent Stonecutter
Emergency Mustelid
Join date: 20 Sep 2005
Posts: 20,263
02-13-2009 12:33
From: Winter Ventura

Why, in the world, would quaternions violate one of the most basic tenets of mathematics? (A*B = B*A)
Because that's not a basic tenet of mathematics.

It's only true for Abelian groups.

Integers and real numbers are abelian groups.

Quaternions are not an abelian group over "multiplication".

Pick up an object. Rotate it 45 degrees to the right, then 90 degrees away from you. Note the appearance.

Now start over and rotate it 90 degrees away from you, then 45 degrees to the right. Note the result.

They are not the same, are they?

Edit: PS: a math geek joke:

Q: What's purple and commutes?
A: An Abelian grape!
_____________________
Argent Stonecutter - http://globalcausalityviolation.blogspot.com/

"And now I'm going to show you something really cool."

Skyhook Station - http://xrl.us/skyhook23
Coonspiracy Store - http://xrl.us/coonstore
Winter Ventura
Eclectic Randomness
Join date: 18 Jul 2006
Posts: 2,579
02-13-2009 12:34
From: Argent Stonecutter
Why do you expect that taking an object oriented <0,0,45> and rotating it -90 degrees in the Y axis would leave it oriented <90, -45, 90>?


Because this isn't an abstract problem.. I can physically take a prim, and rotate it into the correct position and rotation. I know what a correct result should look like. I can then reference the "correctly positioned" prim, and look up it's actual rotation inworld.

The project, is to make a script that DOES IT FOR ME. so I can speedily go through a round store, or whatever, and upgrade 40 vendors.. instead of painstakingly manually rotating each one.
_____________________

● Inworld Store: http://slurl.eclectic-randomness.com
● Website: http://www.eclectic-randomness.com
● Twitter: @WinterVentura
Argent Stonecutter
Emergency Mustelid
Join date: 20 Sep 2005
Posts: 20,263
02-13-2009 12:36
From: Winter Ventura
Because this isn't an abstract problem.. I can physically take a prim, and rotate it into the correct position and rotation. I know what a correct result should look like. I can then reference the "correctly positioned" prim, and look up it's actual rotation inworld.
You just need to perform the operations in the correct order.
_____________________
Argent Stonecutter - http://globalcausalityviolation.blogspot.com/

"And now I'm going to show you something really cool."

Skyhook Station - http://xrl.us/skyhook23
Coonspiracy Store - http://xrl.us/coonstore
Winter Ventura
Eclectic Randomness
Join date: 18 Jul 2006
Posts: 2,579
02-13-2009 12:38
From: Argent Stonecutter
You just need to perform the operations in the correct order.


Note the example above.. I used your suggested order.

Is there maybe some way that this information could get added to the rotations page on the wiki.. as if you were explaining it to someone with only a pedestrian understanding of pre-algebra?
_____________________

● Inworld Store: http://slurl.eclectic-randomness.com
● Website: http://www.eclectic-randomness.com
● Twitter: @WinterVentura
Argent Stonecutter
Emergency Mustelid
Join date: 20 Sep 2005
Posts: 20,263
02-13-2009 12:42
From: Winter Ventura
Note the example above.. I used your suggested order.
I just translated your example into LSL from a location in RL where I don't have access to SL to check it.
_____________________
Argent Stonecutter - http://globalcausalityviolation.blogspot.com/

"And now I'm going to show you something really cool."

Skyhook Station - http://xrl.us/skyhook23
Coonspiracy Store - http://xrl.us/coonstore
Winter Ventura
Eclectic Randomness
Join date: 18 Jul 2006
Posts: 2,579
02-13-2009 12:52
From: Argent Stonecutter
I just translated your example into LSL from a location in RL where I don't have access to SL to check it.


Well thanks for pointing the way, and thanks to Taff for listening to me complain inworld, and finally setting me in the direction I needed to trip over the actual answer.

I don't LIKE the answer.. but I can live with it.

"do the multiplication in the order you'd do it in world."

1. tip the top down 90 degrees
2. rotate 45 degress into place.

It works.. but the expression makes me angry.
_____________________

● Inworld Store: http://slurl.eclectic-randomness.com
● Website: http://www.eclectic-randomness.com
● Twitter: @WinterVentura
Argent Stonecutter
Emergency Mustelid
Join date: 20 Sep 2005
Posts: 20,263
02-13-2009 13:06
From: Winter Ventura

"do the multiplication in the order you'd do it in world."

1. tip the top down 90 degrees
2. rotate 45 degress into place.
OK, see, I honestly thought you were talking about the opposite operation.

Rotations are a seedless grape... they don't commute because they have no center.

/me ducks.
_____________________
Argent Stonecutter - http://globalcausalityviolation.blogspot.com/

"And now I'm going to show you something really cool."

Skyhook Station - http://xrl.us/skyhook23
Coonspiracy Store - http://xrl.us/coonstore
Jesse Barnett
500,000 scoville units
Join date: 21 May 2006
Posts: 4,160
02-13-2009 13:18
Easy as pi:

CODE

//new prim
integer channel = 65;
rotation rotOffset;

default {
state_entry() {
llListen(channel, "", NULL_KEY, "");
llSay(0, "Waiting for target data");
rotOffset = llEuler2Rot(<0,0,-90 * DEG_TO_RAD>);
}

listen(integer channel, string name, key id, string message) {
if (llGetOwnerKey(id) != llGetOwner())
return;
llSay(0, "Target location received");
list commands = llParseString2List(message,["|"],[]);
vector targetPos = (vector) llList2String(commands, 0);
rotation targetRot = (rotation) llList2String(commands, 1);
vector targetScale = (vector) llList2String(commands, 2);
while (llGetPos() != targetPos)
llSetPos(targetPos);
llSetRot(rotOffset * targetRot);
llSay(0, "Position reached, cleaning up.");
llSay(channel, "Okay, I'm here now, you can go.");
llRemoveInventory(llGetScriptName());
}
}

EDIT forgot to mention that this does give you <90,315,90> if your old prim started at <0,0,45>
_____________________
I (who is a she not a he) reserve the right to exercise selective comprehension of the OP's question at anytime.
From: someone
I am still around, just no longer here. See you across the aisle. Hope LL burns in hell for archiving this forum
Hewee Zetkin
Registered User
Join date: 20 Jul 2006
Posts: 2,702
02-13-2009 13:28
Here's an example of how two consecutive rotations may not "add" very intuitively. First rotate 90 degrees about the z-axis, then rotate 90 degrees about the (global) y-axis. If you picture what happens to the local axes (helps to use three fingers on your hand for the axes), you have local x along global y, local y along global z, and local z along global x. If you rotate first 90 degrees about the y-axis then 90 degrees about the (global) x-axis, you get the same result! Or if you rotate 90 degrees about x then 90 degrees about z for that matter!

Neat little swap, eh? Okay. Now the interesting thing is that this is the equivalent of a 120-degree rotation about an axis that in the same direction as <1,1,1> (mid-way between all three axes). If you imagine looking at the origin along this axis, you can imagine what this rotation looks like from that perspective and see that the equivalence is true.