mirror of https://github.com/KLayout/klayout.git
908 lines
25 KiB
C++
908 lines
25 KiB
C++
|
|
/*
|
|
|
|
KLayout Layout Viewer
|
|
Copyright (C) 2006-2017 Matthias Koefferlein
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*/
|
|
|
|
|
|
#include "dbMatrix.h"
|
|
#include "dbTrans.h"
|
|
#include "tlString.h"
|
|
|
|
#include <sstream>
|
|
#include <limits>
|
|
#include <cmath>
|
|
|
|
namespace db
|
|
{
|
|
|
|
inline double mnorm (double x)
|
|
{
|
|
return fabs (x) < 1e-14 ? 0.0 : x;
|
|
}
|
|
|
|
std::string
|
|
Matrix2d::to_string () const
|
|
{
|
|
return tl::sprintf ("(%.12g,%.12g) (%.12g,%.12g)", mnorm (m_m11), mnorm (m_m12), mnorm (m_m21), mnorm (m_m22));
|
|
}
|
|
|
|
std::pair<double, double>
|
|
Matrix2d::mag () const
|
|
{
|
|
double s1 = sqrt (m_m11 * m_m11 + m_m21 * m_m21);
|
|
double s2 = sqrt (m_m12 * m_m12 + m_m22 * m_m22);
|
|
double n = sqrt (fabs (det ()) / (s1 * s2));
|
|
return std::make_pair (n * s1, n * s2);
|
|
}
|
|
|
|
bool
|
|
Matrix2d::has_rotation () const
|
|
{
|
|
return fabs (m_m11 - 1.0) > 1e-10 || fabs (m_m12) > 1e-10 || fabs (m_m21) > 1e-10 || fabs (m_m22 - 1.0) > 1e-10;
|
|
}
|
|
|
|
double
|
|
Matrix2d::angle () const
|
|
{
|
|
std::pair <double, double> m = mag ();
|
|
double u1 = m.first;
|
|
double u2 = is_mirror () ? -m.second : m.second;
|
|
double n11 = m_m11 / u1;
|
|
double n12 = m_m12 / u2;
|
|
double n21 = m_m21 / u1;
|
|
double n22 = m_m22 / u2;
|
|
|
|
// due to rounding error, the sqrt argument can be a negative (but very small)
|
|
double sin_a = 0.5 * sqrt (std::max (0.0, (n21 - n12) * (n21 - n12) - (n11 - n22) * (n11 - n22)));
|
|
double cos_a = 0.5 * sqrt (std::max (0.0, (n11 + n22) * (n11 + n22) - (n21 + n12) * (n21 + n12)));
|
|
if (n11 + n22 < 0.0) {
|
|
cos_a = -cos_a;
|
|
}
|
|
if (n21 - n12 < 0.0) {
|
|
sin_a = -sin_a;
|
|
}
|
|
|
|
return 180.0 * atan2 (sin_a, cos_a) / M_PI;
|
|
}
|
|
|
|
Matrix2d
|
|
Matrix2d::rotation (double a)
|
|
{
|
|
a *= M_PI / 180.0;
|
|
return Matrix2d (cos (a), -sin (a), sin (a), cos (a));
|
|
}
|
|
|
|
bool
|
|
Matrix2d::has_shear () const
|
|
{
|
|
std::pair <double, double> m = mag ();
|
|
double u1 = m.first;
|
|
double u2 = is_mirror () ? -m.second : m.second;
|
|
double n11 = m_m11 / u1;
|
|
double n12 = m_m12 / u2;
|
|
double n21 = m_m21 / u1;
|
|
double n22 = m_m22 / u2;
|
|
|
|
double fsin_a = 0.5 * sqrt ((n21 + n12) * (n21 + n12) + (n11 - n22) * (n11 - n22));
|
|
return fabs (fsin_a) > 1e-10;
|
|
}
|
|
|
|
double
|
|
Matrix2d::shear_angle () const
|
|
{
|
|
std::pair <double, double> m = mag ();
|
|
double u1 = m.first;
|
|
double u2 = is_mirror () ? -m.second : m.second;
|
|
double n11 = m_m11 / u1;
|
|
double n12 = m_m12 / u2;
|
|
double n21 = m_m21 / u1;
|
|
double n22 = m_m22 / u2;
|
|
|
|
double fsin_a = 0.5 * sqrt ((n21 + n12) * (n21 + n12) + (n11 - n22) * (n11 - n22));
|
|
double fcos_a = 0.5 * sqrt ((n21 - n12) * (n21 - n12) + (n11 + n22) * (n11 + n22));
|
|
if ((n21 - n12) * (n22 - n11) < -1e-10 || (n21 + n12) * (n11 + n22) < -1e-10) {
|
|
fsin_a = -fsin_a;
|
|
}
|
|
|
|
return 180.0 * atan2 (fsin_a, fcos_a) / M_PI;
|
|
}
|
|
|
|
Matrix2d
|
|
Matrix2d::shear (double a)
|
|
{
|
|
a *= M_PI / 180.0;
|
|
double cos_a = cos (a);
|
|
double sin_a = sin (a);
|
|
double f = 1.0 / sqrt (cos_a * cos_a - sin_a * sin_a);
|
|
return Matrix2d (f * cos_a, f * sin_a, f * sin_a, f * cos_a);
|
|
}
|
|
|
|
bool
|
|
Matrix2d::is_ortho () const
|
|
{
|
|
return fabs (m_m11 * m_m12 + m_m21 * m_m22) < 1e-10 && fabs (m_m11 * m_m12) < 1e-10 && fabs (m_m21 * m_m22) < 1e-10;
|
|
}
|
|
|
|
bool
|
|
Matrix2d::equal (const Matrix2d &d) const
|
|
{
|
|
return fabs (m_m11 - d.m_m11) < 1e-10 && fabs (m_m12 - d.m_m12) < 1e-10 &&
|
|
fabs (m_m21 - d.m_m21) < 1e-10 && fabs (m_m22 - d.m_m22) < 1e-10;
|
|
}
|
|
|
|
bool
|
|
Matrix2d::less (const Matrix2d &d) const
|
|
{
|
|
if (fabs (m_m11 - d.m_m11) > 1e-10) {
|
|
return m_m11 < d.m_m11;
|
|
}
|
|
if (fabs (m_m12 - d.m_m12) > 1e-10) {
|
|
return m_m12 < d.m_m12;
|
|
}
|
|
if (fabs (m_m21 - d.m_m21) > 1e-10) {
|
|
return m_m21 < d.m_m21;
|
|
}
|
|
if (fabs (m_m22 - d.m_m22) > 1e-10) {
|
|
return m_m22 < d.m_m22;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
|
|
double
|
|
Matrix3d::det () const
|
|
{
|
|
double d = 0.0;
|
|
for (int i0 = 0; i0 < 3; ++i0) {
|
|
for (int j = 0; j < 2; ++j) {
|
|
int i1 = (i0 + j + 1) % 3;
|
|
int i2 = (i1 + 1) % 3;
|
|
double s = (((i0 + i1 + i2) % 2) == 0) ? -1.0 : 1.0;
|
|
d += s * m_m [0][i0] * m_m [1][i1] * m_m [2][i2];
|
|
}
|
|
}
|
|
return d;
|
|
}
|
|
|
|
db::DVector
|
|
Matrix3d::trans (const db::DPoint &p, const db::DVector &v) const
|
|
{
|
|
double t[2][2];
|
|
for (int i = 0; i < 2; ++i) {
|
|
for (int j = 0; j < 2; ++j) {
|
|
t[i][j] = (m_m[i][j] * m_m[2][1 - j] - m_m[i][1 - j] * m_m[2][j]) * (j == 0 ? p.y() : p.x()) + (m_m[2][2] * m_m[i][j] - m_m[i][2] * m_m[2][j]);
|
|
}
|
|
}
|
|
return db::DVector(v.x() * t[0][0] + v.y() * t[0][1], v.x() * t[1][0] + v.y() * t[1][1]);
|
|
}
|
|
|
|
bool
|
|
Matrix3d::can_transform (const db::DPoint &p) const
|
|
{
|
|
double r[3] = { 0, 0, 0 };
|
|
for (int i = 0; i < 3; ++i) {
|
|
r[i] = m_m[i][0] * p.x() + m_m[i][1] * p.y() + m_m[i][2];
|
|
}
|
|
|
|
return (r[2] > (std::abs (r[0]) + std::abs (r[1])) * 1e-10);
|
|
}
|
|
|
|
db::DPoint
|
|
Matrix3d::trans (const db::DPoint &p) const
|
|
{
|
|
double r[3] = { 0, 0, 0 };
|
|
for (int i = 0; i < 3; ++i) {
|
|
r[i] = m_m[i][0] * p.x() + m_m[i][1] * p.y() + m_m[i][2];
|
|
}
|
|
|
|
// safe approximation to the forbidden area where z <= 0
|
|
double z = std::max (r [2], (std::abs (r[0]) + std::abs (r[1])) * 1e-10);
|
|
return db::DPoint (r[0] / z, r[1] / z);
|
|
}
|
|
|
|
Matrix3d
|
|
Matrix3d::inverted () const
|
|
{
|
|
double m[3][3];
|
|
Matrix3d r (1.0);
|
|
|
|
for (int i = 0; i < 3; ++i) {
|
|
for (int j = 0; j < 3; ++j) {
|
|
m[i][j] = m_m[i][j];
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 2; ++i) {
|
|
for (int j = 2; j > i; --j) {
|
|
if (std::abs (m[j][i]) > std::abs (m[j - 1][i])) {
|
|
for (int k = 0; k < 3; ++k) {
|
|
std::swap (m[j][k], m[j - 1][k]);
|
|
std::swap (r.m_m[j][k], r.m_m[j - 1][k]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 3; ++i) {
|
|
for (int j = i + 1; j < 3; ++j) {
|
|
double f = m[j][i] / m[i][i];
|
|
for (int k = 0; k < 3; ++k) {
|
|
m[j][k] -= f * m[i][k];
|
|
r.m_m[j][k] -= f * r.m_m[i][k];
|
|
}
|
|
}
|
|
double f = 1.0 / m[i][i];
|
|
for (int k = 0; k < 3; ++k) {
|
|
m[i][k] *= f;
|
|
r.m_m[i][k] *= f;
|
|
}
|
|
}
|
|
|
|
for (int i = 2; i > 0; --i) {
|
|
for (int j = 0; j < i; ++j) {
|
|
double f = m[j][i];
|
|
for (int k = 0; k < 3; ++k) {
|
|
r.m_m[j][k] -= f * r.m_m[i][k];
|
|
}
|
|
}
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
db::DVector
|
|
Matrix3d::disp () const
|
|
{
|
|
return db::DVector (m_m[0][2] / m_m[2][2], m_m[1][2] / m_m[2][2]);
|
|
}
|
|
|
|
double
|
|
Matrix3d::perspective_tilt_x (double z) const
|
|
{
|
|
db::DVector d = disp ();
|
|
db::Matrix3d m = db::Matrix3d::disp (-d) * *this;
|
|
return 180 * atan (z * (m.m ()[2][0] * m.m ()[1][1] - m.m ()[2][1] * m.m ()[1][0]) / (m.m ()[0][0] * m.m ()[1][1] - m.m ()[0][1] * m.m ()[1][0])) / M_PI;
|
|
}
|
|
|
|
double
|
|
Matrix3d::perspective_tilt_y (double z) const
|
|
{
|
|
db::DVector d = disp ();
|
|
db::Matrix3d m = db::Matrix3d::disp (-d) * *this;
|
|
return 180 * atan (z * (m.m ()[2][1] * m.m ()[0][0] - m.m ()[2][0] * m.m ()[0][1]) / (m.m ()[0][0] * m.m ()[1][1] - m.m ()[0][1] * m.m ()[1][0])) / M_PI;
|
|
}
|
|
|
|
bool
|
|
Matrix3d::has_perspective () const
|
|
{
|
|
return fabs (m_m[2][0]) + fabs (m_m[2][1]) > 1e-10;
|
|
}
|
|
|
|
Matrix3d
|
|
Matrix3d::perspective (double tx, double ty, double z)
|
|
{
|
|
tx *= M_PI / 180.0;
|
|
ty *= M_PI / 180.0;
|
|
return Matrix3d (1.0, 0.0, 0.0, 1.0, 0.0, 0.0, tan (tx) / z, tan (ty) / z);
|
|
}
|
|
|
|
Matrix2d
|
|
Matrix3d::m2d () const
|
|
{
|
|
db::DVector d = disp ();
|
|
db::Matrix3d m = db::Matrix3d::disp (-d) * *this;
|
|
|
|
if (has_perspective ()) {
|
|
m = Matrix3d::perspective (-perspective_tilt_x (1.0), -perspective_tilt_y (1.0), 1.0) * m;
|
|
}
|
|
|
|
return Matrix2d (m.m_m[0][0] / m.m_m[2][2], m.m_m[0][1] / m.m_m[2][2], m.m_m[1][0] / m.m_m[2][2], m.m_m[1][1] / m.m_m[2][2]);
|
|
}
|
|
|
|
std::string
|
|
Matrix3d::to_string () const
|
|
{
|
|
return tl::sprintf ("(%.12g,%.12g,%.12g)", mnorm (m_m[0][0]), mnorm (m_m[0][1]), mnorm (m_m[0][2])) + " "
|
|
+ tl::sprintf ("(%.12g,%.12g,%.12g)", mnorm (m_m[1][0]), mnorm (m_m[1][1]), mnorm (m_m[1][2])) + " "
|
|
+ tl::sprintf ("(%.12g,%.12g,%.12g)", mnorm (m_m[2][0]), mnorm (m_m[2][1]), mnorm (m_m[2][2]));
|
|
}
|
|
|
|
bool
|
|
Matrix3d::is_ortho () const
|
|
{
|
|
return ! has_perspective () && m2d ().is_ortho ();
|
|
}
|
|
|
|
bool
|
|
Matrix3d::equal (const Matrix3d &d) const
|
|
{
|
|
for (unsigned int i = 0; i < 3; ++i) {
|
|
for (unsigned int j = 0; j < 3; ++j) {
|
|
if (fabs (m_m[i][j] - d.m_m[i][j]) > 1e-10) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
Matrix3d::less (const Matrix3d &d) const
|
|
{
|
|
for (unsigned int i = 0; i < 3; ++i) {
|
|
for (unsigned int j = 0; j < 3; ++j) {
|
|
if (fabs (m_m[i][j] - d.m_m[i][j]) > 1e-10) {
|
|
return m_m[i][j] < d.m_m[i][j];
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
|
|
/**
|
|
* @brief Fits a vector set to another using a linear transformation that is a linear combination of two: M = a*A + b*B
|
|
*
|
|
* It returns the best-fit parameters a and b.
|
|
*
|
|
* @param am The matrix A
|
|
* @param bm The matrix B
|
|
* @param p The initial vector set
|
|
* @param q The vector set to fit M*p[i] to
|
|
*/
|
|
static bool
|
|
fit_point_set_with_linear_combination (double &a, double &b, const Matrix2d &am, const Matrix2d &bm, const std::vector<db::DVector> &q, const std::vector<db::DVector> &p)
|
|
{
|
|
double maa = 0.0, mbaab = 0.0, mbb = 0.0, ca = 0.0, cb = 0.0;
|
|
|
|
for (size_t i = 0; i < p.size (); ++i) {
|
|
|
|
db::DVector ap = am * p[i];
|
|
db::DVector bp = bm * p[i];
|
|
|
|
maa += ap.sq_double_length ();
|
|
mbaab += db::sprod (bp, ap);
|
|
mbb += bp.sq_double_length ();
|
|
ca += db::sprod (q [i], ap);
|
|
cb += db::sprod (q [i], bp);
|
|
|
|
}
|
|
|
|
Matrix2d m (maa, mbaab, mbaab, mbb);
|
|
if (fabs (m.det ()) < 1e-10) {
|
|
// fit not possible
|
|
return false;
|
|
} else {
|
|
|
|
db::DVector r = Matrix2d (maa, mbaab, mbaab, mbb).inverted () * db::DVector (ca, cb);
|
|
a = r.x ();
|
|
b = r.y ();
|
|
return true;
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Computes the cost value for a given matrix (sum over the square distances)
|
|
*
|
|
* @param m The matrix
|
|
* @param p The initial vector set
|
|
* @param q The vector set to fit M*p[i] to
|
|
*/
|
|
static double
|
|
compute_distance (const Matrix2d &m, const std::vector<db::DVector> &q, const std::vector<db::DVector> &p)
|
|
{
|
|
double d = 0.0;
|
|
for (size_t i = 0; i < p.size (); ++i) {
|
|
d += (q[i] - m * p[i]).sq_double_length ();
|
|
}
|
|
return d;
|
|
}
|
|
|
|
void
|
|
adjust_matrix (Matrix2d &matrix, db::DVector &disp, const std::vector <db::DPoint> &landmarks_before, const std::vector <db::DPoint> &landmarks_after, MatrixAdjustFlags::Flags flags, int fixed_point)
|
|
{
|
|
tl_assert (landmarks_before.size () == landmarks_after.size ());
|
|
|
|
if (flags > MatrixAdjustFlags::Shear) {
|
|
flags = MatrixAdjustFlags::Shear;
|
|
}
|
|
|
|
if (landmarks_before.size () == 0) {
|
|
flags = MatrixAdjustFlags::None;
|
|
} else if (landmarks_before.size () == 1) {
|
|
flags = std::min (MatrixAdjustFlags::Displacement, flags);
|
|
} else if (landmarks_before.size () == 2) {
|
|
flags = std::min (MatrixAdjustFlags::Magnification, flags);
|
|
} else if (landmarks_before.size () == 3) {
|
|
flags = std::min (MatrixAdjustFlags::Shear, flags);
|
|
}
|
|
|
|
// Don't do anything in "none" mode.
|
|
if (flags == MatrixAdjustFlags::None) {
|
|
return;
|
|
}
|
|
|
|
// Determine initial center of weight or fixed displacement (if there is a fixed point) before and after.
|
|
// Extract the displacement so we have a 0,0-centered vector set.
|
|
// Use the initial transformation as a basis for the adjustment.
|
|
|
|
db::DPoint dp;
|
|
|
|
if (fixed_point >= 0 && fixed_point < int (landmarks_before.size ())) {
|
|
dp = matrix * landmarks_before [fixed_point] + disp;
|
|
} else {
|
|
for (std::vector<db::DPoint>::const_iterator p = landmarks_before.begin (); p != landmarks_before.end (); ++p) {
|
|
dp += matrix * db::DVector (*p) + disp;
|
|
}
|
|
dp *= 1.0 / double (landmarks_before.size ());
|
|
}
|
|
|
|
std::vector<db::DVector> p;
|
|
p.reserve (landmarks_before.size ());
|
|
for (std::vector<db::DPoint>::const_iterator pp = landmarks_before.begin (); pp != landmarks_before.end (); ++pp) {
|
|
p.push_back ((matrix * *pp + disp) - dp);
|
|
}
|
|
|
|
db::DPoint dq;
|
|
if (fixed_point >= 0 && fixed_point < int (landmarks_after.size ())) {
|
|
dq = landmarks_after [fixed_point];
|
|
} else {
|
|
for (std::vector<db::DPoint>::const_iterator q = landmarks_after.begin (); q != landmarks_after.end (); ++q) {
|
|
dq += *q - db::DPoint ();
|
|
}
|
|
dq *= 1.0 / double (landmarks_after.size ());
|
|
}
|
|
|
|
std::vector<db::DVector> q;
|
|
q.reserve (landmarks_after.size ());
|
|
for (std::vector<db::DPoint>::const_iterator qq = landmarks_after.begin (); qq != landmarks_after.end (); ++qq) {
|
|
q.push_back (*qq - dq);
|
|
}
|
|
|
|
// In the special case of RotationMirror and exactly 3 points with a fixed point adjust the
|
|
// other so they form normal vectors to the fixed point. In that case, the intention is very likely to
|
|
// define two axes with their length being not important.
|
|
if (flags == MatrixAdjustFlags::RotationMirror && landmarks_after.size () == 3 && fixed_point >= 0 && fixed_point < 3) {
|
|
for (int i = 0; i < 3; ++i) {
|
|
if (i != fixed_point) {
|
|
double n;
|
|
n = p [i].double_length ();
|
|
if (fabs (n) > 1e-6) {
|
|
p [i] *= 1.0 / n;
|
|
}
|
|
n = q [i].double_length ();
|
|
if (fabs (n) > 1e-6) {
|
|
q [i] *= 1.0 / n;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Extract a Matrix2d rotation matrix according to the extraction mode
|
|
|
|
Matrix2d m (1.0, 0.0, 0.0, 1.0);
|
|
|
|
if (flags == MatrixAdjustFlags::Shear) {
|
|
|
|
// Do a full fit
|
|
|
|
Matrix2d pm;
|
|
Matrix2d qm;
|
|
|
|
for (size_t i = 0; i < p.size (); ++i) {
|
|
double px = p [i].x ();
|
|
double py = p [i].y ();
|
|
double qx = q [i].x ();
|
|
double qy = q [i].y ();
|
|
pm += Matrix2d (px * px, px * py, py * px, py * py);
|
|
qm += Matrix2d (qx * px, qx * py, qy * px, qy * py);
|
|
}
|
|
|
|
// Fallback is Magnification mode if the fit cannot be performed
|
|
if (fabs (pm.det ()) < 1e-10) {
|
|
flags = MatrixAdjustFlags::Magnification;
|
|
} else {
|
|
m = qm * pm.inverted ();
|
|
}
|
|
|
|
}
|
|
|
|
if (flags == MatrixAdjustFlags::Displacement) {
|
|
|
|
// we are done now.
|
|
|
|
} else if (flags == MatrixAdjustFlags::Rotation || flags == MatrixAdjustFlags::Magnification) {
|
|
|
|
// Fit a general rotation matrix to the vector sets and drop the magnification part
|
|
Matrix2d am (1.0, 0.0, 0.0, 1.0), bm (0.0, -1.0, 1.0, 0.0);
|
|
double a = 0.0, b = 0.0;
|
|
if (fit_point_set_with_linear_combination (a, b, am, bm, q, p) && fabs (a) + fabs (b) > 1e-6) {
|
|
double n = flags == MatrixAdjustFlags::Magnification ? 1.0 : 1.0 / sqrt (a * a + b * b);
|
|
m = am * (a * n) + bm * (b * n);
|
|
}
|
|
|
|
} else if (flags == MatrixAdjustFlags::RotationMirror || flags == MatrixAdjustFlags::MagnificationMirror) {
|
|
|
|
// Same as before but perform two tries (with and without mirror) and don't drop the magnification part
|
|
// if magnification adjustment is requested.
|
|
|
|
Matrix2d am1 (1.0, 0.0, 0.0, 1.0), bm1 (0.0, -1.0, 1.0, 0.0);
|
|
double a1 = 0.0, b1 = 0.0;
|
|
double d1 = std::numeric_limits<double>::max ();
|
|
if (fit_point_set_with_linear_combination (a1, b1, am1, bm1, q, p) && fabs (a1) + fabs (b1) > 1e-6) {
|
|
d1 = compute_distance (am1 * a1 + bm1 * b1, q, p);
|
|
}
|
|
|
|
Matrix2d am2 (1.0, 0.0, 0.0, -1.0), bm2 (0.0, 1.0, 1.0, 0.0);
|
|
double a2 = 0.0, b2 = 0.0;
|
|
double d2 = std::numeric_limits<double>::max ();
|
|
if (fit_point_set_with_linear_combination (a2, b2, am2, bm2, q, p) && fabs (a2) + fabs (b2) > 1e-6) {
|
|
d2 = compute_distance (am2 * a2 + bm2 * b2, q, p);
|
|
}
|
|
|
|
if (d1 < std::numeric_limits<double>::max () || d2 < std::numeric_limits<double>::max ()) {
|
|
if (d1 < d2 + 1e-10) {
|
|
double n = flags == MatrixAdjustFlags::MagnificationMirror ? 1.0 : 1.0 / sqrt (a1 * a1 + b1 * b1);
|
|
m = am1 * (a1 * n) + bm1 * (b1 * n);
|
|
} else {
|
|
double n = flags == MatrixAdjustFlags::MagnificationMirror ? 1.0 : 1.0 / sqrt (a2 * a2 + b2 * b2);
|
|
m = am2 * (a2 * n) + bm2 * (b2 * n);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// compute the final transformation
|
|
disp = (dq - db::DPoint ()) + m * (disp - (dp - db::DPoint ()));
|
|
matrix = m * matrix;
|
|
}
|
|
|
|
void
|
|
adjust_matrix (Matrix3d &matrix, const std::vector <db::DPoint> &landmarks_before, const std::vector <db::DPoint> &landmarks_after, MatrixAdjustFlags::Flags flags, int fixed_point)
|
|
{
|
|
tl_assert (landmarks_before.size () == landmarks_after.size ());
|
|
|
|
if (flags > MatrixAdjustFlags::Perspective) {
|
|
flags = MatrixAdjustFlags::Perspective;
|
|
}
|
|
|
|
if (landmarks_before.size () == 0) {
|
|
flags = MatrixAdjustFlags::None;
|
|
} else if (landmarks_before.size () == 1) {
|
|
flags = std::min (MatrixAdjustFlags::Displacement, flags);
|
|
} else if (landmarks_before.size () == 2) {
|
|
flags = std::min (MatrixAdjustFlags::Magnification, flags);
|
|
} else if (landmarks_before.size () == 3) {
|
|
flags = std::min (MatrixAdjustFlags::Shear, flags);
|
|
}
|
|
|
|
if (flags < MatrixAdjustFlags::Perspective) {
|
|
|
|
if (matrix.has_perspective ()) {
|
|
|
|
std::vector<db::DPoint> p;
|
|
p.reserve (landmarks_before.size ());
|
|
for (std::vector<db::DPoint>::const_iterator pp = landmarks_before.begin (); pp != landmarks_before.end (); ++pp) {
|
|
p.push_back (matrix * *pp);
|
|
}
|
|
|
|
Matrix2d m2d (1.0);
|
|
db::DVector d2d;
|
|
adjust_matrix (m2d, d2d, p, landmarks_after, flags, fixed_point);
|
|
|
|
matrix = Matrix3d::disp (d2d) * Matrix3d (m2d) * matrix;
|
|
|
|
} else {
|
|
|
|
Matrix2d m2d (matrix.m2d ());
|
|
db::DVector d2d (matrix.disp ());
|
|
adjust_matrix (m2d, d2d, landmarks_before, landmarks_after, flags, fixed_point);
|
|
|
|
matrix = Matrix3d::disp (d2d) * Matrix3d (m2d);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Determine initial center of weight or fixed displacement (if there is a fixed point) before and after.
|
|
// Extract the displacement so we have a 0,0-centered vector set.
|
|
// Use the initial transformation as a basis for the adjustment.
|
|
|
|
db::DPoint dp;
|
|
|
|
if (fixed_point >= 0 && fixed_point < int (landmarks_before.size ())) {
|
|
dp = matrix * landmarks_before [fixed_point];
|
|
}
|
|
|
|
std::vector<db::DVector> p;
|
|
p.reserve (landmarks_before.size ());
|
|
for (std::vector<db::DPoint>::const_iterator pp = landmarks_before.begin (); pp != landmarks_before.end (); ++pp) {
|
|
p.push_back (matrix * *pp - dp);
|
|
}
|
|
|
|
db::DPoint dq;
|
|
if (fixed_point >= 0 && fixed_point < int (landmarks_after.size ())) {
|
|
dq = landmarks_after [fixed_point];
|
|
}
|
|
|
|
std::vector<db::DVector> q;
|
|
q.reserve (landmarks_after.size ());
|
|
for (std::vector<db::DPoint>::const_iterator qq = landmarks_after.begin (); qq != landmarks_after.end (); ++qq) {
|
|
q.push_back (*qq - dq);
|
|
}
|
|
|
|
// Perform the fit of m11, m12, m13, m21, m22, m23, m31, m32.
|
|
// We needs to solve a set of 8 linear equations whose coefficients we collect in l and whose right side we collect in r.
|
|
|
|
double l[8][8];
|
|
double r[8];
|
|
|
|
for (int i = 0; i < 8; ++i) {
|
|
r[i] = 0.0;
|
|
for (int j = 0; j < 8; ++j) {
|
|
l[i][j] = 0.0;
|
|
}
|
|
}
|
|
|
|
for (size_t n = 0; n < landmarks_after.size (); ++n) {
|
|
|
|
l[0][0] += p[n].x() * p[n].x();
|
|
l[0][1] += p[n].x() * p[n].y();
|
|
l[0][2] += p[n].x();
|
|
l[0][6] -= q[n].x() * p[n].x() * p[n].x();
|
|
l[0][7] -= q[n].x() * p[n].x() * p[n].y();
|
|
r[0] += q[n].x() * p[n].x();
|
|
|
|
l[1][0] += p[n].y() * p[n].x();
|
|
l[1][1] += p[n].y() * p[n].y();
|
|
l[1][2] += p[n].y();
|
|
l[1][6] -= q[n].x() * p[n].y() * p[n].x();
|
|
l[1][7] -= q[n].x() * p[n].y() * p[n].y();
|
|
r[1] += q[n].x() * p[n].y();
|
|
|
|
l[2][0] += p[n].x();
|
|
l[2][1] += p[n].y();
|
|
l[2][2] += 1.0;
|
|
l[2][6] -= q[n].x() * p[n].x();
|
|
l[2][7] -= q[n].x() * p[n].y();
|
|
r[2] += q[n].x();
|
|
|
|
l[3][3] += p[n].x() * p[n].x();
|
|
l[3][4] += p[n].x() * p[n].y();
|
|
l[3][5] += p[n].x();
|
|
l[3][6] -= q[n].y() * p[n].x() * p[n].x();
|
|
l[3][7] -= q[n].y() * p[n].x() * p[n].y();
|
|
r[3] += q[n].y() * p[n].x();
|
|
|
|
l[4][3] += p[n].y() * p[n].x();
|
|
l[4][4] += p[n].y() * p[n].y();
|
|
l[4][5] += p[n].y();
|
|
l[4][6] -= q[n].y() * p[n].y() * p[n].x();
|
|
l[4][7] -= q[n].y() * p[n].y() * p[n].y();
|
|
r[4] += q[n].y() * p[n].y();
|
|
|
|
l[5][3] += p[n].x();
|
|
l[5][4] += p[n].y();
|
|
l[5][5] += 1.0;
|
|
l[5][6] -= q[n].y() * p[n].x();
|
|
l[5][7] -= q[n].y() * p[n].y();
|
|
r[5] += q[n].y();
|
|
|
|
l[6][0] += q[n].x() * p[n].x() * p[n].x();
|
|
l[6][1] += q[n].x() * p[n].x() * p[n].y();
|
|
l[6][2] += q[n].x() * p[n].x();
|
|
l[6][3] += q[n].y() * p[n].x() * p[n].x();
|
|
l[6][4] += q[n].y() * p[n].x() * p[n].y();
|
|
l[6][5] += q[n].y() * p[n].x();
|
|
l[6][6] -= q[n].sq_double_length () * p[n].x() * p[n].x();
|
|
l[6][7] -= q[n].sq_double_length () * p[n].x() * p[n].y();
|
|
r[6] += q[n].sq_double_length () * p[n].x();
|
|
|
|
l[7][0] += q[n].x() * p[n].y() * p[n].x();
|
|
l[7][1] += q[n].x() * p[n].y() * p[n].y();
|
|
l[7][2] += q[n].x() * p[n].y();
|
|
l[7][3] += q[n].y() * p[n].y() * p[n].x();
|
|
l[7][4] += q[n].y() * p[n].y() * p[n].y();
|
|
l[7][5] += q[n].y() * p[n].y();
|
|
l[7][6] -= q[n].sq_double_length () * p[n].y() * p[n].x();
|
|
l[7][7] -= q[n].sq_double_length () * p[n].y() * p[n].y();
|
|
r[7] += q[n].sq_double_length () * p[n].y();
|
|
|
|
}
|
|
|
|
// Now solve the equation system
|
|
|
|
for (int i = 0; i < 7; ++i) {
|
|
for (int j = 7; j > i; --j) {
|
|
if (std::abs (l[j][i]) > std::abs (l[j - 1][i])) {
|
|
for (int k = 0; k < 8; ++k) {
|
|
std::swap (l[j][k], l[j - 1][k]);
|
|
}
|
|
std::swap (r[j], r[j - 1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 8; ++i) {
|
|
for (int j = i + 1; j < 8; ++j) {
|
|
double f = l[j][i] / l[i][i];
|
|
for (int k = 0; k < 8; ++k) {
|
|
l[j][k] -= f * l[i][k];
|
|
}
|
|
r[j] -= f * r[i];
|
|
}
|
|
double f = 1.0 / l[i][i];
|
|
for (int k = 0; k < 8; ++k) {
|
|
l[i][k] *= f;
|
|
}
|
|
r[i] *= f;
|
|
}
|
|
|
|
for (int i = 7; i > 0; --i) {
|
|
for (int j = 0; j < i; ++j) {
|
|
r[j] -= l[j][i] * r[i];
|
|
}
|
|
}
|
|
|
|
// compute the final transformation
|
|
matrix = Matrix3d::disp (dq - db::DPoint ()) * Matrix3d (r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], 1.0) * Matrix3d::disp (db::DPoint () - dp) * matrix;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
namespace tl
|
|
{
|
|
template<> bool test_extractor_impl (tl::Extractor &ex, db::Matrix2d &m)
|
|
{
|
|
double m11 = 0.0, m12 = 0.0, m21 = 0.0, m22 = 0.0;
|
|
|
|
if (! ex.test ("(")) {
|
|
return false;
|
|
}
|
|
if (! ex.try_read (m11)) {
|
|
return false;
|
|
}
|
|
if (! ex.test (",")) {
|
|
return false;
|
|
}
|
|
if (! ex.try_read (m12)) {
|
|
return false;
|
|
}
|
|
if (! ex.test (")")) {
|
|
return false;
|
|
}
|
|
|
|
if (! ex.test ("(")) {
|
|
return false;
|
|
}
|
|
if (! ex.try_read (m21)) {
|
|
return false;
|
|
}
|
|
if (! ex.test (",")) {
|
|
return false;
|
|
}
|
|
if (! ex.try_read (m22)) {
|
|
return false;
|
|
}
|
|
if (! ex.test (")")) {
|
|
return false;
|
|
}
|
|
|
|
m = db::Matrix2d (m11, m12, m21, m22);
|
|
return true;
|
|
}
|
|
|
|
template<> void extractor_impl (tl::Extractor &ex, db::Matrix2d &m)
|
|
{
|
|
if (! test_extractor_impl (ex, m)) {
|
|
ex.error (tl::to_string (QObject::tr ("Expected a 2d matrix specification")));
|
|
}
|
|
}
|
|
|
|
template<> bool test_extractor_impl (tl::Extractor &ex, db::Matrix3d &m)
|
|
{
|
|
double m11 = 0.0, m12 = 0.0, m13 = 0.0, m21 = 0.0, m22 = 0.0, m23 = 0.0, m31 = 0.0, m32 = 0.0, m33 = 0.0;
|
|
|
|
if (! ex.test ("(")) {
|
|
return false;
|
|
}
|
|
if (! ex.try_read (m11)) {
|
|
return false;
|
|
}
|
|
if (! ex.test (",")) {
|
|
return false;
|
|
}
|
|
if (! ex.try_read (m12)) {
|
|
return false;
|
|
}
|
|
if (! ex.test (",")) {
|
|
return false;
|
|
}
|
|
if (! ex.try_read (m13)) {
|
|
return false;
|
|
}
|
|
if (! ex.test (")")) {
|
|
return false;
|
|
}
|
|
|
|
if (! ex.test ("(")) {
|
|
return false;
|
|
}
|
|
if (! ex.try_read (m21)) {
|
|
return false;
|
|
}
|
|
if (! ex.test (",")) {
|
|
return false;
|
|
}
|
|
if (! ex.try_read (m22)) {
|
|
return false;
|
|
}
|
|
if (! ex.test (",")) {
|
|
return false;
|
|
}
|
|
if (! ex.try_read (m23)) {
|
|
return false;
|
|
}
|
|
if (! ex.test (")")) {
|
|
return false;
|
|
}
|
|
|
|
if (! ex.test ("(")) {
|
|
return false;
|
|
}
|
|
if (! ex.try_read (m31)) {
|
|
return false;
|
|
}
|
|
if (! ex.test (",")) {
|
|
return false;
|
|
}
|
|
if (! ex.try_read (m32)) {
|
|
return false;
|
|
}
|
|
if (! ex.test (",")) {
|
|
return false;
|
|
}
|
|
if (! ex.try_read (m33)) {
|
|
return false;
|
|
}
|
|
if (! ex.test (")")) {
|
|
return false;
|
|
}
|
|
|
|
m = db::Matrix3d (m11, m12, m13, m21, m22, m23, m31, m32, m33);
|
|
return true;
|
|
}
|
|
|
|
template<> void extractor_impl (tl::Extractor &ex, db::Matrix3d &m)
|
|
{
|
|
if (! test_extractor_impl (ex, m)) {
|
|
ex.error (tl::to_string (QObject::tr ("Expected a 3d matrix specification")));
|
|
}
|
|
}
|
|
}
|
|
|
|
|