Files
GTASource/rage/scaleform/Src/GKernel/GMatrix2D.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

277 lines
8.2 KiB
C++

/*****************************************************************
Filename : GMatrix2D.cpp
Content : 2D Matrix class implementation
Created : May 29, 2006
Authors : Michael Antonov, Brendan Iribe
History :
Copyright : (c) 2006 Scaleform Corp. All Rights Reserved.
Licensees may use this file in accordance with the valid Scaleform
Commercial License Agreement provided with the software.
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR ANY PURPOSE.
**********************************************************************/
#include "GMatrix2D.h"
#include "GStd.h"
#include "GMsgFormat.h"
#include <stdio.h>
#ifdef GFC_TWIPS_TO_PIXELS //!AB???
#undef GFC_TWIPS_TO_PIXELS
#endif
#define GFC_TWIPS_TO_PIXELS(x) ((x) / 20.f)
#define GFX_STREAM_BUFFER_SIZE 512
//
// GMatrix2D
//
GMatrix2D GMatrix2D::Identity;
bool GMatrix2D::IsValid() const
{
return GFC_ISFINITE(M_[0][0])
&& GFC_ISFINITE(M_[0][1])
&& GFC_ISFINITE(M_[0][2])
&& GFC_ISFINITE(M_[1][0])
&& GFC_ISFINITE(M_[1][1])
&& GFC_ISFINITE(M_[1][2]);
}
// Set the GMatrix2D to identity.
void GMatrix2D::SetIdentity()
{
M_[0][0] = 1.0f;
M_[0][1] = 0.0f;
M_[0][2] = 0.0f;
M_[1][0] = 0.0f;
M_[1][1] = 1.0f;
M_[1][2] = 0.0f;
}
// Concatenate m and the current matrix. When transforming points,
// m happens first, then the original transformation.
GMatrix2D& GMatrix2D::Prepend(const GMatrix2D& m)
{
GMatrix2D t = *this;
M_[0][0] = t.M_[0][0] * m.M_[0][0] + t.M_[0][1] * m.M_[1][0];
M_[1][0] = t.M_[1][0] * m.M_[0][0] + t.M_[1][1] * m.M_[1][0];
M_[0][1] = t.M_[0][0] * m.M_[0][1] + t.M_[0][1] * m.M_[1][1];
M_[1][1] = t.M_[1][0] * m.M_[0][1] + t.M_[1][1] * m.M_[1][1];
M_[0][2] = t.M_[0][0] * m.M_[0][2] + t.M_[0][1] * m.M_[1][2] + t.M_[0][2];
M_[1][2] = t.M_[1][0] * m.M_[0][2] + t.M_[1][1] * m.M_[1][2] + t.M_[1][2];
return *this;
}
// Concatenate the current matrix and m. When transforming points,
// the original transformation happens first, then m.
GMatrix2D& GMatrix2D::Append(const GMatrix2D& m)
{
GMatrix2D t = *this;
M_[0][0] = m.M_[0][0] * t.M_[0][0] + m.M_[0][1] * t.M_[1][0];
M_[1][0] = m.M_[1][0] * t.M_[0][0] + m.M_[1][1] * t.M_[1][0];
M_[0][1] = m.M_[0][0] * t.M_[0][1] + m.M_[0][1] * t.M_[1][1];
M_[1][1] = m.M_[1][0] * t.M_[0][1] + m.M_[1][1] * t.M_[1][1];
M_[0][2] = m.M_[0][0] * t.M_[0][2] + m.M_[0][1] * t.M_[1][2] + m.M_[0][2];
M_[1][2] = m.M_[1][0] * t.M_[0][2] + m.M_[1][1] * t.M_[1][2] + m.M_[1][2];
return *this;
}
// Set this GMatrix2D to a blend of m1 and m2, parameterized by t.
void GMatrix2D::SetLerp(const GMatrix2D& m1, const GMatrix2D& m2, Float t)
{
M_[0][0] = gflerp(m1.M_[0][0], m2.M_[0][0], t);
M_[1][0] = gflerp(m1.M_[1][0], m2.M_[1][0], t);
M_[0][1] = gflerp(m1.M_[0][1], m2.M_[0][1], t);
M_[1][1] = gflerp(m1.M_[1][1], m2.M_[1][1], t);
M_[0][2] = gflerp(m1.M_[0][2], m2.M_[0][2], t);
M_[1][2] = gflerp(m1.M_[1][2], m2.M_[1][2], t);
}
// Formats matrix message to a buffer, but 512 bytes at least
void GMatrix2D::Format(char *pbuffer) const
{
G_Format(GStringDataPtr(pbuffer, GFX_STREAM_BUFFER_SIZE),
"| {0:4.4} {1:4.4} {2:4.4} |\n"
"| {3:4.4} {4:4.4} {5:4.4} |\n",
M_[0][0], M_[0][1], GFC_TWIPS_TO_PIXELS(M_[0][2]),
M_[1][0], M_[1][1], GFC_TWIPS_TO_PIXELS(M_[1][2])
);
}
// Transform GPointF 'p' by our GMatrix2D. Put the result in *result.
void GMatrix2D::Transform(GPointF* result, const GPointF& p) const
{
GASSERT(result);
result->x = M_[0][0] * p.x + M_[0][1] * p.y + M_[0][2];
result->y = M_[1][0] * p.x + M_[1][1] * p.y + M_[1][2];
}
// Transform vector 'v' by our GMatrix2D. Doesn't apply translation.
// Put the result in *result.
void GMatrix2D::TransformVector(GPointF* result, const GPointF& v) const
{
GASSERT(result);
result->x = M_[0][0] * v.x + M_[0][1] * v.y;
result->y = M_[1][0] * v.x + M_[1][1] * v.y;
}
// Transform GPointF 'p' by the inverse of our GMatrix2D. Put result in *result.
void GMatrix2D::TransformByInverse(GPointF* result, const GPointF& p) const
{
GMatrix2D(*this).Invert().Transform(result, p);
}
// Set this GMatrix2D to the inverse of the given GMatrix2D.
void GMatrix2D::SetInverse(const GMatrix2D& m)
{
GASSERT(this != &m);
// Invert the rotation part.
Float det = m.M_[0][0] * m.M_[1][1] - m.M_[0][1] * m.M_[1][0];
if (det == 0.0f)
{
// Not invertible - this happens sometimes (ie. sample6.Swf)
// Arbitrary fallback
SetIdentity();
M_[0][2] = -m.M_[0][2];
M_[1][2] = -m.M_[1][2];
}
else
{
Float invDet = 1.0f / det;
M_[0][0] = m.M_[1][1] * invDet;
M_[1][1] = m.M_[0][0] * invDet;
M_[0][1] = -m.M_[0][1] * invDet;
M_[1][0] = -m.M_[1][0] * invDet;
M_[0][2] = -(M_[0][0] * m.M_[0][2] + M_[0][1] * m.M_[1][2]);
M_[1][2] = -(M_[1][0] * m.M_[0][2] + M_[1][1] * m.M_[1][2]);
}
}
// Return true if this GMatrix2D reverses handedness.
bool GMatrix2D::DoesFlip() const
{
Float det = M_[0][0] * M_[1][1] - M_[0][1] * M_[1][0];
return det < 0.F;
}
// Return the determinant of the 2x2 rotation/scale part only.
Float GMatrix2D::GetDeterminant() const
{
return M_[0][0] * M_[1][1] - M_[1][0] * M_[0][1];
}
// Return the maximum scale factor that this transform
// applies. For assessing scale, when determining acceptable
// errors in tessellation.
Float GMatrix2D::GetMaxScale() const
{
// @@ take the max length of the two basis vectors.
Float basis0Length2 = M_[0][0] * M_[0][0] + M_[1][0] * M_[1][0];
Float basis1Length2 = M_[0][1] * M_[0][1] + M_[1][1] * M_[1][1];
Float maxLength2 = G_Max<Float>(basis0Length2, basis1Length2);
return sqrtf(maxLength2);
}
Float GMatrix2D::GetX() const
{
return M_[0][2];
}
Float GMatrix2D::GetY() const
{
return M_[1][2];
}
Double GMatrix2D::GetXScale() const
{
return sqrt(((Double)M_[0][0]) *((Double)M_[0][0]) + ((Double)M_[1][0]) * ((Double)M_[1][0]));
}
Double GMatrix2D::GetYScale() const
{
return sqrt(((Double)M_[1][1]) * ((Double)M_[1][1]) + ((Double)M_[0][1]) * ((Double)M_[0][1]));
}
Double GMatrix2D::GetRotation() const
{
return atan2((Double)M_[1][0], (Double)M_[0][0]);
}
// This is an axial bound of an oriented (and/or
// sheared, scaled, etc) box.
void GMatrix2D::EncloseTransform(GRectF *pr, const GRectF& r) const
{
// Get the transformed bounding box.
GPointF p0, p1, p2, p3;
Transform(&p0, r.TopLeft());
Transform(&p1, r.TopRight());
Transform(&p2, r.BottomRight());
Transform(&p3, r.BottomLeft());
pr->SetRect(p0, p0);
pr->ExpandToPoint(p1);
pr->ExpandToPoint(p2);
pr->ExpandToPoint(p3);
}
//-------------------
// The code of this functions was taken from the Anti-Grain Geometry
// Project and modified for the use by Scaleform.
// Permission to use without restrictions is hereby granted to
// Scaleform Corp. by the author of Anti-Grain Geometry Project.
//------------------------------------------------------------------------
GMatrix2D& GMatrix2D::SetParlToParl(const Float* src, const Float* dst)
{
SetMatrix (src[2] - src[0], src[4] - src[0],
src[3] - src[1], src[5] - src[1],
src[0], src[1]);
GMatrix2D d(dst[2] - dst[0], dst[4] - dst[0],
dst[3] - dst[1], dst[5] - dst[1],
dst[0], dst[1]);
Invert();
Append(d);
return *this;
}
//------------------------------------------------------------------------
GMatrix2D& GMatrix2D::SetRectToParl(Float x1, Float y1, Float x2, Float y2,
const Float* parl)
{
Float src[6];
src[0] = x1; src[1] = y1;
src[2] = x2; src[3] = y1;
src[4] = x2; src[5] = y2;
return SetParlToParl(src, parl);
}
//------------------------------------------------------------------------
GMatrix2D& GMatrix2D::SetParlToRect(const Float* parl,
Float x1, Float y1, Float x2, Float y2)
{
Float dst[6];
dst[0] = x1; dst[1] = y1;
dst[2] = x2; dst[3] = y1;
dst[4] = x2; dst[5] = y2;
return SetParlToParl(parl, dst);
}