Several fixes for wxAffineMatrix2D transformations.
Correct errors in TransformPoint() and TransformDistance(). Change Rotate() to interpret positive angles as rotating clockwise, for consistency with wxGraphicsContext::Rotate(). Improve the unit test to verify that all the transformations work correctly. Closes #14334. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@71555 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -36,7 +36,7 @@ public:
|
|||||||
virtual bool IsEqual(const wxAffineMatrix2DBase& t) const;
|
virtual bool IsEqual(const wxAffineMatrix2DBase& t) const;
|
||||||
virtual void Translate(wxDouble dx, wxDouble dy);
|
virtual void Translate(wxDouble dx, wxDouble dy);
|
||||||
virtual void Scale(wxDouble xScale, wxDouble yScale);
|
virtual void Scale(wxDouble xScale, wxDouble yScale);
|
||||||
virtual void Rotate(wxDouble ccRadians);
|
virtual void Rotate(wxDouble cRadians);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual wxPoint2DDouble DoTransformPoint(const wxPoint2DDouble& p) const;
|
virtual wxPoint2DDouble DoTransformPoint(const wxPoint2DDouble& p) const;
|
||||||
|
@@ -107,6 +107,12 @@ public:
|
|||||||
The translation in x direction.
|
The translation in x direction.
|
||||||
@param dy
|
@param dy
|
||||||
The translation in y direction.
|
The translation in y direction.
|
||||||
|
|
||||||
|
@code
|
||||||
|
// | 1 0 0 | | m_11 m_12 0 |
|
||||||
|
// matrix' = | 0 1 0 | x | m_21 m_22 0 |
|
||||||
|
// | dx dy 1 | | m_tx m_ty 1 |
|
||||||
|
@endcode
|
||||||
*/
|
*/
|
||||||
void Translate(wxDouble dx, wxDouble dy);
|
void Translate(wxDouble dx, wxDouble dy);
|
||||||
|
|
||||||
@@ -117,6 +123,12 @@ public:
|
|||||||
Scaling in x direction.
|
Scaling in x direction.
|
||||||
@param yScale
|
@param yScale
|
||||||
Scaling in y direction.
|
Scaling in y direction.
|
||||||
|
|
||||||
|
@code
|
||||||
|
// | xScale 0 0 | | m_11 m_12 0 |
|
||||||
|
// matrix' = | 0 yScale 0 | x | m_21 m_22 0 |
|
||||||
|
// | 0 0 1 | | m_tx m_ty 1 |
|
||||||
|
@endcode
|
||||||
*/
|
*/
|
||||||
void Scale(wxDouble xScale, wxDouble yScale);
|
void Scale(wxDouble xScale, wxDouble yScale);
|
||||||
|
|
||||||
@@ -130,12 +142,18 @@ public:
|
|||||||
void Mirror(int direction = wxHORIZONTAL);
|
void Mirror(int direction = wxHORIZONTAL);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Add counter clockwise rotation to this matrix.
|
Add clockwise rotation to this matrix.
|
||||||
|
|
||||||
@param ccRadians
|
@param cRadians
|
||||||
Rotation angle in radians.
|
Rotation angle in radians, clockwise.
|
||||||
|
|
||||||
|
@code
|
||||||
|
// | cos sin 0 | | m_11 m_12 0 |
|
||||||
|
// matrix' = | -sin cos 0 | x | m_21 m_22 0 |
|
||||||
|
// | 0 0 1 | | m_tx m_ty 1 |
|
||||||
|
@endcode
|
||||||
*/
|
*/
|
||||||
void Rotate(wxDouble ccRadians);
|
void Rotate(wxDouble cRadians);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Applies this matrix to the point.
|
Applies this matrix to the point.
|
||||||
@@ -144,6 +162,12 @@ public:
|
|||||||
The point receiving the transformations.
|
The point receiving the transformations.
|
||||||
|
|
||||||
@return The point with the transformations applied.
|
@return The point with the transformations applied.
|
||||||
|
|
||||||
|
@code
|
||||||
|
// | m_11 m_12 0 |
|
||||||
|
// point' = | src.m_x src._my 1 | x | m_21 m_22 0 |
|
||||||
|
// | m_tx m_ty 1 |
|
||||||
|
@endcode
|
||||||
*/
|
*/
|
||||||
wxPoint2DDouble TransformPoint(const wxPoint2DDouble& p) const;
|
wxPoint2DDouble TransformPoint(const wxPoint2DDouble& p) const;
|
||||||
void TransformPoint(wxDouble* x, wxDouble* y) const;
|
void TransformPoint(wxDouble* x, wxDouble* y) const;
|
||||||
@@ -155,6 +179,12 @@ public:
|
|||||||
The source receiving the transformations.
|
The source receiving the transformations.
|
||||||
|
|
||||||
@return The source with the transformations applied.
|
@return The source with the transformations applied.
|
||||||
|
|
||||||
|
@code
|
||||||
|
// | m_11 m_12 0 |
|
||||||
|
// dist' = | src.m_x src._my 0 | x | m_21 m_22 0 |
|
||||||
|
// | m_tx m_ty 1 |
|
||||||
|
@endcode
|
||||||
*/
|
*/
|
||||||
wxPoint2DDouble TransformDistance(const wxPoint2DDouble& p) const;
|
wxPoint2DDouble TransformDistance(const wxPoint2DDouble& p) const;
|
||||||
void TransformDistance(wxDouble* dx, wxDouble* dy) const;
|
void TransformDistance(wxDouble* dx, wxDouble* dy) const;
|
||||||
|
@@ -156,12 +156,12 @@ public:
|
|||||||
virtual void Scale(wxDouble xScale, wxDouble yScale) = 0;
|
virtual void Scale(wxDouble xScale, wxDouble yScale) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Add counter clockwise rotation to this matrix.
|
Add clockwise rotation to this matrix.
|
||||||
|
|
||||||
@param ccRadians
|
@param cRadians
|
||||||
Rotation angle in radians.
|
Rotation angle in radians, clockwise.
|
||||||
*/
|
*/
|
||||||
virtual void Rotate(wxDouble ccRadians) = 0;
|
virtual void Rotate(wxDouble cRadians) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Add mirroring to this matrix.
|
Add mirroring to this matrix.
|
||||||
|
@@ -1299,7 +1299,10 @@ public:
|
|||||||
virtual bool IsIdentity() const;
|
virtual bool IsIdentity() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Rotates this matrix (in radians).
|
Rotates this matrix clockwise (in radians).
|
||||||
|
|
||||||
|
@param radians
|
||||||
|
Rotation angle in radians, clockwise.
|
||||||
*/
|
*/
|
||||||
virtual void Rotate(wxDouble angle);
|
virtual void Rotate(wxDouble angle);
|
||||||
|
|
||||||
|
@@ -127,19 +127,19 @@ void wxAffineMatrix2D::Scale(wxDouble xScale, wxDouble yScale)
|
|||||||
m_22 *= yScale;
|
m_22 *= yScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add the rotation to this matrix (counter clockwise, radians)
|
// add the rotation to this matrix (clockwise, radians)
|
||||||
// | cos -sin 0 | | m_11 m_12 0 |
|
// | cos sin 0 | | m_11 m_12 0 |
|
||||||
// | sin cos 0 | x | m_21 m_22 0 |
|
// | -sin cos 0 | x | m_21 m_22 0 |
|
||||||
// | 0 0 1 | | m_tx m_ty 1 |
|
// | 0 0 1 | | m_tx m_ty 1 |
|
||||||
void wxAffineMatrix2D::Rotate(wxDouble ccRadians)
|
void wxAffineMatrix2D::Rotate(wxDouble cRadians)
|
||||||
{
|
{
|
||||||
wxDouble c = cos(ccRadians);
|
wxDouble c = cos(cRadians);
|
||||||
wxDouble s = sin(ccRadians);
|
wxDouble s = sin(cRadians);
|
||||||
|
|
||||||
wxDouble e11 = c*m_11 - s*m_21;
|
wxDouble e11 = c*m_11 + s*m_21;
|
||||||
wxDouble e12 = c*m_12 - s*m_22;
|
wxDouble e12 = c*m_12 + s*m_22;
|
||||||
m_21 = s*m_11 + c*m_21;
|
m_21 = c*m_21 - s*m_11;
|
||||||
m_22 = s*m_12 + c*m_22;
|
m_22 = c*m_22 - s*m_12;
|
||||||
m_11 = e11;
|
m_11 = e11;
|
||||||
m_12 = e12;
|
m_12 = e12;
|
||||||
}
|
}
|
||||||
@@ -159,7 +159,7 @@ wxAffineMatrix2D::DoTransformPoint(const wxPoint2DDouble& src) const
|
|||||||
return src;
|
return src;
|
||||||
|
|
||||||
return wxPoint2DDouble(src.m_x * m_11 + src.m_y * m_21 + m_tx,
|
return wxPoint2DDouble(src.m_x * m_11 + src.m_y * m_21 + m_tx,
|
||||||
src.m_y * m_12 + src.m_y * m_22 + m_ty);
|
src.m_x * m_12 + src.m_y * m_22 + m_ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
// applies the matrix except for translations
|
// applies the matrix except for translations
|
||||||
@@ -173,7 +173,7 @@ wxAffineMatrix2D::DoTransformDistance(const wxPoint2DDouble& src) const
|
|||||||
return src;
|
return src;
|
||||||
|
|
||||||
return wxPoint2DDouble(src.m_x * m_11 + src.m_y * m_21,
|
return wxPoint2DDouble(src.m_x * m_11 + src.m_y * m_21,
|
||||||
src.m_y * m_12 + src.m_y * m_22);
|
src.m_x * m_12 + src.m_y * m_22);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wxAffineMatrix2D::IsIdentity() const
|
bool wxAffineMatrix2D::IsIdentity() const
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
#pragma hdrstop
|
#pragma hdrstop
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "wx/graphics.h"
|
||||||
#include "wx/dcmemory.h"
|
#include "wx/dcmemory.h"
|
||||||
#include "wx/affinematrix2d.h"
|
#include "wx/affinematrix2d.h"
|
||||||
#include "wx/math.h"
|
#include "wx/math.h"
|
||||||
@@ -42,6 +43,9 @@ private:
|
|||||||
#if wxUSE_DC_TRANSFORM_MATRIX
|
#if wxUSE_DC_TRANSFORM_MATRIX
|
||||||
CPPUNIT_TEST( VMirrorAndTranslate );
|
CPPUNIT_TEST( VMirrorAndTranslate );
|
||||||
CPPUNIT_TEST( Rotate90Clockwise );
|
CPPUNIT_TEST( Rotate90Clockwise );
|
||||||
|
#if wxUSE_GRAPHICS_CONTEXT
|
||||||
|
CPPUNIT_TEST( CompareToGraphicsContext );
|
||||||
|
#endif // wxUSE_GRAPHICS_CONTEXT
|
||||||
#endif // wxUSE_DC_TRANSFORM_MATRIX
|
#endif // wxUSE_DC_TRANSFORM_MATRIX
|
||||||
CPPUNIT_TEST_SUITE_END();
|
CPPUNIT_TEST_SUITE_END();
|
||||||
|
|
||||||
@@ -49,6 +53,9 @@ private:
|
|||||||
#if wxUSE_DC_TRANSFORM_MATRIX
|
#if wxUSE_DC_TRANSFORM_MATRIX
|
||||||
void VMirrorAndTranslate();
|
void VMirrorAndTranslate();
|
||||||
void Rotate90Clockwise();
|
void Rotate90Clockwise();
|
||||||
|
#if wxUSE_GRAPHICS_CONTEXT
|
||||||
|
void CompareToGraphicsContext();
|
||||||
|
#endif // wxUSE_GRAPHICS_CONTEXT
|
||||||
|
|
||||||
wxImage m_imgOrig;
|
wxImage m_imgOrig;
|
||||||
wxBitmap m_bmpOrig;
|
wxBitmap m_bmpOrig;
|
||||||
@@ -133,7 +140,7 @@ void AffineTransformTestCase::Rotate90Clockwise()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
wxAffineMatrix2D matrix;
|
wxAffineMatrix2D matrix;
|
||||||
matrix.Rotate(-0.5 * M_PI);
|
matrix.Rotate(0.5 * M_PI);
|
||||||
matrix.Translate(0, -m_bmpOrig.GetHeight());
|
matrix.Translate(0, -m_bmpOrig.GetHeight());
|
||||||
dc.SetTransformMatrix(matrix);
|
dc.SetTransformMatrix(matrix);
|
||||||
dc.DrawBitmap(m_bmpOrig, 0, 0);
|
dc.DrawBitmap(m_bmpOrig, 0, 0);
|
||||||
@@ -143,4 +150,102 @@ void AffineTransformTestCase::Rotate90Clockwise()
|
|||||||
m_imgOrig.Rotate90(true) );
|
m_imgOrig.Rotate90(true) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if wxUSE_GRAPHICS_CONTEXT
|
||||||
|
void AffineTransformTestCase::CompareToGraphicsContext()
|
||||||
|
{
|
||||||
|
wxPoint2DDouble pointA1(1.0, 3.0), pointA2(60.0, 50.0),
|
||||||
|
pointG1(1.0, 3.0), pointG2(60.0, 50.0);
|
||||||
|
|
||||||
|
// Create affine matrix and transform it
|
||||||
|
wxAffineMatrix2D matrixA1, matrixA2;
|
||||||
|
matrixA2.Rotate(M_PI / 3);
|
||||||
|
matrixA1.Translate(-m_bmpOrig.GetWidth()/2, -m_bmpOrig.GetHeight()/2);
|
||||||
|
matrixA1.Rotate(-M_PI *2/ 6);
|
||||||
|
matrixA1.Translate(m_bmpOrig.GetWidth()/2, m_bmpOrig.GetHeight()/2);
|
||||||
|
matrixA1.Mirror(wxHORIZONTAL);
|
||||||
|
matrixA1.Concat(matrixA2);
|
||||||
|
matrixA1.Mirror(wxVERTICAL);
|
||||||
|
matrixA1.Translate(m_bmpOrig.GetWidth()/2, -m_bmpOrig.GetHeight()/2);
|
||||||
|
matrixA1.Scale(0.9, 0.9);
|
||||||
|
matrixA1.Invert();
|
||||||
|
|
||||||
|
// Create image using first matrix
|
||||||
|
wxBitmap bmpUsingMatrixA1(m_bmpOrig.GetHeight(), m_bmpOrig.GetWidth());
|
||||||
|
|
||||||
|
// Build the transformed image using the transformation matrix
|
||||||
|
{
|
||||||
|
wxMemoryDC dc(bmpUsingMatrixA1);
|
||||||
|
|
||||||
|
if ( !dc.CanUseTransformMatrix() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Draw the bitmap
|
||||||
|
dc.SetTransformMatrix(matrixA1);
|
||||||
|
dc.DrawBitmap(m_bmpOrig, 0, 0);
|
||||||
|
|
||||||
|
// Draw a line
|
||||||
|
matrixA1.TransformPoint(&pointA1.m_x, &pointA1.m_y);
|
||||||
|
matrixA1.TransformDistance(&pointA2.m_x, &pointA2.m_y);
|
||||||
|
|
||||||
|
dc.DrawLine(wxRound(pointA1.m_x), wxRound(pointA1.m_y),
|
||||||
|
wxRound(pointA1.m_x + pointA2.m_x), wxRound(pointA1.m_x + pointA2.m_y));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Create graphics matrix and transform it
|
||||||
|
wxMemoryDC mDc;
|
||||||
|
wxGraphicsContext* gDc = wxGraphicsContext::Create(mDc);
|
||||||
|
wxGraphicsMatrix matrixG1 = gDc->CreateMatrix();
|
||||||
|
wxGraphicsMatrix matrixG2 = gDc->CreateMatrix();
|
||||||
|
matrixG2.Rotate(M_PI / 3);
|
||||||
|
matrixG1.Translate(-m_bmpOrig.GetWidth()/2, -m_bmpOrig.GetHeight()/2);
|
||||||
|
matrixG1.Rotate(-M_PI*2 / 6);
|
||||||
|
matrixG1.Translate(m_bmpOrig.GetWidth()/2, m_bmpOrig.GetHeight()/2);
|
||||||
|
matrixG1.Scale(-1, 1);
|
||||||
|
matrixG1.Concat(matrixG2);
|
||||||
|
matrixG1.Scale(1, -1);
|
||||||
|
matrixG1.Translate(m_bmpOrig.GetWidth()/2, -m_bmpOrig.GetHeight()/2);
|
||||||
|
matrixG1.Scale(0.9, 0.9);
|
||||||
|
matrixG1.Invert();
|
||||||
|
// Create affine matrix from the graphics matrix
|
||||||
|
wxMatrix2D mat2D;
|
||||||
|
wxPoint2DDouble tr;
|
||||||
|
matrixG1.Get(&mat2D.m_11, &mat2D.m_12, &mat2D.m_21, &mat2D.m_22, &tr.m_x, &tr.m_y);
|
||||||
|
wxAffineMatrix2D matrixAG;
|
||||||
|
matrixAG.Set(mat2D, tr);
|
||||||
|
|
||||||
|
delete gDc;
|
||||||
|
|
||||||
|
// Create image using last matrix
|
||||||
|
wxBitmap bmpUsingMatrixAG(m_bmpOrig.GetHeight(), m_bmpOrig.GetWidth());
|
||||||
|
|
||||||
|
// Build the transformed image using the transformation matrix
|
||||||
|
{
|
||||||
|
wxMemoryDC dc(bmpUsingMatrixAG);
|
||||||
|
|
||||||
|
if ( !dc.CanUseTransformMatrix() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Draw the bitmap
|
||||||
|
dc.SetTransformMatrix(matrixAG);
|
||||||
|
dc.DrawBitmap(m_bmpOrig, 0, 0);
|
||||||
|
|
||||||
|
// Draw a line
|
||||||
|
matrixG1.TransformPoint(&pointG1.m_x, &pointG1.m_y);
|
||||||
|
matrixG1.TransformDistance(&pointG2.m_x, &pointG2.m_y);
|
||||||
|
|
||||||
|
dc.DrawLine(wxRound(pointG1.m_x), wxRound(pointG1.m_y),
|
||||||
|
wxRound(pointG1.m_x + pointG2.m_x), wxRound(pointG1.m_x + pointG2.m_y));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CPPUNIT_ASSERT_EQUAL( bmpUsingMatrixA1.ConvertToImage(),
|
||||||
|
bmpUsingMatrixAG.ConvertToImage() );
|
||||||
|
|
||||||
|
// Save the images to check that something _is_ inside the visible area.
|
||||||
|
//bmpUsingMatrixA1.SaveFile("matrixA1.jpg", wxBITMAP_TYPE_JPEG);
|
||||||
|
//bmpUsingMatrixAG.SaveFile("matrixAG.jpg", wxBITMAP_TYPE_JPEG);
|
||||||
|
}
|
||||||
|
#endif // wxUSE_GRAPHICS_CONTEXT
|
||||||
|
|
||||||
#endif // wxUSE_DC_TRANSFORM_MATRIX
|
#endif // wxUSE_DC_TRANSFORM_MATRIX
|
||||||
|
Reference in New Issue
Block a user