913 lines
20 KiB
C++
913 lines
20 KiB
C++
/*
|
|
* SYNOPSYS CONFIDENTIAL - This is an unpublished, proprietary work of Synopsys,
|
|
* Inc., and is fully protected under copyright and trade secret laws. You may
|
|
* not view, use, disclose, copy, or distribute this file or any information
|
|
* contained herein except pursuant to a valid written license from Synopsys.
|
|
*/
|
|
|
|
#ifndef TRANSFORM2_H_
|
|
#define TRANSFORM2_H_
|
|
|
|
// _MSC_VER >= 1400 means we deal with VC8 or higher.
|
|
#if defined( _MSC_VER )
|
|
/*warning C4290: C++ exception specification ignored except to indicate a function is not __declspec(nothrow)*/
|
|
#pragma warning( disable: 4290 )
|
|
#endif
|
|
|
|
#include <math.h>
|
|
#include <string>
|
|
#include <stdio.h>
|
|
|
|
#include "base/port.h"
|
|
#include "base/constants.h"
|
|
#include "base/except.h"
|
|
#include "base/rectangle.h"
|
|
#include "base/round.h"
|
|
|
|
|
|
//
|
|
/// The transform.h file contains the different transform types.
|
|
//
|
|
/*!
|
|
* Usage of transforms:
|
|
* Transforms are applied to row vectors from the right as shown below:
|
|
* OV = IV * T
|
|
* Here, OV and IV are the output and input row vectors and T is the transform.
|
|
*
|
|
* Multiplying transforms:
|
|
* Suppose we have a chain of transforms.
|
|
* OV = IV * T1 * T2
|
|
* OV = IV * T
|
|
* Thus, T can be computed as follows:
|
|
* Transform T;
|
|
* T1.mult(T2, T);
|
|
*
|
|
* A general 2-D transform can be viewed in matrix form as follows:
|
|
* |a11 a12 0|
|
|
* |a21 a22 0|
|
|
* | tx ty 1|
|
|
* A general transform can be viewed as a sequence of 3 steps:
|
|
* 1- Scaling with value (Sx, Sy) (i.e. x' = Sx*x, y' = Sy*y).
|
|
* 2- rotating with angle A.
|
|
* 3- translating with value (tx, ty).
|
|
* In other words, the 2-D transform is the multiplication of the 3 transforms
|
|
* as follows:
|
|
* |Sx 0 0| | cos(A) sin(A) 0| | 1 0 0|
|
|
* |0 Sy 0|*|-sin(A) cos(A) 0|*| 0 1 0|
|
|
* |0 0 1| | 0 0 1| |tx ty 1|
|
|
*
|
|
* Using resolution transforms:
|
|
* In order to mimic applying resolution transforms to each cell individually,
|
|
* a resolution change operation is applied before anything. Suppose we call
|
|
* the resolution change transform RES. Also suppose the transform from the
|
|
* cell to the screen is TS. Thus:
|
|
* OV = IV * RES * TS.
|
|
* In order to change the resolution of a layout, we need to insert the
|
|
* resolution transform RES in the chain. Hence:
|
|
* 1- Create a resolution transform RES(xScale, yScale, true).
|
|
* 2- Assuming T is the total transform, T can be computed as follows:
|
|
* RES.mult(TS, T).
|
|
*/
|
|
namespace base2
|
|
{
|
|
using namespace base;
|
|
|
|
//
|
|
/// The main transformation class.
|
|
//
|
|
class GXX_VISIBLE transform
|
|
{
|
|
// Data
|
|
protected:
|
|
//
|
|
/// 2X2 coefficient submatrix.
|
|
//
|
|
v2pd a11_a22;
|
|
v2pd a21_a12;
|
|
//
|
|
// Offset for the transform.
|
|
//
|
|
v2pd voffset;
|
|
|
|
// Typedefs
|
|
protected:
|
|
|
|
inline double& a11()
|
|
{
|
|
return *(reinterpret_cast<double*>(&a11_a22));
|
|
}
|
|
inline double& a21()
|
|
{
|
|
return *(reinterpret_cast<double*>(&a21_a12));
|
|
}
|
|
inline double& a12()
|
|
{
|
|
return *(reinterpret_cast<double*>(&a21_a12)+1);
|
|
}
|
|
inline double& a22()
|
|
{
|
|
return *(reinterpret_cast<double*>(&a11_a22)+1);
|
|
}
|
|
inline double& offset_x()
|
|
{
|
|
return *(reinterpret_cast<double*>(&voffset));
|
|
}
|
|
inline double& offset_y()
|
|
{
|
|
return *(reinterpret_cast<double*>(&voffset)+1);
|
|
}
|
|
|
|
inline double a11() const
|
|
{
|
|
return *(reinterpret_cast<const double*>(&a11_a22));
|
|
}
|
|
inline double a21() const
|
|
{
|
|
return *(reinterpret_cast<const double*>(&a21_a12));
|
|
}
|
|
inline double a12() const
|
|
{
|
|
return *(reinterpret_cast<const double*>(&a21_a12)+1);
|
|
}
|
|
inline double a22() const
|
|
{
|
|
return *(reinterpret_cast<const double*>(&a11_a22)+1);
|
|
}
|
|
inline double offset_x() const
|
|
{
|
|
return *(reinterpret_cast<const double*>(&voffset));
|
|
}
|
|
inline double offset_y() const
|
|
{
|
|
return *(reinterpret_cast<const double*>(&voffset)+1);
|
|
}
|
|
|
|
// Construction
|
|
public:
|
|
|
|
//
|
|
/// Initialize the function tables.
|
|
//
|
|
static void initFnTables();
|
|
|
|
//
|
|
/// Default constuctor.
|
|
//
|
|
transform()
|
|
{
|
|
a11() = 1.0;
|
|
a22() = 1.0;
|
|
a21() = 0.0;
|
|
a12() = 0.0;
|
|
offset_x() = 0.0;
|
|
offset_y() = 0.0;
|
|
}
|
|
//
|
|
/// Default constuctor.
|
|
//
|
|
transform( v2pd a1122,
|
|
v2pd a2112,
|
|
v2pd o )
|
|
{
|
|
a11_a22 = a1122;
|
|
a21_a12 = a2112;
|
|
voffset = o;
|
|
}
|
|
//
|
|
/// Constructor for a scale offset transform.
|
|
//
|
|
transform(
|
|
double offsetX,
|
|
double offsetY,
|
|
double scaleX,
|
|
double scaleY
|
|
)
|
|
{
|
|
a11() = scaleX;
|
|
a22() = scaleY;
|
|
a21() = 0.0;
|
|
a12() = 0.0;
|
|
offset_x() = offsetX;
|
|
offset_y() = offsetY;
|
|
}
|
|
//
|
|
/// Constructor for a general transform.
|
|
//
|
|
transform(
|
|
double offsetX,
|
|
double offsetY,
|
|
double coeff11,
|
|
double coeff12,
|
|
double coeff21,
|
|
double coeff22
|
|
)
|
|
{
|
|
a11() = coeff11;
|
|
a22() = coeff22;
|
|
a21() = coeff21;
|
|
a12() = coeff12;
|
|
offset_x() = offsetX;
|
|
offset_y() = offsetY;
|
|
}
|
|
//
|
|
/// Constructor for a rotate transform.
|
|
/// @param inCx X coordinate of the center of rotation.
|
|
/// @param inCy Y coordinate of the center of rotation.
|
|
/// @param angle rotation angle in degrees.
|
|
//
|
|
transform(
|
|
double inCx,
|
|
double inCy,
|
|
double angle
|
|
)
|
|
{
|
|
a11() = cos(degreesToRadians(angle));
|
|
a22() = a11();
|
|
a12() = sin(degreesToRadians(angle));
|
|
a21() = -a12();
|
|
offset_x() = inCy * a12() - inCx * a11() + inCx;
|
|
offset_y() = - inCy * a11() - inCx * a12() + inCy;
|
|
}
|
|
#if 0
|
|
//
|
|
/// Constructor for a standard transform.
|
|
//
|
|
transform(
|
|
double offsetX,
|
|
double offsetY,
|
|
stdOrientEnum inOrient = R0
|
|
)
|
|
:
|
|
a11(1.0),
|
|
a12(0.0),
|
|
a21(0.0),
|
|
a22(1.0),
|
|
offset(offsetX,offsetY),
|
|
next(NULL),
|
|
orient(inOrient),
|
|
type(StdTransformType)
|
|
{}
|
|
//
|
|
/// Constructor for a standard transform with magnification.
|
|
//
|
|
transform(
|
|
double offsetX,
|
|
double offsetY,
|
|
stdOrientEnum inOrient,
|
|
double inMag)
|
|
:
|
|
a11(inMag),
|
|
a12(0.0),
|
|
a21(0.0),
|
|
a22(inMag),
|
|
offset(offsetX,offsetY),
|
|
next(NULL),
|
|
orient(inOrient),
|
|
type(StdTransformWithMagType)
|
|
{}
|
|
#endif
|
|
//
|
|
/// Advanced constructor for typical references.
|
|
/// @param offsetX X value of translation offset.
|
|
/// @param offsetY Y value of translation offset.
|
|
/// @param inXFlip boolean reflecting the presence of an X flip.
|
|
/// @param inAngle rotation angle in degrees.
|
|
/// @param inMag magnification.
|
|
//
|
|
transform(
|
|
double offsetX,
|
|
double offsetY,
|
|
bool inXFlip,
|
|
double inAngle,
|
|
double inMag)
|
|
{
|
|
register double multiplier = ( inXFlip ) ? -1.0 : 1.0;
|
|
register double newAngle = degreesToRadians(normalizeAngle(inAngle));
|
|
register double cosine = cos(newAngle);
|
|
register double sine = sin(newAngle);
|
|
|
|
a11() = inMag * cosine;
|
|
a12() = inMag * sine;
|
|
a21() = -inMag * multiplier * sine;
|
|
a22() = inMag * multiplier * cosine;
|
|
offset_x() = offsetX;
|
|
offset_y() = offsetY;
|
|
}
|
|
//
|
|
/// Scale or resolution transform.
|
|
/// @param xScale X value of the scale factor.
|
|
/// @param yScale Y value of the scale factor.
|
|
/// @param rouding A true value means it is a resolution transform.
|
|
/// A false value will result in a standard scale transform.
|
|
//
|
|
transform(
|
|
double xScale,
|
|
double yScale,
|
|
bool rounding )
|
|
{
|
|
a11() = xScale;
|
|
a22() = yScale;
|
|
a21() = 0.;
|
|
a12() = 0.;
|
|
offset_x() = 0.;
|
|
offset_y() = 0.;
|
|
}
|
|
|
|
//
|
|
/// Copy constructor
|
|
//
|
|
transform( const transform & inT)
|
|
{
|
|
a11_a22 = inT.a11_a22;
|
|
a21_a12 = inT.a21_a12;
|
|
voffset = inT.voffset;
|
|
}
|
|
//
|
|
/// Assignment operator
|
|
//
|
|
transform & operator = (const transform &inT)
|
|
{
|
|
a11_a22 = inT.a11_a22;
|
|
a21_a12 = inT.a21_a12;
|
|
voffset = inT.voffset;
|
|
return *this;
|
|
}
|
|
|
|
//
|
|
/// Destructor
|
|
//
|
|
~transform()
|
|
{
|
|
}
|
|
|
|
public:
|
|
//
|
|
/// Equal operator
|
|
//
|
|
bool operator == (const transform &inT)
|
|
{
|
|
// register v2pd cmp = _mm_cmpeq_pd( vx, inT.vx );
|
|
// register v2pd tmp = _mm_cmpeq_pd( vy, inT.vy );
|
|
// cmp = _mm_and_pd( cmp, tmp );
|
|
// tmp = _mm_cmpeq_pd( voffset, inT.voffset );
|
|
// cmp = _mm_and_pd( cmp, tmp );
|
|
|
|
return false;
|
|
}
|
|
|
|
//
|
|
/// Non equal operator
|
|
//
|
|
bool operator != (const transform &inT)
|
|
{
|
|
// register v2pd cmp = _mm_cmpneq_pd( vx, inT.vx );
|
|
// register v2pd tmp = _mm_cmpneq_pd( vy, inT.vy );
|
|
// cmp = _mm_or_pd( cmp, tmp );
|
|
// tmp = _mm_cmpneq_pd( voffset, inT.voffset );
|
|
// cmp = _mm_or_pd( cmp, tmp );
|
|
|
|
return true;
|
|
}
|
|
|
|
#if 0
|
|
//
|
|
/// Test whether this is an identity transform.
|
|
//
|
|
bool isIdentityTransform() const
|
|
throw (except::outOfRange)
|
|
{
|
|
const double onePlusTolerance = 1.0 + relTolerance;
|
|
const double oneMinusTolerance = 1.0 - relTolerance;
|
|
//
|
|
// See if the offsets are beyond the origin.
|
|
//
|
|
if (offset.X() > absTolerance || offset.X() < -absTolerance ||
|
|
offset.Y() > absTolerance || offset.Y() < -absTolerance)
|
|
return false;
|
|
//
|
|
// Check by transform type.
|
|
//
|
|
switch((transformType) type)
|
|
{
|
|
case StdTransformWithMagType:
|
|
if (a11 > onePlusTolerance || a11 < oneMinusTolerance)
|
|
return false;
|
|
|
|
case StdTransformType:
|
|
if (orient.getOri() != R0)
|
|
return false;
|
|
break;
|
|
|
|
case GeneralTransformType:
|
|
if ( a12 > absTolerance ||
|
|
a12 < -absTolerance ||
|
|
a21 > absTolerance ||
|
|
a21 < -absTolerance)
|
|
return false;
|
|
|
|
case ScaleOffsetTransformType:
|
|
case ScaleTransformType:
|
|
if ( a11 > onePlusTolerance ||
|
|
a11 < oneMinusTolerance ||
|
|
a22 > onePlusTolerance ||
|
|
a22 < oneMinusTolerance)
|
|
return false;
|
|
break;
|
|
|
|
case ResolutionTransformType:
|
|
return false;
|
|
break;
|
|
|
|
default:
|
|
throw except::outOfRange("base::transform:isIdentityTransform: Unknown type.");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//
|
|
/// Returns the type of the transform.
|
|
//
|
|
transformType getType() const
|
|
{
|
|
return (transformType) type;
|
|
}
|
|
|
|
stdOrient getOrient() const
|
|
{
|
|
return orient;
|
|
}
|
|
|
|
//
|
|
/// Return the next field.
|
|
//
|
|
const transform *getNext() const
|
|
{
|
|
return next;
|
|
}
|
|
//
|
|
/// Return the next field.
|
|
//
|
|
transform *getNext()
|
|
{
|
|
return next;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
/// Returns the offset point of the transform.
|
|
//
|
|
pod::point<double> getOffset() const
|
|
{
|
|
return pod::point<double>( offset_x(), offset_y() );
|
|
}
|
|
//
|
|
/// Returns the offset point of the transform taking into account also resolution.
|
|
//
|
|
pod::point<double> getOffsetRes() const
|
|
{
|
|
return pod::point<double>( offset_x(), offset_y() );
|
|
}
|
|
//
|
|
/// Set the offset of the transform.
|
|
//
|
|
void setOffset( pod::point<double> off )
|
|
{
|
|
voffset = off.get_pd();
|
|
}
|
|
pod::point<double> getDiagonal() const
|
|
{
|
|
return pod::point<double>(a11(),a22());
|
|
}
|
|
pod::point<double> getRow1() const
|
|
{
|
|
return pod::point<double>(a11(),a12());
|
|
}
|
|
pod::point<double> getRow2() const
|
|
{
|
|
return pod::point<double>(a21(),a22());
|
|
}
|
|
pod::point<double> getCol1() const
|
|
{
|
|
return pod::point<double>(a11(),a21());
|
|
}
|
|
pod::point<double> getCol2() const
|
|
{
|
|
return pod::point<double>(a12(),a22());
|
|
}
|
|
//
|
|
/// Return the magnitude of the vector (1,0).
|
|
//
|
|
double getXVectorMag() const
|
|
{
|
|
if ( a21() == 0.0 )
|
|
return ::fabs(a11());
|
|
else if ( a11() == 0.0 )
|
|
return ::fabs(a21());
|
|
else
|
|
return ::sqrt(a11()*a11() + a21()*a21());
|
|
}
|
|
//
|
|
/// A special function to return the X scale factor. This
|
|
/// is a nonnegative value that reflects the scaling effect
|
|
/// in the X direction.
|
|
//
|
|
double getXScale() const
|
|
{
|
|
return getXVectorMag();
|
|
}
|
|
//
|
|
/// Returns the a11 entry of the matrix. If the matrix is a scaling
|
|
/// one, then this returns the X-scale factor.
|
|
/// The result is not correct for all types of transforms.
|
|
//
|
|
double get11() const
|
|
{
|
|
return a11();
|
|
}
|
|
//
|
|
/// Return the a12 entry of the matrix.
|
|
/// The result is only valid for general transforms.
|
|
/// The result is not correct for all types of transforms.
|
|
//
|
|
double get12() const
|
|
{
|
|
return a12();
|
|
}
|
|
//
|
|
/// Return the a21 entry of the matrix.
|
|
/// The result is only valid for general transforms.
|
|
/// The result is not correct for all types of transforms.
|
|
//
|
|
double get21() const
|
|
{
|
|
return a21();
|
|
}
|
|
//
|
|
/// Returns the a22 entry of the matrix. If the matrix is a scaling
|
|
/// one, then this returns the Y-scale factor.
|
|
/// The result is not correct for all types of transforms.
|
|
//
|
|
double get22() const
|
|
{
|
|
return a22();
|
|
}
|
|
|
|
//
|
|
/// get angle, isXFlip and mag from transformation
|
|
//
|
|
const void getAngleFlipMag( double& angle, bool& isXFlip, double& mag) const;
|
|
|
|
// Complex functions.
|
|
public:
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
/// Modify a single point.
|
|
//
|
|
template <class C>
|
|
pod::point<short> modifyDblToShort(pod::point<C> p) const
|
|
{
|
|
pod::rectangle<short> tmp;
|
|
modify( r, tmp );
|
|
return tmp;
|
|
}
|
|
|
|
template <class C>
|
|
pod::point<int> modifyDblToInt(pod::point<C> p) const
|
|
{
|
|
pod::rectangle<int> tmp;
|
|
modify( r, tmp );
|
|
return tmp;
|
|
}
|
|
|
|
template <class C>
|
|
pod::point<double> modifyDblToDbl(pod::point<C> p) const
|
|
{
|
|
pod::rectangle<double> tmp;
|
|
modify( r, tmp );
|
|
return tmp;
|
|
}
|
|
|
|
template <class C>
|
|
pod::point<short> modifyIntToShort(pod::point<C> p) const
|
|
{
|
|
pod::rectangle<short> tmp;
|
|
modify( r, tmp );
|
|
return tmp;
|
|
}
|
|
|
|
template <class C>
|
|
pod::point<int> modifyIntToInt(pod::point<C> p) const
|
|
{
|
|
pod::rectangle<int> tmp;
|
|
modify( r, tmp );
|
|
return tmp;
|
|
}
|
|
|
|
template <class C>
|
|
pod::point<short> modifyLongToShort(pod::point<C> p) const
|
|
{
|
|
pod::rectangle<short> tmp;
|
|
modify( r, tmp );
|
|
return tmp;
|
|
}
|
|
|
|
template <class C>
|
|
pod::point<int> modifyLongToInt(pod::point<C> p) const
|
|
{
|
|
pod::rectangle<int> tmp;
|
|
modify( r, tmp );
|
|
return tmp;
|
|
}
|
|
|
|
template <class C>
|
|
pod::point<long> modifyLongToLong(pod::point<C> p) const
|
|
{
|
|
pod::rectangle<long> tmp;
|
|
modify( r, tmp );
|
|
return tmp;
|
|
}
|
|
|
|
template<class C>
|
|
pod::rectangle<C> operator()(const pod::rectangle<C> & r) const
|
|
{
|
|
return modify( r );
|
|
}
|
|
|
|
template<class C>
|
|
pod::point<C> operator()(const pod::point<C> & p) const
|
|
{
|
|
return modify( p );
|
|
}
|
|
|
|
transform operator *(const transform & t2 ) const
|
|
{
|
|
return mult( t2 );
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
/// Multiply with another transform.
|
|
//
|
|
void mult(const transform &T2, transform &outT) const
|
|
{
|
|
//
|
|
// new_a11 = t1_a11*t2_a11 + t1_a12*t2_a21
|
|
// new_a22 = t1_a22*t2_a22 + t1_a21*t2_a12
|
|
//
|
|
v2pd m1 = _mm_mul_pd( a11_a22, T2.a11_a22 );
|
|
v2pd m2 = a21_a12;
|
|
m2 = _mm_shuffle_pd( m2, m2, 1 );
|
|
m2 = _mm_mul_pd( m2, T2.a21_a12 );
|
|
outT.a11_a22 = _mm_add_pd( m1, m2 );
|
|
//
|
|
// new_a21 = t1_a21*t2_a11 + t1_a22*t2_a21
|
|
// new_a12 = t1_a12*t2_a22 + t1_a11*t2_a12
|
|
//
|
|
v2pd m3 = _mm_mul_pd( a21_a12, T2.a11_a22 );
|
|
v2pd m4 = a11_a22;
|
|
m4 = _mm_shuffle_pd( m4, m4, 1 );
|
|
m4 = _mm_mul_pd( m4, T2.a21_a12 );
|
|
outT.a21_a12 = _mm_add_pd( m3, m4 );
|
|
//
|
|
// new_xoffset = t1_xoffset*t2_a11 + t1_yoffset*t2_a21+t2_xoffset
|
|
// new_yoffset = t1_yoffset*t2_a22 + t1_xoffset*t2_a12+t2_yoffset
|
|
//
|
|
v2pd m5 = _mm_mul_pd( voffset, T2.a11_a22 );
|
|
v2pd m6 = voffset;
|
|
m6 = _mm_shuffle_pd( m6, m6, 1 );
|
|
m6 = _mm_mul_pd( voffset, T2.a21_a12 );
|
|
|
|
m5 = _mm_add_pd( m5, m6 );
|
|
outT.voffset = _mm_add_pd( m5, T2.voffset );
|
|
}
|
|
|
|
//
|
|
/// Multiply with another transform.
|
|
//
|
|
transform mult(const transform &T2) const
|
|
{
|
|
//
|
|
// new_a11 = t1_a11*t2_a11 + t1_a12*t2_a21
|
|
// new_a22 = t1_a22*t2_a22 + t1_a21*t2_a12
|
|
//
|
|
v2pd m1 = _mm_mul_pd( a11_a22, T2.a11_a22 );
|
|
v2pd m2 = a21_a12;
|
|
m2 = _mm_shuffle_pd( m2, m2, 1 );
|
|
m2 = _mm_mul_pd( m2, T2.a21_a12 );
|
|
v2pd t_a11_a22 = _mm_add_pd( m1, m2 );
|
|
//
|
|
// new_a21 = t1_a21*t2_a11 + t1_a22*t2_a21
|
|
// new_a12 = t1_a12*t2_a22 + t1_a11*t2_a12
|
|
//
|
|
v2pd m3 = _mm_mul_pd( a21_a12, T2.a11_a22 );
|
|
v2pd m4 = a11_a22;
|
|
m4 = _mm_shuffle_pd( m4, m4, 1 );
|
|
m4 = _mm_mul_pd( m4, T2.a21_a12 );
|
|
v2pd t_a21_a12 = _mm_add_pd( m3, m4 );
|
|
//
|
|
// new_xoffset = t1_xoffset*t2_a11 + t1_yoffset*t2_a21+t2_xoffset
|
|
// new_yoffset = t1_yoffset*t2_a22 + t1_xoffset*t2_a12+t2_yoffset
|
|
//
|
|
v2pd m5 = _mm_mul_pd( voffset, T2.a11_a22 );
|
|
v2pd m6 = voffset;
|
|
m6 = _mm_shuffle_pd( m6, m6, 1 );
|
|
m6 = _mm_mul_pd( voffset, T2.a21_a12 );
|
|
|
|
m5 = _mm_add_pd( m5, m6 );
|
|
v2pd t_voffset = _mm_add_pd( m5, T2.voffset );
|
|
//
|
|
// Return the result
|
|
//
|
|
return transform( t_a11_a22, t_a21_a12, t_voffset );
|
|
}
|
|
|
|
//
|
|
/// Scale the transform by the given scalar.
|
|
//
|
|
transform & operator *= (const double d)
|
|
{
|
|
v2pd m = _mm_set1_pd( d );
|
|
a11_a22 = _mm_mul_pd( a11_a22, m );
|
|
a21_a12 = _mm_mul_pd( a21_a12, m );
|
|
voffset = _mm_mul_pd( voffset, m );
|
|
return *this;
|
|
}
|
|
|
|
//
|
|
/// Return the inverse transform of the transform.
|
|
//
|
|
transform getInverse() const
|
|
{
|
|
//
|
|
// Get determinant.
|
|
//
|
|
double det = a11()*a22()-a21()*a12();
|
|
v2pd detx2 = _mm_set1_pd( det );
|
|
//
|
|
// Get inv_a11 and inv_a22
|
|
//
|
|
// inv_a11 = a22/det
|
|
// inv_a11 = a11/det
|
|
//
|
|
v2pd t_a11_a22 = a11_a22;
|
|
t_a11_a22 = _mm_shuffle_pd( t_a11_a22, t_a11_a22, 1 );
|
|
t_a11_a22 = _mm_div_pd( t_a11_a22, detx2 );
|
|
//
|
|
// inv_a21 = -a21/det
|
|
// inv_a12 = -a12/det
|
|
//
|
|
v2pd t_a21_a12 = _mm_set1_pd( 0.0 );
|
|
t_a21_a12 = _mm_sub_pd( t_a21_a12, a21_a12 );
|
|
t_a21_a12 = _mm_div_pd( t_a21_a12, detx2 );
|
|
//
|
|
// inv_xoffset = -xoffset*inv_a11 -yoffset*inv_a21
|
|
// inv_yoffset = -yoffset*inv_a22 -xoffset*inv_a12
|
|
//
|
|
v2pd t_voffset = _mm_set1_pd( 0.0 );
|
|
t_voffset = _mm_sub_pd( t_voffset, voffset );
|
|
t_voffset = _mm_mul_pd( t_voffset, t_a11_a22 );
|
|
|
|
v2pd r_voffset = voffset;
|
|
r_voffset = _mm_shuffle_pd( r_voffset, r_voffset, 1 );
|
|
r_voffset = _mm_mul_pd( r_voffset, t_a21_a12 );
|
|
t_voffset = _mm_sub_pd( t_voffset, r_voffset );
|
|
//
|
|
// Return the result.
|
|
//
|
|
return transform( t_a11_a22, t_a21_a12, t_voffset );
|
|
}
|
|
|
|
|
|
//
|
|
/// This is a convinient function converting coordinate types without loss of precision.
|
|
//
|
|
/*!
|
|
We try to not loss precision and use available functionality
|
|
provided by transform maximally. This function does what
|
|
all modifyXxxxToYyyyy does and is very helpful in generic programming.
|
|
*/
|
|
template <class iC, class oC>
|
|
__forceinline void modify( const pod::point<iC>& rSrc, pod::point<oC>& rDest ) const
|
|
{
|
|
// rDest = modify( rSrc );
|
|
//
|
|
// x = x*a11 + y*a21
|
|
// y = y*a22 + x*a12
|
|
//
|
|
v2pd d = rSrc.get_pd();
|
|
v2pd d_rev = d;
|
|
d_rev = _mm_shuffle_pd( d_rev, d, 1 );
|
|
d = _mm_mul_pd( d, a11_a22 );
|
|
d_rev = _mm_mul_pd( d_rev, a21_a12 );
|
|
d = _mm_add_pd( d, voffset );
|
|
d = _mm_add_pd( d, d_rev );
|
|
rDest.set_pd( d );
|
|
}
|
|
|
|
//
|
|
/// This is a convinient function converting coordinate types without loss of precision.
|
|
//
|
|
/*!
|
|
We try to not loss precision and use available functionality
|
|
provided by transform maximally. This function does what
|
|
all modifyXxxxToYyyyy does and is very helpful in generic programming.
|
|
*/
|
|
template <class C>
|
|
__forceinline pod::point<C> modify( const pod::point<C>& rSrc ) const
|
|
{
|
|
//
|
|
// x = x*a11 + y*a21
|
|
// y = y*a22 + x*a12
|
|
//
|
|
v2pd d = rSrc.get_pd();
|
|
v2pd d_rev = d;
|
|
d_rev = _mm_shuffle_pd( d_rev, d, 1 );
|
|
d = _mm_mul_pd( d, a11_a22 );
|
|
d_rev = _mm_mul_pd( d_rev, a21_a12 );
|
|
d = _mm_add_pd( d, voffset );
|
|
d = _mm_add_pd( d, d_rev );
|
|
return pod::point<C>( d );
|
|
}
|
|
|
|
//
|
|
/// This is pod::rectangle version of above function.
|
|
//
|
|
template <class iC, class oC>
|
|
__forceinline void modify( const pod::rectangle<iC>& rSrc, pod::rectangle<oC>& rDest ) const
|
|
{
|
|
modifyBBox( rSrc, rDest );
|
|
}
|
|
|
|
//
|
|
/// This is pod::rectangle version of above function.
|
|
//
|
|
template <class C>
|
|
__forceinline pod::rectangle<C> modify( const pod::rectangle<C>& rSrc ) const
|
|
{
|
|
pod::rectangle<C> dest;
|
|
modifyBBox( rSrc, dest );
|
|
return dest;
|
|
}
|
|
|
|
//
|
|
/// Transform given bbox.
|
|
//
|
|
/*!
|
|
While we transform bbox, the usual approach of transforming
|
|
lower left and upper right is not satisfactory. In case of
|
|
rotations we need to transform and merge all four vertices
|
|
of bbox to get one of bbox rectangles of a shape which bbox we
|
|
want to transform. The bbox transformed this way will not be the
|
|
smallest bbox of the shape, yet at least this will be a bbox.
|
|
While if we use usual approach, then we will get (in case of rotation)
|
|
a rectangle that is not nessecarily a bbox of original shape.
|
|
|
|
The function also optimizes cases when transform has
|
|
only orthogonal transforms.
|
|
*/
|
|
template <class iC, class oC>
|
|
__forceinline void modifyBBox( const pod::rectangle<iC>& rSrc, pod::rectangle<oC>& rDest ) const
|
|
{
|
|
pod::point<oC> b1 = modify( rSrc.getLowerLeftRef() );
|
|
pod::point<oC> b2 = modify( rSrc.getUpperRightRef() );
|
|
|
|
rDest.setRect( b1, b2 );
|
|
rDest.makeValid();
|
|
|
|
//
|
|
// If we have orthogonal transform?
|
|
// First check get12 since most of transform will
|
|
// have no rotation factor.
|
|
//
|
|
// v2pd a1112 = _mm_shuffle_pd( a11_a22, a21_a12, 1 )
|
|
// v2pd a1112 = _mm_shuffle_pd( a11_a22, a21_a12, 1 )
|
|
if ( a12() != 0. && a11() != 0. )
|
|
{
|
|
pod::point<oC> b3 = modify( rSrc.getUpperLeft() );
|
|
rDest.merge( b3 );
|
|
|
|
pod::point<oC> b4 = modify( rSrc.getLowerRight() );
|
|
rDest.merge( b4 );
|
|
}
|
|
}
|
|
|
|
//
|
|
/// Convert a transform type to a string.
|
|
//
|
|
friend std::ostream & operator << (std::ostream & os, const transform & t);
|
|
|
|
//
|
|
/// Print a transform, mainly for debug purpose.
|
|
//
|
|
void print(FILE *outStream, const char *linePrefix) const;
|
|
|
|
}; // class transform
|
|
|
|
} // namespace base
|
|
|
|
#endif // TRANSFORM_H_
|