diff options
Diffstat (limited to 'dviware/dvisvgm/src/utility.cpp')
-rw-r--r-- | dviware/dvisvgm/src/utility.cpp | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/dviware/dvisvgm/src/utility.cpp b/dviware/dvisvgm/src/utility.cpp index 99e8e194a0..f1ae59ec08 100644 --- a/dviware/dvisvgm/src/utility.cpp +++ b/dviware/dvisvgm/src/utility.cpp @@ -20,6 +20,7 @@ #include <algorithm> #include <cctype> +#include <cmath> #include <fstream> #include <functional> #include <iterator> @@ -27,6 +28,56 @@ using namespace std; +/** Computes the singular value decomposition of a given 2x2 matrix M + * so that M = rotate(phi)*scale(sx,sy)*rotate(theta), where + * rotate(t):={{cos t, -sin t}, {sin t, cos t}} and scale(sx, sy):={{sx, 0}, {0, sy}}. + * The corresponding math can be found in Jim Blinn: "Consider the Lowly 2x2 Matrix" + * https://ieeexplore.ieee.org/document/486688 + * Also published in "Jim Blinn's Corner: Notation, Notation, Notation", pp. 69--95. + * @param[in] m matrix {{m00, m01}, {m10, m11}} where the inner pairs denote the rows + * @return vector {phi, sx, sy, theta} */ +vector<double> math::svd (const double (&m)[2][2]) { + double phi=0, theta=0, sx=0, sy=0; + if (m[0][0] != 0 || m[0][1] != 0 || m[1][0] != 0 || m[1][1] != 0) { + double e = (m[0][0] + m[1][1])/2; // = cos(phi+theta)*(sx+sy)/2 + double f = (m[0][0] - m[1][1])/2; // = cos(phi-theta)*(sx-sy)/2 + double g = (m[1][0] + m[0][1])/2; // = sin(phi-theta)*(sx-sy)/2 + double h = (m[1][0] - m[0][1])/2; // = sin(phi+theta)*(sx+sy)/2 + double hyp1 = hypot(e, h); // = (sx+sy)/2 + double hyp2 = hypot(f, g); // = (sx-sy)/2 + sx = hyp1+hyp2; + sy = hyp1-hyp2; + if (hyp2 == 0) // uniformly scaled rotation? + theta = atan2(h, e); + else if (hyp1 == 0) // uniformly scaled reflection? + theta = -atan2(g, f); + else { + double a1 = atan2(g, f); // = phi-theta (g/f = tan(phi-theta)) + double a2 = atan2(h, e); // = phi+theta (h/e = tan(phi+theta)) + phi = (a2+a1)/2; + theta = (a2-a1)/2; + } + } + return vector<double>{phi, sx, sy, theta}; +} + + +/** Normalizes an angle to the interval [-mod, mod). */ +double math::normalize_angle (double angle, double mod) { + angle = fmod(angle+mod, 2.0*mod); + if (angle < 0) + angle += 2.0*mod; + return angle-mod; +} + + +double math::normalize_0_2pi (double rad) { + rad = fmod(rad, TWO_PI); + if (rad < 0) + rad += TWO_PI; + return rad; +} + /** Returns a given string with leading and trailing whitespace removed. * @param[in] str the string to process @@ -109,6 +160,20 @@ string util::tolower (const string &str) { } +/** Converts a double to a string and strips redundant trailing digits/dots. */ +string util::to_string (double val) { + string str = std::to_string(val); + if (str.find('.') != string::npos) { // double value and not an integer? + size_t pos = str.find_last_not_of('0'); + if (pos != string::npos) // trailing zeros + str.erase(pos+1, string::npos); + if (str.back() == '.') // trailing dot? + str.pop_back(); + } + return str; +} + + /** Returns the integer part of log10 of a given integer \f$n>0\f$. * If \f$n<0\f$, the result is 0. */ int util::ilog10 (int n) { |