/* 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 #include #include 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 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 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 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 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 &q, const std::vector &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 &q, const std::vector &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 &landmarks_before, const std::vector &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::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 p; p.reserve (landmarks_before.size ()); for (std::vector::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::const_iterator q = landmarks_after.begin (); q != landmarks_after.end (); ++q) { dq += *q - db::DPoint (); } dq *= 1.0 / double (landmarks_after.size ()); } std::vector q; q.reserve (landmarks_after.size ()); for (std::vector::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::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::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::max () || d2 < std::numeric_limits::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 &landmarks_before, const std::vector &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 p; p.reserve (landmarks_before.size ()); for (std::vector::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 p; p.reserve (landmarks_before.size ()); for (std::vector::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 q; q.reserve (landmarks_after.size ()); for (std::vector::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"))); } } }