【问题标题】:iOS Objective-C: Determine current 3D rotation of a UIViewiOS Objective-C:确定 UIView 的当前 3D 旋转
【发布时间】:2015-03-09 20:42:53
【问题描述】:

我正在尝试确定 UIView 的当前 3D 旋转。我知道要获得 2D 旋转是:

CGFloat radians = atan2f(view.transform.b, view.transform.a); 

不确定如何获得当前的 3D 旋转。我希望能够自动反转动画并保留其起点。对象的旋转可能受到用户输入的先前旋转的影响,因此我不能假设它的起始值。我将最后一个用户定义的角度存储在对象的属性上,我认为这在实践中很好,但我很想知道如何获取信息。我对此进行了很多研究,但找不到答案。

【问题讨论】:

    标签: ios objective-c core-animation quartz-graphics


    【解决方案1】:

    在一般情况下,4x4 矩阵可能包含透视、(非均匀)缩放、(非均匀)剪切、旋转(围绕可能不是轴对齐的向量)和平移。如果要在一般情况下提取旋转,则需要将矩阵分解为这些“简单”变换中的每一个的分量。

    Graphics Gems II §VII.1 描述了一种算法。附录二提供了算法的代码。代码可以在online here找到。头文件是here。您还需要一些支持文件,例如“C 实用程序”,所以check this link too

    这本书的前言声明代码在公共领域,所以我在下面复制它。

    /* unmatrix.c - given a 4x4 matrix, decompose it into standard operations.
     *
     * Author:  Spencer W. Thomas
     *      University of Michigan
     */
    #include <math.h>
    #include "GraphicsGems.h"
    #include "unmatrix.h"
    
    /* unmatrix - 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.
     */
    int
    unmatrix( mat, tran )
    Matrix4 *mat;
    double tran[16];
    {
        register int i, j;
        Matrix4 locmat;
        Matrix4 pmat, invpmat, tinvpmat;
        /* Vector4 type and functions need to be added to the common set. */
        Vector4 prhs, psol;
        Point3 row[3], pdum3;
    
        locmat = *mat;
        /* Normalize the matrix. */
        if ( locmat.element[3][3] == 0 )
            return 0;
        for ( i=0; i<4;i++ )
            for ( j=0; j<4; j++ )
                locmat.element[i][j] /= locmat.element[3][3];
        /* 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.element[i][3] = 0;
        pmat.element[3][3] = 1;
    
        if ( det4x4(&pmat) == 0.0 )
            return 0;
    
        /* First, isolate perspective.  This is the messiest. */
        if ( locmat.element[0][3] != 0 || locmat.element[1][3] != 0 ||
            locmat.element[2][3] != 0 ) {
            /* prhs is the right hand side of the equation. */
            prhs.x = locmat.element[0][3];
            prhs.y = locmat.element[1][3];
            prhs.z = locmat.element[2][3];
            prhs.w = locmat.element[3][3];
    
            /* 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.
             */
            inverse( &pmat, &invpmat );
            TransposeMatrix4( &invpmat, &tinvpmat );
            V4MulPointByMatrix(&prhs, &tinvpmat, &psol);
    
            /* Stuff the answer away. */
            tran[U_PERSPX] = psol.x;
            tran[U_PERSPY] = psol.y;
            tran[U_PERSPZ] = psol.z;
            tran[U_PERSPW] = psol.w;
            /* Clear the perspective partition. */
            locmat.element[0][3] = locmat.element[1][3] =
                locmat.element[2][3] = 0;
            locmat.element[3][3] = 1;
        } else      /* No perspective. */
            tran[U_PERSPX] = tran[U_PERSPY] = tran[U_PERSPZ] =
                tran[U_PERSPW] = 0;
    
        /* Next take care of translation (easy). */
        for ( i=0; i<3; i++ ) {
            tran[U_TRANSX + i] = locmat.element[3][i];
            locmat.element[3][i] = 0;
        }
    
        /* Now get scale and shear. */
        for ( i=0; i<3; i++ ) {
            row[i].x = locmat.element[i][0];
            row[i].y = locmat.element[i][1];
            row[i].z = locmat.element[i][2];
        }
    
        /* Compute X scale factor and normalize first row. */
        tran[U_SCALEX] = V3Length(&row[0]);
        row[0] = *V3Scale(&row[0], 1.0);
    
        /* Compute XY shear factor and make 2nd row orthogonal to 1st. */
        tran[U_SHEARXY] = V3Dot(&row[0], &row[1]);
        (void)V3Combine(&row[1], &row[0], &row[1], 1.0, -tran[U_SHEARXY]);
    
        /* Now, compute Y scale and normalize 2nd row. */
        tran[U_SCALEY] = V3Length(&row[1]);
        V3Scale(&row[1], 1.0);
        tran[U_SHEARXY] /= tran[U_SCALEY];
    
        /* Compute XZ and YZ shears, orthogonalize 3rd row. */
        tran[U_SHEARXZ] = V3Dot(&row[0], &row[2]);
        (void)V3Combine(&row[2], &row[0], &row[2], 1.0, -tran[U_SHEARXZ]);
        tran[U_SHEARYZ] = V3Dot(&row[1], &row[2]);
        (void)V3Combine(&row[2], &row[1], &row[2], 1.0, -tran[U_SHEARYZ]);
    
        /* Next, get Z scale and normalize 3rd row. */
        tran[U_SCALEZ] = V3Length(&row[2]);
        V3Scale(&row[2], 1.0);
        tran[U_SHEARXZ] /= tran[U_SCALEZ];
        tran[U_SHEARYZ] /= tran[U_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 ( V3Dot( &row[0], V3Cross( &row[1], &row[2], &pdum3) ) < 0 )
            for ( i = 0; i < 3; i++ ) {
                tran[U_SCALEX+i] *= -1;
                row[i].x *= -1;
                row[i].y *= -1;
                row[i].z *= -1;
            }
    
        /* Now, get the rotations out, as described in the gem. */
        tran[U_ROTATEY] = asin(-row[0].z);
        if ( cos(tran[U_ROTATEY]) != 0 ) {
            tran[U_ROTATEX] = atan2(row[1].z, row[2].z);
            tran[U_ROTATEZ] = atan2(row[0].y, row[0].x);
        } else {
            tran[U_ROTATEX] = atan2(-row[2].x, row[1].y);
            tran[U_ROTATEZ] = 0;
        }
        /* All done! */
        return 1;
    }
    
    
    /* transpose rotation portion of matrix a, return b */
    Matrix4 *TransposeMatrix4(a, b)
    Matrix4 *a, *b;
    {
    int i, j;
        for (i=0; i<4; i++)
            for (j=0; j<4; j++)
                b->element[i][j] = a->element[j][i];
        return(b);
    }
    
    /* multiply a hom. point by a matrix and return the transformed point */
    Vector4 *V4MulPointByMatrix(pin, m, pout)
    Vector4 *pin, *pout;
    Matrix4 *m;
    {
        pout->x = (pin->x * m->element[0][0]) + (pin->y * m->element[1][0]) +
            (pin->z * m->element[2][0]) + (pin->w * m->element[3][0]);
        pout->y = (pin->x * m->element[0][1]) + (pin->y * m->element[1][1]) +
            (pin->z * m->element[2][1]) + (pin->w * m->element[3][1]);
        pout->z = (pin->x * m->element[0][2]) + (pin->y * m->element[1][2]) +
            (pin->z * m->element[2][2]) + (pin->w * m->element[3][2]);
        pout->w = (pin->x * m->element[0][3]) + (pin->y * m->element[1][3]) +
            (pin->z * m->element[2][3]) + (pin->w * m->element[3][3]);
            return(pout);
    }
    

    Graphics Gems 代码生成欧拉角。有时,您需要 3D 旋转的不同表示。特别是,四元数通常更方便。您可以在TransformationMatrix.cppdecompose 函数中找到此分解的WebCore 实现。它为旋转产生一个四元数。

    【讨论】:

    • 谢谢!感谢您的帮助。
    • 在应用这些旋转时保存旋转信息,然后读取保存的值,而不是尝试从变换矩阵中提取它们,可能会更干净、更快捷。
    • @DuncanC 我已经这样做了,但我想知道计算的重量级。 3d 数学不是我的强项,我想改进它。
    猜你喜欢
    • 1970-01-01
    • 2012-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多