Difference between revisions of "LlRotBetween"

From Second Life Wiki
Jump to navigation Jump to search
Line 71: Line 71:
<lsl>rotation RotBetween(vector a, vector b)
<lsl>rotation RotBetween(vector a, vector b)
{
{
     float aabb = (a * a) * (b * b); // product of the squared lengths of the arguments
     float aabb = llSqrt((a * a) * (b * b)); // product of the lengths of the arguments
     if (aabb)
     if (aabb)
     {
     {
         float ab = a * b; // dotproduct of the arguments
         float ab = (a * b) / aabb; // normalized dotproduct of the arguments
         vector c = a % b; // 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 crossproduct
         if (cc) // test if the arguments are (anti)parallel
         if (cc) // test if the arguments are (anti)parallel
         {
         {
             float s = llSqrt(aabb); // set up the s-element with a PI/2 angle
             float s = 1.0; // set up the s-element with a PI/2 angle
             if (ab * ab < cc) // compare the cosine squared to the sine squared
             if (ab * ab < cc) // compare the cosine squared to the sine squared
                 s += ab; // use the cosine to adjust the s-element
                 s += ab; // use the cosine to adjust the s-element
             else if (ab > 0.0) // test if the angle is smaller than PI/2
             else if (ab > 0.0) // test if the angle is smaller than PI/2
                 s += llSqrt(aabb - cc); // use the sine to adjust the s-element
                 s += llSqrt(1.0 - cc); // use the sine to adjust the s-element
             else  
             else  
                 s -= llSqrt(aabb - cc); // use the sine to adjust the s-element, and restore the sign
                 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
         }
         }
         else if (ab < 0.0) // test if the arguments are anti-parallel
         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 <1.0, 0.0, 0.0, 0.0>; // the arguments are anti-parallel
     }
     }
     // the arguments are too small or parallel, return zero rotation
     // the arguments are too small or parallel, return zero rotation
     return ZERO_ROTATION;
     return ZERO_ROTATION;
}//Written by Moon Metty, minor optimizations by Strife Onizuka</lsl>
}//Written by Moon Metty, minor changes by Strife Onizuka</lsl>
|cat1=Math/3D
|cat1=Math/3D
|cat2=Rotation
|cat2=Rotation

Revision as of 13:03, 24 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). <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

Reference Implementation

<lsl>//Loosely based on SL source code, reproducing some of it's quirks 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>

Replacement

Due to the annoying quirks of this function Moon Metty wrote a drop in replacement - SCR-309. <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
       vector c = (a % b) / aabb; // normalized crossproduct of the arguments
       float cc = c * c; // squared length of the crossproduct
       if (cc) // test if the arguments are (anti)parallel
       {
           float s = 1.0; // set up the s-element with a PI/2 angle
           if (ab * ab < cc) // compare the cosine squared to the sine squared
               s += ab; // use the cosine to adjust the s-element
           else if (ab > 0.0) // test if the angle is smaller than PI/2
               s += 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
           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
   }
   // the arguments are too small or parallel, return zero rotation
   return ZERO_ROTATION;

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

Source

Signature

function rotation llRotBetween( vector start, vector end );