* Copyright 2008-2010, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephen Deken, stephen.deken@gmail.com
* Stephan Aßmus <superstippi@gmx.de>
*/
#include <AffineTransform.h>
#include <TypeConstants.h>
const BAffineTransform B_AFFINE_IDENTITY_TRANSFORM;
BAffineTransform::BAffineTransform()
:
sx(1.0),
shy(0.0),
shx(0.0),
sy(1.0),
tx(0.0),
ty(0.0)
{
}
BAffineTransform::BAffineTransform(double sx, double shy, double shx,
double sy, double tx, double ty)
:
sx(sx),
shy(shy),
shx(shx),
sy(sy),
tx(tx),
ty(ty)
{
}
BAffineTransform::BAffineTransform(const BAffineTransform& other)
:
sx(other.sx),
shy(other.shy),
shx(other.shx),
sy(other.sy),
tx(other.tx),
ty(other.ty)
{
}
BAffineTransform::~BAffineTransform()
{
}
bool
BAffineTransform::IsFixedSize() const
{
return true;
}
type_code
BAffineTransform::TypeCode() const
{
return B_AFFINE_TRANSFORM_TYPE;
}
ssize_t
BAffineTransform::FlattenedSize() const
{
return 6 * sizeof(double);
}
status_t
BAffineTransform::Flatten(void* _buffer, ssize_t size) const
{
if (_buffer == NULL || size < BAffineTransform::FlattenedSize())
return B_BAD_VALUE;
double* buffer = reinterpret_cast<double*>(_buffer);
buffer[0] = sx;
buffer[1] = shy;
buffer[2] = shx;
buffer[3] = sy;
buffer[4] = tx;
buffer[5] = ty;
return B_OK;
}
status_t
BAffineTransform::Unflatten(type_code code, const void* _buffer, ssize_t size)
{
if (_buffer == NULL || size < BAffineTransform::FlattenedSize()
|| code != BAffineTransform::TypeCode()) {
return B_BAD_VALUE;
}
const double* buffer = reinterpret_cast<const double*>(_buffer);
sx = buffer[0];
shy = buffer[1];
shx = buffer[2];
sy = buffer[3];
tx = buffer[4];
ty = buffer[5];
return B_OK;
}
BAffineTransform
BAffineTransform::AffineTranslation(double x, double y)
{
return BAffineTransform(1.0, 0.0, 0.0, 1.0, x, y);
}
BAffineTransform
BAffineTransform::AffineRotation(double angle)
{
return BAffineTransform(cos(angle), sin(angle), -sin(angle), cos(angle),
0.0, 0.0);
}
BAffineTransform
BAffineTransform::AffineScaling(double x, double y)
{
return BAffineTransform(x, 0.0, 0.0, y, 0.0, 0.0);
}
BAffineTransform
BAffineTransform::AffineScaling(double scale)
{
return BAffineTransform(scale, 0.0, 0.0, scale, 0.0, 0.0);
}
BAffineTransform
BAffineTransform::AffineShearing(double x, double y)
{
return BAffineTransform(1.0, tan(y), tan(x), 1.0, 0.0, 0.0);
}
BPoint
BAffineTransform::Apply(const BPoint& point) const
{
double x = point.x;
double y = point.y;
Apply(&x, &y);
return BPoint(x, y);
}
BPoint
BAffineTransform::ApplyInverse(const BPoint& point) const
{
double x = point.x;
double y = point.y;
ApplyInverse(&x, &y);
return BPoint(x, y);
}
void
BAffineTransform::Apply(BPoint* point) const
{
if (point == NULL)
return;
double x = point->x;
double y = point->y;
Apply(&x, &y);
point->x = x;
point->y = y;
}
void
BAffineTransform::ApplyInverse(BPoint* point) const
{
if (point == NULL)
return;
double x = point->x;
double y = point->y;
ApplyInverse(&x, &y);
point->x = x;
point->y = y;
}
void
BAffineTransform::Apply(BPoint* points, uint32 count) const
{
if (points != NULL) {
for (uint32 i = 0; i < count; ++i)
Apply(&points[i]);
}
}
void
BAffineTransform::ApplyInverse(BPoint* points, uint32 count) const
{
if (points != NULL) {
for (uint32 i = 0; i < count; ++i)
ApplyInverse(&points[i]);
}
}
const BAffineTransform&
BAffineTransform::TranslateBy(const BPoint& delta)
{
return TranslateBy(delta.x, delta.y);
}
BAffineTransform
BAffineTransform::TranslateByCopy(double x, double y) const
{
BAffineTransform copy(*this);
copy.TranslateBy(x, y);
return copy;
}
BAffineTransform
BAffineTransform::TranslateByCopy(const BPoint& delta) const
{
return TranslateByCopy(delta.x, delta.y);
}
const BAffineTransform&
BAffineTransform::RotateBy(const BPoint& center, double angle)
{
TranslateBy(-center.x, -center.y);
RotateBy(angle);
return TranslateBy(center.x, center.y);
}
BAffineTransform
BAffineTransform::RotateByCopy(double angle) const
{
BAffineTransform copy(*this);
copy.RotateBy(angle);
return copy;
}
BAffineTransform
BAffineTransform::RotateByCopy(const BPoint& center, double angle) const
{
BAffineTransform copy(*this);
copy.RotateBy(center, angle);
return copy;
}
const BAffineTransform&
BAffineTransform::ScaleBy(const BPoint& center, double scale)
{
return ScaleBy(center, scale, scale);
}
const BAffineTransform&
BAffineTransform::ScaleBy(const BPoint& center, double x, double y)
{
TranslateBy(-center.x, -center.y);
ScaleBy(x, y);
return TranslateBy(center.x, center.y);
}
const BAffineTransform&
BAffineTransform::ScaleBy(const BPoint& scale)
{
return ScaleBy(scale.x, scale.y);
}
const BAffineTransform&
BAffineTransform::ScaleBy(const BPoint& center, const BPoint& scale)
{
return ScaleBy(center, scale.x, scale.y);
}
BAffineTransform
BAffineTransform::ScaleByCopy(double scale) const
{
return ScaleByCopy(scale, scale);
}
BAffineTransform
BAffineTransform::ScaleByCopy(const BPoint& center, double scale) const
{
return ScaleByCopy(center, scale, scale);
}
BAffineTransform
BAffineTransform::ScaleByCopy(double x, double y) const
{
BAffineTransform copy(*this);
copy.ScaleBy(x, y);
return copy;
}
BAffineTransform
BAffineTransform::ScaleByCopy(const BPoint& center, double x, double y) const
{
BAffineTransform copy(*this);
copy.ScaleBy(center, x, y);
return copy;
}
BAffineTransform
BAffineTransform::ScaleByCopy(const BPoint& scale) const
{
return ScaleByCopy(scale.x, scale.y);
}
BAffineTransform
BAffineTransform::ScaleByCopy(const BPoint& center, const BPoint& scale) const
{
return ScaleByCopy(center, scale.x, scale.y);
}
const BAffineTransform&
BAffineTransform::SetScale(double scale)
{
return SetScale(scale, scale);
}
const BAffineTransform&
BAffineTransform::SetScale(double x, double y)
{
double tx;
double ty;
double rotation;
double shearX;
double shearY;
if (!GetAffineParameters(&tx, &ty, &rotation, NULL, NULL,
&shearX, &shearY)) {
return *this;
}
BAffineTransform result;
result.ShearBy(shearX, shearY);
result.ScaleBy(x, y);
result.RotateBy(rotation);
result.TranslateBy(tx, ty);
return *this = result;
}
const BAffineTransform&
BAffineTransform::ShearBy(const BPoint& center, double x, double y)
{
TranslateBy(-center.x, -center.y);
ShearBy(x, y);
return TranslateBy(center.x, center.y);
}
const BAffineTransform&
BAffineTransform::ShearBy(const BPoint& shear)
{
return ShearBy(shear.x, shear.y);
}
const BAffineTransform&
BAffineTransform::ShearBy(const BPoint& center, const BPoint& shear)
{
return ShearBy(center, shear.x, shear.y);
}
BAffineTransform
BAffineTransform::ShearByCopy(double x, double y) const
{
BAffineTransform copy(*this);
copy.ShearBy(x, y);
return copy;
}
BAffineTransform
BAffineTransform::ShearByCopy(const BPoint& center, double x, double y) const
{
BAffineTransform copy(*this);
copy.ShearBy(center, x, y);
return copy;
}
BAffineTransform
BAffineTransform::ShearByCopy(const BPoint& shear) const
{
BAffineTransform copy(*this);
copy.ShearBy(shear);
return copy;
}
BAffineTransform
BAffineTransform::ShearByCopy(const BPoint& center, const BPoint& shear) const
{
BAffineTransform copy(*this);
copy.ShearBy(center, shear);
return copy;
}
const BAffineTransform&
BAffineTransform::PreMultiply(const BAffineTransform& other)
{
double t0 = sx * other.sx + shy * other.shx;
double t2 = shx * other.sx + sy * other.shx;
double t4 = tx * other.sx + ty * other.shx + other.tx;
shy = sx * other.shy + shy * other.sy;
sy = shx * other.shy + sy * other.sy;
ty = tx * other.shy + ty * other.sy + other.ty;
sx = t0;
shx = t2;
tx = t4;
return *this;
}
bool
BAffineTransform::IsValid(double epsilon) const
{
return fabs(sx) > epsilon && fabs(sy) > epsilon;
}
static inline bool
IsEqualEpsilon(double v1, double v2, double epsilon)
{
return fabs(v1 - v2) <= double(epsilon);
}
bool
BAffineTransform::IsIdentity(double epsilon) const
{
return IsEqualEpsilon(sx, 1.0, epsilon)
&& IsEqualEpsilon(shy, 0.0, epsilon)
&& IsEqualEpsilon(shx, 0.0, epsilon)
&& IsEqualEpsilon(sy, 1.0, epsilon)
&& IsEqualEpsilon(tx, 0.0, epsilon)
&& IsEqualEpsilon(ty, 0.0, epsilon);
}
bool
BAffineTransform::IsDilation(double epsilon) const
{
return IsEqualEpsilon(shy, 0.0, epsilon)
&& IsEqualEpsilon(shx, 0.0, epsilon);
}
bool
BAffineTransform::IsEqual(const BAffineTransform& other, double epsilon) const
{
return IsEqualEpsilon(sx, other.sx, epsilon)
&& IsEqualEpsilon(shy, other.shy, epsilon)
&& IsEqualEpsilon(shx, other.shx, epsilon)
&& IsEqualEpsilon(sy, other.sy, epsilon)
&& IsEqualEpsilon(tx, other.tx, epsilon)
&& IsEqualEpsilon(ty, other.ty, epsilon);
}
const BAffineTransform&
BAffineTransform::Invert()
{
double d = InverseDeterminant();
double t0 = sy * d;
sy = sx * d;
shy = -shy * d;
shx = -shx * d;
double t4 = -tx * t0 - ty * shx;
ty = -tx * shy - ty * sy;
sx = t0;
tx = t4;
return *this;
}
const BAffineTransform&
BAffineTransform::FlipX()
{
sx = -sx;
shy = -shy;
tx = -tx;
return *this;
}
const BAffineTransform&
BAffineTransform::FlipY()
{
shx = -shx;
sy = -sy;
ty = -ty;
return *this;
}
const BAffineTransform&
BAffineTransform::Reset()
{
sx = sy = 1.0;
shy = shx = tx = ty = 0.0;
return *this;
}
void
BAffineTransform::GetTranslation(double* _tx, double* _ty) const
{
if (_tx)
*_tx = tx;
if (_ty)
*_ty = ty;
}
double
BAffineTransform::Rotation() const
{
double x1 = 0.0;
double y1 = 0.0;
double x2 = 1.0;
double y2 = 0.0;
Apply(&x1, &y1);
Apply(&x2, &y2);
return atan2(y2 - y1, x2 - x1);
}
double
BAffineTransform::Scale() const
{
double x = 0.707106781 * sx + 0.707106781 * shx;
double y = 0.707106781 * shy + 0.707106781 * sy;
return sqrt(x * x + y * y);
}
void
BAffineTransform::GetScale(double* _sx, double* _sy) const
{
double x1 = 0.0;
double y1 = 0.0;
double x2 = 1.0;
double y2 = 1.0;
BAffineTransform t(*this);
t.PreMultiply(AffineRotation(-Rotation()));
t.Apply(&x1, &y1);
t.Apply(&x2, &y2);
if (_sx)
*_sx = x2 - x1;
if (_sy)
*_sy = y2 - y1;
}
void
BAffineTransform::GetScaleAbs(double* _sx, double* _sy) const
{
if (_sx)
*_sx = sqrt(sx * sx + shx * shx);
if (_sy)
*_sy = sqrt(shy * shy + sy * sy);
}
bool
BAffineTransform::GetAffineParameters(double* _translationX,
double* _translationY, double* _rotation, double* _scaleX, double* _scaleY,
double* _shearX, double* _shearY) const
{
GetTranslation(_translationX, _translationY);
double rotation = Rotation();
if (_rotation != NULL)
*_rotation = rotation;
double x1 = 0.0;
double y1 = 0.0;
double x2 = 1.0;
double y2 = 0.0;
double x3 = 0.0;
double y3 = 1.0;
BAffineTransform t(*this);
t.PreMultiply(AffineRotation(-rotation));
t.Apply(&x1, &y1);
t.Apply(&x2, &y2);
t.Apply(&x3, &y3);
double shearX = y2 - y1;
double shearY = x3 - x1;
x1 = 0.0;
y1 = 0.0;
x2 = 1.0;
y2 = 0.0;
x3 = 0.0;
y3 = 1.0;
t.PreMultiplyInverse(AffineShearing(shearX, shearY));
t.Apply(&x1, &y1);
t.Apply(&x2, &y2);
t.Apply(&x3, &y3);
double scaleX = x2 - x1;
double scaleY = y3 - y1;
if (_scaleX != NULL)
*_scaleX = scaleX;
if (_scaleY != NULL)
*_scaleY = scaleY;
if (scaleX == 0.0 && scaleY == 0.0)
return false;
if (_shearX != NULL)
*_shearX = shearX / scaleX;
if (_shearY != NULL)
*_shearY = shearY / scaleY;
return true;
}