performer's matrix-to-HPR algorithm

New Message Reply Date view Thread view Subject view Author view

Don Hatch (hatch++at++hell.engr.sgi.com)
Tue, 15 Sep 1998 11:11:13 -0700


On Sep 15, 11:46am, Michael.Kellner++at++szm.de wrote:
> Subject:
>
> Hi pfFriends,
>
> In my Performer application I used pfGetOrthoMatCoord() to
> retrieve HPR from the matrix. Now, for a small converter
> application (non Performer) with it's own matrix handling
> I have also to retrieve this values. I used the following code:
>
> void matrix_get_rot( MATRIX mat, float * xrot, float * yrot, float *
> zrot )
> {
> double cy;
>
> cy = sqrt( mat[0][0]*mat[0][0] + mat[0][1]*mat[0][1] );
>
> if( cy > 16 * FLT_EPSILON ) {
>
> *xrot = atan2f( mat[1][2], mat[2][2] );
> *yrot = atan2f( -mat[0][2], cy );
> *zrot = atan2f( mat[0][1], mat[0][0] );
> }
> else {
>
> *xrot = atan2f( -mat[2][1], mat[1][1] );
> *yrot = atan2f( mat[0][2], cy );
> *zrot = 0.f;
> }
> }
>
> Alternatively I used the Performer function pfGetOrthoMatCoord():
>
> void matrix_get_rot( MATRIX mat, float * xrot, float * yrot, float *
> zrot )
> {
>
> pfMatrix pfmatrix;
> pfCoord pfcoord;
>
> pfSetMatRow( pfmatrix, 0, mat[0][0], mat[0][1], mat[0][2], 0.f
> );
> pfSetMatRow( pfmatrix, 1, mat[1][0], mat[1][1], mat[1][2], 0.f
> );
> pfSetMatRow( pfmatrix, 2, mat[2][0], mat[2][1], mat[2][2], 0.f
> );
>
> pfGetOrthoMatCoord( pfmatrix, & pfcoord );
>
> *xrot = pfcoord.hpr[1];
> *yrot = pfcoord.hpr[2];
> *zrot = pfcoord.hpr[0];
> }
>
> The result values shows me that the Performer function returns
> higher precision values than my code.
> So, could anybody give me a pointer how I can increase the precision
> or is it a secret how Performer computes this values?
>
> Thank you
>
> Michael

I'm having a hard time following your version.
In the non-degenerate case, you set:
    *xrot (i.e. pitch) = atan2f(mat[1][2], mat[2][2])
but this doesn't seem right, since mat[1][2] is sin(pitch)
and mat[2][2] is cos(roll)*cos(pitch).
So I'm lost right there...

It's true that Performer's algorithm is exceptionally stable
(it was substantially improved shortly after Performer 2.0).
Here is an explanation of the current algorithm...
============================================================================
    Choose the roll first; then (effectively) factor out the roll from
    the rest of the transformation by multiplying by the inverse of the
    roll matrix on the left; this gives the heading/pitch matrix,
    from which the pitch and heading are well-defined and well-behaved.

    For reference, the composite matrix is:
        [cr 0 -sr] [1 0 0 ] [ ch sh 0]
        [0 1 0 ] * [0 cp sp] * [-sh ch 0]
        [sr 0 cr] [0 -sp cp] [ 0 0 1]

        [cr 0 -sr] [ ch sh 0 ]
    = [0 1 0 ] * [-cp*sh cp*ch sp]
        [sr 0 cr] [ sp*sh -sp*ch cp]

        [(cr*ch - sr*sp*sh) (cr*sh + sr*sp*ch) -sr*cp]
    = [ -cp*sh cp*ch sp]
        [(sr*ch + cr*sp*sh) (sr*sh - cr*sp*ch) cr*cp]

    The inverse of the roll matrix is:
        [ cr 0 sr]
        [ 0 1 0 ]
        [-sr 0 cr]

    Here is the code:

        r = pfArcTan2(-mat[0][2], mat[2][2]);// returns 0 if both 0; that's fine
        pfSinCos(r, &sr, &cr);
        ch = cr*mat[0][0] + sr*mat[2][0];
        sh = cr*mat[0][1] + sr*mat[2][1];
        cp = cr*mat[2][2] - sr*mat[0][2];// guaranteed >=0 so pitch in [-90..90]
        sp = mat[1][2];
        h = pfArcTan2(sh, ch);
        p = pfArcTan2(sp, cp);

    The above 8 lines work fine in all cases, but they squander
    2 trig functions and a bunch of arithmetic in the common case
    that mat[0][2] == 0 (i.e. roll == 0 or 180, or pitch == +-90).
    Adding a test for this case, the complete algorithm would be:

        if (mat[0][2] == 0) // (roll == 0 or 180) or (pitch == +-90)
        {
            if (mat[2][2] >= 0)
            {
                r = 0; // cr == 1, sr == 0
                ch = mat[0][0];
                sh = mat[0][1];
                cp = mat[2][2];
            }
            else
            {
                r = 180; // cr == -1, sr = 0
                ch = -mat[0][0];
                sh = -mat[0][1];
                cp = -mat[2][2];
            }
        }
        else
        {
            r = pfArcTan2(-mat[0][2], mat[2][2]);
            pfSinCos(r, &sr, &cr);
            ch = cr*mat[0][0] + sr*mat[2][0];
            sh = cr*mat[0][1] + sr*mat[2][1];
            cp = cr*mat[2][2] - sr*mat[0][2];
        }
        sp = mat[1][2];
        h = pfArcTan2(sh, ch);
        p = pfArcTan2(sp, cp);
==============================================================================

Don

-- 
Don Hatch  hatch++at++sgi.com  (650) 933-5150  Silicon Graphics, Inc.

New Message Reply Date view Thread view Subject view Author view

This archive was generated by hypermail 2.0b2 on Tue Sep 15 1998 - 11:11:18 PDT

This message has been cleansed for anti-spam protection. Replace '++at++' in any mail addresses with the '@' symbol.