|
|
/***************************************************************************\
* * File: Matrix.cpp * * Description: * Matrix.cpp implements common Matrix and Vector operations. * * * History: * 3/25/2000: JStall: Created * * Copyright (C) 2000 by Microsoft Corporation. All rights reserved. * \***************************************************************************/
#include "stdafx.h"
#include "Base.h"
#include "Matrix.h"
#include "Rect.h"
/***************************************************************************\
***************************************************************************** * * class Vector3 * ***************************************************************************** \***************************************************************************/
#if DBG
//------------------------------------------------------------------------------
void Vector3::Dump() const { Trace(" | %6.2f, %6.2f, %6.2f |\n", m_rgfl[0], m_rgfl[1], m_rgfl[2]); }
#endif // DBG
/***************************************************************************\
***************************************************************************** * * class Matrix3 * ***************************************************************************** \***************************************************************************/
/*
//
// Standard multiplication of A by the current Matrix. This can be used
// as a template to be optimized for different cases.
//
Vector3 rgvT0 = m_rgv[0]; Vector3 rgvT1 = m_rgv[1]; Vector3 rgvT2 = m_rgv[2];
m_rgv[0].Set(A[0][0] * rgvT0[0] + A[0][1] * rgvT1[0] + A[0][2] * rgvT2[0], A[0][0] * rgvT0[1] + A[0][1] * rgvT1[1] + A[0][2] * rgvT2[1], A[0][0] * rgvT0[2] + A[0][1] * rgvT1[2] + A[0][2] * rgvT2[2]);
m_rgv[1].Set(A[1][0] * rgvT0[0] + A[1][1] * rgvT1[0] + A[1][2] * rgvT2[0], A[1][0] * rgvT0[1] + A[1][1] * rgvT1[1] + A[1][2] * rgvT2[1], A[1][0] * rgvT0[2] + A[1][1] * rgvT1[2] + A[1][2] * rgvT2[2]);
m_rgv[2].Set(A[2][0] * rgvT0[0] + A[2][1] * rgvT1[0] + A[2][2] * rgvT2[0], A[2][0] * rgvT0[1] + A[2][1] * rgvT1[1] + A[2][2] * rgvT2[1], A[2][0] * rgvT0[2] + A[2][1] * rgvT1[2] + A[2][2] * rgvT2[2]); */
/***************************************************************************\
* * Matrix3::ApplyLeft * * ApplyLeft() left-multiples the given GDI matrix to the current matrix and * stores the result in the current matrix. * * mCurrent = pxfLeft * mCurrent * \***************************************************************************/
void Matrix3::ApplyLeft( IN const XFORM * pxfLeft) // GDI matrix to left-multiply
{ const XFORM * pxf = pxfLeft;
Vector3 rgvT0 = m_rgv[0]; Vector3 rgvT1 = m_rgv[1]; Vector3 rgvT2 = m_rgv[2];
m_rgv[0].Set(pxf->eM11 * rgvT0[0] + pxf->eM12 * rgvT1[0], pxf->eM11 * rgvT0[1] + pxf->eM12 * rgvT1[1], pxf->eM11 * rgvT0[2] + pxf->eM12 * rgvT1[2]);
m_rgv[1].Set(pxf->eM21 * rgvT0[0] + pxf->eM22 * rgvT1[0], pxf->eM21 * rgvT0[1] + pxf->eM22 * rgvT1[1], pxf->eM21 * rgvT0[2] + pxf->eM22 * rgvT1[2]);
m_rgv[2].Set(pxf->eDx * rgvT0[0] + pxf->eDy * rgvT1[0] + rgvT2[0], pxf->eDx * rgvT0[1] + pxf->eDy * rgvT1[1] + rgvT2[1], pxf->eDx * rgvT0[2] + pxf->eDy * rgvT1[2] + rgvT2[2]);
m_fIdentity = FALSE; m_fOnlyTranslate = FALSE; }
/***************************************************************************\
* * Matrix3::ApplyLeft * * ApplyLeft() left-multiples the given matrix to the current matrix and * stores the result in the current matrix. * * mCurrent = mLeft * mCurrent * \***************************************************************************/
void Matrix3::ApplyLeft( IN const Matrix3 & mLeft) // Matrix to left-multiply
{ if (mLeft.m_fIdentity) { return; }
if (m_fOnlyTranslate && mLeft.m_fOnlyTranslate) { m_rgv[2].Set(0, m_rgv[2][0] + mLeft.m_rgv[2][0]); m_rgv[2].Set(1, m_rgv[2][1] + mLeft.m_rgv[2][1]);
m_fIdentity = FALSE; return; }
const Vector3 & A0 = mLeft.m_rgv[0]; const Vector3 & A1 = mLeft.m_rgv[1]; const Vector3 & A2 = mLeft.m_rgv[2];
Vector3 B0 = m_rgv[0]; Vector3 B1 = m_rgv[1]; Vector3 B2 = m_rgv[2];
m_rgv[0].Set(A0[0] * B0[0] + A0[1] * B1[0] + A0[2] * B2[0], A0[0] * B0[1] + A0[1] * B1[1] + A0[2] * B2[1], A0[0] * B0[2] + A0[1] * B1[2] + A0[2] * B2[2]);
m_rgv[1].Set(A1[0] * B0[0] + A1[1] * B1[0] + A1[2] * B2[0], A1[0] * B0[1] + A1[1] * B1[1] + A1[2] * B2[1], A1[0] * B0[2] + A1[1] * B1[2] + A1[2] * B2[2]);
m_rgv[2].Set(A2[0] * B0[0] + A2[1] * B1[0] + A2[2] * B2[0], A2[0] * B0[1] + A2[1] * B1[1] + A2[2] * B2[1], A2[0] * B0[2] + A2[1] * B1[2] + A2[2] * B2[2]);
m_fIdentity = FALSE; m_fOnlyTranslate = FALSE; }
/***************************************************************************\
* * Matrix3::ApplyRight * * ApplyRight() right-multiples the given matrix to the current matrix and * stores the result in the current matrix. * * mCurrent = mCurrent * mRight * \***************************************************************************/
void Matrix3::ApplyRight( IN const Matrix3 & mRight) // Matrix to right-multiply
{ if (mRight.m_fIdentity) { return; }
if (m_fOnlyTranslate && mRight.m_fOnlyTranslate) { m_rgv[2].Set(0, m_rgv[2][0] + mRight.m_rgv[2][0]); m_rgv[2].Set(1, m_rgv[2][1] + mRight.m_rgv[2][1]);
m_fIdentity = FALSE; return; }
Vector3 A0 = m_rgv[0]; Vector3 A1 = m_rgv[1]; Vector3 A2 = m_rgv[2];
const Vector3 & B0 = mRight.m_rgv[0]; const Vector3 & B1 = mRight.m_rgv[1]; const Vector3 & B2 = mRight.m_rgv[2];
m_rgv[0].Set(A0[0] * B0[0] + A0[1] * B1[0] + A0[2] * B2[0], A0[0] * B0[1] + A0[1] * B1[1] + A0[2] * B2[1], A0[0] * B0[2] + A0[1] * B1[2] + A0[2] * B2[2]);
m_rgv[1].Set(A1[0] * B0[0] + A1[1] * B1[0] + A1[2] * B2[0], A1[0] * B0[1] + A1[1] * B1[1] + A1[2] * B2[1], A1[0] * B0[2] + A1[1] * B1[2] + A1[2] * B2[2]);
m_rgv[2].Set(A2[0] * B0[0] + A2[1] * B1[0] + A2[2] * B2[0], A2[0] * B0[1] + A2[1] * B1[1] + A2[2] * B2[1], A2[0] * B0[2] + A2[1] * B1[2] + A2[2] * B2[2]);
m_fIdentity = FALSE; m_fOnlyTranslate = FALSE; }
/***************************************************************************\
* * Matrix3::ApplyRight * * ApplyRight() right-multiples the given matrix to the current matrix and * stores the result in the current matrix. * \***************************************************************************/
void Matrix3::Get( OUT XFORM * pxf // GDI matrix to receive information
) const { pxf->eM11 = m_rgv[0][0]; pxf->eM12 = m_rgv[0][1]; pxf->eM21 = m_rgv[1][0]; pxf->eM22 = m_rgv[1][1]; pxf->eDx = m_rgv[2][0]; pxf->eDy = m_rgv[2][1]; }
/***************************************************************************\
* * Matrix3::Execute * * Execute() applies to given matrix on the collection of points, * transforming each appropriately. * \***************************************************************************/
void Matrix3::Execute( IN OUT POINT * rgpt, // Points to apply matrix on
IN int cPoints) const // Number of points
{ if (m_fIdentity) { return; }
POINT ptT, ptN; POINT * pptCur = rgpt;
if (m_fOnlyTranslate) { //
// Only have translated so far, so can just offset the points without
// going through an entire transformation.
//
while (cPoints-- > 0) { ptT = *pptCur;
ptN.x = ptT.x + (int) m_rgv[2][0]; ptN.y = ptT.y + (int) m_rgv[2][1];
*pptCur++ = ptN; } } else { while (cPoints-- > 0) { ptT = *pptCur;
ptN.x = (int) (ptT.x * m_rgv[0][0] + ptT.y * m_rgv[1][0] + m_rgv[2][0] + 0.5f); ptN.y = (int) (ptT.x * m_rgv[0][1] + ptT.y * m_rgv[1][1] + m_rgv[2][1] + 0.5f);
*pptCur++ = ptN; } } }
/***************************************************************************\
* * Matrix3::ComputeBounds * * ComputeBounds() computes the bounding box that will contain the given * transformed rectangle. * \***************************************************************************/
void Matrix3::ComputeBounds( OUT RECT * prcBounds, // The bound of the transformation
IN const RECT * prcLogical, // The logical rectangle to transform
IN EHintBounds hb // Hinting for border pixels
) const { if (m_fIdentity) { AssertMsg(InlineIsRectNormalized(prcLogical), "Ensure normalized rect"); *prcBounds = *prcLogical; return; }
if (m_fOnlyTranslate) { //
// Only have translated, so the bounding
//
AssertMsg(InlineIsRectNormalized(prcLogical), "Ensure normalized rect");
*prcBounds = *prcLogical; InlineOffsetRect(prcBounds, (int) m_rgv[2][0], (int) m_rgv[2][1]); return; }
POINT rgpt[4]; rgpt[0].x = prcLogical->left; rgpt[0].y = prcLogical->top; rgpt[1].x = prcLogical->right; rgpt[1].y = prcLogical->top;
rgpt[2].x = prcLogical->right; rgpt[2].y = prcLogical->bottom; rgpt[3].x = prcLogical->left; rgpt[3].y = prcLogical->bottom;
Execute(rgpt, _countof(rgpt));
prcBounds->left = min(min(rgpt[0].x, rgpt[1].x), min(rgpt[2].x, rgpt[3].x)); prcBounds->top = min(min(rgpt[0].y, rgpt[1].y), min(rgpt[2].y, rgpt[3].y)); prcBounds->right = max(max(rgpt[0].x, rgpt[1].x), max(rgpt[2].x, rgpt[3].x)); prcBounds->bottom = max(max(rgpt[0].y, rgpt[1].y), max(rgpt[2].y, rgpt[3].y));
if (hb == hbOutside) { //
// Just converted from int to float back to int, so we may have rounding
// errors. To compensate, need to inflate the given rectangle so that
// it overlaps these errors.
//
InlineInflateRect(prcBounds, 1, 1); } }
/***************************************************************************\
* * Matrix3::ComputeRgn * * ComputeRgn() builds a region for the quadrilateral generated by applying * this matrix to the given rectangle. * \***************************************************************************/
int Matrix3::ComputeRgn( IN HRGN hrgnDest, IN const RECT * prcLogical, IN SIZE sizeOffsetPxl ) const { AssertMsg(hrgnDest != NULL, "Must specify a valid (real) region");
if (m_fIdentity || m_fOnlyTranslate){ AssertMsg(InlineIsRectNormalized(prcLogical), "Ensure normalized rect");
RECT rcBounds = *prcLogical; InlineOffsetRect(&rcBounds, ((int) m_rgv[2][0]) + sizeOffsetPxl.cx, ((int) m_rgv[2][1]) + sizeOffsetPxl.cy); BOOL fSuccess = SetRectRgn(hrgnDest, rcBounds.left, rcBounds.top, rcBounds.right, rcBounds.bottom); return fSuccess ? SIMPLEREGION : ERROR; }
POINT rgpt[4]; rgpt[0].x = prcLogical->left; rgpt[0].y = prcLogical->top; rgpt[1].x = prcLogical->right; rgpt[1].y = prcLogical->top;
rgpt[2].x = prcLogical->right; rgpt[2].y = prcLogical->bottom; rgpt[3].x = prcLogical->left; rgpt[3].y = prcLogical->bottom;
Execute(rgpt, _countof(rgpt));
HRGN hrgnTemp = CreatePolygonRgn(rgpt, _countof(rgpt), WINDING); if (hrgnTemp == NULL) { return ERROR; } int nResult; nResult = OffsetRgn(hrgnTemp, sizeOffsetPxl.cx, sizeOffsetPxl.cy); AssertMsg((nResult == SIMPLEREGION) || (nResult == COMPLEXREGION), "Just successfully created region should be either simple or complex");
nResult = CombineRgn(hrgnDest, hrgnTemp, NULL, RGN_COPY); DeleteObject(hrgnTemp);
return nResult; }
/***************************************************************************\
* * Matrix3::SetIdentity * * SetIdentity() resets the matrix to the identity matrix. * \***************************************************************************/
void Matrix3::SetIdentity() { m_rgv[0].Set(1.0f, 0.0f, 0.0f); m_rgv[1].Set(0.0f, 1.0f, 0.0f); m_rgv[2].Set(0.0f, 0.0f, 1.0f);
m_fIdentity = TRUE; m_fOnlyTranslate = TRUE; }
/***************************************************************************\
* * Matrix3::Rotate * * Rotate() rotates the matrix by the specified angle. The specific * orientation of clockwise or counterclockwise depends on how the matrix * is being applied. For MM_TEXT, this is clockwise. * \***************************************************************************/
void Matrix3::Rotate( IN float flRotationRad) // Rotation angle in radians
{ float flCos = (float) cos(flRotationRad); float flSin = (float) sin(flRotationRad); float flSinN = - flSin;
Vector3 rgvT0 = m_rgv[0]; Vector3 rgvT1 = m_rgv[1]; Vector3 rgvT2 = m_rgv[2];
m_rgv[0].Set(flCos * rgvT0[0] + flSin * rgvT1[0], flCos * rgvT0[1] + flSin * rgvT1[1], flCos * rgvT0[2] + flSin * rgvT1[2]);
m_rgv[1].Set(flSinN * rgvT0[0] + flCos * rgvT1[0], flSinN * rgvT0[1] + flCos * rgvT1[1], flSinN * rgvT0[2] + flCos * rgvT1[2]);
m_fIdentity = FALSE; m_fOnlyTranslate = FALSE; }
/***************************************************************************\
* * Matrix3::Translate * * Translate() offsets the matrix. * \***************************************************************************/
void Matrix3::Translate( IN float flOffsetX, // Horizontal offset
IN float flOffsetY) // Vertical offset
{ if (m_fOnlyTranslate) { AssertMsg(fabs(m_rgv[2][2] - 1.0f) < 0.00001f, "Should still be 1.0f");
m_rgv[2].Set(m_rgv[2][0] + flOffsetX, m_rgv[2][1] + flOffsetY, 1.0f);
m_fIdentity = FALSE; return; }
Vector3 rgvT0 = m_rgv[0]; Vector3 rgvT1 = m_rgv[1]; Vector3 rgvT2 = m_rgv[2];
m_rgv[2].Set(flOffsetX * rgvT0[0] + flOffsetY * rgvT1[0] + rgvT2[0], flOffsetX * rgvT0[1] + flOffsetY * rgvT1[1] + rgvT2[1], flOffsetX * rgvT0[2] + flOffsetY * rgvT1[2] + rgvT2[2]);
m_fIdentity = FALSE; }
/***************************************************************************\
* * Matrix3::Scale * * Scale() scales the matrix. * \***************************************************************************/
void Matrix3::Scale( IN float flScaleX, // Horizontal scaling
IN float flScaleY) // Vertical scaling
{ Vector3 rgvT0 = m_rgv[0]; Vector3 rgvT1 = m_rgv[1]; Vector3 rgvT2 = m_rgv[2];
m_rgv[0].Set(flScaleX * rgvT0[0], flScaleX * rgvT0[1], flScaleX * rgvT0[2]);
m_rgv[1].Set(flScaleY * rgvT1[0], flScaleY * rgvT1[1], flScaleY * rgvT1[2]);
m_fIdentity = FALSE; m_fOnlyTranslate = FALSE; }
#if DBG
//------------------------------------------------------------------------------
void Matrix3::Dump() const { m_rgv[0].Dump(); m_rgv[1].Dump(); m_rgv[2].Dump(); }
#endif // DBG
|