agg: Pull in updated perspective transformation

The new version has many more features than the old one. This update
is necessary for an upcoming update to Icon-O-Matic adding perspective
transformers.

This update is pulled from https://github.com/ghaerr/agg-2.6 at commit
e7db22bd12700118257b4cb780539c421e01aa51 with our changes applied on
top. Note that this repository isn't necessarily the chosen upstream
that all future updates should be pulled from. See the discussion
starting at [1] for more information.

This also updates the affine transformation since the newer perspective
transformation requires the newer version.

[1] https://discuss.haiku-os.org/t/gsoc-2023-progress-on-perspective-transformation-haiku-project/13594/34

Change-Id: Ic578eec15fbb9131338b3c605c737ce1bfb252ca
Reviewed-on: https://review.haiku-os.org/c/haiku/+/6808
Reviewed-by: Adrien Destugues <[email protected]>
This commit is contained in:
Zardshard
2023-08-22 09:44:20 +00:00
committed by Adrien Destugues
parent efbeada748
commit a1c86e7ada
3 changed files with 923 additions and 274 deletions
+221 -112
View File
@@ -2,8 +2,8 @@
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
@@ -19,44 +19,44 @@
#ifndef AGG_TRANS_AFFINE_INCLUDED
#define AGG_TRANS_AFFINE_INCLUDED
#include <math.h>
#include <cmath>
#include "agg_basics.h"
namespace agg
{
const double affine_epsilon = 1e-14; // About of precision of doubles
const double affine_epsilon = 1e-14;
//============================================================trans_affine
//
// See Implementation agg_trans_affine.cpp
//
// Affine transformation are linear transformations in Cartesian coordinates
// (strictly speaking not only in Cartesian, but for the beginning we will
// think so). They are rotation, scaling, translation and skewing.
// After any affine transformation a line segment remains a line segment
// and it will never become a curve.
// (strictly speaking not only in Cartesian, but for the beginning we will
// think so). They are rotation, scaling, translation and skewing.
// After any affine transformation a line segment remains a line segment
// and it will never become a curve.
//
// There will be no math about matrix calculations, since it has been
// There will be no math about matrix calculations, since it has been
// described many times. Ask yourself a very simple question:
// "why do we need to understand and use some matrix stuff instead of just
// "why do we need to understand and use some matrix stuff instead of just
// rotating, scaling and so on". The answers are:
//
// 1. Any combination of transformations can be done by only 4 multiplications
// and 4 additions in floating point.
// 2. One matrix transformation is equivalent to the number of consecutive
// discrete transformations, i.e. the matrix "accumulates" all transformations
// in the order of their settings. Suppose we have 4 transformations:
// discrete transformations, i.e. the matrix "accumulates" all transformations
// in the order of their settings. Suppose we have 4 transformations:
// * rotate by 30 degrees,
// * scale X to 2.0,
// * scale Y to 1.5,
// * move to (100, 100).
// The result will depend on the order of these transformations,
// * scale X to 2.0,
// * scale Y to 1.5,
// * move to (100, 100).
// The result will depend on the order of these transformations,
// and the advantage of matrix is that the sequence of discret calls:
// rotate(30), scaleX(2.0), scaleY(1.5), move(100,100)
// rotate(30), scaleX(2.0), scaleY(1.5), move(100,100)
// will have exactly the same result as the following matrix transformations:
//
//
// affine_matrix m;
// m *= rotate_matrix(30);
// m *= rotate_matrix(30);
// m *= scaleX_matrix(2.0);
// m *= scaleY_matrix(1.5);
// m *= move_matrix(100,100);
@@ -64,7 +64,7 @@ namespace agg
// m.transform_my_point_at_last(x, y);
//
// What is the good of it? In real life we will set-up the matrix only once
// and then transform many points, let alone the convenience to set any
// and then transform many points, let alone the convenience to set any
// combination of transformations.
//
// So, how to use it? Very easy - literally as it's shown above. Not quite,
@@ -77,71 +77,87 @@ namespace agg
// m.transform(&x, &y);
//
// The affine matrix is all you need to perform any linear transformation,
// but all transformations have origin point (0,0). It means that we need to
// but all transformations have origin point (0,0). It means that we need to
// use 2 translations if we want to rotate someting around (100,100):
//
//
// m *= agg::trans_affine_translation(-100.0, -100.0); // move to (0,0)
// m *= agg::trans_affine_rotation(30.0 * 3.1415926 / 180.0); // rotate
// m *= agg::trans_affine_translation(100.0, 100.0); // move back to (100,100)
//----------------------------------------------------------------------
class trans_affine
struct trans_affine
{
public:
double sx, shy, shx, sy, tx, ty;
//------------------------------------------ Construction
// Construct an identity matrix - it does not transform anything
// Identity matrix
trans_affine() :
m0(1.0), m1(0.0), m2(0.0), m3(1.0), m4(0.0), m5(0.0)
sx(1.0), shy(0.0), shx(0.0), sy(1.0), tx(0.0), ty(0.0)
{}
// Construct a custom matrix. Usually used in derived classes
trans_affine(double v0, double v1, double v2, double v3, double v4, double v5) :
m0(v0), m1(v1), m2(v2), m3(v3), m4(v4), m5(v5)
// Custom matrix. Usually used in derived classes
trans_affine(double v0, double v1, double v2,
double v3, double v4, double v5) :
sx(v0), shy(v1), shx(v2), sy(v3), tx(v4), ty(v5)
{}
// Construct a matrix to transform a parallelogram to another one.
trans_affine(const double* rect, const double* parl)
{
parl_to_parl(rect, parl);
}
// Custom matrix from m[6]
explicit trans_affine(const double* m) :
sx(m[0]), shy(m[1]), shx(m[2]), sy(m[3]), tx(m[4]), ty(m[5])
{}
// Construct a matrix to transform a rectangle to a parallelogram.
trans_affine(double x1, double y1, double x2, double y2,
// Rectangle to a parallelogram.
trans_affine(double x1, double y1, double x2, double y2,
const double* parl)
{
rect_to_parl(x1, y1, x2, y2, parl);
}
// Construct a matrix to transform a parallelogram to a rectangle.
trans_affine(const double* parl,
// Parallelogram to a rectangle.
trans_affine(const double* parl,
double x1, double y1, double x2, double y2)
{
parl_to_rect(parl, x1, y1, x2, y2);
}
// Arbitrary parallelogram transformation.
trans_affine(const double* src, const double* dst)
{
parl_to_parl(src, dst);
}
//---------------------------------- Parellelogram transformations
// Calculate a matrix to transform a parallelogram to another one.
// src and dst are pointers to arrays of three points
// (double[6], x,y,...) that identify three corners of the
// parallelograms assuming implicit fourth points.
// There are also transformations rectangtle to parallelogram and
// parellelogram to rectangle
const trans_affine& parl_to_parl(const double* src,
// transform a parallelogram to another one. Src and dst are
// pointers to arrays of three points (double[6], x1,y1,...) that
// identify three corners of the parallelograms assuming implicit
// fourth point. The arguments are arrays of double[6] mapped
// to x1,y1, x2,y2, x3,y3 where the coordinates are:
// *-----------------*
// / (x3,y3)/
// / /
// /(x1,y1) (x2,y2)/
// *-----------------*
const trans_affine& parl_to_parl(const double* src,
const double* dst);
const trans_affine& rect_to_parl(double x1, double y1,
double x2, double y2,
const trans_affine& rect_to_parl(double x1, double y1,
double x2, double y2,
const double* parl);
const trans_affine& parl_to_rect(const double* parl,
double x1, double y1,
const trans_affine& parl_to_rect(const double* parl,
double x1, double y1,
double x2, double y2);
//------------------------------------------ Operations
// Reset - actually load an identity matrix
// Reset - load an identity matrix
const trans_affine& reset();
// Direct transformations operations
const trans_affine& translate(double x, double y);
const trans_affine& rotate(double a);
const trans_affine& scale(double s);
const trans_affine& scale(double x, double y);
// Multiply matrix to another one
const trans_affine& multiply(const trans_affine& m);
@@ -154,8 +170,8 @@ namespace agg
// Multiply inverse of "m" to "this" and assign the result to "this"
const trans_affine& premultiply_inv(const trans_affine& m);
// Invert matrix. Do not try to invert degenerate matrices,
// there's no check for validity. If you set scale to 0 and
// Invert matrix. Do not try to invert degenerate matrices,
// there's no check for validity. If you set scale to 0 and
// then try to invert matrix, expect unpredictable result.
const trans_affine& invert();
@@ -169,38 +185,38 @@ namespace agg
// Store matrix to an array [6] of double
void store_to(double* m) const
{
*m++ = m0; *m++ = m1; *m++ = m2; *m++ = m3; *m++ = m4; *m++ = m5;
*m++ = sx; *m++ = shy; *m++ = shx; *m++ = sy; *m++ = tx; *m++ = ty;
}
// Load matrix from an array [6] of double
const trans_affine& load_from(const double* m)
{
m0 = *m++; m1 = *m++; m2 = *m++; m3 = *m++; m4 = *m++; m5 = *m++;
sx = *m++; shy = *m++; shx = *m++; sy = *m++; tx = *m++; ty = *m++;
return *this;
}
//------------------------------------------- Operators
// Multiply current matrix to another one
// Multiply the matrix by another one
const trans_affine& operator *= (const trans_affine& m)
{
return multiply(m);
}
// Multiply current matrix to inverse of another one
// Multiply the matrix by inverse of another one
const trans_affine& operator /= (const trans_affine& m)
{
return multiply_inv(m);
}
// Multiply current matrix to another one and return
// Multiply the matrix by another one and return
// the result in a separete matrix.
trans_affine operator * (const trans_affine& m) const
{
return trans_affine(*this).multiply(m);
}
// Multiply current matrix to inverse of another one
// Multiply the matrix by inverse of another one
// and return the result in a separete matrix.
trans_affine operator / (const trans_affine& m) const
{
@@ -227,86 +243,136 @@ namespace agg
}
//-------------------------------------------- Transformations
// Direct transformation x and y
// Direct transformation of x and y
void transform(double* x, double* y) const;
// Direct transformation x and y, 2x2 matrix only, no translation
// Direct transformation of x and y, 2x2 matrix only, no translation
void transform_2x2(double* x, double* y) const;
// Inverse transformation x and y. It works slower than the
// direct transformation, so if the performance is critical
// it's better to invert() the matrix and then use transform()
// Inverse transformation of x and y. It works slower than the
// direct transformation. For massive operations it's better to
// invert() the matrix and then use direct transformations.
void inverse_transform(double* x, double* y) const;
//-------------------------------------------- Auxiliary
// Calculate the determinant of matrix
double determinant() const
{
return 1.0 / (m0 * m3 - m1 * m2);
return sx * sy - shy * shx;
}
// Get the average scale (by X and Y).
// Calculate the reciprocal of the determinant
double determinant_reciprocal() const
{
return 1.0 / (sx * sy - shy * shx);
}
// Get the average scale (by X and Y).
// Basically used to calculate the approximation_scale when
// decomposinting curves into line segments.
double scale() const;
// Check to see if the matrix is not degenerate
bool is_valid(double epsilon = affine_epsilon) const;
// Check to see if it's an identity matrix
bool is_identity(double epsilon = affine_epsilon) const;
// Check to see if two matrices are equal
bool is_equal(const trans_affine& m, double epsilon = affine_epsilon) const;
// Determine the major parameters. Use carefully considering degenerate matrices
// Determine the major parameters. Use with caution considering
// possible degenerate cases.
double rotation() const;
void translation(double* dx, double* dy) const;
void scaling(double* sx, double* sy) const;
void scaling_abs(double* sx, double* sy) const
{
*sx = sqrt(m0*m0 + m2*m2);
*sy = sqrt(m1*m1 + m3*m3);
}
private:
double m0;
double m1;
double m2;
double m3;
double m4;
double m5;
void scaling(double* x, double* y) const;
void scaling_abs(double* x, double* y) const;
};
//------------------------------------------------------------------------
inline void trans_affine::transform(double* x, double* y) const
{
double tx = *x;
*x = tx * m0 + *y * m2 + m4;
*y = tx * m1 + *y * m3 + m5;
double tmp = *x;
*x = tmp * sx + *y * shx + tx;
*y = tmp * shy + *y * sy + ty;
}
//------------------------------------------------------------------------
inline void trans_affine::transform_2x2(double* x, double* y) const
{
double tx = *x;
*x = tx * m0 + *y * m2;
*y = tx * m1 + *y * m3;
double tmp = *x;
*x = tmp * sx + *y * shx;
*y = tmp * shy + *y * sy;
}
//------------------------------------------------------------------------
inline void trans_affine::inverse_transform(double* x, double* y) const
{
double d = determinant();
double a = (*x - m4) * d;
double b = (*y - m5) * d;
*x = a * m3 - b * m2;
*y = b * m0 - a * m1;
double d = determinant_reciprocal();
double a = (*x - tx) * d;
double b = (*y - ty) * d;
*x = a * sy - b * shx;
*y = b * sx - a * shy;
}
//------------------------------------------------------------------------
inline double trans_affine::scale() const
{
double x = M_SQRT1_2 * m0 + M_SQRT1_2 * m2;
double y = M_SQRT1_2 * m1 + M_SQRT1_2 * m3;
return sqrt(x*x + y*y);
double x = M_SQRT1_2 * sx + M_SQRT1_2 * shx;
double y = M_SQRT1_2 * shy + M_SQRT1_2 * sy;
return std::sqrt(x*x + y*y);
}
//------------------------------------------------------------------------
inline const trans_affine& trans_affine::translate(double x, double y)
{
tx += x;
ty += y;
return *this;
}
//------------------------------------------------------------------------
inline const trans_affine& trans_affine::rotate(double a)
{
double ca = std::cos(a);
double sa = std::sin(a);
double t0 = sx * ca - shy * sa;
double t2 = shx * ca - sy * sa;
double t4 = tx * ca - ty * sa;
shy = sx * sa + shy * ca;
sy = shx * sa + sy * ca;
ty = tx * sa + ty * ca;
sx = t0;
shx = t2;
tx = t4;
return *this;
}
//------------------------------------------------------------------------
inline const trans_affine& trans_affine::scale(double x, double y)
{
double mm0 = x; // Possible hint for the optimizer
double mm3 = y;
sx *= mm0;
shx *= mm0;
tx *= mm0;
shy *= mm3;
sy *= mm3;
ty *= mm3;
return *this;
}
//------------------------------------------------------------------------
inline const trans_affine& trans_affine::scale(double s)
{
double m = s; // Possible hint for the optimizer
sx *= m;
shx *= m;
tx *= m;
shy *= m;
sy *= m;
ty *= m;
return *this;
}
//------------------------------------------------------------------------
@@ -321,8 +387,7 @@ namespace agg
{
trans_affine t = m;
t.invert();
multiply(t);
return *this;
return multiply(t);
}
//------------------------------------------------------------------------
@@ -333,29 +398,39 @@ namespace agg
return *this = t.multiply(*this);
}
//------------------------------------------------------------------------
inline void trans_affine::scaling_abs(double* x, double* y) const
{
// Used to calculate scaling coefficients in image resampling.
// When there is considerable shear this method gives us much
// better estimation than just sx, sy.
*x = std::sqrt(sx * sx + shx * shx);
*y = std::sqrt(shy * shy + sy * sy);
}
//====================================================trans_affine_rotation
// Rotation matrix. sin() and cos() are calculated twice for the same angle.
// There's no harm because the performance of sin()/cos() is very good on all
// modern processors. Besides, this operation is not going to be invoked too
// modern processors. Besides, this operation is not going to be invoked too
// often.
class trans_affine_rotation : public trans_affine
{
public:
trans_affine_rotation(double a) :
trans_affine(cos(a), sin(a), -sin(a), cos(a), 0.0, 0.0)
trans_affine_rotation(double a) :
trans_affine(std::cos(a), std::sin(a), -std::sin(a), std::cos(a), 0.0, 0.0)
{}
};
//====================================================trans_affine_scaling
// Scaling matrix. sx, sy - scale coefficients by X and Y respectively
// Scaling matrix. x, y - scale coefficients by X and Y respectively
class trans_affine_scaling : public trans_affine
{
public:
trans_affine_scaling(double sx, double sy) :
trans_affine(sx, 0.0, 0.0, sy, 0.0, 0.0)
trans_affine_scaling(double x, double y) :
trans_affine(x, 0.0, 0.0, y, 0.0, 0.0)
{}
trans_affine_scaling(double s) :
trans_affine_scaling(double s) :
trans_affine(s, 0.0, 0.0, s, 0.0, 0.0)
{}
};
@@ -365,8 +440,8 @@ namespace agg
class trans_affine_translation : public trans_affine
{
public:
trans_affine_translation(double tx, double ty) :
trans_affine(1.0, 0.0, 0.0, 1.0, tx, ty)
trans_affine_translation(double x, double y) :
trans_affine(1.0, 0.0, 0.0, 1.0, x, y)
{}
};
@@ -375,33 +450,67 @@ namespace agg
class trans_affine_skewing : public trans_affine
{
public:
trans_affine_skewing(double sx, double sy) :
trans_affine(1.0, tan(sy), tan(sx), 1.0, 0.0, 0.0)
trans_affine_skewing(double x, double y) :
trans_affine(1.0, std::tan(y), std::tan(x), 1.0, 0.0, 0.0)
{}
};
//===============================================trans_affine_line_segment
// Rotate, Scale and Translate, associating 0...dist with line segment
// Rotate, Scale and Translate, associating 0...dist with line segment
// x1,y1,x2,y2
class trans_affine_line_segment : public trans_affine
{
public:
trans_affine_line_segment(double x1, double y1, double x2, double y2,
trans_affine_line_segment(double x1, double y1, double x2, double y2,
double dist)
{
double dx = x2 - x1;
double dy = y2 - y1;
if(dist > 0.0)
{
multiply(trans_affine_scaling(sqrt(dx * dx + dy * dy) / dist));
multiply(trans_affine_scaling(std::sqrt(dx * dx + dy * dy) / dist));
}
multiply(trans_affine_rotation(atan2(dy, dx)));
multiply(trans_affine_rotation(std::atan2(dy, dx)));
multiply(trans_affine_translation(x1, y1));
}
};
//============================================trans_affine_reflection_unit
// Reflection matrix. Reflect coordinates across the line through
// the origin containing the unit vector (ux, uy).
// Contributed by John Horigan
class trans_affine_reflection_unit : public trans_affine
{
public:
trans_affine_reflection_unit(double ux, double uy) :
trans_affine(2.0 * ux * ux - 1.0,
2.0 * ux * uy,
2.0 * ux * uy,
2.0 * uy * uy - 1.0,
0.0, 0.0)
{}
};
//=================================================trans_affine_reflection
// Reflection matrix. Reflect coordinates across the line through
// the origin at the angle a or containing the non-unit vector (x, y).
// Contributed by John Horigan
class trans_affine_reflection : public trans_affine_reflection_unit
{
public:
trans_affine_reflection(double a) :
trans_affine_reflection_unit(std::cos(a), std::sin(a))
{}
trans_affine_reflection(double x, double y) :
trans_affine_reflection_unit(x / std::sqrt(x * x + y * y), y / std::sqrt(x * x + y * y))
{}
};
}
+646 -106
View File
@@ -19,125 +19,213 @@
#ifndef AGG_TRANS_PERSPECTIVE_INCLUDED
#define AGG_TRANS_PERSPECTIVE_INCLUDED
#include "agg_basics.h"
#include "agg_simul_eq.h"
#include <cmath>
#include "agg_trans_affine.h"
namespace agg
{
//=======================================================trans_perspective
class trans_perspective
struct trans_perspective
{
public:
//--------------------------------------------------------------------
trans_perspective() : m_valid(false) {}
double sx, shy, w0, shx, sy, w1, tx, ty, w2;
//------------------------------------------------------- Construction
// Identity matrix
trans_perspective() :
sx (1), shy(0), w0(0),
shx(0), sy (1), w1(0),
tx (0), ty (0), w2(1) {}
//--------------------------------------------------------------------
// Arbitrary quadrangle transformations
trans_perspective(const double* src, const double* dst)
{
quad_to_quad(src, dst);
}
// Custom matrix
trans_perspective(double v0, double v1, double v2,
double v3, double v4, double v5,
double v6, double v7, double v8) :
sx (v0), shy(v1), w0(v2),
shx(v3), sy (v4), w1(v5),
tx (v6), ty (v7), w2(v8) {}
// Custom matrix from m[9]
explicit trans_perspective(const double* m) :
sx (m[0]), shy(m[1]), w0(m[2]),
shx(m[3]), sy (m[4]), w1(m[5]),
tx (m[6]), ty (m[7]), w2(m[8]) {}
//--------------------------------------------------------------------
// Direct transformations
// From affine
explicit trans_perspective(const trans_affine& a) :
sx (a.sx ), shy(a.shy), w0(0),
shx(a.shx), sy (a.sy ), w1(0),
tx (a.tx ), ty (a.ty ), w2(1) {}
// Rectangle to quadrilateral
trans_perspective(double x1, double y1, double x2, double y2,
const double* quad)
{
rect_to_quad(x1, y1, x2, y2, quad);
}
const double* quad);
//--------------------------------------------------------------------
// Reverse transformations
// Quadrilateral to rectangle
trans_perspective(const double* quad,
double x1, double y1, double x2, double y2)
double x1, double y1, double x2, double y2);
// Arbitrary quadrilateral transformations
trans_perspective(const double* src, const double* dst);
//-------------------------------------- Quadrilateral transformations
// The arguments are double[8] that are mapped to quadrilaterals:
// x1,y1, x2,y2, x3,y3, x4,y4
bool quad_to_quad(const double* qs, const double* qd);
bool rect_to_quad(double x1, double y1,
double x2, double y2,
const double* q);
bool quad_to_rect(const double* q,
double x1, double y1,
double x2, double y2);
// Map square (0,0,1,1) to the quadrilateral and vice versa
bool square_to_quad(const double* q);
bool quad_to_square(const double* q);
//--------------------------------------------------------- Operations
// Reset - load an identity matrix
const trans_perspective& reset();
// Invert matrix. Returns false in degenerate case
bool invert();
// Direct transformations operations
const trans_perspective& translate(double x, double y);
const trans_perspective& rotate(double a);
const trans_perspective& scale(double s);
const trans_perspective& scale(double x, double y);
// Multiply the matrix by another one
const trans_perspective& multiply(const trans_perspective& m);
// Multiply "m" by "this" and assign the result to "this"
const trans_perspective& premultiply(const trans_perspective& m);
// Multiply matrix to inverse of another one
const trans_perspective& multiply_inv(const trans_perspective& m);
// Multiply inverse of "m" by "this" and assign the result to "this"
const trans_perspective& premultiply_inv(const trans_perspective& m);
// Multiply the matrix by another one
const trans_perspective& multiply(const trans_affine& m);
// Multiply "m" by "this" and assign the result to "this"
const trans_perspective& premultiply(const trans_affine& m);
// Multiply the matrix by inverse of another one
const trans_perspective& multiply_inv(const trans_affine& m);
// Multiply inverse of "m" by "this" and assign the result to "this"
const trans_perspective& premultiply_inv(const trans_affine& m);
//--------------------------------------------------------- Load/Store
void store_to(double* m) const;
const trans_perspective& load_from(const double* m);
//---------------------------------------------------------- Operators
// Multiply the matrix by another one
const trans_perspective& operator *= (const trans_perspective& m)
{
quad_to_rect(quad, x1, y1, x2, y2);
return multiply(m);
}
const trans_perspective& operator *= (const trans_affine& m)
{
return multiply(m);
}
//--------------------------------------------------------------------
// Set the transformations using two arbitrary quadrangles.
void quad_to_quad(const double* src, const double* dst)
// Multiply the matrix by inverse of another one
const trans_perspective& operator /= (const trans_perspective& m)
{
double left[8][8];
double right[8][1];
unsigned i;
for (i = 0; i < 4; i++)
{
unsigned ix = i * 2;
unsigned iy = ix + 1;
left[ix][0] = 1.0;
left[ix][1] = src[ix];
left[ix][2] = src[iy];
left[ix][3] = 0.0;
left[ix][4] = 0.0;
left[ix][5] = 0.0;
left[ix][6] = -src[ix] * dst[ix];
left[ix][7] = -src[iy] * dst[ix];
right[ix][0] = dst[ix];
left[iy][0] = 0.0;
left[iy][1] = 0.0;
left[iy][2] = 0.0;
left[iy][3] = 1.0;
left[iy][4] = src[ix];
left[iy][5] = src[iy];
left[iy][6] = -src[ix] * dst[iy];
left[iy][7] = -src[iy] * dst[iy];
right[iy][0] = dst[iy];
}
m_valid = simul_eq<8, 1>::solve(left, right, m_mtx);
return multiply_inv(m);
}
const trans_perspective& operator /= (const trans_affine& m)
{
return multiply_inv(m);
}
//--------------------------------------------------------------------
// Set the direct transformations, i.e., rectangle -> quadrangle
void rect_to_quad(double x1, double y1, double x2, double y2,
const double* quad)
// Multiply the matrix by another one and return
// the result in a separete matrix.
trans_perspective operator * (const trans_perspective& m) const
{
double src[8];
src[0] = src[6] = x1;
src[2] = src[4] = x2;
src[1] = src[3] = y1;
src[5] = src[7] = y2;
quad_to_quad(src, quad);
return trans_perspective(*this).multiply(m);
}
trans_perspective operator * (const trans_affine& m) const
{
return trans_perspective(*this).multiply(m);
}
//--------------------------------------------------------------------
// Set the reverse transformations, i.e., quadrangle -> rectangle
void quad_to_rect(const double* quad,
double x1, double y1, double x2, double y2)
// Multiply the matrix by inverse of another one
// and return the result in a separete matrix.
trans_perspective operator / (const trans_perspective& m) const
{
double dst[8];
dst[0] = dst[6] = x1;
dst[2] = dst[4] = x2;
dst[1] = dst[3] = y1;
dst[5] = dst[7] = y2;
quad_to_quad(quad, dst);
return trans_perspective(*this).multiply_inv(m);
}
trans_perspective operator / (const trans_affine& m) const
{
return trans_perspective(*this).multiply_inv(m);
}
//--------------------------------------------------------------------
// Check if the equations were solved successfully
bool is_valid() const { return m_valid; }
//--------------------------------------------------------------------
// Transform a point (x, y)
void transform(double* x, double* y) const
// Calculate and return the inverse matrix
trans_perspective operator ~ () const
{
double tx = *x;
double ty = *y;
double d = 1.0 / (m_mtx[6][0] * tx + m_mtx[7][0] * ty + 1.0);
*x = (m_mtx[0][0] + m_mtx[1][0] * tx + m_mtx[2][0] * ty) * d;
*y = (m_mtx[3][0] + m_mtx[4][0] * tx + m_mtx[5][0] * ty) * d;
trans_perspective ret = *this;
ret.invert();
return ret;
}
// Equal operator with default epsilon
bool operator == (const trans_perspective& m) const
{
return is_equal(m, affine_epsilon);
}
// Not Equal operator with default epsilon
bool operator != (const trans_perspective& m) const
{
return !is_equal(m, affine_epsilon);
}
//---------------------------------------------------- Transformations
// Direct transformation of x and y
void transform(double* x, double* y) const;
// Direct transformation of x and y, affine part only
void transform_affine(double* x, double* y) const;
// Direct transformation of x and y, 2x2 matrix only, no translation
void transform_2x2(double* x, double* y) const;
// Inverse transformation of x and y. It works slow because
// it explicitly inverts the matrix on every call. For massive
// operations it's better to invert() the matrix and then use
// direct transformations.
void inverse_transform(double* x, double* y) const;
//---------------------------------------------------------- Auxiliary
const trans_perspective& from_affine(const trans_affine& a);
double determinant() const;
double determinant_reciprocal() const;
bool is_valid(double epsilon = affine_epsilon) const;
bool is_identity(double epsilon = affine_epsilon) const;
bool is_equal(const trans_perspective& m,
double epsilon = affine_epsilon) const;
// Determine the major affine parameters. Use with caution
// considering possible degenerate cases.
double scale() const;
double rotation() const;
void translation(double* dx, double* dy) const;
void scaling(double* x, double* y) const;
void scaling_abs(double* x, double* y) const;
//--------------------------------------------------------------------
class iterator_x
{
@@ -153,17 +241,16 @@ namespace agg
double y;
iterator_x() {}
iterator_x(double tx, double ty, double step, const double m[8][1]) :
den(m[6][0] * tx + m[7][0] * ty + 1.0),
den_step(m[6][0] * step),
nom_x(m[0][0] + m[1][0] * tx + m[2][0] * ty),
nom_x_step(m[1][0] * step),
nom_y(m[3][0] + m[4][0] * tx + m[5][0] * ty),
nom_y_step(m[4][0] * step),
iterator_x(double px, double py, double step, const trans_perspective& m) :
den(px * m.w0 + py * m.w1 + m.w2),
den_step(m.w0 * step),
nom_x(px * m.sx + py * m.shx + m.tx),
nom_x_step(step * m.sx),
nom_y(px * m.shy + py * m.sy + m.ty),
nom_y_step(step * m.shy),
x(nom_x / den),
y(nom_y / den)
{
}
{}
void operator ++ ()
{
@@ -179,14 +266,467 @@ namespace agg
//--------------------------------------------------------------------
iterator_x begin(double x, double y, double step) const
{
return iterator_x(x, y, step, m_mtx);
return iterator_x(x, y, step, *this);
}
private:
double m_mtx[8][1];
bool m_valid;
};
//------------------------------------------------------------------------
inline bool trans_perspective::square_to_quad(const double* q)
{
double dx = q[0] - q[2] + q[4] - q[6];
double dy = q[1] - q[3] + q[5] - q[7];
if(dx == 0.0 && dy == 0.0)
{
// Affine case (parallelogram)
//---------------
sx = q[2] - q[0];
shy = q[3] - q[1];
w0 = 0.0;
shx = q[4] - q[2];
sy = q[5] - q[3];
w1 = 0.0;
tx = q[0];
ty = q[1];
w2 = 1.0;
}
else
{
double dx1 = q[2] - q[4];
double dy1 = q[3] - q[5];
double dx2 = q[6] - q[4];
double dy2 = q[7] - q[5];
double den = dx1 * dy2 - dx2 * dy1;
if(den == 0.0)
{
// Singular case
//---------------
sx = shy = w0 = shx = sy = w1 = tx = ty = w2 = 0.0;
return false;
}
// General case
//---------------
double u = (dx * dy2 - dy * dx2) / den;
double v = (dy * dx1 - dx * dy1) / den;
sx = q[2] - q[0] + u * q[2];
shy = q[3] - q[1] + u * q[3];
w0 = u;
shx = q[6] - q[0] + v * q[6];
sy = q[7] - q[1] + v * q[7];
w1 = v;
tx = q[0];
ty = q[1];
w2 = 1.0;
}
return true;
}
//------------------------------------------------------------------------
inline bool trans_perspective::invert()
{
double d0 = sy * w2 - w1 * ty;
double d1 = w0 * ty - shy * w2;
double d2 = shy * w1 - w0 * sy;
double d = sx * d0 + shx * d1 + tx * d2;
if(d == 0.0)
{
sx = shy = w0 = shx = sy = w1 = tx = ty = w2 = 0.0;
return false;
}
d = 1.0 / d;
trans_perspective a = *this;
sx = d * d0;
shy = d * d1;
w0 = d * d2;
shx = d * (a.w1 *a.tx - a.shx*a.w2);
sy = d * (a.sx *a.w2 - a.w0 *a.tx);
w1 = d * (a.w0 *a.shx - a.sx *a.w1);
tx = d * (a.shx*a.ty - a.sy *a.tx);
ty = d * (a.shy*a.tx - a.sx *a.ty);
w2 = d * (a.sx *a.sy - a.shy*a.shx);
return true;
}
//------------------------------------------------------------------------
inline bool trans_perspective::quad_to_square(const double* q)
{
if(!square_to_quad(q)) return false;
invert();
return true;
}
//------------------------------------------------------------------------
inline bool trans_perspective::quad_to_quad(const double* qs,
const double* qd)
{
trans_perspective p;
if(! quad_to_square(qs)) return false;
if(!p.square_to_quad(qd)) return false;
multiply(p);
return true;
}
//------------------------------------------------------------------------
inline bool trans_perspective::rect_to_quad(double x1, double y1,
double x2, double y2,
const double* q)
{
double r[8];
r[0] = r[6] = x1;
r[2] = r[4] = x2;
r[1] = r[3] = y1;
r[5] = r[7] = y2;
return quad_to_quad(r, q);
}
//------------------------------------------------------------------------
inline bool trans_perspective::quad_to_rect(const double* q,
double x1, double y1,
double x2, double y2)
{
double r[8];
r[0] = r[6] = x1;
r[2] = r[4] = x2;
r[1] = r[3] = y1;
r[5] = r[7] = y2;
return quad_to_quad(q, r);
}
//------------------------------------------------------------------------
inline trans_perspective::trans_perspective(double x1, double y1,
double x2, double y2,
const double* quad)
{
rect_to_quad(x1, y1, x2, y2, quad);
}
//------------------------------------------------------------------------
inline trans_perspective::trans_perspective(const double* quad,
double x1, double y1,
double x2, double y2)
{
quad_to_rect(quad, x1, y1, x2, y2);
}
//------------------------------------------------------------------------
inline trans_perspective::trans_perspective(const double* src,
const double* dst)
{
quad_to_quad(src, dst);
}
//------------------------------------------------------------------------
inline const trans_perspective& trans_perspective::reset()
{
sx = 1; shy = 0; w0 = 0;
shx = 0; sy = 1; w1 = 0;
tx = 0; ty = 0; w2 = 1;
return *this;
}
//------------------------------------------------------------------------
inline const trans_perspective&
trans_perspective::multiply(const trans_perspective& a)
{
trans_perspective b = *this;
sx = a.sx *b.sx + a.shx*b.shy + a.tx*b.w0;
shx = a.sx *b.shx + a.shx*b.sy + a.tx*b.w1;
tx = a.sx *b.tx + a.shx*b.ty + a.tx*b.w2;
shy = a.shy*b.sx + a.sy *b.shy + a.ty*b.w0;
sy = a.shy*b.shx + a.sy *b.sy + a.ty*b.w1;
ty = a.shy*b.tx + a.sy *b.ty + a.ty*b.w2;
w0 = a.w0 *b.sx + a.w1 *b.shy + a.w2*b.w0;
w1 = a.w0 *b.shx + a.w1 *b.sy + a.w2*b.w1;
w2 = a.w0 *b.tx + a.w1 *b.ty + a.w2*b.w2;
return *this;
}
//------------------------------------------------------------------------
inline const trans_perspective&
trans_perspective::multiply(const trans_affine& a)
{
trans_perspective b = *this;
sx = a.sx *b.sx + a.shx*b.shy + a.tx*b.w0;
shx = a.sx *b.shx + a.shx*b.sy + a.tx*b.w1;
tx = a.sx *b.tx + a.shx*b.ty + a.tx*b.w2;
shy = a.shy*b.sx + a.sy *b.shy + a.ty*b.w0;
sy = a.shy*b.shx + a.sy *b.sy + a.ty*b.w1;
ty = a.shy*b.tx + a.sy *b.ty + a.ty*b.w2;
return *this;
}
//------------------------------------------------------------------------
inline const trans_perspective&
trans_perspective::premultiply(const trans_perspective& b)
{
trans_perspective a = *this;
sx = a.sx *b.sx + a.shx*b.shy + a.tx*b.w0;
shx = a.sx *b.shx + a.shx*b.sy + a.tx*b.w1;
tx = a.sx *b.tx + a.shx*b.ty + a.tx*b.w2;
shy = a.shy*b.sx + a.sy *b.shy + a.ty*b.w0;
sy = a.shy*b.shx + a.sy *b.sy + a.ty*b.w1;
ty = a.shy*b.tx + a.sy *b.ty + a.ty*b.w2;
w0 = a.w0 *b.sx + a.w1 *b.shy + a.w2*b.w0;
w1 = a.w0 *b.shx + a.w1 *b.sy + a.w2*b.w1;
w2 = a.w0 *b.tx + a.w1 *b.ty + a.w2*b.w2;
return *this;
}
//------------------------------------------------------------------------
inline const trans_perspective&
trans_perspective::premultiply(const trans_affine& b)
{
trans_perspective a = *this;
sx = a.sx *b.sx + a.shx*b.shy;
shx = a.sx *b.shx + a.shx*b.sy;
tx = a.sx *b.tx + a.shx*b.ty + a.tx;
shy = a.shy*b.sx + a.sy *b.shy;
sy = a.shy*b.shx + a.sy *b.sy;
ty = a.shy*b.tx + a.sy *b.ty + a.ty;
w0 = a.w0 *b.sx + a.w1 *b.shy;
w1 = a.w0 *b.shx + a.w1 *b.sy;
w2 = a.w0 *b.tx + a.w1 *b.ty + a.w2;
return *this;
}
//------------------------------------------------------------------------
const trans_perspective&
trans_perspective::multiply_inv(const trans_perspective& m)
{
trans_perspective t = m;
t.invert();
return multiply(t);
}
//------------------------------------------------------------------------
const trans_perspective&
trans_perspective::multiply_inv(const trans_affine& m)
{
trans_affine t = m;
t.invert();
return multiply(t);
}
//------------------------------------------------------------------------
const trans_perspective&
trans_perspective::premultiply_inv(const trans_perspective& m)
{
trans_perspective t = m;
t.invert();
return *this = t.multiply(*this);
}
//------------------------------------------------------------------------
const trans_perspective&
trans_perspective::premultiply_inv(const trans_affine& m)
{
trans_perspective t(m);
t.invert();
return *this = t.multiply(*this);
}
//------------------------------------------------------------------------
inline const trans_perspective&
trans_perspective::translate(double x, double y)
{
tx += x;
ty += y;
return *this;
}
//------------------------------------------------------------------------
inline const trans_perspective& trans_perspective::rotate(double a)
{
multiply(trans_affine_rotation(a));
return *this;
}
//------------------------------------------------------------------------
inline const trans_perspective& trans_perspective::scale(double s)
{
multiply(trans_affine_scaling(s));
return *this;
}
//------------------------------------------------------------------------
inline const trans_perspective& trans_perspective::scale(double x, double y)
{
multiply(trans_affine_scaling(x, y));
return *this;
}
//------------------------------------------------------------------------
inline void trans_perspective::transform(double* px, double* py) const
{
double x = *px;
double y = *py;
double m = 1.0 / (x*w0 + y*w1 + w2);
*px = m * (x*sx + y*shx + tx);
*py = m * (x*shy + y*sy + ty);
}
//------------------------------------------------------------------------
inline void trans_perspective::transform_affine(double* x, double* y) const
{
double tmp = *x;
*x = tmp * sx + *y * shx + tx;
*y = tmp * shy + *y * sy + ty;
}
//------------------------------------------------------------------------
inline void trans_perspective::transform_2x2(double* x, double* y) const
{
double tmp = *x;
*x = tmp * sx + *y * shx;
*y = tmp * shy + *y * sy;
}
//------------------------------------------------------------------------
inline void trans_perspective::inverse_transform(double* x, double* y) const
{
trans_perspective t(*this);
if(t.invert()) t.transform(x, y);
}
//------------------------------------------------------------------------
inline void trans_perspective::store_to(double* m) const
{
*m++ = sx; *m++ = shy; *m++ = w0;
*m++ = shx; *m++ = sy; *m++ = w1;
*m++ = tx; *m++ = ty; *m++ = w2;
}
//------------------------------------------------------------------------
inline const trans_perspective& trans_perspective::load_from(const double* m)
{
sx = *m++; shy = *m++; w0 = *m++;
shx = *m++; sy = *m++; w1 = *m++;
tx = *m++; ty = *m++; w2 = *m++;
return *this;
}
//------------------------------------------------------------------------
inline const trans_perspective&
trans_perspective::from_affine(const trans_affine& a)
{
sx = a.sx; shy = a.shy; w0 = 0;
shx = a.shx; sy = a.sy; w1 = 0;
tx = a.tx; ty = a.ty; w2 = 1;
return *this;
}
//------------------------------------------------------------------------
inline double trans_perspective::determinant() const
{
return sx * (sy * w2 - ty * w1) +
shx * (ty * w0 - shy * w2) +
tx * (shy * w1 - sy * w0);
}
//------------------------------------------------------------------------
inline double trans_perspective::determinant_reciprocal() const
{
return 1.0 / determinant();
}
//------------------------------------------------------------------------
inline bool trans_perspective::is_valid(double epsilon) const
{
return std::fabs(sx) > epsilon && std::fabs(sy) > epsilon && std::fabs(w2) > epsilon;
}
//------------------------------------------------------------------------
inline bool trans_perspective::is_identity(double epsilon) const
{
return is_equal_eps(sx, 1.0, epsilon) &&
is_equal_eps(shy, 0.0, epsilon) &&
is_equal_eps(w0, 0.0, epsilon) &&
is_equal_eps(shx, 0.0, epsilon) &&
is_equal_eps(sy, 1.0, epsilon) &&
is_equal_eps(w1, 0.0, epsilon) &&
is_equal_eps(tx, 0.0, epsilon) &&
is_equal_eps(ty, 0.0, epsilon) &&
is_equal_eps(w2, 1.0, epsilon);
}
//------------------------------------------------------------------------
inline bool trans_perspective::is_equal(const trans_perspective& m,
double epsilon) const
{
return is_equal_eps(sx, m.sx, epsilon) &&
is_equal_eps(shy, m.shy, epsilon) &&
is_equal_eps(w0, m.w0, epsilon) &&
is_equal_eps(shx, m.shx, epsilon) &&
is_equal_eps(sy, m.sy, epsilon) &&
is_equal_eps(w1, m.w1, epsilon) &&
is_equal_eps(tx, m.tx, epsilon) &&
is_equal_eps(ty, m.ty, epsilon) &&
is_equal_eps(w2, m.w2, epsilon);
}
//------------------------------------------------------------------------
inline double trans_perspective::scale() const
{
double x = 0.707106781 * sx + 0.707106781 * shx;
double y = 0.707106781 * shy + 0.707106781 * sy;
return std::sqrt(x*x + y*y);
}
//------------------------------------------------------------------------
inline double trans_perspective::rotation() const
{
double x1 = 0.0;
double y1 = 0.0;
double x2 = 1.0;
double y2 = 0.0;
transform(&x1, &y1);
transform(&x2, &y2);
return std::atan2(y2-y1, x2-x1);
}
//------------------------------------------------------------------------
void trans_perspective::translation(double* dx, double* dy) const
{
*dx = tx;
*dy = ty;
}
//------------------------------------------------------------------------
void trans_perspective::scaling(double* x, double* y) const
{
double x1 = 0.0;
double y1 = 0.0;
double x2 = 1.0;
double y2 = 1.0;
trans_perspective t(*this);
t *= trans_affine_rotation(-rotation());
t.transform(&x1, &y1);
t.transform(&x2, &y2);
*x = x2 - x1;
*y = y2 - y1;
}
//------------------------------------------------------------------------
void trans_perspective::scaling_abs(double* x, double* y) const
{
*x = std::sqrt(sx * sx + shx * shx);
*y = std::sqrt(shy * shy + sy * sy);
}
}
#endif