On Fri, Sep 26, 2014 at 04:10:23PM -0500, Derek Foreman wrote:
> Adds an internal weston_matrix_decompose() which takes a matrix and decomposes
> translation, rotation, shear and scale parameters to re-create it.
> 
> This information is cached the first time any helper functions are used to 
> extract
> these values and dirtied whenever a matrix multiplication takes place.
> 
> Note that these may not be the same values used to compose the matrix in the 
> first
> place.
> ---
>  shared/matrix.c | 359 
> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  shared/matrix.h |  31 +++++
>  2 files changed, 390 insertions(+)
> 
> diff --git a/shared/matrix.c b/shared/matrix.c
> index 4f0b6b7..dafffa5 100644
> --- a/shared/matrix.c
> +++ b/shared/matrix.c
> @@ -27,6 +27,7 @@
>  #include <string.h>
>  #include <stdlib.h>
>  #include <math.h>
> +#include <stdbool.h>
>  
>  #ifdef IN_WESTON
>  #include <wayland-server.h>
> @@ -51,6 +52,7 @@ weston_matrix_init(struct weston_matrix *matrix)
>       static const struct weston_matrix identity = {
>               .d = { 1, 0, 0, 0,  0, 1, 0, 0,  0, 0, 1, 0,  0, 0, 0, 1 },
>               .type = 0,
> +             .dirty = true,
>       };
>  
>       memcpy(matrix, &identity, sizeof identity);
> @@ -74,6 +76,7 @@ weston_matrix_multiply(struct weston_matrix *m, const 
> struct weston_matrix *n)
>                       tmp.d[i] += row[j] * column[j * 4];
>       }
>       tmp.type = m->type | n->type;
> +     tmp.dirty = true;
>       memcpy(m, &tmp, sizeof tmp);
>  }
>  
> @@ -271,3 +274,359 @@ weston_matrix_invert(struct weston_matrix *inverse,
>  
>       return 0;
>  }
> +
> +/*
> + * Some of the following matrix/vector operations are taken
> + * from Graphics Gems I. In the process of converting them
> + * to weston data types, some of the original generality may
> + * have been destroyed. None of them should be used for
> + * general purpose operations without careful inspection,
> + * as soom only operate on parts of vectors, or do nothing

some

> + * to ensure input matrices can be use as output matrices...
> + */
> +
> +static void
> +transpose(struct weston_matrix *dst, struct weston_matrix *src)
> +{
> +     int i,j;
> +
> +     for (i = 0; i < 4; i++)
> +             for (j = 0; j < 4; j++)
> +                     dst->d[i * 4 + j] = src->d[i + j * 4];
> +}
> +
> +static double
> +dotn(int n, struct weston_vector *a, struct weston_vector *b)
> +{
> +     int i;
> +     double out = 0.0;
> +
> +     for (i = 0; i < n; i++)
> +             out += a->f[i] * b->f[i];
> +
> +     return out;
> +}

for weston_vectors, n = 3 always, right?  Why not simplify it down
further and just

 +static double
 +dot(struct weston_vector *a, struct weston_vector *b)
 +{
 +      return a->f[0] * b->f[0] +
 +             a->f[1] * b->f[1] +
 +             a->f[2] * b->f[2];
 +}

> +static struct weston_vector *
> +cross3(struct weston_vector *a, struct weston_vector *b, struct 
> weston_vector *out)
> +{
> +     out->f[0] = a->f[1] * b->f[2] - a->f[2] * b->f[1];
> +     out->f[1] = a->f[2] * b->f[0] - a->f[0] * b->f[2];
> +     out->f[2] = a->f[0] * b->f[1] - a->f[1] * b->f[0];
> +     out->f[3] = 1.0;
> +
> +     return out;
> +}
> +
> +static double
> +weston_vector_length(struct weston_vector *in)
> +{
> +     return sqrt(in->f[0] * in->f[0] +
> +                 in->f[1] * in->f[1] +
> +                 in->f[2] * in->f[2]);
> +}

Can't we do:

        return sqrt( dot(in, in) );

> +static void
> +weston_vector_normalize(struct weston_vector *in)
> +{
> +     double len = weston_vector_length(in);
> +     if (len == 0)
> +             len = 1;
> +     in->f[0] = in->f[0] / len;
> +     in->f[1] = in->f[1] / len;
> +     in->f[2] = in->f[2] / len;
> +}
> +
> +static void
> +weston_vector_combine(struct weston_vector *out,
> +                               struct weston_vector *a,
> +                               struct weston_vector *b,
> +                               double scale_a,
> +                               double scale_b)
> +{
> +     out->f[0] = scale_a * a->f[0] + scale_b * b->f[0];
> +     out->f[1] = scale_a * a->f[1] + scale_b * b->f[1];
> +     out->f[2] = scale_a * a->f[2] + scale_b * b->f[2];
> +}
> +
> +static double
> +det2x2(double a, double b, double c, double d)
> +{
> +     return a * d - b * c;
> +}
> +
> +static double
> +det3x3(double a1, double a2, double a3,
> +       double b1, double b2, double b3,
> +       double c1, double c2, double c3 )
> +{
> +     return a1 * det2x2( b2, b3, c2, c3 )
> +          - b1 * det2x2( a2, a3, c2, c3 )
> +          + c1 * det2x2( a2, a3, b2, b3 );
> +}
> +
> +/*
> + * double = det4x4( matrix )
> + *
> + * calculate the determinant of a 4x4 matrix.
> + */
> +static double
> +det4x4(struct weston_matrix *m)
> +{
> +     double a1, a2, a3, a4, b1, b2, b3, b4, c1, c2, c3, c4, d1, d2, d3, d4;
> +
> +     /* assign to individual variable names to aid selecting */
> +     /*  correct elements */
> +
> +     a1 = m->d[0];
> +     b1 = m->d[1];
> +     c1 = m->d[2];
> +     d1 = m->d[3];
> +
> +     a2 = m->d[4];
> +     b2 = m->d[5];
> +     c2 = m->d[6];
> +     d2 = m->d[7];
> +
> +     a3 = m->d[8];
> +     b3 = m->d[9];
> +     c3 = m->d[10];
> +     d3 = m->d[11];
> +
> +     a4 = m->d[12];
> +     b4 = m->d[13];
> +     c4 = m->d[14];
> +     d4 = m->d[15];
> +
> +     return a1 * det3x3( b2, b3, b4, c2, c3, c4, d2, d3, d4)
> +          - b1 * det3x3( a2, a3, a4, c2, c3, c4, d2, d3, d4)
> +          + c1 * det3x3( a2, a3, a4, b2, b3, b4, d2, d3, d4)
> +          - d1 * det3x3( a2, a3, a4, b2, b3, b4, c2, c3, c4);
> +}
> +
> +/* Based on code from Graphics Gems II:
> + * unmatrix.c - given a 4x4 matrix, decompose it into standard operations.
> + *
> + * Author:   Spencer W. Thomas
> + *           University of Michigan

Is he providing the code under open source friendly terms?
Might want to be explicit about it if so.

> + */
> +enum weston_decomposition_indices {
> +     WESTON_MATRIX_SCALEX,
> +     WESTON_MATRIX_SCALEY,
> +     WESTON_MATRIX_SCALEZ,
> +     WESTON_MATRIX_SHEARXY,
> +     WESTON_MATRIX_SHEARXZ,
> +     WESTON_MATRIX_SHEARYZ,
> +     WESTON_MATRIX_ROTATEX,
> +     WESTON_MATRIX_ROTATEY,
> +     WESTON_MATRIX_ROTATEZ,
> +     WESTON_MATRIX_TRANSX,
> +     WESTON_MATRIX_TRANSY,
> +     WESTON_MATRIX_TRANSZ,
> +     WESTON_MATRIX_PERSPX,
> +     WESTON_MATRIX_PERSPY,
> +     WESTON_MATRIX_PERSPZ,
> +     WESTON_MATRIX_PERSPW
> +};
> +
> +/* Decompose a non-degenerate 4x4 transformation matrix into
> + *   the sequence of transformations that produced it.
> + * [Sx][Sy][Sz][Shearx/y][Sx/z][Sz/y][Rx][Ry][Rz][Tx][Ty][Tz][P(x,y,z,w)]
> + *
> + * The coefficient of each transformation is returned in the corresponding
> + * element of the vector tran.
> + *
> + * Returns 1 upon success, 0 if the matrix is singular.
> + */
> +static int
> +weston_matrix_decompose(struct weston_matrix *mat)
> +{
> +     int i;
> +     struct weston_matrix locmat;
> +     struct weston_matrix pmat, invpmat, tinvpmat;
> +     struct weston_vector prhs;
> +     struct weston_vector row[3], pdum3;
> +     float *tran = mat->decomposition;
> +
> +     if (!mat->dirty) {
> +             return !mat->singular;
> +     }
> +     mat->dirty = false;
> +     locmat = *mat;
> +     /* Normalize the matrix. */
> +     if (locmat.d[15] == 0)
> +             goto singular;
> +
> +     for (i = 0; i < 16; i++ )
> +             locmat.d[i] /= locmat.d[15];
> +     /* pmat is used to solve for perspective, but it also provides
> +      * an easy way to test for singularity of the upper 3x3 component.
> +      */
> +     pmat = locmat;
> +     for (i = 0; i < 3; i++)
> +             pmat.d[i * 4 + 3] = 0;
> +     pmat.d[15] = 1;
> +
> +     if (det4x4(&pmat) == 0.0)
> +             goto singular;
> +
> +     /* First, isolate perspective.  This is the messiest. */
> +     if (locmat.d[3] != 0 || locmat.d[7] != 0 || locmat.d[11] != 0 ) {
> +             /* prhs is the right hand side of the equation. */
> +             prhs.f[0] = locmat.d[3];
> +             prhs.f[1] = locmat.d[7];
> +             prhs.f[2] = locmat.d[11];
> +             prhs.f[3] = locmat.d[15];
> +
> +             /* Solve the equation by inverting pmat and multiplying
> +              * prhs by the inverse.  (This is the easiest way, not
> +              * necessarily the best.)
> +              * inverse function (and det4x4, above) from the Matrix
> +              * Inversion gem in the first volume.
> +              */
> +             if (weston_matrix_invert(&invpmat, &pmat) < 0)
> +                     goto singular;
> +             transpose(&tinvpmat, &invpmat);
> +             weston_matrix_transform(&tinvpmat, &prhs);
> +
> +             /* Stuff the answer away. */
> +             tran[WESTON_MATRIX_PERSPX] = prhs.f[0];
> +             tran[WESTON_MATRIX_PERSPY] = prhs.f[1];
> +             tran[WESTON_MATRIX_PERSPZ] = prhs.f[2];
> +             tran[WESTON_MATRIX_PERSPW] = prhs.f[3];
> +             /* Clear the perspective partition. */
> +             locmat.d[3] = locmat.d[7] = locmat.d[11] = 0;
> +             locmat.d[15] = 1;
> +     } else          /* No perspective. */
> +             tran[WESTON_MATRIX_PERSPX] = tran[WESTON_MATRIX_PERSPY]
> +                                        = tran[WESTON_MATRIX_PERSPZ]
> +                                        = tran[WESTON_MATRIX_PERSPW] = 0;
> +
> +     /* Next take care of translation (easy). */
> +     for (i = 0; i < 3; i++) {
> +             tran[WESTON_MATRIX_TRANSX + i] = locmat.d[12 + i];
> +             locmat.d[12 + i] = 0;
> +     }
> +
> +     /* Now get scale and shear. */
> +     for (i = 0; i < 3; i++) {
> +             row[i].f[0] = locmat.d[i * 4];
> +             row[i].f[1] = locmat.d[i * 4 + 1];
> +             row[i].f[2] = locmat.d[i * 4 + 2];
> +     }
> +
> +     /* Compute X scale factor and normalize first row. */
> +     tran[WESTON_MATRIX_SCALEX] = weston_vector_length(&row[0]);
> +     weston_vector_normalize(&row[0]);
> +
> +     /* Compute XY shear factor and make 2nd row orthogonal to 1st. */
> +     tran[WESTON_MATRIX_SHEARXY] = dotn(3, &row[0], &row[1]);
> +     weston_vector_combine(&row[1], &row[1], &row[0], 1.0, 
> -tran[WESTON_MATRIX_SHEARXY]);
> +
> +     /* Now, compute Y scale and normalize 2nd row. */
> +     tran[WESTON_MATRIX_SCALEY] = weston_vector_length(&row[1]);
> +     weston_vector_normalize(&row[1]);
> +     tran[WESTON_MATRIX_SHEARXY] /= tran[WESTON_MATRIX_SCALEY];
> +
> +     /* Compute XZ and YZ shears, orthogonalize 3rd row. */
> +     tran[WESTON_MATRIX_SHEARXZ] = dotn(3, &row[0], &row[2]);
> +     weston_vector_combine(&row[2], &row[2], &row[0], 1.0, 
> -tran[WESTON_MATRIX_SHEARXZ]);
> +     tran[WESTON_MATRIX_SHEARYZ] = dotn(3, &row[1], &row[2]);
> +     weston_vector_combine(&row[2], &row[2], &row[1], 1.0, 
> -tran[WESTON_MATRIX_SHEARYZ]);
> +
> +     /* Next, get Z scale and normalize 3rd row. */
> +     tran[WESTON_MATRIX_SCALEZ] = weston_vector_length(&row[2]);
> +     weston_vector_normalize(&row[2]);
> +     tran[WESTON_MATRIX_SHEARXZ] /= tran[WESTON_MATRIX_SCALEZ];
> +     tran[WESTON_MATRIX_SHEARYZ] /= tran[WESTON_MATRIX_SCALEZ];
> +
> +     /* At this point, the matrix (in rows[]) is orthonormal.
> +      * Check for a coordinate system flip.  If the determinant
> +      * is -1, then negate the matrix and the scaling factors.
> +      */
> +     if ( dotn(3, &row[0], cross3(&row[1], &row[2], &pdum3) ) < 0 )
> +             for ( i = 0; i < 3; i++ ) {
> +                     tran[WESTON_MATRIX_SCALEX+i] *= -1;
> +                     row[i].f[0] *= -1;
> +                     row[i].f[1] *= -1;
> +                     row[i].f[2] *= -1;
> +             }
> +
> +     /* Now, get the rotations out, as described in the gem. */
> +     tran[WESTON_MATRIX_ROTATEY] = asin(-row[0].f[2]);
> +     if ( cos(tran[WESTON_MATRIX_ROTATEY]) != 0 ) {
> +             tran[WESTON_MATRIX_ROTATEX] = atan2(row[1].f[2], row[2].f[2]);
> +             tran[WESTON_MATRIX_ROTATEZ] = atan2(row[0].f[1], row[0].f[0]);
> +     } else {
> +             tran[WESTON_MATRIX_ROTATEX] = atan2(-row[2].f[0], row[1].f[1]);
> +             tran[WESTON_MATRIX_ROTATEZ] = 0;
> +     }
> +     /* All done! */
> +     return 1;
> +singular:
> +     mat->singular = true;
> +     return 0;
> +}
> +
> +WL_EXPORT float
> +weston_matrix_get_scale_x(struct weston_matrix *matrix)
> +{
> +     weston_matrix_decompose(matrix);
> +     return matrix->decomposition[WESTON_MATRIX_SCALEX];
> +}
> +
> +WL_EXPORT float
> +weston_matrix_get_scale_y(struct weston_matrix *matrix)
> +{
> +     weston_matrix_decompose(matrix);
> +     return matrix->decomposition[WESTON_MATRIX_SCALEY];
> +}
> +
> +WL_EXPORT float
> +weston_matrix_get_scale_z(struct weston_matrix *matrix)
> +{
> +     weston_matrix_decompose(matrix);
> +     return matrix->decomposition[WESTON_MATRIX_SCALEZ];
> +}
> +
> +WL_EXPORT float
> +weston_matrix_get_translation_x(struct weston_matrix *matrix)
> +{
> +     weston_matrix_decompose(matrix);
> +     return matrix->decomposition[WESTON_MATRIX_TRANSX];
> +}
> +
> +WL_EXPORT float
> +weston_matrix_get_translation_y(struct weston_matrix *matrix)
> +{
> +     weston_matrix_decompose(matrix);
> +     return matrix->decomposition[WESTON_MATRIX_TRANSY];
> +}
> +
> +WL_EXPORT float
> +weston_matrix_get_translation_z(struct weston_matrix *matrix)
> +{
> +     weston_matrix_decompose(matrix);
> +     return matrix->decomposition[WESTON_MATRIX_TRANSZ];
> +}
> +
> +WL_EXPORT float
> +weston_matrix_get_rotation_x(struct weston_matrix *matrix)
> +{
> +     weston_matrix_decompose(matrix);
> +     return matrix->decomposition[WESTON_MATRIX_ROTATEX];
> +}
> +
> +WL_EXPORT float
> +weston_matrix_get_rotation_y(struct weston_matrix *matrix)
> +{
> +     weston_matrix_decompose(matrix);
> +     return matrix->decomposition[WESTON_MATRIX_ROTATEY];
> +}
> +
> +WL_EXPORT float
> +weston_matrix_get_rotation_z(struct weston_matrix *matrix)
> +{
> +     weston_matrix_decompose(matrix);
> +     return matrix->decomposition[WESTON_MATRIX_ROTATEZ];
> +}
> diff --git a/shared/matrix.h b/shared/matrix.h
> index e5cf636..d771ac0 100644
> --- a/shared/matrix.h
> +++ b/shared/matrix.h
> @@ -28,6 +28,8 @@
>  extern "C" {
>  #endif
>  
> +#include <stdbool.h>
> +
>  enum weston_matrix_transform_type {
>       WESTON_MATRIX_TRANSFORM_TRANSLATE       = (1 << 0),
>       WESTON_MATRIX_TRANSFORM_SCALE           = (1 << 1),
> @@ -38,6 +40,9 @@ enum weston_matrix_transform_type {
>  struct weston_matrix {
>       float d[16];
>       unsigned int type;
> +     bool dirty;
> +     bool singular;
> +     float decomposition[16];
>  };
>  
>  struct weston_vector {
> @@ -61,6 +66,32 @@ weston_matrix_transform(struct weston_matrix *matrix, 
> struct weston_vector *v);
>  int
>  weston_matrix_invert(struct weston_matrix *inverse,
>                    const struct weston_matrix *matrix);
> +float
> +weston_matrix_get_scale_x(struct weston_matrix *matrix);
> +
> +float
> +weston_matrix_get_scale_y(struct weston_matrix *matrix);
> +
> +float
> +weston_matrix_get_scale_z(struct weston_matrix *matrix);
> +
> +float
> +weston_matrix_get_translation_x(struct weston_matrix *matrix);
> +
> +float
> +weston_matrix_get_translation_y(struct weston_matrix *matrix);
> +
> +float
> +weston_matrix_get_translation_z(struct weston_matrix *matrix);
> +
> +float
> +weston_matrix_get_rotation_x(struct weston_matrix *matrix);
> +
> +float
> +weston_matrix_get_rotation_y(struct weston_matrix *matrix);
> +
> +float
> +weston_matrix_get_rotation_z(struct weston_matrix *matrix);
>  
>  #ifdef UNIT_TEST
>  #  define MATRIX_TEST_EXPORT WL_EXPORT

Other than these two trivial things, this all looks like it'll make for
a great refactoring.  I read through all the rest of the patches but
didn't see anything to comment on, so for the set:

Reviewed-by: Bryce Harrington <br...@osg.samsung.com>

> -- 
> 2.1.0
> 
> _______________________________________________
> wayland-devel mailing list
> wayland-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/wayland-devel
_______________________________________________
wayland-devel mailing list
wayland-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to