diff --git a/toonz/sources/include/tools/assistant.h b/toonz/sources/include/tools/assistant.h index c2b2322..ac44d4c 100644 --- a/toonz/sources/include/tools/assistant.h +++ b/toonz/sources/include/tools/assistant.h @@ -66,8 +66,15 @@ public: virtual TTrackPoint transformPoint(const TTrackPoint &point) const { return point; } + + // this function uses in calcTrackWeight to select best guideline, + // does not use for drawing + virtual TPointD nearestPoint(const TPointD &point) const + { return transformPoint(TTrackPoint(point)).position; } + virtual void draw(bool active, bool enabled) const { } + void draw(bool active = false) const { draw(active, true); } diff --git a/toonz/sources/include/tools/assistants/guidelineellipse.h b/toonz/sources/include/tools/assistants/guidelineellipse.h index c8b1607..faefda3 100644 --- a/toonz/sources/include/tools/assistants/guidelineellipse.h +++ b/toonz/sources/include/tools/assistants/guidelineellipse.h @@ -28,17 +28,27 @@ class DVAPI TGuidelineEllipse : public TGuideline { public: const TAffine matrix; const TAffine matrixInv; + const double Rx; + const double Ry; TGuidelineEllipse( bool enabled, double magnetism, - TAffine matrix ); + const TAffine &matrix ); TGuidelineEllipse( bool enabled, double magnetism, - TAffine matrix, - TAffine matrixInv ); + const TAffine &matrix, + const TAffine &matrixInv ); + + TGuidelineEllipse( + bool enabled, + double magnetism, + const TAffine &matrix, + const TAffine &matrixInv, + double Rx, + double Ry ); //! returns false when ellipse is invisible static bool truncateEllipse( @@ -49,6 +59,7 @@ public: static int calcSegmentsCount(const TAffine &ellipseMatrix, double pixelSize); TTrackPoint transformPoint(const TTrackPoint &point) const override; + TPointD nearestPoint(const TPointD &point) const override; void draw(bool active, bool enabled) const override; }; diff --git a/toonz/sources/tnztools/assistant.cpp b/toonz/sources/tnztools/assistant.cpp index 2807468..daab0f6 100644 --- a/toonz/sources/tnztools/assistant.cpp +++ b/toonz/sources/tnztools/assistant.cpp @@ -78,8 +78,8 @@ TGuideline::calcTrackWeight(const TTrack &track, const TAffine &toScreen, bool & double weight = length*logNormalDistribuitionUnscaled(midStepLength, snapLenght, snapScale); sumWeight += weight; - TTrackPoint ntp = transformPoint(mid); - double deviation = tdistance(toScreen*ntp.position, p); + TPointD ntp = nearestPoint(mid.position); + double deviation = tdistance(toScreen*ntp, p); sumDeviation += weight*deviation; } prev = p; diff --git a/toonz/sources/tnztools/assistants/guidelineellipse.cpp b/toonz/sources/tnztools/assistants/guidelineellipse.cpp index 51014c2..c18934f 100644 --- a/toonz/sources/tnztools/assistants/guidelineellipse.cpp +++ b/toonz/sources/tnztools/assistants/guidelineellipse.cpp @@ -3,32 +3,112 @@ #include <tools/assistants/guidelineellipse.h> // TnzCore includes -#include "tgl.h" +#include <tgl.h> +#include <tmathutil.h> + + + +// returns a point nearest to the ellipse with center in zero +static TPointD findNearestPoint(const TPointD &p, double Rx, double Ry) { + Rx = fabs(Rx); + Ry = fabs(Ry); + TPointD ep; + + if (isAlmostZero(Rx)) { + ep.y = p.y < -Ry ? -Ry + : p.y > Ry ? Ry : p.y; + return ep; + } + + if (isAlmostZero(Ry)) { + ep.x = p.x < -Rx ? -Rx + : p.x > Rx ? Rx : p.x; + return ep; + } + + double k = 1/Rx; + double x0 = p.x*k; + double y0 = p.y*k; + k *= Ry; + k *= k; // k = (Ry/Rx)^2 + double l = k - 1; + + double a = l*l; + double b = 2*l*x0; + double c = x0*x0 + y0*y0*k - l*l; + double d = -b; + double e = -x0*x0; + + double dist = INFINITY; + std::complex<double> roots[4]; + int cnt = solveEquation4(roots, a, b, c, d, e); + for(int i = 0; i < cnt; ++i) { + if (!isAlmostZero(roots[i].imag())) + continue; + + double x = roots[i].real(); + double y; + if (isAlmostZero(fabs(x) - 1)) { + y = 0; + } else + if (fabs(x) < 1) { + y = sqrt(k*(1 - x*x)); + if (y0 < 0) y = -y; + } else { + continue; + } + + double dd = (x0-x)*(x0-x) + (y0-y)*(y0-y); + if (dd < dist) + { ep.x = x*Rx; ep.y = y*Rx; dist = dd; } + } + return ep; +} + //***************************************************************************************** // TGuidelineEllipse implementation //***************************************************************************************** + TGuidelineEllipse::TGuidelineEllipse( bool enabled, double magnetism, - TAffine matrix + const TAffine &matrix, + const TAffine &matrixInv, + double Rx, + double Ry ): TGuideline(enabled, magnetism), matrix(matrix), - matrixInv(matrix.inv()) { } + matrixInv(matrixInv), + Rx(Rx), + Ry(Ry) + { } TGuidelineEllipse::TGuidelineEllipse( bool enabled, double magnetism, - TAffine matrix, - TAffine matrixInv + const TAffine &matrix, + const TAffine &matrixInv ): - TGuideline(enabled, magnetism), - matrix(matrix), - matrixInv(matrixInv) { } + TGuidelineEllipse( + enabled, magnetism, matrix, matrixInv, + sqrt(matrix.a11*matrix.a11 + matrix.a21*matrix.a21), + sqrt(matrix.a12*matrix.a12 + matrix.a22*matrix.a22) ) + { } + + +TGuidelineEllipse::TGuidelineEllipse( + bool enabled, + double magnetism, + const TAffine &matrix +): + TGuidelineEllipse(enabled, magnetism, matrix, matrix.inv()) + { } + TTrackPoint @@ -43,6 +123,18 @@ TGuidelineEllipse::transformPoint(const TTrackPoint &point) const } +TPointD +TGuidelineEllipse::nearestPoint(const TPointD &point) const +{ + TPointD p = matrixInv*point; + p = findNearestPoint(TPointD(p.x*Rx, p.y*Ry), Rx, Ry); + if (!isAlmostZero(Rx)) p.x /= Rx; + if (!isAlmostZero(Ry)) p.y /= Ry; + return matrix*p; +} + + + bool TGuidelineEllipse::truncateEllipse( TAngleRangeSet &ranges,