测试二维变换矩阵是否正交

发布于 2024-08-18 07:39:41 字数 9483 浏览 8 评论 0原文

我们发现 HP 的打印机驱动程序无法正确处理许多打印机的 PlgBlt()。

我们的目的是自己处理任何正交旋转,并且只让打印机处理缩放和平移(它似乎处理正确)。

在我即将将位图“绘制”到打印机的 DC 的代码中,我有一个可用的 2D 矩阵。

我在数学方面很弱,但我对矩阵数学的了解足以使用它们来转换 2D 或 3D 坐标。但底层的数学对我来说是不透明的。

所以,我需要的是检测给定的二维矩阵在其变换中是否正交(无论如何旋转方面)。我想问这个问题的另一种方法是:如何从二维矩阵中获取旋转向量?如果我知道以弧度或度为单位的旋转角度,我可以判断它是否正交(0,90,180,270)。

据推测,该代码在这个主题上是通用的,但下面是我们使用的代码的基础知识,以防有帮助:

typedef double ThreeByThreeMatrix[3][3];    // 3x3 for an X, Y coordinate transformation matrix

然后有一个包装器用于处理最明显的操作:

class Simple2DTransform
{
public:

    ////////////////////////////////////////////////////
    // Construction
    ////////////////////////////////////////////////////

    // we always begin life as an identity matrix (you can then apply scale, skew, etc.)
    Simple2DTransform() 
    {
        Reset(); 
    }

    ////////////////////////////////////////////////////
    // Operators
    ////////////////////////////////////////////////////

    bool operator == (const Simple2DTransform & rhs) const
    {
        return memcmp(m_matrix, rhs.m_matrix, sizeof(m_matrix)) == 0;
    }

    Simple2DTransform & operator *= (const Simple2DTransform & rhs)
    {
        return *this = GetCrossProduct(rhs);
    }

    ////////////////////////////////////////////////////
    // Setup
    ////////////////////////////////////////////////////

    // reset to the identity matrix
    Simple2DTransform & Reset()
    {
        memcpy(m_matrix, GetIdentityMatrix(), sizeof(m_matrix)); 
        return *this;
    }

    // combine with the specified translation
    Simple2DTransform & Translate(double x_shift, double y_shift)
    {
        Simple2DTransform transform;
        translate(x_shift, y_shift, transform.m_matrix);
        return *this *= transform;
    }

    // combine with the specified operations (these are cumulative operations, so rotating twice by 1 degree gives a total of 2 degrees rotation)
    Simple2DTransform & Rotate(double radians)
    {
        Simple2DTransform transform;
        rotate(radians, transform.m_matrix);
        return *this *= transform;
    }

    // apply a heterogeneous scale factor
    Simple2DTransform & Scale(double x_scale, double y_scale)
    {
        Simple2DTransform transform;
        scale(x_scale, y_scale, transform.m_matrix);
        return *this *= transform;
    }

    // apply a homogeneous scale factor
    Simple2DTransform & Scale(double scale) 
    {
        return Scale(scale, scale); 
    }

    // apply a skew
    Simple2DTransform & Skew(double skew)
    {
        Simple2DTransform transform;
        skew_y(skew, transform.m_matrix);
        return *this *= transform;
    }

    ////////////////////////////////////////////////////
    // Queries
    ////////////////////////////////////////////////////

    // return the cross product of this and the given matrix
    Simple2DTransform GetCrossProduct(const Simple2DTransform & rhs) const
    {
        Simple2DTransform result;
        GEMM(m_matrix, rhs.m_matrix, result.m_matrix);
        return result;
    }

    // returns the inverse of ourselves
    Simple2DTransform GetInverse() const
    {
        // note: invert mucks with both matrices, so we use a copy of ourselves for it
        Simple2DTransform original(*this), inverse;
        invert(original.m_matrix, inverse.m_matrix);
        return inverse;
    }

    // derivative values
    double GetCoefficient(int i, int j) const
    {
        return m_matrix[i][j];
    }

    // return the cross product
    double GetDeterminate() const
    {
        return m_matrix[0][0]*m_matrix[1][1] - m_matrix[0][1]*m_matrix[1][0];
    }

    // returns the square root of the determinate (this ignores heterogeneous scaling factors)
    double GetScale() const
    {
        return sqrt(abs(GetDeterminate()));
    }

    // returns true if there is a scale factor
    bool IsStretched() const
    {
        return (abs(abs(m_matrix[0][0]) - abs(m_matrix[1][1])) > 1.0e-7
             || abs(abs(m_matrix[0][1]) - abs(m_matrix[1][0])) > 1.0e-7);
    }

    // true if we're the identity matrix
    bool IsIdentity() const
    {
        return memcmp(m_matrix, GetIdentityMatrix(), sizeof(m_matrix)) == 0;
    }

    // returns true if this represents the same transformation as the given subtable
    bool IsSubtableEqual(const SUBTABLE * pSubTable) const
    {
        ASSERT(pSubTable);
        if (abs(pSubTable->tran1 - m_matrix[0][0]) > 1.0e-7)
            return false;
        if (abs(pSubTable->tran2 - m_matrix[1][0]) > 1.0e-7)
            return false;
        if (abs(pSubTable->tran3 - m_matrix[0][1]) > 1.0e-7)
            return false;
        if (abs(pSubTable->tran4 - m_matrix[1][1]) > 1.0e-7)
            return false;
        return true;
    }

    ////////////////////////////////////////////////////
    // Application / Execution
    ////////////////////////////////////////////////////

    void Transform(const SimplePoint & point, SimplePoint & newpoint) const
    {
        newpoint.x = point.x * m_matrix[0][0] + point.y * m_matrix[1][0] + m_matrix[2][0];
        newpoint.y = point.x * m_matrix[0][1] + point.y * m_matrix[1][1] + m_matrix[2][1];
    }

    void Transform(SimplePoint & point) const
    {
        SimplePoint newpoint;
        Transform(point, newpoint);
        point = newpoint;
    }

    void Transform(const SimpleRect & rect, SimpleRect & newrect) const
    {
        newrect.minX = rect.minX * m_matrix[0][0] + rect.minY * m_matrix[1][0] + m_matrix[2][0];
        newrect.minY = rect.minX * m_matrix[0][1] + rect.minY * m_matrix[1][1] + m_matrix[2][1];
        newrect.maxX = rect.maxX * m_matrix[0][0] + rect.maxY * m_matrix[1][0] + m_matrix[2][0];
        newrect.maxY = rect.maxX * m_matrix[0][1] + rect.maxY * m_matrix[1][1] + m_matrix[2][1];
    }

    void Transform(SimpleRect & rect) const
    {
        SimpleRect newrect;
        Transform(rect, newrect);
        rect = newrect;
    }

    void Transform(CPoint & point) const
    {
        SimplePoint newpoint(point);
        Transform(newpoint);
        point.x = (int)(newpoint.x > 0.0 ? newpoint.x + 0.5 : newpoint.x - 0.5);
        point.y = (int)(newpoint.y > 0.0 ? newpoint.y + 0.5 : newpoint.y - 0.5);
    }

    void Transform(SimplePoint point, CPoint & transformed) const
    {
        Transform(point);
        transformed.x = (int)(point.x > 0.0 ? point.x + 0.5 : point.x - 0.5);
        transformed.y = (int)(point.y > 0.0 ? point.y + 0.5 : point.y - 0.5);
    }

    void Transform(CPoint point, SimplePoint & transformed) const
    {
        transformed = point;
        Transform(transformed);
    }

    void Transform(double dx, double dy, double & x, double & y) const
    {
        SimplePoint point(dx, dy);
        Transform(point);
        x = point.x;
        y = point.y;
    }

    void Transform(double & x, double & y) const
    {
        SimplePoint point(x, y);
        Transform(point);
        x = point.x;
        y = point.y;
    }

    SimplePoint GetTransformed(SimplePoint point) const
    {
        Transform(point);
        return point;
    }

    CPoint GetTransformed(CPoint point) const
    {
        Transform(point);
        return point;
    }

    SimpleRect GetTransformed(const CRect & rect) const
    {
        return SimpleRect(GetTransformed(SimplePoint(rect.left, rect.bottom)), GetTransformed(SimplePoint(rect.right, rect.top)));
    }

    SimpleRect GetTransformed(double x1, double y1, double x2, double y2) const
    {
        return SimpleRect(GetTransformed(SimplePoint(x1, y1)), GetTransformed(SimplePoint(x2, y2)));
    }

    double GetTransformedX(double x, double y) const
    {
        return GetTransformed(SimplePoint(x, y)).x;
    }

    double GetTransformedY(double x, double y) const
    {
        return GetTransformed(SimplePoint(x, y)).y;
    }

    double GetTransformedX(int x, int y) const
    {
        return GetTransformed(SimplePoint(x, y)).x;
    }

    double GetTransformedY(int x, int y) const
    {
        return GetTransformed(SimplePoint(x, y)).y;
    }

    double GetTransformedX(const SimplePoint & point) const
    {
        return GetTransformed(point).x;
    }

    double GetTransformedY(const SimplePoint & point) const
    {
        return GetTransformed(point).y;
    }

    int GetTransformedIntX(double x, double y) const
    {
        CPoint point;
        Transform(SimplePoint(x, y), point);
        return point.x;
    }

    int GetTransformedIntY(double x, double y) const
    {
        CPoint point;
        Transform(SimplePoint(x, y), point);
        return point.y;
    }

    int GetTransformedIntX(const SimplePoint & point) const
    {
        CPoint pt;
        Transform(point, pt);
        return pt.x;
    }

    int GetTransformedIntY(const SimplePoint & point) const
    {
        CPoint pt;
        Transform(point, pt);
        return pt.y;
    }

protected:
    ////////////////////////////////////////////////////
    // Static Class Operations
    ////////////////////////////////////////////////////

    static const ThreeByThreeMatrix & GetIdentityMatrix();

    ////////////////////////////////////////////////////
    // Instance Variables
    ////////////////////////////////////////////////////

    ThreeByThreeMatrix  m_matrix;
};

We have found that HP's printer drivers fail to handle PlgBlt()s properly for many of their printers.

Our intention is to handle any orthogonal rotations ourselves, and only have the printer handle scale and translation (which it seems to handle correctly).

I have a 2D matrix available to me at the point in code where I am about to "draw" the bitmap to the printer's DC.

I am weak as hell in math, and I understand just enough about matrix math to use them to transform 2D or 3D coordinates. But the underlying math is opaque to me.

So, what I need is to detect if a given 2D matrix is orthogonal in its transformation (the rotation aspect anyway). I suppose another way to ask this question would be: How do I get the rotation vector back out of the 2D matrix? If I knew the angle of rotation in radians or degrees, I could say whether its orthogonal or not (0,90,180,270).

Presumably, the code is generic on this subject, but below is the basics of the code we use, in case that helps:

typedef double ThreeByThreeMatrix[3][3];    // 3x3 for an X, Y coordinate transformation matrix

Then there's a wrapper for that which handles most obvious operations:

class Simple2DTransform
{
public:

    ////////////////////////////////////////////////////
    // Construction
    ////////////////////////////////////////////////////

    // we always begin life as an identity matrix (you can then apply scale, skew, etc.)
    Simple2DTransform() 
    {
        Reset(); 
    }

    ////////////////////////////////////////////////////
    // Operators
    ////////////////////////////////////////////////////

    bool operator == (const Simple2DTransform & rhs) const
    {
        return memcmp(m_matrix, rhs.m_matrix, sizeof(m_matrix)) == 0;
    }

    Simple2DTransform & operator *= (const Simple2DTransform & rhs)
    {
        return *this = GetCrossProduct(rhs);
    }

    ////////////////////////////////////////////////////
    // Setup
    ////////////////////////////////////////////////////

    // reset to the identity matrix
    Simple2DTransform & Reset()
    {
        memcpy(m_matrix, GetIdentityMatrix(), sizeof(m_matrix)); 
        return *this;
    }

    // combine with the specified translation
    Simple2DTransform & Translate(double x_shift, double y_shift)
    {
        Simple2DTransform transform;
        translate(x_shift, y_shift, transform.m_matrix);
        return *this *= transform;
    }

    // combine with the specified operations (these are cumulative operations, so rotating twice by 1 degree gives a total of 2 degrees rotation)
    Simple2DTransform & Rotate(double radians)
    {
        Simple2DTransform transform;
        rotate(radians, transform.m_matrix);
        return *this *= transform;
    }

    // apply a heterogeneous scale factor
    Simple2DTransform & Scale(double x_scale, double y_scale)
    {
        Simple2DTransform transform;
        scale(x_scale, y_scale, transform.m_matrix);
        return *this *= transform;
    }

    // apply a homogeneous scale factor
    Simple2DTransform & Scale(double scale) 
    {
        return Scale(scale, scale); 
    }

    // apply a skew
    Simple2DTransform & Skew(double skew)
    {
        Simple2DTransform transform;
        skew_y(skew, transform.m_matrix);
        return *this *= transform;
    }

    ////////////////////////////////////////////////////
    // Queries
    ////////////////////////////////////////////////////

    // return the cross product of this and the given matrix
    Simple2DTransform GetCrossProduct(const Simple2DTransform & rhs) const
    {
        Simple2DTransform result;
        GEMM(m_matrix, rhs.m_matrix, result.m_matrix);
        return result;
    }

    // returns the inverse of ourselves
    Simple2DTransform GetInverse() const
    {
        // note: invert mucks with both matrices, so we use a copy of ourselves for it
        Simple2DTransform original(*this), inverse;
        invert(original.m_matrix, inverse.m_matrix);
        return inverse;
    }

    // derivative values
    double GetCoefficient(int i, int j) const
    {
        return m_matrix[i][j];
    }

    // return the cross product
    double GetDeterminate() const
    {
        return m_matrix[0][0]*m_matrix[1][1] - m_matrix[0][1]*m_matrix[1][0];
    }

    // returns the square root of the determinate (this ignores heterogeneous scaling factors)
    double GetScale() const
    {
        return sqrt(abs(GetDeterminate()));
    }

    // returns true if there is a scale factor
    bool IsStretched() const
    {
        return (abs(abs(m_matrix[0][0]) - abs(m_matrix[1][1])) > 1.0e-7
             || abs(abs(m_matrix[0][1]) - abs(m_matrix[1][0])) > 1.0e-7);
    }

    // true if we're the identity matrix
    bool IsIdentity() const
    {
        return memcmp(m_matrix, GetIdentityMatrix(), sizeof(m_matrix)) == 0;
    }

    // returns true if this represents the same transformation as the given subtable
    bool IsSubtableEqual(const SUBTABLE * pSubTable) const
    {
        ASSERT(pSubTable);
        if (abs(pSubTable->tran1 - m_matrix[0][0]) > 1.0e-7)
            return false;
        if (abs(pSubTable->tran2 - m_matrix[1][0]) > 1.0e-7)
            return false;
        if (abs(pSubTable->tran3 - m_matrix[0][1]) > 1.0e-7)
            return false;
        if (abs(pSubTable->tran4 - m_matrix[1][1]) > 1.0e-7)
            return false;
        return true;
    }

    ////////////////////////////////////////////////////
    // Application / Execution
    ////////////////////////////////////////////////////

    void Transform(const SimplePoint & point, SimplePoint & newpoint) const
    {
        newpoint.x = point.x * m_matrix[0][0] + point.y * m_matrix[1][0] + m_matrix[2][0];
        newpoint.y = point.x * m_matrix[0][1] + point.y * m_matrix[1][1] + m_matrix[2][1];
    }

    void Transform(SimplePoint & point) const
    {
        SimplePoint newpoint;
        Transform(point, newpoint);
        point = newpoint;
    }

    void Transform(const SimpleRect & rect, SimpleRect & newrect) const
    {
        newrect.minX = rect.minX * m_matrix[0][0] + rect.minY * m_matrix[1][0] + m_matrix[2][0];
        newrect.minY = rect.minX * m_matrix[0][1] + rect.minY * m_matrix[1][1] + m_matrix[2][1];
        newrect.maxX = rect.maxX * m_matrix[0][0] + rect.maxY * m_matrix[1][0] + m_matrix[2][0];
        newrect.maxY = rect.maxX * m_matrix[0][1] + rect.maxY * m_matrix[1][1] + m_matrix[2][1];
    }

    void Transform(SimpleRect & rect) const
    {
        SimpleRect newrect;
        Transform(rect, newrect);
        rect = newrect;
    }

    void Transform(CPoint & point) const
    {
        SimplePoint newpoint(point);
        Transform(newpoint);
        point.x = (int)(newpoint.x > 0.0 ? newpoint.x + 0.5 : newpoint.x - 0.5);
        point.y = (int)(newpoint.y > 0.0 ? newpoint.y + 0.5 : newpoint.y - 0.5);
    }

    void Transform(SimplePoint point, CPoint & transformed) const
    {
        Transform(point);
        transformed.x = (int)(point.x > 0.0 ? point.x + 0.5 : point.x - 0.5);
        transformed.y = (int)(point.y > 0.0 ? point.y + 0.5 : point.y - 0.5);
    }

    void Transform(CPoint point, SimplePoint & transformed) const
    {
        transformed = point;
        Transform(transformed);
    }

    void Transform(double dx, double dy, double & x, double & y) const
    {
        SimplePoint point(dx, dy);
        Transform(point);
        x = point.x;
        y = point.y;
    }

    void Transform(double & x, double & y) const
    {
        SimplePoint point(x, y);
        Transform(point);
        x = point.x;
        y = point.y;
    }

    SimplePoint GetTransformed(SimplePoint point) const
    {
        Transform(point);
        return point;
    }

    CPoint GetTransformed(CPoint point) const
    {
        Transform(point);
        return point;
    }

    SimpleRect GetTransformed(const CRect & rect) const
    {
        return SimpleRect(GetTransformed(SimplePoint(rect.left, rect.bottom)), GetTransformed(SimplePoint(rect.right, rect.top)));
    }

    SimpleRect GetTransformed(double x1, double y1, double x2, double y2) const
    {
        return SimpleRect(GetTransformed(SimplePoint(x1, y1)), GetTransformed(SimplePoint(x2, y2)));
    }

    double GetTransformedX(double x, double y) const
    {
        return GetTransformed(SimplePoint(x, y)).x;
    }

    double GetTransformedY(double x, double y) const
    {
        return GetTransformed(SimplePoint(x, y)).y;
    }

    double GetTransformedX(int x, int y) const
    {
        return GetTransformed(SimplePoint(x, y)).x;
    }

    double GetTransformedY(int x, int y) const
    {
        return GetTransformed(SimplePoint(x, y)).y;
    }

    double GetTransformedX(const SimplePoint & point) const
    {
        return GetTransformed(point).x;
    }

    double GetTransformedY(const SimplePoint & point) const
    {
        return GetTransformed(point).y;
    }

    int GetTransformedIntX(double x, double y) const
    {
        CPoint point;
        Transform(SimplePoint(x, y), point);
        return point.x;
    }

    int GetTransformedIntY(double x, double y) const
    {
        CPoint point;
        Transform(SimplePoint(x, y), point);
        return point.y;
    }

    int GetTransformedIntX(const SimplePoint & point) const
    {
        CPoint pt;
        Transform(point, pt);
        return pt.x;
    }

    int GetTransformedIntY(const SimplePoint & point) const
    {
        CPoint pt;
        Transform(point, pt);
        return pt.y;
    }

protected:
    ////////////////////////////////////////////////////
    // Static Class Operations
    ////////////////////////////////////////////////////

    static const ThreeByThreeMatrix & GetIdentityMatrix();

    ////////////////////////////////////////////////////
    // Instance Variables
    ////////////////////////////////////////////////////

    ThreeByThreeMatrix  m_matrix;
};

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

任谁 2024-08-25 07:39:41

设 A = (0, 0),B = (1, 0)。将两者通过矩阵变换得到A'和B'。测量向量 B' - A' 的角度。这应该给你角度。

要测量矢量的角度,您可以使用atan2 (B'.y - A'.y, ​​B'.x - A'.x)

Let A = (0, 0), B = (1, 0). Transform both through the matrix to get A' and B'. Measure the angle of the vector B' - A'. That should give you the angle.

To measure the angle of the vector, you can use atan2 (B'.y - A'.y, B'.x - A'.x)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文