Difference between revisions of "LlRotBetween"

From Second Life Wiki
Jump to navigation Jump to search
m
m (update... nothing for me to do this revision)
Line 52: Line 52:
     if (aabb)
     if (aabb)
     {
     {
         float ab = (a * b) / aabb; // normalized dotproduct of the arguments
         float ab = (a * b) / aabb; // normalized dotproduct of the arguments (cosine)
         vector c = (a % b) / aabb; // normalized crossproduct of the arguments
         vector c = (a % b) / aabb; // normalized crossproduct of the arguments
         float cc = c * c; // squared length of the crossproduct
         float cc = c * c; // squared length of the normalized crossproduct (sine)
         if (cc) // test if the arguments are (anti)parallel
         if (cc) // test if the arguments are (anti)parallel
         {
         {
             float s = 1.0; // set up the s-element with a PI/2 angle
             float s;
             if (ab * ab < cc) // compare the cosine squared to the sine squared
             if (ab > -0.707107) // test if the angle is smaller than 3/4 PI  
                s += ab; // use the cosine to adjust the s-element
                 s = 1.0 + ab; // use the cosine to adjust the s-element
            else if (ab > 0.0) // test if the angle is smaller than PI/2
             else
                 s += llSqrt(1.0 - cc); // use the sine to adjust the s-element
                 s = 1.0 - llSqrt(1.0 - cc); // use the sine to adjust the s-element
             else  
                 s -= llSqrt(1.0 - cc); // use the sine to adjust the s-element, and restore the sign
             float m = llSqrt(cc + s * s); // the magnitude of the quaternion
             float m = llSqrt(cc + s * s); // the magnitude of the quaternion
             return <c.x / m, c.y / m, c.z / m, s / m>; // return the normalized quaternion
             return <c.x / m, c.y / m, c.z / m, s / m>; // return the normalized quaternion
Line 70: Line 68:
             return <1.0, 0.0, 0.0, 0.0>; // the arguments are anti-parallel
             return <1.0, 0.0, 0.0, 0.0>; // the arguments are anti-parallel
     }
     }
     // the arguments are too small or parallel, return zero rotation
     return ZERO_ROTATION; // the arguments are too small or parallel, return zero rotation
    return ZERO_ROTATION;
}//Written by Moon Metty, optimized by Strife Onizuka</lsl>
}//Written by Moon Metty, minor changes by Strife Onizuka</lsl>


===Bad Reference Implementation===
===Bad Reference Implementation===

Revision as of 07:30, 27 September 2012

Summary

Function: rotation llRotBetween( vector start, vector end );
0.0 Forced Delay
10.0 Energy

Returns a rotation that is the shortest rotation between the direction start and the direction end

• vector start
• vector end

Specification

start and end are directions and are relative to the origin <0.0, 0.0, 0.0>. If you have coordinates relative to a different origin, subtract that origin from the input vectors.

Caveats

  • start * llRotBetween(start, end) == end is only true if start and end have the same magnitude and neither have a magnitude of zero (see #Useful Snippets for a workaround).
    • This of course is ignoring floating point precision errors.
  • Rotations are from -PI to +PI around each axis.

Examples

<lsl>llRotBetween(<1.0, 0.0, 0.0>, <0.0, -1.0, 0.0>) // will return <0.00000, 0.00000, -0.70711, 0.70711> (which represents -90 degrees on the z axis)

llRotBetween(<0.0, 0.0, 0.0>, <0.0, -1.0, 0.0>) // will return <0.00000, 0.00000, 0.00000, 1.00000> (which represents a zero angle on all axis)

// because <0.0, 0.0, 0.0> does not convey a direction.</lsl>

Useful Snippets

This function adjusts the magnitude of the quaternion so start * llRotBetween(start, end) == end is true as long as neither have a magnitude really close to zero. They do not have to have the same magnitude. (If either is too close to zero than this will return an unadjusted quaternion). While this is mathematically correct, it won't help with floating point rounding errors, so it's more accurate to say start * return ≈ end. <lsl>rotation RotBetween(vector start, vector end) //adjusts quaternion magnitude so (start * return == end) {//Authors note: I have never had a use for this but it's good to know how to do it if I did.

   rotation rot = llRotBetween(start, end);
   float d = start * start;
   if(d)//is 'start' zero?
       if((d = llPow(end * end / d, 0.25)))//is 'end' zero?
           return <rot.x * d, rot.y * d, rot.z * d, rot.s * d>;
   return rot;

}//Strife Onizuka</lsl>

Notes

Vectors that are near opposite each other in direction may lead to erroneous results. <lsl> // First Vector is due north second vector is ALMOST due south. rotation lRotation = llRotBetween( <0., 1., 0.>, <-0.001, -.1, 0.> ); llSay(0, lRotation ); // Provides a result of <1.00000, 0.00000, 0.00000, 0.00000>. </lsl>

See Also

Functions

•  llAngleBetween

Deep Notes

Replacement

Due to the annoying quirks of this function Moon Metty wrote a drop in replacement. - SCR-309. The maximum error is reported to be 2.7e-7 @ 2.2 radians. <lsl>rotation RotBetween(vector a, vector b) {

   float aabb = llSqrt((a * a) * (b * b)); // product of the lengths of the arguments
   if (aabb)
   {
       float ab = (a * b) / aabb; // normalized dotproduct of the arguments (cosine)
       vector c = (a % b) / aabb; // normalized crossproduct of the arguments
       float cc = c * c; // squared length of the normalized crossproduct (sine)
       if (cc) // test if the arguments are (anti)parallel
       {
           float s;
           if (ab > -0.707107) // test if the angle is smaller than 3/4 PI 
               s = 1.0 + ab; // use the cosine to adjust the s-element
           else
               s = 1.0 - llSqrt(1.0 - cc); // use the sine to adjust the s-element
           float m = llSqrt(cc + s * s); // the magnitude of the quaternion
           return <c.x / m, c.y / m, c.z / m, s / m>; // return the normalized quaternion
       }
       else if (ab < 0.0) // test if the angle is smaller than PI/2
           return <1.0, 0.0, 0.0, 0.0>; // the arguments are anti-parallel
   }
   return ZERO_ROTATION; // the arguments are too small or parallel, return zero rotation

}//Written by Moon Metty, optimized by Strife Onizuka</lsl>

Bad Reference Implementation

<lsl>//Loosely based on SL source code, like SL's version, it's not very accurate. You want to use the above version. rotation llRotBetween(vector start, vector end) {

   vector v1 = llVecNorm(start);
   vector v2 = llVecNorm(end);
   float dot = v1 * v2;
   vector axis = v1 % v2;
   if (dot < -0.9999999) {
       // 180 degrees or there abouts
       vector ortho_axis = llVecNorm(<1.f, 0.f, 0.f> - (sn * (sn.x / (sn * sn))));
       if (ortho_axis)
           return < ortho_axis.x, ortho_axis.y, ortho_axis.z, 0.f>;
       return <0.0, 0.0, 1.0, 0.0>;
   }
   else if(dot > 0.9999999) {
       //parallel
       return ZERO_ROTATION;
   }
   dot = dot + 1.0;
   float m = llPow((axis * axis) + (dot * dot), -0.5);
   return <axis.x * m, axis.y * m, axis.z * m, dot * m>;

}</lsl>

Source

Signature

function rotation llRotBetween( vector start, vector end );