From 7235b0836a932ef0590b66f512e0e31a3a100709 Mon Sep 17 00:00:00 2001 From: Rodney <rodney.baker@gmail.com> Date: Oct 03 2023 19:34:54 +0000 Subject: Merge pull request #5100 from blackwarthog/feature08j_assistants_squash Improve assistants --- diff --git a/toonz/sources/common/tgeometry/tgeometry.cpp b/toonz/sources/common/tgeometry/tgeometry.cpp index d8bb6b6..7724948 100644 --- a/toonz/sources/common/tgeometry/tgeometry.cpp +++ b/toonz/sources/common/tgeometry/tgeometry.cpp @@ -61,9 +61,8 @@ TAffine TAffine::inv() const { (a22 == 0.0 ? std::numeric_limits<double>::max() / (1 << 16) : 1.0 / a22); return TAffine(inv_a11, 0, -a13 * inv_a11, 0, inv_a22, -a23 * inv_a22); - } else if (a11 == 0.0 && a22 == 0.0) { - assert(a12 != 0.0); - assert(a21 != 0.0); + } else + if (a11 == 0.0 && a22 == 0.0) { double inv_a21 = (a21 == 0.0 ? std::numeric_limits<double>::max() / (1 << 16) : 1.0 / a21); @@ -235,8 +234,79 @@ TScale::TScale(const TPointD ¢er, double s) { //================================================================================================== -TPoint4D TAffine4::operator*(const TPoint4D &b) const { - return TPoint4D( +T3DPointD TAffine3::operator*(const T3DPointD &b) const { + return T3DPointD( + b.x*a11 + b.y*a21 + b.z*a31, + b.x*a12 + b.y*a22 + b.z*a32, + b.x*a13 + b.y*a23 + b.z*a33 ); +} + +TAffine3 TAffine3::operator*(const TAffine3 &b) const { + return TAffine3( + *this * b.rowX(), + *this * b.rowY(), + *this * b.rowZ() ); +} + +TAffine3 TAffine3::operator*=(const TAffine3 &b) + { return *this = *this * b; } + +TAffine3 TAffine3::inv() const { + TAffine3 r; + r.a11 = a22*a33 - a32*a23; + r.a12 = a32*a13 - a12*a33; + r.a13 = a12*a23 - a22*a13; + + double det = r.a11*a11 + r.a12*a21 + r.a12*a31; + det = fabs(det) > TConsts::epsilon ? 1.0/det : 0.0; + r.a11 *= det; + r.a12 *= det; + r.a13 *= det; + + r.a21 = (a31*a23 - a21*a33)*det; + r.a22 = (a11*a33 - a31*a13)*det; + r.a23 = (a21*a13 - a11*a23)*det; + r.a31 = (a21*a32 - a31*a22)*det; + r.a32 = (a31*a12 - a11*a32)*det; + r.a33 = (a11*a22 - a21*a12)*det; + return r; +} + +TAffine TAffine3::get2d() const { + return TAffine( + a11, a21, a31, + a12, a22, a32 ); +} + +TAffine3 TAffine3::translation2d(double x, double y) { + TAffine3 r; + r.rowZ().x = x; + r.rowZ().y = y; + return r; +} + +TAffine3 TAffine3::scale2d(double x, double y) { + TAffine3 r; + r.a11 = x; + r.a22 = y; + return r; +} + +TAffine3 TAffine3::rotation2d(double angle) { + TAffine3 r; + double s = sin(angle); + double c = cos(angle); + r.a11 = c; + r.a12 = s; + r.a21 = -s; + r.a22 = c; + return r; +} + +//================================================================================================== + +T4DPointD TAffine4::operator*(const T4DPointD &b) const { + return T4DPointD( b.x*a11 + b.y*a21 + b.z*a31 + b.w*a41, b.x*a12 + b.y*a22 + b.z*a32 + b.w*a42, b.x*a13 + b.y*a23 + b.z*a33 + b.w*a43, @@ -290,6 +360,13 @@ TAffine TAffine4::get2d(double z) const { a12, a22, z*a32 + a42 ); } +TAffine3 TAffine4::get2dPersp(double z) const { + return TAffine3( + T3DPointD( a11 , a12 , a14 ), + T3DPointD( a21 , a22 , a24 ), + T3DPointD( a31*z+a41 , a32*z+a42 , a34*z+a44 ) ); +} + TAffine4 TAffine4::translation(double x, double y, double z) { TAffine4 r; r.rowW().x = x; diff --git a/toonz/sources/include/tgeometry.h b/toonz/sources/include/tgeometry.h index 34b9902..9542e3e 100644 --- a/toonz/sources/include/tgeometry.h +++ b/toonz/sources/include/tgeometry.h @@ -26,7 +26,8 @@ inline double logNormalDistribuition(double x, double x0, double w) //============================================================================= -template <class T> class TPoint4T; +template <class T> class T3DPointT; +template <class T> class T4DPointT; /* * This is an example of how to use the TPointT, the TRectT and the TAffine @@ -40,149 +41,104 @@ public: T x, y; inline TPointT() : x(0), y(0){}; - inline TPointT(T _x, T _y) : x(_x), y(_y){}; - inline TPointT(const TPointT &point) : x(point.x), y(point.y){}; - inline explicit TPointT(const TPoint4T<T> &point); - - inline TPointT &operator=(const TPointT &a) { - x = a.x; - y = a.y; - return *this; - }; - - inline TPointT &operator+=(const TPointT &a) { - x += a.x; - y += a.y; - return *this; - }; - inline TPointT &operator-=(const TPointT &a) { - x -= a.x; - y -= a.y; - return *this; - }; - inline TPointT operator+(const TPointT &a) const { - return TPointT(x + a.x, y + a.y); - }; - inline TPointT operator-(const TPointT &a) const { - return TPointT(x - a.x, y - a.y); - }; - inline TPointT operator-() const { return TPointT(-x, -y); }; -}; - -template <class T> -class TPoint4T { -public: - union { - struct { T x, y, z, w; }; - T a[4]; - }; + inline TPointT(T x, T y) : x(x), y(y){}; + inline explicit TPointT(const T3DPointT<T> &p); + inline explicit TPointT(const T4DPointT<T> &p); + + inline TPointT& operator+=(const TPointT &a) + { return x += a.x, y += a.y, *this; }; + inline TPointT& operator-=(const TPointT &a) + { return x -= a.x, y -= a.y, *this; }; + inline TPointT operator+(const TPointT &a) const + { return TPointT(x + a.x, y + a.y); }; + inline TPointT operator-(const TPointT &a) const + { return TPointT(x - a.x, y - a.y); }; + inline TPointT operator-() const + { return TPointT(-x, -y); }; + + //! Scalar(dot) Product + inline T operator*(const TPointT &a) const + { return x*a.x + y*a.y; } + + inline TPointT operator*=(T a) + { return x *= a, y *= a, *this; } + inline TPointT operator*(T a) const + { return TPointT(x*a, y*a); } + friend inline TPointT operator*(T a, const TPointT &b) + { return TPointT(a*b.x, a*b.y); } + + inline TPointT operator/(T a) const + { return TPointT(x/a, y/a); } + inline TPointT operator/=(T a) + { return x /= a, y /= a, *this; } + + inline bool operator==(const TPointT &a) const + { return x == a.x && y == a.y; } + inline bool operator!=(const TPointT &a) const + { return !(*this == a); } + + friend inline std::ostream &operator<<(std::ostream &out, const TPointT &p) + { return out << "(" << p.x << ", " << p.y << ")"; } + + /*! Rotate a point 90 degrees (counterclockwise). + \param p a point. + \return the rotated point + \sa rotate270 */ + friend inline TPointT rotate90(const TPointT &p) // 90 counterclockwise + { return TPointT(-p.y, p.x); } + + /*! Rotate a point 270 degrees (clockwise). + \param p a point. + \return the rotated point + \sa rotate90 */ + friend inline TPointT rotate270(const TPointT &p) // 90 clockwise + { return TPointT(p.y, -p.x); } + + //! This helper function returns the square of the absolute value of the point + friend inline T norm2(const TPointT &a) + { return a*a; } + + //! This helper function returns the square of the distance between two points + friend inline T tdistance2(const TPointT &a, const TPointT &b) + { return norm2(a - b); } - inline TPoint4T(): - x(), y(), z(), w() { }; - inline TPoint4T(T x, T y, T z, T w): - x(x), y(y), z(z), w(w) { }; - inline explicit TPoint4T(const TPointT<T> &p, T w = (T)1): - x(p.x), y(p.y), z(), w(w) { }; + //! the cross product + friend inline T cross(const TPointT &a, const TPointT &b) + { return a.x*b.y - a.y*b.x; } }; -template <class T> -inline TPointT<T>::TPointT(const TPoint4T<T> &point) : x(point.x), y(point.y){}; - -/*! \relates TPointT -* Rotate a point 90 degrees (counterclockwise). -\param p a point. -\return the rotated point -\sa rotate270 -*/ -template <class T> -inline TPointT<T> rotate90(const TPointT<T> &p) // 90 counterclockwise -{ - return TPointT<T>(-p.y, p.x); -} -/*! \relates TPointT -* Rotate a point 270 degrees (clockwise). -\param p a point. -\return the rotated point -\sa rotate90 -*/ -template <class T> -inline TPointT<T> rotate270(const TPointT<T> &p) // 90 clockwise -{ - return TPointT<T>(p.y, -p.x); -} - -/*! -\relates TPointT -*/ -template <class T> // Scalar(dot) Product -inline T operator*(const TPointT<T> &a, const TPointT<T> &b) { - return a.x * b.x + a.y * b.y; -} - -//----------------------------------------------------------------------------- +template <> +inline bool TPointT<double>::operator==(const TPointT<double> &a) const + { return tdistance2(*this, a) <= TConsts::epsilon * TConsts::epsilon; } -template <class T> -inline std::ostream &operator<<(std::ostream &out, const TPointT<T> &p) { - return out << "(" << p.x << ", " << p.y << ")"; -} //----------------------------------------------------------------------------- typedef TPointT<int> TPoint, TPointI; typedef TPointT<double> TPointD; -typedef TPoint4T<double> TPoint4D; #ifdef _WIN32 template class DVAPI TPointT<int>; template class DVAPI TPointT<double>; #endif -template <class T> -inline bool operator==(const TPointT<T> &p0, const TPointT<T> &p1) { - return p0.x == p1.x && p0.y == p1.y; -} -template<class T> -inline bool operator!=(const TPointT<T> &p0, const TPointT<T> &p1) { - return p0.x != p1.x || p0.y != p1.y; -} //----------------------------------------------------------------------------- -//!\relates TPointT -inline TPoint operator*(int a, const TPoint &p) { - return TPoint(a * p.x, a * p.y); -} - -//!\relates TPointT -inline TPoint operator*(const TPoint &p, int a) { - return TPoint(a * p.x, a * p.y); -} - -//!\relates TPointT -inline TPointD operator*(double a, const TPointD &p) { - return TPointD(a * p.x, a * p.y); -} - -//!\relates TPointT -inline TPointD operator*(const TPointD &p, double a) { - return TPointD(a * p.x, a * p.y); -} - -//----------------------------------------------------------------------------- /*! \relates TPointT -This helper function returns the square of the absolute value of the specified -point (a TPointI) +This helper function converts a TPoint (TPointT<int>) into a TPointD */ -inline int norm2(const TPointI &p) { return p.x * p.x + p.y * p.y; } +inline TPointD convert(const TPoint &p) + { return TPointD(p.x, p.y); } -//----------------------------------------------------------------------------- /*! \relates TPointT -This helper function returns the square of the absolute value of the specified -point (a TPointD) +This helper function converts a TPointD (TPointT<double>) into a TPoint */ -inline double norm2(const TPointD &p) { return p.x * p.x + p.y * p.y; } +inline TPoint convert(const TPointD &p) + { return TPoint(tround(p.x), tround(p.y)); } + /*! \relates TPointT @@ -196,8 +152,8 @@ This helper function returns the normalized version of the specified point */ inline TPointD normalize(const TPointD &p) { double n = norm(p); - assert(n != 0.0); - return (1.0 / n) * p; + assert(n); + return p*(1/n); } /*! @@ -212,56 +168,10 @@ inline TPointD normalizeOrZero(const TPointD &p) { /*! \relates TPointT -This helper function converts a TPoint (TPointT<int>) into a TPointD -*/ -inline TPointD convert(const TPoint &p) { return TPointD(p.x, p.y); } - -/*! -\relates TPointT -This helper function converts a TPointD (TPointT<double>) into a TPoint -*/ -inline TPoint convert(const TPointD &p) { - return TPoint(tround(p.x), tround(p.y)); -} - -/*! -\relates TPointT -This helper function returns the square of the distance between two points -*/ -inline double tdistance2(const TPointD &p1, const TPointD &p2) { - return norm2(p2 - p1); -} - -inline bool operator==(const TPointD &p0, const TPointD &p1) { - return tdistance2(p0, p1) <= TConsts::epsilon * TConsts::epsilon; -} -inline bool operator!=(const TPointD &p0, const TPointD &p1) { - return !(p0 == p1); -} - -/*! -\relates TPointT This helper function returns the distance between two points */ -inline double tdistance(const TPointD &p1, const TPointD &p2) { - return norm(p2 - p1); -} - -/*! -the cross product -\relates TPointT -*/ -inline double cross(const TPointD &a, const TPointD &b) { - return a.x * b.y - a.y * b.x; -} - -/*! -the cross product -\relates TPoint -*/ -inline int cross(const TPoint &a, const TPoint &b) { - return a.x * b.y - a.y * b.x; -} +inline double tdistance(const TPointD &p1, const TPointD &p2) + { return norm(p2 - p1); } /*! returns the angle of the point p in polar coordinates @@ -276,59 +186,73 @@ class DVAPI T3DPointT { public: T x, y, z; - T3DPointT() : x(0), y(0), z(0) {} - - T3DPointT(T _x, T _y, T _z) : x(_x), y(_y), z(_z) {} - T3DPointT(const TPointT<T> &_p, T _z) : x(_p.x), y(_p.y), z(_z) {} - - T3DPointT(const T3DPointT &_p) : x(_p.x), y(_p.y), z(_p.z) {} - - inline T3DPointT &operator=(const T3DPointT &a) { - x = a.x; - y = a.y; - z = a.z; - return *this; - } - - inline T3DPointT &operator+=(const T3DPointT &a) { - x += a.x; - y += a.y; - z += a.z; - return *this; - } - - inline T3DPointT &operator-=(const T3DPointT &a) { - x -= a.x; - y -= a.y; - z -= a.z; - return *this; - } - - inline T3DPointT operator+(const T3DPointT &a) const { - return T3DPointT(x + a.x, y + a.y, z + a.z); - } - - inline T3DPointT operator-(const T3DPointT &a) const { - return T3DPointT(x - a.x, y - a.y, z - a.z); - } - - inline T3DPointT operator-() const { return T3DPointT(-x, -y, -z); } - - bool operator==(const T3DPointT &p) const { - return x == p.x && y == p.y && z == p.z; - } - - bool operator!=(const T3DPointT &p) const { - return x != p.x || y != p.y || z != p.z; + inline T3DPointT() : x(), y(), z() {} + inline T3DPointT(T x, T y, T z) : x(x), y(y), z(z) {} + inline T3DPointT(const TPointT<T> &p, T z) : x(p.x), y(p.y), z(z) {} + inline explicit T3DPointT(const T4DPointT<T> &p); + + inline TPointT<T>& xy() { return *(TPointT<T>*)this; } + inline const TPointT<T>& xy() const { return *(const TPointT<T>*)this; } + + inline T3DPointT &operator+=(const T3DPointT &a) + { return x += a.x, y += a.y, z += a.z, *this; } + inline T3DPointT &operator-=(const T3DPointT &a) + { return x -= a.x, y -= a.y, z -= a.z, *this; } + inline T3DPointT operator+(const T3DPointT &a) const + { return T3DPointT(x + a.x, y + a.y, z + a.z); } + inline T3DPointT operator-(const T3DPointT &a) const + { return T3DPointT(x - a.x, y - a.y, z - a.z); } + inline T3DPointT operator-() const + { return T3DPointT(-x, -y, -z); } + + //! Scalar(dot) Product + inline T operator*(const T3DPointT &a) const + { return x*a.x + y*a.y + z*a.z; } + + inline T3DPointT operator*=(T a) + { return x *= a, y *= a, z *= a, *this; } + inline T3DPointT operator*(T a) const + { return T3DPointT(x*a, y*a, z*a); } + friend inline T3DPointT operator*(T a, const T3DPointT &b) + { return T3DPointT(a*b.x, a*b.y, a*b.z); } + + inline T3DPointT operator/(T a) const + { return T3DPointT(x/a, y/a, z/a); } + inline T3DPointT operator/=(T a) + { return x /= a, y /= a, z /= a, *this; } + + inline bool operator==(const T3DPointT &a) const + { return x == a.x && y == a.y && z == a.z; } + inline bool operator!=(const T3DPointT &a) const + { return !(*this == a); } + + friend inline std::ostream &operator<<(std::ostream &out, const T3DPointT &p) + { return out << "(" << p.x << ", " << p.y << ", " << p.z << ")"; } + + //! This helper function returns the square of the absolute value of the point + friend inline T norm2(const T3DPointT &a) + { return a*a; } + + //! This helper function returns the square of the distance between two points + friend inline T tdistance2(const T3DPointT &a, const T3DPointT &b) + { return norm2(a - b); } + + //! the cross product + friend inline T3DPointT cross(const T3DPointT &a, const T3DPointT &b) { + return T3DPointT( a.y*b.z - b.y*a.z, + a.z*b.x - b.z*a.x, + a.x*b.y - b.x*a.y); } }; -//============================================================================= +template <> +inline bool T3DPointT<double>::operator==(const T3DPointT<double> &a) const + { return tdistance2(*this, a) <= TConsts::epsilon * TConsts::epsilon; } template <class T> -inline std::ostream &operator<<(std::ostream &out, const T3DPointT<T> &p) { - return out << "(" << p.x << ", " << p.y << ", " << p.z << ")"; -} +inline TPointT<T>::TPointT(const T3DPointT<T> &p) : x(p.x), y(p.y) {}; + +//============================================================================= typedef T3DPointT<int> T3DPoint, T3DPointI; typedef T3DPointT<double> T3DPointD; @@ -340,72 +264,86 @@ template class DVAPI T3DPointT<double>; //----------------------------------------------------------------------------- -//!\relates T3DPointT -template <class T> -inline T3DPointT<T> operator*(T a, const T3DPointT<T> &p) { - return T3DPointT<T>(a * p.x, a * p.y, a * p.z); -} +inline T3DPointD convert(const T3DPoint &p) + { return T3DPointD(p.x, p.y, p.z); } +inline T3DPoint convert(const T3DPointD &p) + { return T3DPoint(tround(p.x), tround(p.y), tround(p.z)); } -//!\relates TPointT -template <class T> -inline T3DPointT<T> operator*(const T3DPointT<T> &p, T a) { - return T3DPointT<T>(a * p.x, a * p.y, a * p.z); +inline double norm(const T3DPointD &p) + { return std::sqrt(norm2(p)); } + +inline T3DPointD normalize(const T3DPointD &p) { + double n = norm(p); + assert(n); + return p*(1/n); } -//----------------------------------------------------------------------------- -/*! -\relates TPointT -This helper function returns the square of the absolute value of the specified -point (a TPointI) -*/ -template <class T> -inline T norm2(const T3DPointT<T> &p) { - return p.x * p.x + p.y * p.y + p.z * p.z; +inline T3DPointD normalizeOrZero(const T3DPointD &p) { + double n = norm2(p); + return fabs(n) > TConsts::epsilon*TConsts::epsilon ? p*(1/sqrt(n)) : T3DPointD(); } -/*! -*/ +inline double tdistance(const T3DPointD &p1, const T3DPointD &p2) + { return norm(p2 - p1); } + +//============================================================================= + template <class T> -inline T norm(const T3DPointT<T> &p) { - return std::sqrt(norm2(p)); -} +class DVAPI T4DPointT { +public: + T x, y, z, w; -/*! -*/ -inline T3DPointD normalize(const T3DPointD &p) { - double n = norm(p); - assert(n != 0.0); - return (1.0 / n) * p; -} + inline T4DPointT() : x(), y(), z(), w() {} + inline T4DPointT(T x, T y, T z, T w) : x(x), y(y), z(z), w(w) {} + inline T4DPointT(const TPointT<T> &p, T z, T w) : x(p.x), y(p.y), z(z), w(w) {} + inline T4DPointT(const T3DPointT<T> &p, T w) : x(p.x), y(p.y), z(p.z), w(w) {} -/*! -*/ -inline T3DPointD convert(const T3DPoint &p) { return T3DPointD(p.x, p.y, p.z); } + inline TPointT<T>& xy() { return *(TPointT<T>*)this; } + inline T3DPointT<T>& xyz() { return *(T3DPointT<T>*)this; } -/*! -*/ -inline T3DPoint convert(const T3DPointD &p) { - return T3DPoint(tround(p.x), tround(p.y), tround(p.z)); -} + inline const TPointT<T>& xy() const { return *(const TPointT<T>*)this; } + inline const T3DPointT<T>& xyz() const { return *(const T3DPointT<T>*)this; } + + inline bool operator==(const T4DPointT &p) const + { return x == p.x && y == p.y && z == p.z && w == p.w; } + inline bool operator!=(const T4DPointT &p) const + { return !(*this == p); } + + friend inline std::ostream &operator<<(std::ostream &out, const T4DPointT &p) + { return out << "(" << p.x << ", " << p.y << ", " << p.z << ", " << p.w << ")"; } +}; -//! -template <class T> -inline T tdistance(const T3DPointT<T> &p1, const T3DPointT<T> &p2) { - return norm<T>(p2 - p1); +template <> +inline bool T4DPointT<double>::operator==(const T4DPointT<double> &a) const { + T4DPointT<double> d(x - a.x, y - a.y, z - a.z, w - a.w); + return d.x*d.x + d.y*d.y + d.z*d.z + d.w*d.w + <= TConsts::epsilon * TConsts::epsilon; } -//! template <class T> -inline T tdistance2(const T3DPointT<T> &p1, const T3DPointT<T> &p2) { - return norm2<T>(p2 - p1); -} - -//! +inline TPointT<T>::TPointT(const T4DPointT<T> &p) : x(p.x), y(p.y) {}; template <class T> -inline T3DPointT<T> cross(const T3DPointT<T> &a, const T3DPointT<T> &b) { - return T3DPointT<T>(a.y * b.z - b.y * a.z, a.z * b.x - b.z * a.x, - a.x * b.y - b.x * a.y); -} +inline T3DPointT<T>::T3DPointT(const T4DPointT<T> &p) : x(p.x), y(p.y), z(p.z) {}; + +//============================================================================= + +typedef T4DPointT<int> T4DPoint, T4DPointI; +typedef T4DPointT<double> T4DPointD; + +#ifdef _WIN32 +template class DVAPI T4DPointT<int>; +template class DVAPI T4DPointT<double>; +#endif + +//----------------------------------------------------------------------------- + +//!\relates T4DPointT + +inline T4DPointD convert(const T4DPoint &p) + { return T4DPointD(p.x, p.y, p.z, p.w); } +inline T4DPoint convert(const T4DPointD &p) + { return T4DPoint(tround(p.x), tround(p.y), tround(p.z), tround(p.w)); } + //============================================================================= /*! TThickPoint describe a thick point. @@ -1267,6 +1205,71 @@ inline std::ostream &operator<<(std::ostream &out, const TAffine &a) { //============================================================================= +//! This class performs basic manipulations of affine transformations in 2D space. +//! with ability of perspective transform +//! the matrix is transposed to TAffine and equal to OpenGL cells order + +class DVAPI TAffine3 { +public: + union { + struct { + double a11, a12, a13; + double a21, a22, a23; + double a31, a32, a33; + }; + double m[3][3]; + double a[9]; + }; + + inline TAffine3(): + a11(1.0), a12(0.0), a13(0.0), + a21(0.0), a22(1.0), a23(0.0), + a31(0.0), a32(0.0), a33(1.0) { } + + inline explicit TAffine3(const TAffine &a): + a11(a.a11), a12(a.a21), a13(0.0), + a21(a.a12), a22(a.a22), a23(0.0), + a31(a.a13), a32(a.a23), a33(1.0) { } + + inline TAffine3( + const T3DPointD &rowX, + const T3DPointD &rowY, + const T3DPointD &rowZ + ): + a11(rowX.x), a12(rowX.y), a13(rowX.z), + a21(rowY.x), a22(rowY.y), a23(rowY.z), + a31(rowZ.x), a32(rowZ.y), a33(rowZ.z) { } + + inline T3DPointD& row(int index) + { return *(T3DPointD*)(m[index]); } + inline const T3DPointD& row(int index) const + { return *(const T3DPointD*)(m[index]); } + + inline T3DPointD& rowX() { return row(0); } + inline T3DPointD& rowY() { return row(1); } + inline T3DPointD& rowZ() { return row(2); } + + inline const T3DPointD& rowX() const { return row(0); } + inline const T3DPointD& rowY() const { return row(1); } + inline const T3DPointD& rowZ() const { return row(2); } + + T3DPointD operator*(const T3DPointD &b) const; + TAffine3 operator*(const TAffine3 &b) const; + TAffine3 operator*=(const TAffine3 &b); + + TAffine3 inv() const; + + TAffine get2d() const; + + inline static TAffine3 identity() { return TAffine3(); } + static TAffine3 translation2d(double x, double y); + static TAffine3 scale2d(double x, double y); + static TAffine3 rotation2d(double angle); +}; + + +//============================================================================= + //! This class performs basic manipulations of affine transformations in 3D space. //! the matrix is transposed to TAffine and equal to OpenGL @@ -1296,38 +1299,39 @@ public: a41(a.a13), a42(a.a23), a43(0.0), a44(1.0) { } inline TAffine4( - const TPoint4D &rowX, - const TPoint4D &rowY, - const TPoint4D &rowZ, - const TPoint4D &rowW + const T4DPointD &rowX, + const T4DPointD &rowY, + const T4DPointD &rowZ, + const T4DPointD &rowW ): a11(rowX.x), a12(rowX.y), a13(rowX.z), a14(rowX.w), a21(rowY.x), a22(rowY.y), a23(rowY.z), a24(rowY.w), a31(rowZ.x), a32(rowZ.y), a33(rowZ.z), a34(rowZ.w), a41(rowW.x), a42(rowW.y), a43(rowW.z), a44(rowW.w) { } - inline TPoint4D& row(int index) - { return *(TPoint4D*)(m[index]); } - inline const TPoint4D& row(int index) const - { return *(const TPoint4D*)(m[index]); } + inline T4DPointD& row(int index) + { return *(T4DPointD*)(m[index]); } + inline const T4DPointD& row(int index) const + { return *(const T4DPointD*)(m[index]); } - inline TPoint4D& rowX() { return row(0); } - inline TPoint4D& rowY() { return row(1); } - inline TPoint4D& rowZ() { return row(2); } - inline TPoint4D& rowW() { return row(3); } + inline T4DPointD& rowX() { return row(0); } + inline T4DPointD& rowY() { return row(1); } + inline T4DPointD& rowZ() { return row(2); } + inline T4DPointD& rowW() { return row(3); } - inline const TPoint4D& rowX() const { return row(0); } - inline const TPoint4D& rowY() const { return row(1); } - inline const TPoint4D& rowZ() const { return row(2); } - inline const TPoint4D& rowW() const { return row(3); } + inline const T4DPointD& rowX() const { return row(0); } + inline const T4DPointD& rowY() const { return row(1); } + inline const T4DPointD& rowZ() const { return row(2); } + inline const T4DPointD& rowW() const { return row(3); } - TPoint4D operator*(const TPoint4D &b) const; + T4DPointD operator*(const T4DPointD &b) const; TAffine4 operator*(const TAffine4 &b) const; TAffine4 operator*=(const TAffine4 &b); TAffine4 inv() const; TAffine get2d(double z = 0.0) const; + TAffine3 get2dPersp(double z = 0.0) const; inline static TAffine4 identity() { return TAffine4(); } static TAffine4 translation(double x, double y, double z); diff --git a/toonz/sources/include/tmetaimage.h b/toonz/sources/include/tmetaimage.h index 9d360c7..5992c4f 100644 --- a/toonz/sources/include/tmetaimage.h +++ b/toonz/sources/include/tmetaimage.h @@ -186,7 +186,7 @@ public: data().touch(); } void dataChanged(const TVariant &value) - { if (m_locks == 0) onDataChanged(value); } + { LockEvents lock(*this); if (m_locks == 1) onDataChanged(value); } void fixData() { LockEvents lock(*this); onFixData(); } }; diff --git a/toonz/sources/include/tools/assistant.h b/toonz/sources/include/tools/assistant.h index f79377a..a8e8ea3 100644 --- a/toonz/sources/include/tools/assistant.h +++ b/toonz/sources/include/tools/assistant.h @@ -108,6 +108,7 @@ public: Circle, CircleFill, CircleCross, + CircleDiagonalCross, CircleDots, CircleDoubleDots, }; @@ -211,6 +212,8 @@ protected: const TStringId m_idPoints; const TStringId m_idX; const TStringId m_idY; + const TStringId m_idZ; + const TStringId m_idW; const TStringId m_idMagnetism; TAssistantPointMap m_points; @@ -315,12 +318,14 @@ protected: double getDrawingAlpha(bool enabled = true) const; double getDrawingGridAlpha() const; - void drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize, double alpha) const; - void drawMark(const TPointD &p, const TPointD &normal, double pixelSize, double alpha) const; - void drawDot(const TPointD &p, double alpha) const; - void drawPoint(const TAssistantPoint &point, double pixelSize) const; - void drawIndex(const TPointD &p, int index, bool selected, double pixelSize) const; + static void drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize, double alpha0, double alpha1); + static void drawMark(const TPointD &p, const TPointD &normal, double pixelSize, double alpha); + static void drawDot(const TPointD &p, double alpha); + static void drawPoint(const TAssistantPoint &point, double pixelSize); + static void drawIndex(const TPointD &p, int index, bool selected, double pixelSize); + static inline void drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize, double alpha) + { drawSegment(p0, p1, pixelSize, alpha, alpha); } inline void drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize) const { drawSegment(p0, p1, pixelSize, getDrawingAlpha()); } inline void drawDot(const TPointD &p) const @@ -366,6 +371,7 @@ public: void updateTranslation() const override; virtual void getGuidelines(const TPointD &position, const TAffine &toTool, TGuidelineList &outGuidelines) const; + // calc W-coefficient and i-bounds for formula: x0 + 1/(i*W + 1) static bool calcPerspectiveStep( double minStep, double minX, @@ -373,9 +379,9 @@ public: double x0, double x1, double x2, - double &outK, - double &outMin, - double &outMax ); + double &outW, + double &outMinI, + double &outMaxI ); static bool scanAssistants( TTool *tool, diff --git a/toonz/sources/include/tools/modifiers/modifierclone.h b/toonz/sources/include/tools/modifiers/modifierclone.h index 214e70f..763d28c 100644 --- a/toonz/sources/include/tools/modifiers/modifierclone.h +++ b/toonz/sources/include/tools/modifiers/modifierclone.h @@ -44,12 +44,21 @@ public: public: bool keepOriginals; TTrackTransformList transforms; + int skipFirst; + int skipLast; - TModifierClone(bool keepOriginals = true); + explicit TModifierClone( + bool keepOriginals = true, + int skipFirst = 0, + int skipLast = 0 ); void modifyTrack( const TTrack &track, TTrackList &outTracks ) override; + + void modifyTracks( + const TTrackList &tracks, + TTrackList &outTracks ) override; }; #endif diff --git a/toonz/sources/include/tools/modifiers/modifierjitter.h b/toonz/sources/include/tools/modifiers/modifierjitter.h index 2f048e4..b2ee006 100644 --- a/toonz/sources/include/tools/modifiers/modifierjitter.h +++ b/toonz/sources/include/tools/modifiers/modifierjitter.h @@ -32,7 +32,14 @@ public: const unsigned int seedY; const double frequency; const double amplitude; - Interpolator(TTrack &track, double period, double amplitude); + const bool keepFirstPoint; + const bool keepLastPoint; + Interpolator( + TTrack &track, + double period, + double amplitude, + bool keepFirstPoint, + bool keepLastPoint ); TTrackPoint interpolateFromOriginal(double originalIndex); TTrackPoint interpolate(double index) override; }; @@ -41,11 +48,17 @@ public: double period; double amplitude; int skipFirst; + int skipLast; + bool keepFirstPoint; + bool keepLastPoint; - TModifierJitter( + explicit TModifierJitter( double period = 30, double amplitude = 10, - int skipFirst = 0 ); + int skipFirst = 0, + int skipLast = 0, + bool keepFirstPoint = false, + bool keepLastPoint = false ); void modifyTrack( const TTrack &track, diff --git a/toonz/sources/include/tools/replicator.h b/toonz/sources/include/tools/replicator.h index 0556802..71c89b4 100644 --- a/toonz/sources/include/tools/replicator.h +++ b/toonz/sources/include/tools/replicator.h @@ -27,16 +27,31 @@ class DVAPI TReplicator : public TAssistantBase { public: typedef std::vector<TPointD> PointList; + const TStringId m_idSkipFirst; + const TStringId m_idSkipLast; + static const int multiplierSoftLimit; static const int multiplierLimit; TReplicator(TMetaObject &object); + void updateTranslation() const override; + + inline int getSkipFirst() const + { return std::max(0, (int)data()[m_idSkipFirst].getDouble()); } + inline int getSkipLast() const + { return std::max(0, (int)data()[m_idSkipLast].getDouble()); } + + inline void setSkipFirst(int x) + { if (getSkipFirst() != (double)x) data()[m_idSkipFirst].setDouble((double)x); } + inline void setSkipLast(int x) + { if (getSkipLast() != (double)x) data()[m_idSkipLast].setDouble((double)x); } + virtual int getMultipler() const; virtual void getPoints(const TAffine &toTool, PointList &points) const; virtual void getModifiers(const TAffine &toTool, TInputModifier::List &outModifiers) const; - static void transformPoints(const TAffine &aff, PointList &points, int count); + static void transformPoints(const TAffine &aff, PointList &points, int i0, int i1); static void drawReplicatorPoints(const TPointD *points, int count); //! return summary multiplier, or 0 is no replicators found diff --git a/toonz/sources/tnztools/CMakeLists.txt b/toonz/sources/tnztools/CMakeLists.txt index 1518eee..8c77431 100644 --- a/toonz/sources/tnztools/CMakeLists.txt +++ b/toonz/sources/tnztools/CMakeLists.txt @@ -33,6 +33,9 @@ set(HEADERS stylepickertool.h toonzvectorbrushtool.h vectorselectiontool.h + assistants/assistantellipse.h + assistants/assistantline.h + assistants/assistantvanishingpoint.h ../include/tools/RGBpicker.h ../include/tools/cursormanager.h ../include/tools/cursors.h @@ -141,9 +144,11 @@ set(SOURCES modifiers/modifiertest.cpp assistants/guidelineline.cpp assistants/guidelineellipse.cpp - assistants/assistantvanishingpoint.cpp - assistants/assistantline.cpp assistants/assistantellipse.cpp + assistants/assistantfisheye.cpp + assistants/assistantline.cpp + assistants/assistantperspective.cpp + assistants/assistantvanishingpoint.cpp assistants/replicatoraffine.cpp assistants/replicatorgrid.cpp assistants/replicatorjitter.cpp diff --git a/toonz/sources/tnztools/assistant.cpp b/toonz/sources/tnztools/assistant.cpp index a342f5e..40061e4 100644 --- a/toonz/sources/tnztools/assistant.cpp +++ b/toonz/sources/tnztools/assistant.cpp @@ -158,6 +158,8 @@ TAssistantBase::TAssistantBase(TMetaObject &object): m_idPoints("points"), m_idX("x"), m_idY("y"), + m_idZ("z"), + m_idW("w"), m_basePoint() { addProperty( new TBoolProperty(m_idEnabled.str(), getEnabled()) ); @@ -433,11 +435,16 @@ TAssistantBase::getDrawingGridAlpha() const //--------------------------------------------------------------------------------------------------- void -TAssistantBase::drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize, double alpha) const { - double colorBlack[4] = { 0.0, 0.0, 0.0, alpha }; - double colorWhite[4] = { 1.0, 1.0, 1.0, alpha }; +TAssistantBase::drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize, double alpha0, double alpha1) { + double colors[][4] = { + { 1, 1, 1, alpha0 }, + { 1, 1, 1, alpha1 }, + { 0, 0, 0, alpha0 }, + { 0, 0, 0, alpha1 }, + }; - if (drawFlags & DRAW_ERROR) colorBlack[0] = 1; + if (drawFlags & DRAW_ERROR) + colors[2][0] = colors[3][0] = 1; glPushAttrib(GL_ALL_ATTRIB_BITS); tglEnableBlending(); @@ -447,10 +454,12 @@ TAssistantBase::drawSegment(const TPointD &p0, const TPointD &p1, double pixelSi if (k > TConsts::epsilon*TConsts::epsilon) { k = 0.5*pixelSize*lineWidthScale/sqrt(k); d = TPointD(-k*d.y, k*d.x); - glColor4dv(colorWhite); - tglDrawSegment(p0 - d, p1 - d); - glColor4dv(colorBlack); - tglDrawSegment(p0 + d, p1 + d); + glBegin(GL_LINES); + glColor4dv(colors[0]); tglVertex(p0 - d); + glColor4dv(colors[1]); tglVertex(p1 - d); + glColor4dv(colors[2]); tglVertex(p0 + d); + glColor4dv(colors[3]); tglVertex(p1 + d); + glEnd(); } glPopAttrib(); } @@ -458,7 +467,7 @@ TAssistantBase::drawSegment(const TPointD &p0, const TPointD &p1, double pixelSi //--------------------------------------------------------------------------------------------------- void -TAssistantBase::drawMark(const TPointD &p, const TPointD &normal, double pixelSize, double alpha) const { +TAssistantBase::drawMark(const TPointD &p, const TPointD &normal, double pixelSize, double alpha) { TPointD d = normal*5*pixelSize; drawSegment(p - d,p + d, pixelSize, alpha); } @@ -466,7 +475,7 @@ TAssistantBase::drawMark(const TPointD &p, const TPointD &normal, double pixelSi //--------------------------------------------------------------------------------------------------- void -TAssistantBase::drawDot(const TPointD &p, double alpha) const { +TAssistantBase::drawDot(const TPointD &p, double alpha) { double colorBlack[4] = { 0.0, 0.0, 0.0, alpha }; double colorWhite[4] = { 1.0, 1.0, 1.0, alpha }; @@ -491,7 +500,7 @@ TAssistantBase::drawDot(const TPointD &p, double alpha) const { //--------------------------------------------------------------------------------------------------- void -TAssistantBase::drawPoint(const TAssistantPoint &point, double pixelSize) const { +TAssistantBase::drawPoint(const TAssistantPoint &point, double pixelSize) { if (!point.visible) return; double radius = point.radius; @@ -518,26 +527,31 @@ TAssistantBase::drawPoint(const TAssistantPoint &point, double pixelSize) const tglDrawDisk(point.position, radius*pixelSize); } - TPointD crossDx(pixelSize*crossSize, 0.0); - TPointD crossDy(0.0, pixelSize*crossSize); + TPointD crossA(pixelSize*crossSize, 0.0); + TPointD crossB(0.0, pixelSize*crossSize); TPointD gridDx(pixelSize*radius, 0.0); TPointD gridDy(0.0, pixelSize*radius); + bool cross = point.type == TAssistantPoint::CircleCross + || point.type == TAssistantPoint::CircleDiagonalCross; + if (point.type == TAssistantPoint::CircleDiagonalCross) + { crossA.y = crossB.y; crossB.x = crossA.x; } + // back line tglEnableLineSmooth(true, 2.0*width*lineWidthScale); glColor4dv(colorWhite); - if (point.type == TAssistantPoint::CircleCross) { - tglDrawSegment(point.position - crossDx, point.position + crossDx); - tglDrawSegment(point.position - crossDy, point.position + crossDy); + if (cross) { + tglDrawSegment(point.position - crossA, point.position + crossA); + tglDrawSegment(point.position - crossB, point.position + crossB); } tglDrawCircle(point.position, radius*pixelSize); // front line glLineWidth(width * lineWidthScale); glColor4dv(colorBlack); - if (point.type == TAssistantPoint::CircleCross) { - tglDrawSegment(point.position - crossDx, point.position + crossDx); - tglDrawSegment(point.position - crossDy, point.position + crossDy); + if (cross) { + tglDrawSegment(point.position - crossA, point.position + crossA); + tglDrawSegment(point.position - crossB, point.position + crossB); } tglDrawCircle(point.position, radius*pixelSize); @@ -568,7 +582,7 @@ TAssistantBase::drawPoint(const TAssistantPoint &point, double pixelSize) const //--------------------------------------------------------------------------------------------------- void -TAssistantBase::drawIndex(const TPointD &p, int index, bool selected, double pixelSize) const { +TAssistantBase::drawIndex(const TPointD &p, int index, bool selected, double pixelSize) { static const int segments[7][4] = { { 0, 2, 1, 2 }, // A { 1, 1, 1, 2 }, // B + A + @@ -720,28 +734,67 @@ TAssistant::calcPerspectiveStep( double x0, double x1, double x2, - double &outK, - double &outMin, - double &outMax ) + double &outW, + double &outMinI, + double &outMaxI ) { - outK = outMin = outMax = 0.0; - - double dx1 = x1 - x0; - double dx2 = x2 - x0; - if (fabs(dx1) <= TConsts::epsilon) return false; - if (fabs(dx2) <= TConsts::epsilon) return false; - if ((dx1 < 0.0) != (dx2 < 0.0)) dx2 = -dx2; - if (fabs(dx2 - dx1) <= minStep) return false; - if (fabs(dx2) < fabs(dx1)) std::swap(dx1, dx2); - - if (x0 <= minX + TConsts::epsilon && dx1 < 0.0) return false; - if (x0 >= maxX - TConsts::epsilon && dx1 > 0.0) return false; - - outK = dx2/dx1; - double minI = log(minStep/fabs(dx1*(1.0 - 1.0/outK)))/log(outK); - outMin = dx1*pow(outK, floor(minI - TConsts::epsilon)); - if (fabs(outMin) < TConsts::epsilon) return false; - outMax = (dx1 > 0.0 ? maxX : minX) - x0; + outW = outMinI = outMaxI = 0; + if (!(minStep > TConsts::epsilon)) return false; + if (!(minX + TConsts::epsilon < maxX)) return false; + + minX -= x0; + maxX -= x0; + x1 -= x0; + x2 -= x0; + + // check and fix input + if (!(fabs(x1) > TConsts::epsilon)) return false; + if (!(fabs(x2) > TConsts::epsilon)) return false; + if (!(fabs(x1 - x2) > TConsts::epsilon)) return false; + if ((x1 < 0) != (x2 < 0)) x2 = -x2; + if (fabs(x2) < fabs(x1)) std::swap(x1, x2); + + // check if bounds is behind the horizon + if (x1 < 0 && !(minX < -TConsts::epsilon)) return false; + if (x1 > 0 && !(maxX > TConsts::epsilon)) return false; + + // calc W and initial bounds + double w = 1/x2 - 1/x1; + double d = sqrt(fabs(w/minStep)); + if (x1 < 0) d = -d; + double i0 = (d - 1)/w; + double i1 = -1/w; + + // min/max bounds + if (x1 < 0) { + if (maxX < -TConsts::epsilon) + i0 = std::max( i0, (1 - maxX)/(maxX*w) ); + i1 = std::min( i1, (1 - minX)/(minX*w) ); + } else { + if (minX > TConsts::epsilon) + i0 = std::max( i0, (1 - minX)/(minX*w) ); + i1 = std::min( i1, (1 - maxX)/(maxX*w) ); + } + + // i must be iterable (i < i + 1) + const double bound = 1e10; + i0 = i0 > -bound ? (i0 < bound ? i0 : bound) : -bound; + i1 = i1 > -bound ? (i1 < bound ? i1 : bound) : -bound; + + // tune beginning of grid to place it axact to grid points + // must be: i0 + integer == ig + double ig = (1 - x1)/(x1*w); + double frac = ig - floor(ig); + i0 = ceil(i0 - frac + TConsts::epsilon) + frac; + i1 -= TConsts::epsilon; + + // restrict count + if (i1 - i0 > 10000) return false; + if (!(i0 < i1)) return false; + + outW = w; + outMinI = i0; + outMaxI = i1; return true; } diff --git a/toonz/sources/tnztools/assistants/assistantellipse.cpp b/toonz/sources/tnztools/assistants/assistantellipse.cpp index 29e2060..dbca015 100644 --- a/toonz/sources/tnztools/assistants/assistantellipse.cpp +++ b/toonz/sources/tnztools/assistants/assistantellipse.cpp @@ -1,528 +1,666 @@ // TnzTools includes -#include <tools/assistant.h> -#include <tools/assistants/guidelineline.h> -#include <tools/assistants/guidelineellipse.h> - -// TnzCore includes -#include <tgl.h> - -// std includes -#include <limits> +#include "assistantellipse.h" //***************************************************************************************** // TAssistantEllipse implementation //***************************************************************************************** -class TAssistantEllipse final : public TAssistant { - Q_DECLARE_TR_FUNCTIONS(TAssistantEllipse) -public: - const TStringId m_idRestricktA; - const TStringId m_idRestricktB; - const TStringId m_idRepeat; - const TStringId m_idGrid; - const TStringId m_idPerspective; - -protected: - TAssistantPoint &m_center; - TAssistantPoint &m_a; - TAssistantPoint &m_b; - TAssistantPoint &m_grid0; - TAssistantPoint &m_grid1; - -public: - TAssistantEllipse(TMetaObject &object): - TAssistant(object), - m_idRestricktA("restrictA"), - m_idRestricktB("restrictB"), - m_idRepeat("repeat"), - m_idGrid("grid"), - m_idPerspective("perspective"), - m_center( addPoint("center", TAssistantPoint::CircleCross) ), - m_a( addPoint("a", TAssistantPoint::CircleFill, TPointD(100.0, 0.0)) ), - m_b( addPoint("b", TAssistantPoint::Circle, TPointD(0.0, 50.0)) ), - m_grid0( addPoint("grid0", TAssistantPoint::CircleDoubleDots, TPointD( 0.0,-50.0)) ), - m_grid1( addPoint("grid1", TAssistantPoint::CircleDots, TPointD( 25.0,-75.0)) ) - { - addProperty( new TBoolProperty(m_idRestricktA.str(), getRestrictA()) ); - addProperty( new TBoolProperty(m_idRestricktB.str(), getRestrictB()) ); - addProperty( new TBoolProperty(m_idRepeat.str(), getRepeat()) ); - addProperty( new TBoolProperty(m_idGrid.str(), getGrid()) ); - addProperty( new TBoolProperty(m_idPerspective.str(), getPerspective()) ); - } - - static QString getLocalName() - { return tr("Ellipse"); } - - void updateTranslation() const override { - TAssistant::updateTranslation(); - setTranslation(m_idRestricktA, tr("Restrict A")); - setTranslation(m_idRestricktB, tr("Restrict B")); - setTranslation(m_idRepeat, tr("Repeat")); - setTranslation(m_idGrid, tr("Grid")); - setTranslation(m_idPerspective, tr("Perspective")); - } - - inline bool getRestrictA() const - { return data()[m_idRestricktA].getBool(); } - inline bool getRestrictB() const - { return data()[m_idRestricktB].getBool(); } - inline bool getRepeat() const - { return data()[m_idRepeat].getBool(); } - inline bool getGrid() const - { return data()[m_idGrid].getBool(); } - inline bool getPerspective() const - { return data()[m_idPerspective].getBool(); } - - void onDataChanged(const TVariant &value) override { - TAssistant::onDataChanged(value); - m_grid0.visible = m_grid1.visible = getGrid(); +TAssistantEllipse::TAssistantEllipse(TMetaObject &object): + TAssistant(object), + m_idCircle("circle"), + m_idRestrictA("restrictA"), + m_idRestrictB("restrictB"), + m_idRepeat("repeat"), + m_idGrid("grid"), + m_idPerspective("perspective"), + m_idPerspectiveDepth("perspectiveDepth"), + m_center( addPoint("center", TAssistantPoint::CircleCross) ), + m_a( addPoint("a", TAssistantPoint::CircleFill, TPointD(150, 0)) ), + m_b( addPoint("b", TAssistantPoint::Circle, TPointD( 0, 100)) ), + m_grid0( addPoint("grid0", TAssistantPoint::CircleDoubleDots, TPointD( 10, -30)) ), + m_grid1( addPoint("grid1", TAssistantPoint::CircleDots, TPointD( 60, -60)) ) +{ + addProperty( new TBoolProperty(m_idCircle.str(), getCircle()) ); + addProperty( new TBoolProperty(m_idRestrictA.str(), getRestrictA()) ); + addProperty( new TBoolProperty(m_idRestrictB.str(), getRestrictB()) ); + addProperty( new TBoolProperty(m_idRepeat.str(), getRepeat()) ); + addProperty( new TBoolProperty(m_idGrid.str(), getGrid()) ); + addProperty( new TBoolProperty(m_idPerspective.str(), getPerspective()) ); + addProperty( new TBoolProperty(m_idPerspectiveDepth.str(), getPerspectiveDepth()) ); +} + + +QString TAssistantEllipse::getLocalName() + { return tr("Ellipse"); } + + +void TAssistantEllipse::updateTranslation() const { + TAssistant::updateTranslation(); + setTranslation(m_idCircle, tr("Circle")); + setTranslation(m_idRestrictA, tr("Restrict A")); + setTranslation(m_idRestrictB, tr("Restrict B")); + setTranslation(m_idRepeat, tr("Repeat")); + setTranslation(m_idGrid, tr("Grid")); + setTranslation(m_idPerspective, tr("Perspective")); + setTranslation(m_idPerspectiveDepth, tr("Depth")); +} + + +void TAssistantEllipse::onDataChanged(const TVariant &value) { + TAssistant::onDataChanged(value); + m_grid0.visible = m_grid1.visible = getGrid(); + if (getCircle() == m_b.visible) { + m_b.visible = !getCircle(); + if (!m_b.visible) + fixBAndGrid(m_center.position, m_a.position, m_b.position); } - -private: - void fixBAndGgid1(const TPointD &previousCenter, const TPointD &previousA) { - TPointD dx = previousA - previousCenter; - double l = norm2(dx); - if (l <= TConsts::epsilon*TConsts::epsilon) return; - dx = dx*(1.0/sqrt(l)); - TPointD dy(-dx.y, dx.x); - - double r2 = dy*(m_b.position - m_center.position); - - TPointD g1 = m_grid1.position - m_grid0.position; - g1 = TPointD(dx*g1, dy*g1); - - dx = m_a.position - m_center.position; - l = norm2(dx); - if (l <= TConsts::epsilon*TConsts::epsilon) return; - dx = dx*(1.0/sqrt(l)); - dy = TPointD(-dx.y, dx.x); - - m_grid1.position = m_grid0.position + dx*g1.x + dy*g1.y; - m_b.position = m_center.position + dy*r2; +} + + +void TAssistantEllipse::fixBAndGrid( + TPointD prevCenter, + TPointD prevA, + TPointD prevB ) +{ + const TPointD ¢er = m_center.position; + TPointD da0 = prevA - prevCenter; + TPointD da1 = m_a.position - center; + double la0 = norm2(da0); + double la1 = norm2(da1); + if (!(la0 > TConsts::epsilon) || !(la1 > TConsts::epsilon)) + return; + + TPointD db = m_b.position - center; + TPointD dp0 = TPointD(-da0.y, da0.x); + TPointD dp1 = TPointD(-da1.y, da1.x); + if (getCircle()) { + m_b.position = center + (db*dp0 < 0 ? -dp1 : dp1); + } else { + m_b.position = db*dp0/la0*dp1 + center; } -public: - void onMovePoint(TAssistantPoint &point, const TPointD &position) override { - TPointD previousCenter = m_center.position; - TPointD previousA = m_a.position; - point.position = position; - if (&point == &m_center) { - m_a.position += m_center.position - previousCenter; - m_b.position += m_center.position - previousCenter; - } else - if (&point == &m_a || &point == &m_b) - fixBAndGgid1(previousCenter, previousA); + TPointD db0 = prevB - prevCenter; + TPointD db1 = m_b.position - center; + double lb0 = norm2(db0); + double lb1 = norm2(db1); + if (!(lb0 > TConsts::epsilon) || !(lb1 > TConsts::epsilon)) + return; + + TPointD dg0 = m_grid0.position - center; + TPointD dg1 = m_grid1.position - center; + m_grid0.position = dg0*da0/la0*da1 + dg0*db0/lb0*db1 + center; + m_grid1.position = dg1*da0/la0*da1 + dg1*db0/lb0*db1 + center; +} + + +void TAssistantEllipse::onMovePoint(TAssistantPoint &point, const TPointD &position) { + TPointD prevCenter = m_center.position; + TPointD prevA = m_a.position; + TPointD prevB = m_b.position; + point.position = position; + if (&point == &m_center) { + TPointD d = m_center.position - prevCenter; + m_a.position += d; + m_b.position += d; + m_grid0.position += d; + m_grid1.position += d; + } else + if (&point == &m_a || &point == &m_b) { + fixBAndGrid(prevCenter, prevA, prevB); } - - TAffine calcEllipseMatrix() const { - TPointD da = m_a.position - m_center.position; - TPointD db = m_b.position - m_center.position; - double r1 = norm(da); - if (r1 <= TConsts::epsilon) return TAffine::zero(); - double r2 = fabs( (rotate90(da)*db)*(1.0/r1) ); - if (r2 <= TConsts::epsilon) return TAffine::zero(); - return TAffine::translation(m_center.position) - * TAffine::rotation(atan(da)) - * TAffine::scale(r1, r2); +} + + +TAffine TAssistantEllipse::calcEllipseMatrix() const { + TPointD da = m_a.position - m_center.position; + TPointD db = m_b.position - m_center.position; + double r1 = norm(da); + if (r1 <= TConsts::epsilon) return TAffine::zero(); + double r2 = fabs( (rotate90(da)*db)*(1.0/r1) ); + if (r2 <= TConsts::epsilon) return TAffine::zero(); + return TAffine::translation(m_center.position) + * TAffine::rotation(atan(da)) + * TAffine::scale(r1, r2); +} + + +void TAssistantEllipse::getGuidelines( + const TPointD &position, + const TAffine &toTool, + TGuidelineList &outGuidelines ) const +{ + bool restrictA = getRestrictA(); + bool restrictB = getRestrictB(); + bool repeat = getRepeat(); + + TAffine matrix = calcEllipseMatrix(); + if (matrix.isZero()) return; + if (!restrictA && restrictB) { + std::swap(matrix.a11, matrix.a12); + std::swap(matrix.a21, matrix.a22); + std::swap(restrictA, restrictB); } - void getGuidelines( - const TPointD &position, - const TAffine &toTool, - TGuidelineList &outGuidelines ) const override - { - bool restrictA = getRestrictA(); - bool restrictB = getRestrictB(); - bool repeat = getRepeat(); - - TAffine matrix = calcEllipseMatrix(); - if (matrix.isZero()) return; - if (!restrictA && restrictB) { - std::swap(matrix.a11, matrix.a12); - std::swap(matrix.a21, matrix.a22); - std::swap(restrictA, restrictB); + matrix = toTool*matrix; + TAffine matrixInv = matrix.inv(); + + if (restrictA && restrictB) { + // ellipse + outGuidelines.push_back(TGuidelineP( + new TGuidelineEllipse( + getEnabled(), + getMagnetism(), + matrix, + matrixInv ))); + } else + if (!restrictA && !restrictB) { + // scaled ellipse + TPointD p = matrixInv*position; + double l = norm(p); + outGuidelines.push_back(TGuidelineP( + new TGuidelineEllipse( + getEnabled(), + getMagnetism(), + matrix * TAffine::scale(l) ))); + } else { // restrictA + TPointD p = matrixInv*position; + if (repeat) { + double ox = round(0.5*p.x)*2.0; + p.x -= ox; + matrix *= TAffine::translation(ox, 0.0); } - matrix = toTool*matrix; - TAffine matrixInv = matrix.inv(); - - if (restrictA && restrictB) { - // ellipse + // scale by Y + if (p.x <= TConsts::epsilon - 1.0) { + // line x = -1 outGuidelines.push_back(TGuidelineP( - new TGuidelineEllipse( + new TGuidelineInfiniteLine( getEnabled(), getMagnetism(), - matrix, - matrixInv ))); + matrix*TPointD(-1.0, 0.0), + matrix*TPointD(-1.0, 1.0) ))); } else - if (!restrictA && !restrictB) { - // scaled ellipse - TPointD p = matrixInv*position; - double l = norm(p); + if (p.x >= 1.0 - TConsts::epsilon) { + // line x = 1 + outGuidelines.push_back(TGuidelineP( + new TGuidelineInfiniteLine( + getEnabled(), + getMagnetism(), + matrix*TPointD(1.0, 0.0), + matrix*TPointD(1.0, 1.0) ))); + } else { + // ellipse scaled by Y + double k = fabs(p.y/sqrt(1.0 - p.x*p.x)); outGuidelines.push_back(TGuidelineP( new TGuidelineEllipse( getEnabled(), getMagnetism(), - matrix * TAffine::scale(l) ))); - } else { // restrictA - TPointD p = matrixInv*position; - if (repeat) { - double ox = round(0.5*p.x)*2.0; - p.x -= ox; - matrix *= TAffine::translation(ox, 0.0); - } - - // scale by Y - if (p.x <= TConsts::epsilon - 1.0) { - // line x = -1 - outGuidelines.push_back(TGuidelineP( - new TGuidelineInfiniteLine( - getEnabled(), - getMagnetism(), - matrix*TPointD(-1.0, 0.0), - matrix*TPointD(-1.0, 1.0) ))); - } else - if (p.x >= 1.0 - TConsts::epsilon) { - // line x = 1 - outGuidelines.push_back(TGuidelineP( - new TGuidelineInfiniteLine( - getEnabled(), - getMagnetism(), - matrix*TPointD(1.0, 0.0), - matrix*TPointD(1.0, 1.0) ))); - } else { - // ellipse scaled by Y - double k = fabs(p.y/sqrt(1.0 - p.x*p.x)); - outGuidelines.push_back(TGuidelineP( - new TGuidelineEllipse( - getEnabled(), - getMagnetism(), - matrix * TAffine::scale(1.0, k) ))); - } + matrix * TAffine::scale(1.0, k) ))); } } - -private: - void drawEllipseRanges( - const TAngleRangeSet &ranges, - const TAffine &ellipseMatrix, - const TAffine &screenMatrixInv, - double pixelSize, - double alpha ) const - { - assert(ranges.check()); - TAngleRangeSet actualRanges(ranges); - const TRectD oneBox(-1.0, -1.0, 1.0, 1.0); - if (!TGuidelineEllipse::truncateEllipse(actualRanges, ellipseMatrix.inv()*screenMatrixInv, oneBox)) - return; - assert(actualRanges.check()); - - int segments = TGuidelineEllipse::calcSegmentsCount(ellipseMatrix, pixelSize); - double da = M_2PI/segments; - double s = sin(da); - double c = cos(da); - - for(TAngleRangeSet::Iterator i(actualRanges); i; ++i) { - double a0 = i.d0(); - double a1 = i.d1greater(); - int cnt = (int)floor((a1 - a0)/da); - TPointD r(cos(a0), sin(a0)); - TPointD p0 = ellipseMatrix*r; - for(int j = 0; j < cnt; ++j) { - r = TPointD(r.x*c - r.y*s, r.y*c + r.x*s); - TPointD p1 = ellipseMatrix*r; - drawSegment(p0, p1, pixelSize, alpha); - p0 = p1; - } - drawSegment(p0, ellipseMatrix*TPointD(cos(a1), sin(a1)), pixelSize, alpha); +} + + +void TAssistantEllipse::drawEllipseRanges( + const TAngleRangeSet &ranges, + const TAffine &ellipseMatrix, + const TAffine &screenMatrixInv, + double pixelSize, + double alpha ) +{ + assert(ranges.check()); + TAngleRangeSet actualRanges(ranges); + const TRectD oneBox(-1.0, -1.0, 1.0, 1.0); + if (!TGuidelineEllipse::truncateEllipse(actualRanges, ellipseMatrix.inv()*screenMatrixInv, oneBox)) + return; + assert(actualRanges.check()); + + int segments = TGuidelineEllipse::calcSegmentsCount(ellipseMatrix, pixelSize); + double da = M_2PI/segments; + double s = sin(da); + double c = cos(da); + + for(TAngleRangeSet::Iterator i(actualRanges); i; ++i) { + double a0 = i.d0(); + double a1 = i.d1greater(); + int cnt = (int)floor((a1 - a0)/da); + TPointD r(cos(a0), sin(a0)); + TPointD p0 = ellipseMatrix*r; + for(int j = 0; j < cnt; ++j) { + r = TPointD(r.x*c - r.y*s, r.y*c + r.x*s); + TPointD p1 = ellipseMatrix*r; + drawSegment(p0, p1, pixelSize, alpha); + p0 = p1; } + drawSegment(p0, ellipseMatrix*TPointD(cos(a1), sin(a1)), pixelSize, alpha); } - - void drawEllipse( - const TAffine &ellipseMatrix, - const TAffine &screenMatrixInv, - double pixelSize, - double alpha ) const - { drawEllipseRanges(TAngleRangeSet(true), ellipseMatrix, screenMatrixInv, pixelSize, alpha); } - - void drawRuler(const TAffine &ellipseMatrix, double pixelSize) const { - double minStep = 10.0*pixelSize; - double alpha = getDrawingAlpha(); - - TAffine em = ellipseMatrix; - TAffine ellipseMatrixInv = ellipseMatrix.inv(); - TPointD g0 = ellipseMatrixInv*m_grid0.position; - TPointD g1 = ellipseMatrixInv*m_grid1.position; - if (norm2(g0) <= TConsts::epsilon*TConsts::epsilon) return; - if (norm2(g1) <= TConsts::epsilon*TConsts::epsilon) return; - double ga0 = atan(g0); - double ga1 = atan(g1); - - // x and y radiuses - TPointD r( norm2(TPointD(em.a11, em.a21)), norm2(TPointD(em.a12, em.a22)) ); - double avgR = 0.5*(r.x + r.y); - if (avgR <= TConsts::epsilon*TConsts::epsilon) return; - avgR = sqrt(avgR); - double actualMinStep = minStep/avgR; - r.x = sqrt(r.x); - r.y = sqrt(r.y); - - // remove radiuses from ellipse matrix - double rkx = r.x > TConsts::epsilon ? 1.0/r.x : 0.0; - double rky = r.y > TConsts::epsilon ? 1.0/r.y : 0.0; - em.a11 *= rkx; em.a21 *= rkx; - em.a12 *= rky; em.a22 *= rky; - - if (getPerspective()) { - // draw perspective - if (ga0 < 0.0) { if (ga1 > 0.0) ga1 -= M_2PI; } - else { if (ga1 < 0.0) ga1 += M_2PI; } - double k = 0.0, begin = 0.0, end = 0.0; - if (!calcPerspectiveStep(actualMinStep, 0.0, M_2PI, 0.0, fabs(ga0), ga1, k, begin, end)) return; - for(double a = begin; fabs(a) < fabs(end); a *= k) { - TPointD p( cos(a), (ga0 < 0.0 ? -1.0 : 1.0)*sin(a) ); - TPointD n( p.x*r.y, p.y*r.x ); // perp to allipse - double nl2 = norm2(n); - if (nl2 > TConsts::epsilon*TConsts::epsilon) { - p.x *= r.x; - p.y *= r.y; - n = n*(1.0/sqrt(nl2)); - drawMark(em*p, em.transformDirection(n), pixelSize, alpha); - } +} + + +void TAssistantEllipse::drawRuler( + const TAffine &ellipseMatrix, + const TPointD &grid0, + const TPointD &grid1, + bool perspective, + double alpha +) { + double pixelSize = sqrt(tglGetPixelSize2()); + double minStep = (perspective ? 5 : 10)*pixelSize; + + TAffine em = ellipseMatrix; + TAffine ellipseMatrixInv = ellipseMatrix.inv(); + TPointD g0 = ellipseMatrixInv * grid0; + TPointD g1 = ellipseMatrixInv * grid1; + if (norm2(g0) <= TConsts::epsilon*TConsts::epsilon) return; + if (norm2(g1) <= TConsts::epsilon*TConsts::epsilon) return; + double ga0 = atan(g0); + double ga1 = atan(g1); + + // x and y radiuses + TPointD r( norm2(TPointD(em.a11, em.a21)), norm2(TPointD(em.a12, em.a22)) ); + double avgR = 0.5*(r.x + r.y); + if (avgR <= TConsts::epsilon*TConsts::epsilon) return; + avgR = sqrt(avgR); + double actualMinStep = minStep/avgR; + r.x = sqrt(r.x); + r.y = sqrt(r.y); + + // remove radiuses from ellipse matrix + double rkx = r.x > TConsts::epsilon ? 1.0/r.x : 0.0; + double rky = r.y > TConsts::epsilon ? 1.0/r.y : 0.0; + em.a11 *= rkx; em.a21 *= rkx; + em.a12 *= rky; em.a22 *= rky; + + if (perspective) { + // draw perspective + if (ga0 < 0.0) { if (ga1 > 0.0) ga1 -= M_2PI; } + else { if (ga1 < 0.0) ga1 += M_2PI; } + double w, i0, i1; + double bound0 = ga0 < 0 ? -M_2PI : 0; + double bound1 = ga0 < 0 ? 0 : M_2PI; + if (!calcPerspectiveStep(actualMinStep, bound0, bound1, 0, ga0, ga1, w, i0, i1)) return; + for(double i = i0; i < i1; i += 1) { + double a = 1/(i*w + 1); + TPointD p( cos(a), sin(a) ); + TPointD n( p.x*r.y, p.y*r.x ); // perp to allipse + double nl2 = norm2(n); + if (nl2 > TConsts::epsilon*TConsts::epsilon) { + p.x *= r.x; + p.y *= r.y; + n = n*(1.0/sqrt(nl2)); + drawMark(em*p, em.transformDirection(n), pixelSize, alpha); } - } else { - // draw linear - double da = ga1 - ga0; - if (da < 0.0) { da = -da; std::swap(ga0, ga1); } - if (ga1 - ga0 > M_PI) { da = M_2PI - da; std::swap(ga0, ga1); } - if (da < actualMinStep) return; - for(double a = ga0 - floor(M_PI/da)*da; a < ga0 + M_PI; a += da) { - TPointD p( cos(a), sin(a) ); - TPointD n( p.x*r.y, p.y*r.x ); // perp to allipse - double nl2 = norm2(n); - if (nl2 > TConsts::epsilon*TConsts::epsilon) { - p.x *= r.x; - p.y *= r.y; - n = n*(1.0/sqrt(nl2)); - drawMark(em*p, em.transformDirection(n), pixelSize, alpha); - } + } + } else { + // draw linear + double da = ga1 - ga0; + if (da < 0.0) { da = -da; std::swap(ga0, ga1); } + if (ga1 - ga0 > M_PI) { da = M_2PI - da; std::swap(ga0, ga1); } + if (da < actualMinStep) return; + for(double a = ga0 - floor(M_PI/da)*da; a < ga0 + M_PI; a += da) { + TPointD p( cos(a), sin(a) ); + TPointD n( p.x*r.y, p.y*r.x ); // perp to allipse + double nl2 = norm2(n); + if (nl2 > TConsts::epsilon*TConsts::epsilon) { + p.x *= r.x; + p.y *= r.y; + n = n*(1.0/sqrt(nl2)); + drawMark(em*p, em.transformDirection(n), pixelSize, alpha); } } } +} + + +void TAssistantEllipse::drawConcentricGrid( + const TAffine &ellipseMatrix, + const TPointD &grid0, + const TPointD &grid1, + bool perspective, + double alpha ) +{ + TAffine4 modelview, projection; + glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a); + glGetDoublev(GL_PROJECTION_MATRIX, projection.a); + TAffine screenMatrix = (projection*modelview).get2d(); + TAffine screenMatrixInv = screenMatrix.inv(); + + double pixelSize = sqrt(tglGetPixelSize2()); + double minStep = (perspective ? 2.5 : 10.0)*pixelSize; + TAffine ellipseMatrixInv = ellipseMatrix.inv(); + + // calculate bounds + TAffine matrixInv = ellipseMatrixInv * screenMatrixInv; + TPointD o = matrixInv * TPointD(-1.0, -1.0); + TPointD dx = matrixInv.transformDirection( TPointD(2.0, 0.0) ); + TPointD dy = matrixInv.transformDirection( TPointD(0.0, 2.0) ); + double max = 0.0; + double min = std::numeric_limits<double>::infinity(); + + // distance to points + TPointD corners[] = { o, o+dx, o+dx+dy, o+dy }; + for(int i = 0; i < 4; ++i) { + double k = norm(corners[i]); + if (k < min) min = k; + if (k > max) max = k; + } - void drawConcentricGrid( - const TAffine &ellipseMatrix, - const TAffine &screenMatrixInv, - double pixelSize ) const - { - double minStep = 20.0*pixelSize; - double alpha = getDrawingGridAlpha(); - TAffine ellipseMatrixInv = ellipseMatrix.inv(); - - // calculate bounds - TAffine matrixInv = ellipseMatrixInv * screenMatrixInv; - TPointD o = matrixInv * TPointD(-1.0, -1.0); - TPointD dx = matrixInv.transformDirection( TPointD(2.0, 0.0) ); - TPointD dy = matrixInv.transformDirection( TPointD(0.0, 2.0) ); - double max = 0.0; - double min = std::numeric_limits<double>::infinity(); - - // distance to points - TPointD corners[] = { o, o+dx, o+dx+dy, o+dy }; - for(int i = 0; i < 4; ++i) { - double k = norm(corners[i]); - if (k < min) min = k; - if (k > max) max = k; - } - - // distance to sides - TPointD lines[] = { dx, dy, -1.0*dx, -1.0*dy }; - int positive = 0, negative = 0; - for(int i = 0; i < 4; ++i) { - double len2 = norm2(lines[i]); - if (len2 <= TConsts::epsilon*TConsts::epsilon) continue; - double k = (corners[i]*rotate90(lines[i]))/sqrt(len2); - if (k > TConsts::epsilon) ++positive; - if (k < TConsts::epsilon) ++negative; - double l = -(corners[i]*lines[i]); - if (l <= TConsts::epsilon || l >= len2 - TConsts::epsilon) continue; - k = fabs(k); - if (k < min) min = k; - if (k > max) max = k; - } - - // if center is inside bounds - if (min < 0.0 || positive == 0 || negative == 0) min = 0.0; - if (max <= min) return; + // distance to sides + TPointD lines[] = { dx, dy, -1.0*dx, -1.0*dy }; + int positive = 0, negative = 0; + for(int i = 0; i < 4; ++i) { + double len2 = norm2(lines[i]); + if (len2 <= TConsts::epsilon*TConsts::epsilon) continue; + double k = (corners[i]*rotate90(lines[i]))/sqrt(len2); + if (k > TConsts::epsilon) ++positive; + if (k < TConsts::epsilon) ++negative; + double l = -(corners[i]*lines[i]); + if (l <= TConsts::epsilon || l >= len2 - TConsts::epsilon) continue; + k = fabs(k); + if (k < min) min = k; + if (k > max) max = k; + } - // draw - const TAffine &em = ellipseMatrix; - double r = sqrt(0.5*(norm2(TPointD(em.a11, em.a21)) + norm2(TPointD(em.a12, em.a22)))); - double actualMinStep = minStep/r; - double gs0 = norm(ellipseMatrixInv*m_grid0.position); - double gs1 = norm(ellipseMatrixInv*m_grid1.position); - if (gs0 <= TConsts::epsilon*TConsts::epsilon) return; - if (gs1 <= TConsts::epsilon*TConsts::epsilon) return; - - if (getPerspective()) { - // draw perspective - double k = 0.0, begin = 0.0, end = 0.0; - if (!calcPerspectiveStep(actualMinStep, min, max, 0.0, gs0, gs1, k, begin, end)) return; - for(double x = begin; fabs(x) < fabs(end); x *= k) - drawEllipse(ellipseMatrix * TAffine::scale(x), screenMatrixInv, pixelSize, alpha); - } else { - // draw linear - double dx = fabs(gs1 - gs0); - if (dx*r < minStep) return; - for(double x = gs0 + ceil((min - gs0)/dx)*dx; x < max; x += dx) - drawEllipse(ellipseMatrix * TAffine::scale(x), screenMatrixInv, pixelSize, alpha); + // if center is inside bounds + if (min < 0.0 || positive == 0 || negative == 0) min = 0.0; + if (max <= min) return; + + // draw + const TAffine &em = ellipseMatrix; + double r = sqrt(std::min( norm2(TPointD(em.a11, em.a21)), norm2(TPointD(em.a12, em.a22)) )); + double actualMinStep = minStep/r; + double gs0 = norm(ellipseMatrixInv*grid0); + double gs1 = norm(ellipseMatrixInv*grid1); + if (gs0 <= TConsts::epsilon*TConsts::epsilon) return; + if (gs1 <= TConsts::epsilon*TConsts::epsilon) return; + + if (perspective) { + // draw perspective + double w, i0, i1; + actualMinStep /= 2; + if (!calcPerspectiveStep(actualMinStep, min, max, 0, gs0, gs1, w, i0, i1)) return; + for(double i = i0; i < i1; i += 1) { + double x = 1/(i*w + 1); + + double curStep = fabs(w*x*x); + double curAlpha = (curStep - actualMinStep)/actualMinStep; + if (curAlpha < 0) continue; + if (curAlpha > 1) curAlpha = 1; + + drawEllipse(ellipseMatrix * TAffine::scale(x), screenMatrixInv, pixelSize, alpha*curAlpha); } + } else { + // draw linear + double dx = fabs(gs1 - gs0); + if (dx*r < minStep) return; + for(double x = gs0 + ceil((min - gs0)/dx)*dx; x < max; x += dx) + drawEllipse(ellipseMatrix * TAffine::scale(x), screenMatrixInv, pixelSize, alpha); } - - void drawParallelGrid( - const TAffine &ellipseMatrix, - const TAffine &screenMatrixInv, - double pixelSize ) const - { - double minStep = 10.0*pixelSize; - double alpha = getDrawingGridAlpha(); - TAffine ellipseMatrixInv = ellipseMatrix.inv(); - - const TAffine &em = ellipseMatrix; - double r = sqrt(0.5*(norm2(TPointD(em.a11, em.a21)) + norm2(TPointD(em.a12, em.a22)))); - double actualMinStep = minStep/r; - TPointD g0 = ellipseMatrixInv*m_grid0.position; - TPointD g1 = ellipseMatrixInv*m_grid1.position; - if (getRepeat()) - { g0.x -= round(0.5*g0.x)*2.0; g1.x -= round(0.5*g1.x)*2.0; } - if (fabs(g0.x) >= 1.0 - TConsts::epsilon) return; - if (fabs(g1.x) >= 1.0 - TConsts::epsilon) return; - double gs0 = g0.y/sqrt(1.0 - g0.x*g0.x); - double gs1 = g1.y/sqrt(1.0 - g1.x*g1.x); - if (fabs(gs0) >= 1.0 - TConsts::epsilon) return; - if (fabs(gs1) >= 1.0 - TConsts::epsilon) return; - - TAngleRangeSet ranges; - ranges.add( TAngleRangeSet::fromDouble(0.0), TAngleRangeSet::fromDouble(M_PI) ); - - if (getPerspective()) { - // draw perspective (actually angular) - double k = 0.0, begin = 0.0, end = 0.0; - double a0 = asin(gs0); - double a1 = asin(gs1); - double da = fabs(a1 - a0); - if (fabs(sin(da)) < 2.0*actualMinStep) return; - for(double a = a0 + ceil((-M_PI_2 - a0)/da)*da; a < M_PI_2; a += da) - drawEllipseRanges( - ranges, - ellipseMatrix*TAffine::scale(a < 0.0 ? -1.0 : 1.0, sin(a)), - screenMatrixInv, - pixelSize, - alpha ); - } else { - // draw linear - double dx = fabs(gs1 - gs0); - if (dx < actualMinStep) return; - for(double x = gs0 + ceil((-1.0 - gs0)/dx)*dx; x < 1.0; x += dx) - drawEllipseRanges( - ranges, - ellipseMatrix*TAffine::scale(x < 0.0 ? -1.0 : 1.0, x), - screenMatrixInv, - pixelSize, - alpha ); +} + + +void TAssistantEllipse::drawParallelGrid( + const TAffine &ellipseMatrix, + const TPointD &grid0, + const TPointD &grid1, + const TPointD *bound0, + const TPointD *bound1, + bool perspective, + bool perspectiveDepth, + bool repeat, + double alpha ) +{ + struct { + const bool repeat; + const TAffine ellipseMatrixInv; + double convert(const TPointD &p) { + TPointD pp = ellipseMatrixInv*p; + if (repeat) + pp.x -= round(pp.x/2)*2; + if (!(fabs(pp.x) < 1 - TConsts::epsilon)) + return pp.y < 0 ? -INFINITY : INFINITY; + return pp.y/sqrt(1 - pp.x*pp.x); } + } helper = { repeat, ellipseMatrix.inv() }; + + + TAffine4 modelview, projection; + glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a); + glGetDoublev(GL_PROJECTION_MATRIX, projection.a); + TAffine screenMatrix = (projection*modelview).get2d(); + TAffine screenMatrixInv = screenMatrix.inv(); + + double pixelSize = sqrt(tglGetPixelSize2()); + double minStep = (perspective ? 5 : 10)*pixelSize; + + const TAffine &em = ellipseMatrix; + double r = sqrt(0.5*(norm2(TPointD(em.a11, em.a21)) + norm2(TPointD(em.a12, em.a22)))); + double actualMinStep = minStep/r; + + // convert grid + double gs0 = helper.convert(grid0); + double gs1 = helper.convert(grid1); + if (!(fabs(gs0) < 1 - TConsts::epsilon)) return; + if (!(fabs(gs1) < 1 - TConsts::epsilon)) return; + + // convert bounds + double bs0 = -INFINITY, bs1 = INFINITY; + if (bound0 && bound1) { + bs0 = helper.convert(*bound0); + bs1 = helper.convert(*bound1); + } else + if (bound0) { + bs0 = helper.convert(*bound0); + if (bs0 < 0) bs1 = -INFINITY; + } else + if (bound1) { + bs1 = helper.convert(*bound1); + if (bs1 < 0) bs0 = INFINITY; } - - void draw( - const TAffine &ellipseMatrix, - const TAffine &screenMatrixInv, - double ox, - double pixelSize, - bool enabled ) const - { - const double crossSize = 0.1; - - double alpha = getDrawingAlpha(enabled); - bool grid = getGrid(); - bool ruler = getRestrictA() && getRestrictB(); - bool concentric = !getRestrictA() && !getRestrictB(); - - drawSegment( ellipseMatrix*TPointD(-crossSize, 0.0), - ellipseMatrix*TPointD( crossSize, 0.0), pixelSize, alpha); - drawSegment( ellipseMatrix*TPointD(0.0, -crossSize), - ellipseMatrix*TPointD(0.0, crossSize), pixelSize, alpha); - drawEllipse(ellipseMatrix, screenMatrixInv, pixelSize, alpha); - if (ox > 1.0) - drawSegment( ellipseMatrix*TPointD(-1.0, -1.0), - ellipseMatrix*TPointD(-1.0, 1.0), pixelSize, alpha); - else if (ox < -1.0) - drawSegment( ellipseMatrix*TPointD( 1.0, -1.0), - ellipseMatrix*TPointD( 1.0, 1.0), pixelSize, alpha); - - if (!grid) return; - - if (ruler) { - drawRuler(ellipseMatrix, pixelSize); - } else - if (concentric) { - drawConcentricGrid(ellipseMatrix, screenMatrixInv, pixelSize); - } else { - drawParallelGrid(ellipseMatrix, screenMatrixInv, pixelSize); + + if (bs0 > bs1) std::swap(bs0, bs1); + bs0 -= TConsts::epsilon; + bs1 += TConsts::epsilon; + + // prepare ranges + TAngleRangeSet ranges; + ranges.add( TAngleRangeSet::fromDouble(0.0), TAngleRangeSet::fromDouble(M_PI) ); + + if (perspectiveDepth) { + // draw perspective depth + if (!( fabs(gs0) > TConsts::epsilon + && fabs(gs1) > TConsts::epsilon + && fabs(gs0) < 1 - TConsts::epsilon + && fabs(gs1) < 1 - TConsts::epsilon + && fabs(gs1 - gs0) > TConsts::epsilon )) return; + + // the formula is: x = 1/sqrt(1 + i*i) + double ig0 = sqrt(1/(gs0*gs0) - 1); + double ig1 = sqrt(1/(gs1*gs1) - 1); + double di = fabs(ig1 - ig0); + double i0 = ig0 - floor(ig0/di)*di; + if (i0 <= TConsts::epsilon) i0 += di; + + actualMinStep /= 2; + double sign = gs0 < 0 ? -1 : 1; + for(double i = i0; i+di != i; i += di) { + double x = 1/sqrt(1 + i*i); + + double curStep = fabs( di*i*x*x*x ); + double curAlpha = (curStep - actualMinStep)/actualMinStep; + if (curAlpha < 0) { if (i == i0) continue; else break; } + if (curAlpha > 1) curAlpha = 1; + + x *= sign; + if (x < bs0 || bs1 < x) continue; + + drawEllipseRanges( + ranges, + ellipseMatrix*TAffine::scale(sign, x), + screenMatrixInv, + pixelSize, + alpha * curAlpha ); + } + } else + if (perspective) { + // draw perspective + if (!( fabs(gs0) < 1 - TConsts::epsilon + && fabs(gs1) < 1 - TConsts::epsilon + && fabs(gs1 - gs0) > TConsts::epsilon )) return; + + // the formula is: x = i/sqrt(1 + i*i) + double ig0 = gs0/sqrt(1 - gs0*gs0); + double ig1 = gs1/sqrt(1 - gs1*gs1); + double di = fabs(ig1 - ig0); + double i0 = ig0 - round(ig0/di)*di; + + actualMinStep /= 2; + for(int j = 0; j < 2; ++j, di = -di, i0 += di) + for(double i = i0; i+di != i; i += di) { + double x = i/sqrt(1 + i*i); + + double curAlpha = 1; + if (fabs(i) > TConsts::epsilon) { + double curStep = fabs( di*x*(1 - x*x)/i ); + curAlpha = (curStep - actualMinStep)/actualMinStep; + if (curAlpha < 0) { if (i == i0) continue; else break; } + if (curAlpha > 1) curAlpha = 1; + } + if (x < bs0 || bs1 < x) continue; + + drawEllipseRanges( + ranges, + ellipseMatrix*TAffine::scale(x < 0.0 ? -1.0 : 1.0, x), + screenMatrixInv, + pixelSize, + alpha * curAlpha ); } + } else { + // draw linear + double dx = fabs(gs1 - gs0); + if (dx < actualMinStep) return; + for(double x = gs0 + ceil((-1.0 - gs0)/dx)*dx; x < 1.0; x += dx) { + if (x < bs0 || bs1 < x) continue; + drawEllipseRanges( + ranges, + ellipseMatrix*TAffine::scale(x < 0.0 ? -1.0 : 1.0, x), + screenMatrixInv, + pixelSize, + alpha ); + } + } +} + + +void TAssistantEllipse::draw( + const TAffine &ellipseMatrix, + const TAffine &screenMatrixInv, + double ox, + double pixelSize, + bool enabled ) const +{ + const double crossSize = 0.1; + + double alpha = getDrawingAlpha(enabled); + double gridAlpha = getDrawingGridAlpha(); + bool grid = getGrid(); + bool ruler = getRestrictA() && getRestrictB(); + bool concentric = !getRestrictA() && !getRestrictB(); + + drawSegment( ellipseMatrix*TPointD(-crossSize, 0.0), + ellipseMatrix*TPointD( crossSize, 0.0), pixelSize, alpha); + drawSegment( ellipseMatrix*TPointD(0.0, -crossSize), + ellipseMatrix*TPointD(0.0, crossSize), pixelSize, alpha); + drawEllipse(ellipseMatrix, screenMatrixInv, pixelSize, alpha); + if (ox > 1.0) + drawSegment( ellipseMatrix*TPointD(-1.0, -1.0), + ellipseMatrix*TPointD(-1.0, 1.0), pixelSize, alpha); + else if (ox < -1.0) + drawSegment( ellipseMatrix*TPointD( 1.0, -1.0), + ellipseMatrix*TPointD( 1.0, 1.0), pixelSize, alpha); + + if (!grid) return; + + if (ruler) { + drawRuler( + ellipseMatrix, + m_grid0.position, + m_grid1.position, + getPerspective(), + alpha ); + } else + if (concentric) { + drawConcentricGrid( + ellipseMatrix, + m_grid0.position, + m_grid1.position, + getPerspective(), + gridAlpha ); + } else { + drawParallelGrid( + ellipseMatrix, + m_grid0.position, + m_grid1.position, + nullptr, + nullptr, + getPerspective(), + getPerspectiveDepth(), + getRepeat(), + gridAlpha ); } +} -public: - void draw(TToolViewer *viewer, bool enabled) const override { - bool restrictA = getRestrictA(); - bool restrictB = getRestrictB(); - bool repeat = getRepeat(); - double minStep = 30.0; - - TAffine ellipseMatrix = calcEllipseMatrix(); - if (ellipseMatrix.isZero()) return; - if (!restrictA && restrictB) { - std::swap(ellipseMatrix.a11, ellipseMatrix.a12); - std::swap(ellipseMatrix.a21, ellipseMatrix.a22); - } - // common data about viewport - const TRectD oneBox(-1.0, -1.0, 1.0, 1.0); - TAffine4 modelview, projection; - glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a); - glGetDoublev(GL_PROJECTION_MATRIX, projection.a); - TAffine matrix = (projection*modelview).get2d(); - TAffine matrixInv = matrix.inv(); - double pixelSize = sqrt(tglGetPixelSize2()); - - if (!repeat || restrictA == restrictB || norm(TPointD(ellipseMatrix.a11, ellipseMatrix.a21)) < minStep*pixelSize) { - draw(ellipseMatrix, matrixInv, 0.0, pixelSize, enabled); - } else { - // calculate bounds - TPointD o(ellipseMatrix.a13, ellipseMatrix.a23); - TPointD proj(ellipseMatrix.a11, ellipseMatrix.a21); - proj = proj * (1.0/norm2(proj)); - TPointD corners[4] = { - TPointD(oneBox.x0, oneBox.y0), - TPointD(oneBox.x0, oneBox.y1), - TPointD(oneBox.x1, oneBox.y0), - TPointD(oneBox.x1, oneBox.y1) }; - double minX = 0.0, maxX = 0.0; - for(int i = 0; i < 4; ++i) { - double x = proj * (matrixInv*corners[i] - o); - if (i == 0 || x < minX) minX = x; - if (i == 0 || x > maxX) maxX = x; - } - if (maxX <= minX) return; +void TAssistantEllipse::draw(TToolViewer*, bool enabled) const { + bool restrictA = getRestrictA(); + bool restrictB = getRestrictB(); + bool repeat = getRepeat(); + double minStep = 30.0; + + TAffine ellipseMatrix = calcEllipseMatrix(); + if (ellipseMatrix.isZero()) return; + if (!restrictA && restrictB) { + std::swap(ellipseMatrix.a11, ellipseMatrix.a12); + std::swap(ellipseMatrix.a21, ellipseMatrix.a22); + } - // draw - for(double ox = round(0.5*minX)*2.0; ox - 1.0 < maxX; ox += 2.0) - draw(ellipseMatrix*TAffine::translation(ox, 0.0), matrixInv, ox, pixelSize, enabled); + // common data about viewport + const TRectD oneBox(-1.0, -1.0, 1.0, 1.0); + TAffine4 modelview, projection; + glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a); + glGetDoublev(GL_PROJECTION_MATRIX, projection.a); + TAffine matrix = (projection*modelview).get2d(); + TAffine matrixInv = matrix.inv(); + double pixelSize = sqrt(tglGetPixelSize2()); + + if (!repeat || restrictA == restrictB || norm(TPointD(ellipseMatrix.a11, ellipseMatrix.a21)) < minStep*pixelSize) { + draw(ellipseMatrix, matrixInv, 0.0, pixelSize, enabled); + } else { + // calculate bounds + TPointD o(ellipseMatrix.a13, ellipseMatrix.a23); + TPointD proj(ellipseMatrix.a11, ellipseMatrix.a21); + proj = proj * (1.0/norm2(proj)); + TPointD corners[4] = { + TPointD(oneBox.x0, oneBox.y0), + TPointD(oneBox.x0, oneBox.y1), + TPointD(oneBox.x1, oneBox.y0), + TPointD(oneBox.x1, oneBox.y1) }; + double minX = 0.0, maxX = 0.0; + for(int i = 0; i < 4; ++i) { + double x = proj * (matrixInv*corners[i] - o); + if (i == 0 || x < minX) minX = x; + if (i == 0 || x > maxX) maxX = x; } + if (maxX <= minX) return; + + // draw + for(double ox = round(0.5*minX)*2.0; ox - 1.0 < maxX; ox += 2.0) + draw(ellipseMatrix*TAffine::translation(ox, 0.0), matrixInv, ox, pixelSize, enabled); } -}; +} //***************************************************************************************** diff --git a/toonz/sources/tnztools/assistants/assistantellipse.h b/toonz/sources/tnztools/assistants/assistantellipse.h new file mode 100644 index 0000000..394ea96 --- /dev/null +++ b/toonz/sources/tnztools/assistants/assistantellipse.h @@ -0,0 +1,133 @@ +#pragma once + +#ifndef ASSISTANTELLIPSE_INCLUDED +#define ASSISTANTELLIPSE_INCLUDED + + +// TnzTools includes +#include <tools/assistant.h> +#include <tools/assistants/guidelineline.h> +#include <tools/assistants/guidelineellipse.h> + +// TnzCore includes +#include <tgl.h> + +// std includes +#include <limits> + + +//***************************************************************************************** +// TAssistantEllipse definition +//***************************************************************************************** + +class TAssistantEllipse final : public TAssistant { + Q_DECLARE_TR_FUNCTIONS(TAssistantEllipse) +public: + const TStringId m_idCircle; + const TStringId m_idRestrictA; + const TStringId m_idRestrictB; + const TStringId m_idRepeat; + const TStringId m_idGrid; + const TStringId m_idPerspective; + const TStringId m_idPerspectiveDepth; + +protected: + TAssistantPoint &m_center; + TAssistantPoint &m_a; + TAssistantPoint &m_b; + TAssistantPoint &m_grid0; + TAssistantPoint &m_grid1; + +public: + TAssistantEllipse(TMetaObject &object); + + static QString getLocalName(); + + void updateTranslation() const override; + + inline bool getCircle() const + { return data()[m_idCircle].getBool(); } + inline bool getRestrictA() const + { return data()[m_idRestrictA].getBool(); } + inline bool getRestrictB() const + { return data()[m_idRestrictB].getBool(); } + inline bool getRepeat() const + { return data()[m_idRepeat].getBool(); } + inline bool getGrid() const + { return data()[m_idGrid].getBool(); } + inline bool getPerspective() const + { return data()[m_idPerspective].getBool(); } + inline bool getPerspectiveDepth() const + { return data()[m_idPerspectiveDepth].getBool(); } + + void onDataChanged(const TVariant &value) override; + +private: + void fixBAndGrid( + TPointD prevCenter, + TPointD prevA, + TPointD prevB ); + +public: + void onMovePoint(TAssistantPoint &point, const TPointD &position) override; + + TAffine calcEllipseMatrix() const; + + void getGuidelines( + const TPointD &position, + const TAffine &toTool, + TGuidelineList &outGuidelines ) const override; + + static void drawEllipseRanges( + const TAngleRangeSet &ranges, + const TAffine &ellipseMatrix, + const TAffine &screenMatrixInv, + double pixelSize, + double alpha ); + + static inline void drawEllipse( + const TAffine &ellipseMatrix, + const TAffine &screenMatrixInv, + double pixelSize, + double alpha ) + { drawEllipseRanges(TAngleRangeSet(true), ellipseMatrix, screenMatrixInv, pixelSize, alpha); } + + static void drawRuler( + const TAffine &ellipseMatrix, + const TPointD &grid0, + const TPointD &grid1, + bool perspective, + double alpha ); + + static void drawConcentricGrid( + const TAffine &ellipseMatrix, + const TPointD &grid0, + const TPointD &grid1, + bool perspective, + double alpha ); + + static void drawParallelGrid( + const TAffine &ellipseMatrix, + const TPointD &grid0, + const TPointD &grid1, + const TPointD *bound0, + const TPointD *bound1, + bool perspective, + bool perspectiveDepth, + bool repeat, + double alpha ); + +private: + void draw( + const TAffine &ellipseMatrix, + const TAffine &screenMatrixInv, + double ox, + double pixelSize, + bool enabled ) const; + +public: + void draw(TToolViewer *viewer, bool enabled) const override; +}; + + +#endif diff --git a/toonz/sources/tnztools/assistants/assistantfisheye.cpp b/toonz/sources/tnztools/assistants/assistantfisheye.cpp new file mode 100644 index 0000000..6ebfe5f --- /dev/null +++ b/toonz/sources/tnztools/assistants/assistantfisheye.cpp @@ -0,0 +1,315 @@ + + +// TnzTools includes +#include <tools/assistant.h> +#include <tools/assistants/guidelineline.h> +#include <tools/assistants/guidelineellipse.h> + +#include "assistantellipse.h" + +// TnzCore includes +#include <tgl.h> + +// std includes +#include <limits> + + +//***************************************************************************************** +// TAssistantFisheye implementation +//***************************************************************************************** + +class TAssistantFisheye final : public TAssistant { + Q_DECLARE_TR_FUNCTIONS(TAssistantEllipse) +public: + const TStringId m_idCircle; + const TStringId m_idGrid; + const TStringId m_idGridDepth; + const TStringId m_idFlipGrids; + +protected: + TAssistantPoint &m_center; + TAssistantPoint &m_a; + TAssistantPoint &m_b; + TAssistantPoint &m_grid0; + TAssistantPoint &m_grid1; + TAssistantPoint &m_gridD0; + TAssistantPoint &m_gridD1; + +public: + TAssistantFisheye(TMetaObject &object): + TAssistant(object), + m_idCircle("circle"), + m_idGrid("grid"), + m_idGridDepth("gridDepth"), + m_idFlipGrids("flipGrids"), + m_center( addPoint("center", TAssistantPoint::CircleCross) ), + m_a( addPoint("a", TAssistantPoint::CircleFill, TPointD(200, 0)) ), + m_b( addPoint("b", TAssistantPoint::Circle, TPointD( 0, 200)) ), + m_grid0( addPoint("grid0", TAssistantPoint::CircleDoubleDots, TPointD( -25, 25)) ), + m_grid1( addPoint("grid1", TAssistantPoint::CircleDots, TPointD( 25, -25)) ), + m_gridD0( addPoint("gridD0", TAssistantPoint::CircleDoubleDots, TPointD( -70, -70)) ), + m_gridD1( addPoint("gridD1", TAssistantPoint::CircleDots, TPointD( -90, -90)) ) + { + addProperty( new TBoolProperty(m_idCircle.str(), getCircle()) ); + addProperty( new TBoolProperty(m_idGrid.str(), getGrid()) ); + addProperty( new TBoolProperty(m_idGridDepth.str(), getGridDepth()) ); + addProperty( new TBoolProperty(m_idFlipGrids.str(), getFlipGrids()) ); + } + + static QString getLocalName() + { return tr("Fish Eye"); } + + void updateTranslation() const override { + TAssistant::updateTranslation(); + setTranslation(m_idCircle, tr("Circle")); + setTranslation(m_idGrid, tr("Grid")); + setTranslation(m_idGridDepth, tr("Depth Grid")); + setTranslation(m_idFlipGrids, tr("Flip Grids")); + } + + inline bool getCircle() const + { return data()[m_idCircle].getBool(); } + inline bool getGrid() const + { return data()[m_idGrid].getBool(); } + inline bool getGridDepth() const + { return data()[m_idGridDepth].getBool(); } + inline bool getFlipGrids() const + { return data()[m_idFlipGrids].getBool(); } + + void onDataChanged(const TVariant &value) override { + TAssistant::onDataChanged(value); + m_grid0.visible = m_grid1.visible = getGrid(); + m_gridD0.visible = m_gridD1.visible = getGridDepth(); + bool prevB = m_b.visible; + m_b.visible = !getCircle(); + if (prevB && !m_b.visible) + fixBAndGrid(m_center.position, m_a.position, m_b.position); + } + +private: + void fixBAndGrid( + TPointD prevCenter, + TPointD prevA, + TPointD prevB ) + { + const TPointD ¢er = m_center.position; + TPointD da0 = prevA - prevCenter; + TPointD da1 = m_a.position - center; + double la0 = norm2(da0); + double la1 = norm2(da1); + if (!(la0 > TConsts::epsilon) || !(la1 > TConsts::epsilon)) + return; + + TPointD db = m_b.position - center; + TPointD dp0 = TPointD(-da0.y, da0.x); + TPointD dp1 = TPointD(-da1.y, da1.x); + if (getCircle()) { + m_b.position = center + (db*dp0 < 0 ? -dp1 : dp1); + } else { + m_b.position = db*dp0/la0*dp1 + center; + } + + TPointD db0 = prevB - prevCenter; + TPointD db1 = m_b.position - center; + double lb0 = norm2(db0); + double lb1 = norm2(db1); + if (!(lb0 > TConsts::epsilon) || !(lb1 > TConsts::epsilon)) + return; + + TPointD dg0 = m_grid0.position - center; + TPointD dg1 = m_grid1.position - center; + TPointD dgD0 = m_gridD0.position - center; + TPointD dgD1 = m_gridD1.position - center; + m_grid0.position = dg0*da0/la0*da1 + dg0*db0/lb0*db1 + center; + m_grid1.position = dg1*da0/la0*da1 + dg1*db0/lb0*db1 + center; + m_gridD0.position = dgD0*da0/la0*da1 + dgD0*db0/lb0*db1 + center; + m_gridD1.position = dgD1*da0/la0*da1 + dgD1*db0/lb0*db1 + center; + } + + void fixGridD1() { + TAffine em = calcEllipseMatrix(); + TAffine emi = em.inv(); + TPointD d0 = emi*m_gridD0.position; + TPointD d1 = emi*m_gridD1.position; + double l0 = norm2(d0); + double l1 = norm2(d1); + if ( l0 > TConsts::epsilon*TConsts::epsilon + && l1 > TConsts::epsilon*TConsts::epsilon ) + m_gridD1.position = em*( d0*sqrt(l1/l0) ); + } + +public: + void onMovePoint(TAssistantPoint &point, const TPointD &position) override { + TPointD prevCenter = m_center.position; + TPointD prevA = m_a.position; + TPointD prevB = m_b.position; + point.position = position; + if (&point == &m_center) { + TPointD d = m_center.position - prevCenter; + m_a.position += d; + m_b.position += d; + m_grid0.position += d; + m_grid1.position += d; + m_gridD0.position += d; + m_gridD1.position += d; + } else + if (&point == &m_a || &point == &m_b) { + fixBAndGrid(prevCenter, prevA, prevB); + } else + if (&point == &m_gridD0 || &point == &m_gridD1) { + fixGridD1(); + } + } + + TAffine calcEllipseMatrix() const { + TPointD da = m_a.position - m_center.position; + TPointD db = m_b.position - m_center.position; + double r1 = norm(da); + if (r1 <= TConsts::epsilon) return TAffine::zero(); + double r2 = fabs( (rotate90(da)*db)*(1.0/r1) ); + if (r2 <= TConsts::epsilon) return TAffine::zero(); + return TAffine::translation(m_center.position) + * TAffine::rotation(atan(da)) + * TAffine::scale(r1, r2); + } + + void getGuidelines( + const TPointD &position, + const TAffine &toTool, + TGuidelineList &outGuidelines ) const override + { + TAffine matrix = calcEllipseMatrix(); + if (matrix.isZero()) return; + + matrix = toTool*matrix; + TAffine matrixInv = matrix.inv(); + + TPointD p = matrixInv*position; + double l = norm(p); + + if (l > TConsts::epsilon) { + // radius + outGuidelines.push_back(TGuidelineP( + new TGuidelineLine( + getEnabled(), + getMagnetism(), + matrix*TPointD(0, 0), + matrix*(p/l) ))); + } + + if (!(l < 1.0 - TConsts::epsilon)) { + // bound ellipse + outGuidelines.push_back(TGuidelineP( + new TGuidelineEllipse( + getEnabled(), + getMagnetism(), + matrix ))); + } else { + // ellipse scaled by X + double kx = fabs(p.x/sqrt(1.0 - p.y*p.y)); + outGuidelines.push_back(TGuidelineP( + new TGuidelineEllipse( + getEnabled(), + getMagnetism(), + matrix * TAffine::scale(kx, 1.0) ))); + + // ellipse scaled by Y + double ky = fabs(p.y/sqrt(1.0 - p.x*p.x)); + outGuidelines.push_back(TGuidelineP( + new TGuidelineEllipse( + getEnabled(), + getMagnetism(), + matrix * TAffine::scale(1.0, ky) ))); + } + } + +public: + void draw(TToolViewer*, bool enabled) const override { + TAffine ellipseMatrix = calcEllipseMatrix(); + if (ellipseMatrix.isZero()) return; + + TAffine ellipseMatrix2 = ellipseMatrix; + std::swap(ellipseMatrix.a11, ellipseMatrix.a12); + std::swap(ellipseMatrix.a21, ellipseMatrix.a22); + + // common data about viewport + const TRectD oneBox(-1.0, -1.0, 1.0, 1.0); + TAffine4 modelview, projection; + glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a); + glGetDoublev(GL_PROJECTION_MATRIX, projection.a); + TAffine matrix = (projection*modelview).get2d(); + TAffine matrixInv = matrix.inv(); + + double pixelSize = sqrt(tglGetPixelSize2()); + const double crossSize = 0.1; + double alpha = getDrawingAlpha(enabled); + double gridAlpha = getDrawingGridAlpha(); + + drawSegment( ellipseMatrix*TPointD(-crossSize, 0.0), + ellipseMatrix*TPointD( crossSize, 0.0), pixelSize, alpha); + drawSegment( ellipseMatrix*TPointD(0.0, -crossSize), + ellipseMatrix*TPointD(0.0, crossSize), pixelSize, alpha); + TAssistantEllipse::drawEllipse(ellipseMatrix, matrixInv, pixelSize, alpha); + + const TPointD *bound = getGrid() && getGridDepth() ? &m_gridD0.position : nullptr; + const TPointD *boundA = nullptr; + const TPointD *boundB = bound; + if (getFlipGrids()) std::swap(boundA, boundB); + + if (getGrid()) { + TAssistantEllipse::drawParallelGrid( + ellipseMatrix, m_grid0.position, m_grid1.position, + boundA, boundB, true, false, false, gridAlpha ); + TAssistantEllipse::drawParallelGrid( + ellipseMatrix2, m_grid0.position, m_grid1.position, + boundA, boundB, true, false, false, gridAlpha ); + } + + if (getGridDepth()) { + TAssistantEllipse::drawParallelGrid( + ellipseMatrix, m_gridD0.position, m_gridD1.position, + boundB, boundA, false, true, false, gridAlpha ); + TAssistantEllipse::drawParallelGrid( + ellipseMatrix2, m_gridD0.position, m_gridD1.position, + boundB, boundA, false, true, false, gridAlpha ); + } + + if (bound) { + TPointD b = ellipseMatrix.inv()*(*bound); + double r = norm2(b); + if (r < 1 - TConsts::epsilon) { + double bx = b.x/sqrt(1 - b.y*b.y); + double by = b.y/sqrt(1 - b.x*b.x); + + TAngleRangeSet ranges; + ranges.add( TAngleRangeSet::fromDouble(-M_PI/2), TAngleRangeSet::fromDouble(M_PI/2) ); + TAssistantEllipse::drawEllipseRanges( + ranges, + ellipseMatrix*TAffine::scale(bx, 1), + matrixInv, + pixelSize, + alpha ); + + ranges.clear(); + ranges.add( TAngleRangeSet::fromDouble(0.0), TAngleRangeSet::fromDouble(M_PI) ); + TAssistantEllipse::drawEllipseRanges( + ranges, + ellipseMatrix*TAffine::scale(1, by), + matrixInv, + pixelSize, + alpha ); + + TPointD bb = bound == boundA ? TPointD(0, 0) : b/sqrt(r); + drawSegment( ellipseMatrix*b, ellipseMatrix*bb, pixelSize, alpha ); + } + } + } +}; + + +//***************************************************************************************** +// Registration +//***************************************************************************************** + +static TAssistantTypeT<TAssistantFisheye> assistantFisheye("assistantFisheye"); + diff --git a/toonz/sources/tnztools/assistants/assistantline.cpp b/toonz/sources/tnztools/assistants/assistantline.cpp index f0a1fd6..6563d01 100644 --- a/toonz/sources/tnztools/assistants/assistantline.cpp +++ b/toonz/sources/tnztools/assistants/assistantline.cpp @@ -1,354 +1,374 @@ // TnzTools includes -#include <tools/assistant.h> -#include <tools/assistants/guidelineline.h> +#include "assistantline.h" -// TnzCore includes -#include <tgl.h> - //***************************************************************************************** // TAssistantLine implementation //***************************************************************************************** -class TAssistantLine final : public TAssistant { - Q_DECLARE_TR_FUNCTIONS(TAssistantVanishingPoint) -public: - const TStringId m_idRestricktA; - const TStringId m_idRestricktB; - const TStringId m_idParallel; - const TStringId m_idGrid; - const TStringId m_idPerspective; - -protected: - TAssistantPoint &m_a; - TAssistantPoint &m_b; - TAssistantPoint &m_grid0; - TAssistantPoint &m_grid1; - -public: - TAssistantLine(TMetaObject &object): - TAssistant(object), - m_idRestricktA("restrictA"), - m_idRestricktB("restrictB"), - m_idParallel("parallel"), - m_idGrid("grid"), - m_idPerspective("perspective"), - m_a( addPoint("a", TAssistantPoint::CircleCross) ), - m_b( addPoint("b", TAssistantPoint::Circle, TPointD(100.0, 0.0)) ), - m_grid0( addPoint("grid0", TAssistantPoint::CircleDoubleDots, TPointD( 0.0,-50.0)) ), - m_grid1( addPoint("grid1", TAssistantPoint::CircleDots, TPointD( 25.0,-75.0)) ) - { - addProperty( new TBoolProperty(m_idRestricktA.str(), getRestrictA()) ); - addProperty( new TBoolProperty(m_idRestricktB.str(), getRestrictB()) ); - addProperty( new TBoolProperty(m_idParallel.str(), getParallel()) ); - addProperty( new TBoolProperty(m_idGrid.str(), getGrid()) ); - addProperty( new TBoolProperty(m_idPerspective.str(), getPerspective()) ); - } - - static QString getLocalName() - { return tr("Line"); } - - void updateTranslation() const override { - TAssistant::updateTranslation(); - setTranslation(m_idRestricktA, tr("Restrict A")); - setTranslation(m_idRestricktB, tr("Restrict B")); - setTranslation(m_idParallel, tr("Parallel")); - setTranslation(m_idGrid, tr("Grid")); - setTranslation(m_idPerspective, tr("Perspective")); - } - - inline bool getRestrictA() const - { return data()[m_idRestricktA].getBool(); } - inline bool getRestrictB() const - { return data()[m_idRestricktB].getBool(); } - inline bool getParallel() const - { return data()[m_idParallel].getBool(); } - inline bool getGrid() const - { return data()[m_idGrid].getBool(); } - inline bool getPerspective() const - { return data()[m_idPerspective].getBool(); } - - void onDataChanged(const TVariant &value) override { - TAssistant::onDataChanged(value); - m_grid0.visible = getGrid() - || (getParallel() && (getRestrictA() || getRestrictB())); - m_grid1.visible = getGrid(); - } - -private: - void fixGrid1(const TPointD &previousA, const TPointD &previousB) { - TPointD dx = previousB - previousA; - double l = norm2(dx); - if (l <= TConsts::epsilon*TConsts::epsilon) return; - dx = dx*(1.0/sqrt(l)); - TPointD dy(-dx.y, dx.x); +TAssistantLine::TAssistantLine(TMetaObject &object): + TAssistant(object), + m_idRestricktA("restrictA"), + m_idRestricktB("restrictB"), + m_idParallel("parallel"), + m_idGrid("grid"), + m_idPerspective("perspective"), + m_a( addPoint("a", TAssistantPoint::CircleCross) ), + m_b( addPoint("b", TAssistantPoint::Circle, TPointD(100.0, 0.0)) ), + m_grid0( addPoint("grid0", TAssistantPoint::CircleDoubleDots, TPointD( 0.0,-50.0)) ), + m_grid1( addPoint("grid1", TAssistantPoint::CircleDots, TPointD( 25.0,-75.0)) ) +{ + addProperty( new TBoolProperty(m_idRestricktA.str(), getRestrictA()) ); + addProperty( new TBoolProperty(m_idRestricktB.str(), getRestrictB()) ); + addProperty( new TBoolProperty(m_idParallel.str(), getParallel()) ); + addProperty( new TBoolProperty(m_idGrid.str(), getGrid()) ); + addProperty( new TBoolProperty(m_idPerspective.str(), getPerspective()) ); +} + + +QString TAssistantLine::getLocalName() + { return tr("Line"); } + + +void TAssistantLine::updateTranslation() const { + TAssistant::updateTranslation(); + setTranslation(m_idRestricktA, tr("Restrict A")); + setTranslation(m_idRestricktB, tr("Restrict B")); + setTranslation(m_idParallel, tr("Parallel")); + setTranslation(m_idGrid, tr("Grid")); + setTranslation(m_idPerspective, tr("Perspective")); +} + + +void TAssistantLine::onDataChanged(const TVariant &value) { + TAssistant::onDataChanged(value); + m_grid0.visible = getGrid() + || (getParallel() && (getRestrictA() || getRestrictB())); + m_grid1.visible = getGrid(); +} + + +void TAssistantLine::fixGrid(const TPointD &prevA, const TPointD &prevB) { + TPointD dx0 = prevB - prevA; + TPointD dx1 = m_b.position - m_a.position; + double l0 = norm2(dx0); + double l1 = norm2(dx1); + if (!( l0 > TConsts::epsilon*TConsts::epsilon + && l1 > TConsts::epsilon*TConsts::epsilon )) return; + dx0 *= 1/sqrt(l0); + dx1 *= 1/sqrt(l1); + TPointD dy0(-dx0.y, dx0.x); + TPointD dy1(-dx1.y, dx1.x); + + if (getParallel()) { TPointD g1 = m_grid1.position - m_grid0.position; - g1 = TPointD(dx*g1, dy*g1); - - dx = m_b.position - m_a.position; - l = norm2(dx); - if (l <= TConsts::epsilon*TConsts::epsilon) return; - dx = dx*(1.0/sqrt(l)); - dy = TPointD(-dx.y, dx.x); - - m_grid1.position = m_grid0.position + dx*g1.x + dy*g1.y; - } - -public: - void onMovePoint(TAssistantPoint &point, const TPointD &position) override { - TPointD previousA = m_a.position; - TPointD previousB = m_b.position; - point.position = position; - if (&point != &m_grid1) - fixGrid1(previousA, previousB); + m_grid1.position = m_grid0.position + g1*dx0*dx1 + g1*dy0*dy1; + } else { + TPointD g0 = m_grid0.position - prevA; + TPointD g1 = m_grid1.position - prevA; + m_grid0.position = m_a.position + g0*dx0*dx1 + g0*dy0*dy1; + m_grid1.position = m_a.position + g1*dx0*dx1 + g1*dy0*dy1; } - - void getGuidelines( - const TPointD &position, - const TAffine &toTool, - TGuidelineList &outGuidelines ) const override - { - bool restrictA = getRestrictA(); - bool restrictB = getRestrictB(); - bool parallel = getParallel(); - bool perspective = getPerspective(); - - TPointD a = toTool*m_a.position; - TPointD b = toTool*m_b.position; - TPointD ab = b - a; - double abLen2 = norm2(ab); - if (abLen2 < TConsts::epsilon*TConsts::epsilon) return; - - if (parallel) { - TPointD abp = rotate90(ab); - TPointD ag = toTool*m_grid0.position - a; - double k = abp*ag; - if (fabs(k) <= TConsts::epsilon) { - if (restrictA || restrictB) return; - a = position; - } else { - k = (abp*(position - a))/k; - a = a + ag*k; - } - if (perspective && (restrictA || restrictB)) - ab = ab*k; - b = a + ab; +} + + +void TAssistantLine::onMovePoint(TAssistantPoint &point, const TPointD &position) { + TPointD prevA = m_a.position; + TPointD prevB = m_b.position; + point.position = position; + if (&point != &m_grid1) + fixGrid(prevA, prevB); +} + + +void TAssistantLine::getGuidelines( + const TPointD &position, + const TAffine &toTool, + TGuidelineList &outGuidelines ) const +{ + bool restrictA = getRestrictA(); + bool restrictB = getRestrictB(); + bool parallel = getParallel(); + bool perspective = getPerspective(); + + TPointD a = toTool*m_a.position; + TPointD b = toTool*m_b.position; + TPointD ab = b - a; + double abLen2 = norm2(ab); + if (abLen2 < TConsts::epsilon*TConsts::epsilon) return; + + if (parallel) { + TPointD abp = rotate90(ab); + TPointD ag = toTool*m_grid0.position - a; + double k = abp*ag; + if (fabs(k) <= TConsts::epsilon) { + if (restrictA || restrictB) return; + a = position; + } else { + k = (abp*(position - a))/k; + a = a + ag*k; } - - if (restrictA && restrictB) - outGuidelines.push_back(TGuidelineP( - new TGuidelineLine( - getEnabled(), getMagnetism(), a, b ))); - else if (restrictA) - outGuidelines.push_back(TGuidelineP( - new TGuidelineRay( - getEnabled(), getMagnetism(), a, b ))); - else if (restrictB) - outGuidelines.push_back(TGuidelineP( - new TGuidelineRay( - getEnabled(), getMagnetism(), b, a ))); // b first - else - outGuidelines.push_back(TGuidelineP( - new TGuidelineInfiniteLine( - getEnabled(), getMagnetism(), a, b ))); + if (perspective && (restrictA || restrictB)) + ab = ab*k; + b = a + ab; } -private: - void drawRuler(const TPointD &a, const TPointD &b, double pixelSize, bool perspective) const { - double minStep = 10.0*pixelSize; - double alpha = getDrawingAlpha(); - - TPointD direction = b - a; - double l2 = norm2(direction); - if (l2 <= TConsts::epsilon*TConsts::epsilon) return; - double dirLen = sqrt(l2); - TPointD dirProj = direction*(1.0/l2); - TPointD normal = TPointD(-direction.y, direction.x)*(1.0/dirLen); - - double xg0 = dirProj*(m_grid0.position - a); - double xg1 = dirProj*(m_grid1.position - a); - - if (perspective) { - // draw perspective - double xa0 = dirProj*(m_a.position - a); - double k = 0.0, begin = 0.0, end = 0.0; - if (!calcPerspectiveStep(minStep/dirLen, 0.0, 1.0, xa0, xg0, xg1, k, begin, end)) return; - for(double x = begin; fabs(x) < fabs(end); x *= k) - drawMark(a + direction*(xa0 + x), normal, pixelSize, alpha); - } else { - // draw linear - double dx = fabs(xg1 - xg0); - if (dx*dirLen < minStep) return; - for(double x = xg0 - floor(xg0/dx)*dx; x < 1.0; x += dx) - drawMark(a + direction*x, normal, pixelSize, alpha); + if (restrictA && restrictB) + outGuidelines.push_back(TGuidelineP( + new TGuidelineLine( + getEnabled(), getMagnetism(), a, b ))); + else if (restrictA) + outGuidelines.push_back(TGuidelineP( + new TGuidelineRay( + getEnabled(), getMagnetism(), a, b ))); + else if (restrictB) + outGuidelines.push_back(TGuidelineP( + new TGuidelineRay( + getEnabled(), getMagnetism(), b, a ))); // b first + else + outGuidelines.push_back(TGuidelineP( + new TGuidelineInfiniteLine( + getEnabled(), getMagnetism(), a, b ))); +} + + +void TAssistantLine::drawRuler( + const TPointD &a, + const TPointD &b, + const TPointD &grid0, + const TPointD &grid1, + const TPointD *perspectiveBase, + double alpha ) +{ + double pixelSize = sqrt(tglGetPixelSize2()); + double minStep = (perspectiveBase ? 5 : 10)*pixelSize; + + TPointD direction = b - a; + double l2 = norm2(direction); + if (l2 <= TConsts::epsilon*TConsts::epsilon) return; + double dirLen = sqrt(l2); + TPointD dirProj = direction*(1.0/l2); + TPointD normal = TPointD(-direction.y, direction.x)*(1.0/dirLen); + + double xg0 = dirProj*(grid0 - a); + double xg1 = dirProj*(grid1 - a); + + if (perspectiveBase) { + // draw perspective + double xa0 = dirProj*(*perspectiveBase - a); + double w, i0, i1; + if (!calcPerspectiveStep(minStep/dirLen, 0, 1, xa0, xg0, xg1, w, i0, i1)) return; + for(double i = i0; i < i1; i += 1) { + double x = xa0 + 1/(i*w + 1); + drawMark(a + direction*x, normal, pixelSize, alpha); } + } else { + // draw linear + double dx = fabs(xg1 - xg0); + if (dx*dirLen < minStep) return; + for(double x = xg0 - floor(xg0/dx)*dx; x < 1.0; x += dx) + drawMark(a + direction*x, normal, pixelSize, alpha); + } +} + + +void TAssistantLine::drawLine( + const TAffine &matrix, + const TAffine &matrixInv, + double pixelSize, + const TPointD &a, + const TPointD &b, + bool restrictA, + bool restrictB, + double alpha ) +{ + const TRectD oneBox(-1.0, -1.0, 1.0, 1.0); + TPointD aa = matrix*a; + TPointD bb = matrix*b; + if ( restrictA && restrictB ? TGuidelineLineBase::truncateLine(oneBox, aa, bb) + : restrictA ? TGuidelineLineBase::truncateRay (oneBox, aa, bb) + : restrictB ? TGuidelineLineBase::truncateRay (oneBox, bb, aa) // aa first + : TGuidelineLineBase::truncateInfiniteLine(oneBox, aa, bb) ) + drawSegment(matrixInv*aa, matrixInv*bb, pixelSize, alpha); +} + + +void TAssistantLine::drawGrid( + const TPointD &a, + const TPointD &b, + const TPointD &grid0, + const TPointD &grid1, + bool restrictA, + bool restrictB, + bool perspective, + double alpha ) +{ + TAffine4 modelview, projection; + glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a); + glGetDoublev(GL_PROJECTION_MATRIX, projection.a); + TAffine matrix = (projection*modelview).get2d(); + TAffine matrixInv = matrix.inv(); + double pixelSize = sqrt(tglGetPixelSize2()); + double minStep = (perspective ? 2.5 : 10)*pixelSize; + + TPointD ab = b - a; + double abLen2 = norm2(ab); + if (abLen2 < TConsts::epsilon*TConsts::epsilon) return; + double abLen = sqrt(abLen2); + + TPointD abp = rotate90(ab); + TPointD ag = grid0 - a; + if (fabs(abp*ag) <= TConsts::epsilon) { + if (restrictA || restrictB) return; + ag = abp; + } + double agLen2 = norm2(ag); + if (agLen2 < TConsts::epsilon*TConsts::epsilon) return; + double abpAgK = 1.0/(abp*ag); + TPointD abpAgProj = abp*abpAgK; + + // draw restriction lines + if (perspective) { + if (restrictA) drawLine(matrix, matrixInv, pixelSize, a, a + ag, false, false, alpha); + if (restrictB) drawLine(matrix, matrixInv, pixelSize, a, a + ag + ab, false, false, alpha); + // horizon + if (!restrictA) drawLine(matrix, matrixInv, pixelSize, a - ab, a, restrictA, restrictB, alpha); else + if (!restrictB) drawLine(matrix, matrixInv, pixelSize, a, a + ab, restrictA, restrictB, alpha); + } else { + if (restrictA) drawLine(matrix, matrixInv, pixelSize, a, a + ag, false, false, alpha); + if (restrictB) drawLine(matrix, matrixInv, pixelSize, b, b + ag, false, false, alpha); } - void drawLine( - const TAffine &matrix, - const TAffine &matrixInv, - double pixelSize, - const TPointD &a, - const TPointD &b, - bool restrictA, - bool restrictB, - double alpha ) const - { - const TRectD oneBox(-1.0, -1.0, 1.0, 1.0); - TPointD aa = matrix*a; - TPointD bb = matrix*b; - if ( restrictA && restrictB ? TGuidelineLineBase::truncateLine(oneBox, aa, bb) - : restrictA ? TGuidelineLineBase::truncateRay (oneBox, aa, bb) - : restrictB ? TGuidelineLineBase::truncateRay (oneBox, bb, aa) // aa first - : TGuidelineLineBase::truncateInfiniteLine(oneBox, aa, bb) ) - drawSegment(matrixInv*aa, matrixInv*bb, pixelSize, alpha); + double minStepX = fabs(minStep*abLen*abpAgK); + if (minStepX <= TConsts::epsilon) return; + + // calculate bounds + const TRectD oneBox(-1.0, -1.0, 1.0, 1.0); + TPointD corners[4] = { + TPointD(oneBox.x0, oneBox.y0), + TPointD(oneBox.x0, oneBox.y1), + TPointD(oneBox.x1, oneBox.y0), + TPointD(oneBox.x1, oneBox.y1) }; + double minX = 0.0, maxX = 0.0; + for(int i = 0; i < 4; ++i) { + double x = abpAgProj * (matrixInv*corners[i] - a); + if (i == 0 || x < minX) minX = x; + if (i == 0 || x > maxX) maxX = x; } + if (maxX <= minX) return; - void drawGrid( - const TAffine &matrix, - const TAffine &matrixInv, - double pixelSize, - bool restrictA, - bool restrictB, - bool perspective ) const - { - double minStep = 10.0*pixelSize; - - double alpha = getDrawingGridAlpha(); - TPointD a = m_a.position; - TPointD b = m_b.position; - TPointD ab = b - a; - double abLen2 = norm2(ab); - if (abLen2 < TConsts::epsilon*TConsts::epsilon) return; - double abLen = sqrt(abLen2); - - TPointD g0 = m_grid0.position; - TPointD g1 = m_grid1.position; + double x0 = abpAgProj*(grid0 - a); + double x1 = abpAgProj*(grid1 - a); - TPointD abp = rotate90(ab); - TPointD ag = g0 - a; - if (fabs(abp*ag) <= TConsts::epsilon) { - if (restrictA || restrictB) return; - ag = abp; - } - double agLen2 = norm2(ag); - if (agLen2 < TConsts::epsilon*TConsts::epsilon) return; - double agLen = sqrt(agLen2); - double abpAgK = 1.0/(abp*ag); - TPointD abpAgProj = abp*abpAgK; - - // draw restriction lines - if (perspective) { - if (restrictA) drawLine(matrix, matrixInv, pixelSize, a, a + ag, false, false, alpha); - if (restrictB) drawLine(matrix, matrixInv, pixelSize, a, a + ag + ab, false, false, alpha); - } else { - if (restrictA) drawLine(matrix, matrixInv, pixelSize, a, a + ag, false, false, alpha); - if (restrictB) drawLine(matrix, matrixInv, pixelSize, b, b + ag, false, false, alpha); + if (perspective) { + double w, i0, i1; + minStepX /= 2; + + if (!calcPerspectiveStep(minStepX, minX, maxX, 0, x0, x1, w, i0, i1)) return; + double abk = 1.0/fabs(x0); + for(double i = i0; i < i1; i += 1) { + double x = 1/(i*w + 1); + + double curStep = fabs(w*x*x); + double curAlpha = (curStep - minStepX)/minStepX; + if (curAlpha < 0) continue; + if (curAlpha > 1) curAlpha = 1; + + TPointD ca = a + ag*x; + TPointD cb = ca + ab*(abk*x); + drawLine(matrix, matrixInv, pixelSize, ca, cb, restrictA, restrictB, alpha*curAlpha); } - - double minStepX = fabs(minStep*abLen*abpAgK); - if (minStepX <= TConsts::epsilon) return; - - // calculate bounds - const TRectD oneBox(-1.0, -1.0, 1.0, 1.0); - TPointD corners[4] = { - TPointD(oneBox.x0, oneBox.y0), - TPointD(oneBox.x0, oneBox.y1), - TPointD(oneBox.x1, oneBox.y0), - TPointD(oneBox.x1, oneBox.y1) }; - double minX = 0.0, maxX = 0.0; - for(int i = 0; i < 4; ++i) { - double x = abpAgProj * (matrixInv*corners[i] - a); - if (i == 0 || x < minX) minX = x; - if (i == 0 || x > maxX) maxX = x; + } else { + double dx = fabs(x1 - x0); + if (dx < minStepX) return; + for(double x = x0 + ceil((minX - x0)/dx)*dx; x < maxX; x += dx) { + TPointD ca = a + ag*x; + drawLine(matrix, matrixInv, pixelSize, ca, ca + ab, restrictA, restrictB, alpha); } - if (maxX <= minX) return; - - double x0 = abpAgProj*(g0 - a); - double x1 = abpAgProj*(g1 - a); - - if (perspective) { - double k = 0.0, begin = 0.0, end = 0.0; - if (!calcPerspectiveStep(minStepX, minX, maxX, 0.0, x0, x1, k, begin, end)) return; - double abk = 1.0/fabs(x0); - for(double x = begin; fabs(x) < fabs(end); x *= k) { - TPointD ca = a + ag*x; - TPointD cb = ca + ab*(abk*x); - drawLine(matrix, matrixInv, pixelSize, ca, cb, restrictA, restrictB, alpha); - } + } +} + + +void TAssistantLine::draw(TToolViewer*, bool enabled) const { + double alpha = getDrawingAlpha(enabled); + bool restrictA = getRestrictA(); + bool restrictB = getRestrictB(); + bool parallel = getParallel(); + bool grid = getGrid(); + bool perspective = getPerspective(); + + // common data about viewport + const TRectD oneBox(-1.0, -1.0, 1.0, 1.0); + TAffine4 modelview, projection; + glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a); + glGetDoublev(GL_PROJECTION_MATRIX, projection.a); + TAffine matrix = (projection*modelview).get2d(); + TAffine matrixInv = matrix.inv(); + double pixelSize = sqrt(tglGetPixelSize2()); + + // calculate range + TPointD aa = matrix*m_a.position; + TPointD bb = matrix*m_b.position; + bool success = false; + if (restrictA && restrictB) + success = TGuidelineLineBase::truncateLine(oneBox, aa, bb); + else if (restrictA) + success = TGuidelineLineBase::truncateRay(oneBox, aa, bb); + else if (restrictB) + success = TGuidelineLineBase::truncateRay(oneBox, bb, aa); + else + success = TGuidelineLineBase::truncateInfiniteLine(oneBox, aa, bb); + + if (!success) { + // line is out of screen, bud grid still can be visible + if (grid && getParallel()) + drawGrid( + m_a.position, + m_b.position, + m_grid0.position, + m_grid1.position, + restrictA, + restrictB, + perspective, + getDrawingGridAlpha() ); + return; + } + + TPointD a = matrixInv*aa; + TPointD b = matrixInv*bb; + + // draw line + drawSegment(a, b, pixelSize, alpha); + + // draw restriction marks + if (restrictA || (!parallel && grid && perspective)) + drawDot(m_a.position); + if (restrictB) + drawDot(m_b.position); + + if (grid) { + if (getParallel()) { + drawGrid( + m_a.position, + m_b.position, + m_grid0.position, + m_grid1.position, + restrictA, + restrictB, + perspective, + getDrawingGridAlpha() ); } else { - double dx = fabs(x1 - x0); - if (dx < minStepX) return; - for(double x = x0 + ceil((minX - x0)/dx)*dx; x < maxX; x += dx) { - TPointD ca = a + ag*x; - drawLine(matrix, matrixInv, pixelSize, ca, ca + ab, restrictA, restrictB, alpha); - } + drawRuler( + a, b, m_grid0.position, m_grid1.position, + perspective ? &m_a.position : nullptr, getDrawingAlpha() ); } } +} -public: - void draw(TToolViewer *viewer, bool enabled) const override { - double alpha = getDrawingAlpha(enabled); - bool restrictA = getRestrictA(); - bool restrictB = getRestrictB(); - bool parallel = getParallel(); - bool grid = getGrid(); - bool perspective = getPerspective(); - - // common data about viewport - const TRectD oneBox(-1.0, -1.0, 1.0, 1.0); - TAffine4 modelview, projection; - glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a); - glGetDoublev(GL_PROJECTION_MATRIX, projection.a); - TAffine matrix = (projection*modelview).get2d(); - TAffine matrixInv = matrix.inv(); - double pixelSize = sqrt(tglGetPixelSize2()); - - // calculate range - TPointD aa = matrix*m_a.position; - TPointD bb = matrix*m_b.position; - bool success = false; - if (restrictA && restrictB) - success = TGuidelineLineBase::truncateLine(oneBox, aa, bb); - else if (restrictA) - success = TGuidelineLineBase::truncateRay(oneBox, aa, bb); - else if (restrictB) - success = TGuidelineLineBase::truncateRay(oneBox, bb, aa); - else - success = TGuidelineLineBase::truncateInfiniteLine(oneBox, aa, bb); - - if (!success) { - // line is out of screen, bud grid still can be visible - if (grid && getParallel()) - drawGrid(matrix, matrixInv, pixelSize, restrictA, restrictB, perspective); - return; - } - - TPointD a = matrixInv*aa; - TPointD b = matrixInv*bb; - - // draw line - drawSegment(a, b, pixelSize, alpha); - - // draw restriction marks - if (restrictA || (!parallel && grid && perspective)) - drawDot(m_a.position); - if (restrictB) - drawDot(m_b.position); - - if (grid) { - if (getParallel()) { - drawGrid(matrix, matrixInv, pixelSize, restrictA, restrictB, perspective); - } else { - drawRuler(a, b, pixelSize, perspective); - } - } - } -}; //***************************************************************************************** diff --git a/toonz/sources/tnztools/assistants/assistantline.h b/toonz/sources/tnztools/assistants/assistantline.h new file mode 100644 index 0000000..135ac5f --- /dev/null +++ b/toonz/sources/tnztools/assistants/assistantline.h @@ -0,0 +1,98 @@ +#pragma once + +#ifndef ASSISTANTLINE_INCLUDED +#define ASSISTANTLINE_INCLUDED + + +// TnzTools includes +#include <tools/assistant.h> +#include <tools/assistants/guidelineline.h> + + +// TnzCore includes +#include <tgl.h> + + +//***************************************************************************************** +// TAssistantLine definition +//***************************************************************************************** + +class TAssistantLine final : public TAssistant { + Q_DECLARE_TR_FUNCTIONS(TAssistantVanishingPoint) +public: + const TStringId m_idRestricktA; + const TStringId m_idRestricktB; + const TStringId m_idParallel; + const TStringId m_idGrid; + const TStringId m_idPerspective; + +protected: + TAssistantPoint &m_a; + TAssistantPoint &m_b; + TAssistantPoint &m_grid0; + TAssistantPoint &m_grid1; + +public: + TAssistantLine(TMetaObject &object); + + static QString getLocalName(); + + void updateTranslation() const override; + + inline bool getRestrictA() const + { return data()[m_idRestricktA].getBool(); } + inline bool getRestrictB() const + { return data()[m_idRestricktB].getBool(); } + inline bool getParallel() const + { return data()[m_idParallel].getBool(); } + inline bool getGrid() const + { return data()[m_idGrid].getBool(); } + inline bool getPerspective() const + { return data()[m_idPerspective].getBool(); } + + void onDataChanged(const TVariant &value) override; + +private: + void fixGrid(const TPointD &prevA, const TPointD &prevB); + +public: + void onMovePoint(TAssistantPoint &point, const TPointD &position) override; + + void getGuidelines( + const TPointD &position, + const TAffine &toTool, + TGuidelineList &outGuidelines ) const override; + + static void drawRuler( + const TPointD &a, + const TPointD &b, + const TPointD &grid0, + const TPointD &grid1, + const TPointD *perspectiveBase, + double alpha ); + + static void drawLine( + const TAffine &matrix, + const TAffine &matrixInv, + double pixelSize, + const TPointD &a, + const TPointD &b, + bool restrictA, + bool restrictB, + double alpha ); + + static void drawGrid( + const TPointD &a, + const TPointD &b, + const TPointD &grid0, + const TPointD &grid1, + bool restrictA, + bool restrictB, + bool perspective, + double alpha ); + + void draw(TToolViewer *viewer, bool enabled) const override; +}; + + +#endif diff --git a/toonz/sources/tnztools/assistants/assistantperspective.cpp b/toonz/sources/tnztools/assistants/assistantperspective.cpp new file mode 100644 index 0000000..3aabbe3 --- /dev/null +++ b/toonz/sources/tnztools/assistants/assistantperspective.cpp @@ -0,0 +1,453 @@ + + +// TnzTools includes +#include <tools/assistant.h> +#include <tools/assistants/guidelineline.h> + +#include "assistantline.h" +#include "assistantvanishingpoint.h" + +// TnzCore includes +#include <tgl.h> + + +//***************************************************************************************** +// TAssistantPerspective implementation +//***************************************************************************************** + +class TAssistantPerspective final : public TAssistant { + Q_DECLARE_TR_FUNCTIONS(TAssistantPerspective) +public: + const TStringId m_idParallelX; + const TStringId m_idParallelY; + const TStringId m_idParallelZ; + const TStringId m_idGridXY; + const TStringId m_idGridYZ; + const TStringId m_idGridZX; + const TStringId m_idShowBox; + +protected: + TAssistantPoint &m_o; + TAssistantPoint &m_x; + TAssistantPoint &m_y; + TAssistantPoint &m_z; + TAssistantPoint &m_xy; + TAssistantPoint &m_yz; + TAssistantPoint &m_zx; + TAssistantPoint &m_vx; + TAssistantPoint &m_vy; + TAssistantPoint &m_vz; + +public: + TAssistantPerspective(TMetaObject &object): + TAssistant(object), + m_idParallelX("parallelX"), + m_idParallelY("parallelY"), + m_idParallelZ("parallelZ"), + m_idGridXY("gridXY"), + m_idGridYZ("gridYZ"), + m_idGridZX("gridZX"), + m_idShowBox("showBox"), + m_o ( addPoint("o", TAssistantPoint::CircleCross) ), + m_x ( addPoint("x", TAssistantPoint::CircleFill, TPointD( 50, 0 )) ), + m_y ( addPoint("y", TAssistantPoint::CircleFill, TPointD( 0, 50 )) ), + m_z ( addPoint("z", TAssistantPoint::CircleFill, TPointD( 25, 25 )) ), + m_xy ( addPoint("xy", TAssistantPoint::Circle, TPointD( 50, 50 )) ), + m_yz ( addPoint("yz", TAssistantPoint::Circle, TPointD( 25, 25 )) ), + m_zx ( addPoint("zx", TAssistantPoint::Circle, TPointD( 75, 25 )) ), + m_vx ( addPoint("vx", TAssistantPoint::Circle) ), + m_vy ( addPoint("vy", TAssistantPoint::Circle) ), + m_vz ( addPoint("vz", TAssistantPoint::Circle) ) + { + addProperty( new TBoolProperty(m_idParallelX.str(), getParallelX()) ); + addProperty( new TBoolProperty(m_idParallelY.str(), getParallelY()) ); + addProperty( new TBoolProperty(m_idParallelZ.str(), getParallelZ()) ); + addProperty( new TBoolProperty(m_idGridXY.str(), getGridXY()) ); + addProperty( new TBoolProperty(m_idGridYZ.str(), getGridYZ()) ); + addProperty( new TBoolProperty(m_idGridZX.str(), getGridZX()) ); + addProperty( new TBoolProperty(m_idShowBox.str(), getShowBox()) ); + } + + + static QString getLocalName() + { return tr("Perspective"); } + + + void updateTranslation() const override { + TAssistant::updateTranslation(); + setTranslation(m_idParallelX, tr("Parallel X")); + setTranslation(m_idParallelY, tr("Parallel Y")); + setTranslation(m_idParallelZ, tr("Parallel Z")); + setTranslation(m_idGridXY, tr("Grid XY")); + setTranslation(m_idGridYZ, tr("Grid YZ")); + setTranslation(m_idGridZX, tr("Grid ZX")); + setTranslation(m_idShowBox, tr("Show Box")); + } + + + inline bool getParallelX() const + { return data()[m_idParallelX].getBool(); } + inline bool getParallelY() const + { return data()[m_idParallelY].getBool(); } + inline bool getParallelZ() const + { return data()[m_idParallelZ].getBool(); } + inline bool getGridXY() const + { return data()[m_idGridXY].getBool(); } + inline bool getGridYZ() const + { return data()[m_idGridYZ].getBool(); } + inline bool getGridZX() const + { return data()[m_idGridZX].getBool(); } + inline bool getShowBox() const + { return data()[m_idShowBox].getBool(); } + + void onDataChanged(const TVariant &value) override { + TAssistant::onDataChanged(value); + m_xy.visible = !getParallelX() || !getParallelY(); + m_yz.visible = !getParallelY() || !getParallelZ(); + m_zx.visible = !getParallelZ() || !getParallelX(); + fixPoints(); + } + + +private: + void fixAxisPoint( + TPointD &a, + const TAssistantPoint &v, + const TPointD &oldV ) const + { + const TPointD &o = m_o.position; + if (!v.visible) + { a = o + v.position; return; } + + TPointD dv = v.position - o; + TPointD oldDv = oldV - o; + double l = norm2(oldDv); + double ln = norm2(dv); + if (!(l > TConsts::epsilon) || !(ln > TConsts::epsilon)) + return; + + double d = (a - o)*oldDv; + a = o + dv*(d/l); + } + + + inline void fixAxisPoint( + TAssistantPoint &a, + const TAssistantPoint &v, + const TPointD &oldV ) const + { fixAxisPoint(a.position, v, oldV); } + + + void fixVanishingPoint( + TAssistantPoint &v, + const TPointD &a, + const TPointD &oldA ) const + { + const TPointD &o = m_o.position; + TPointD da = a - o; + if (!v.visible) + { v.position = da; return; } + + TPointD oldDa = oldA - o; + double l = norm2(oldDa); + double ln = norm2(da); + if (!(l > TConsts::epsilon) || !(ln > TConsts::epsilon)) + return; + + double d = (v.position - o)*oldDa; + v.position = o + da*(d/l); + } + + + inline void fixVanishingPoint( + TAssistantPoint &v, + const TAssistantPoint &a, + const TPointD &oldA ) const + { fixVanishingPoint(v, a.position, oldA); } + + + void fixVanishingPoint( + TAssistantPoint &v, + const TPointD &a0, + const TPointD &a1, + const TPointD &b0, + const TPointD &b1 ) const + { + TPointD da = a1 - a0; + TPointD db = b1 - b0; + const TPointD ab = b0 - a0; + double k = db.x*da.y - db.y*da.x; + + if ( (&v == &m_vx && getParallelX()) + || (&v == &m_vy && getParallelY()) + || (&v == &m_vz && getParallelZ()) ) + k = 0; + + if (fabs(k) > TConsts::epsilon) { + double lb = (da.x*ab.y - da.y*ab.x)/k; + v.position.x = lb*db.x + b0.x; + v.position.y = lb*db.y + b0.y; + v.visible = true; + } else { + v.position = da; + v.visible = false; + } + } + + + inline void fixVanishingPoint( + TAssistantPoint &v, + const TAssistantPoint &a0, + const TAssistantPoint &a1, + const TAssistantPoint &b0, + const TAssistantPoint &b1 ) const + { fixVanishingPoint(v, a0.position, a1.position, b0.position, b1.position); } + + + static bool lineCross( + TPointD &p, + const TPointD &a, + const TPointD &b, + const TPointD &da, + const TPointD &db ) + { + double d = da.x*db.y - da.y*db.x; + if (!(fabs(d) > TConsts::epsilon)) + return false; + d = 1/d; + p = TPointD( + (a.y*db.x + b.x*db.y)*da.x - (a.x*da.y + b.y*da.x)*db.x, + (a.y*da.x + b.x*da.y)*db.y - (a.x*db.y + b.y*db.x)*da.y )*d; + return true; + } + + + void fixSidePoint( + TPointD &p, + const TPointD &a, // pass 'a' and 'b' by copy + const TPointD &b, + const TAssistantPoint &va, + const TAssistantPoint &vb ) const + { + + TPointD da = va.visible ? va.position - a : va.position; + TPointD db = vb.visible ? vb.position - b : vb.position; + lineCross(p, a, b, da, db); + } + + + inline void fixSidePoint( + TAssistantPoint &p, + const TAssistantPoint &a, + const TAssistantPoint &b, + const TAssistantPoint &va, + const TAssistantPoint &vb ) const + { fixSidePoint(p.position, a.position, b.position, va, vb); } + + + void fixSidePoints() { + fixSidePoint(m_xy, m_x, m_y, m_vy, m_vx); + fixSidePoint(m_yz, m_y, m_z, m_vz, m_vy); + fixSidePoint(m_zx, m_z, m_x, m_vx, m_vz); + } + + + void addGuideline( + const TPointD &position, + const TAffine &toTool, + const TAssistantPoint &v, + TGuidelineList &outGuidelines ) const + { + if (v.visible) { + TPointD p = toTool * v.position; + if (tdistance2(p, position) > 4*TConsts::epsilon*TConsts::epsilon) + outGuidelines.push_back( + new TGuidelineRay( + getEnabled(), + getMagnetism(), + p, + position )); + } else { + TPointD d = toTool.transformDirection(v.position); + if (norm2(d) > 4*TConsts::epsilon*TConsts::epsilon) + outGuidelines.push_back( + new TGuidelineInfiniteLine( + getEnabled(), + getMagnetism(), + position, + position + d )); + } + } + +public: + void onFixPoints() override { + fixVanishingPoint(m_vx, m_o, m_x, m_y, m_xy); + fixVanishingPoint(m_vy, m_o, m_y, m_x, m_xy); + fixVanishingPoint(m_vz, m_o, m_z, m_x, m_zx); + fixSidePoints(); + } + + void onMovePoint(TAssistantPoint &point, const TPointD &position) override { + if (!point.visible) + return; + + TPointD old = point.position; + point.position = position; + + if (&point == &m_o) { + TPointD d = m_o.position - old; + m_x.position += d; + m_y.position += d; + m_z.position += d; + m_xy.position += d; + m_yz.position += d; + m_zx.position += d; + if (m_vx.visible) m_vx.position += d; + if (m_vy.visible) m_vy.position += d; + if (m_vz.visible) m_vz.position += d; + } else + if (&point == &m_x) { + fixVanishingPoint(m_vx, m_x, old); + fixSidePoints(); + } else + if (&point == &m_y) { + fixVanishingPoint(m_vy, m_y, old); + fixSidePoints(); + } else + if (&point == &m_z) { + fixVanishingPoint(m_vz, m_z, old); + fixSidePoints(); + } else + if (&point == &m_xy) { + fixVanishingPoint(m_vx, m_o, m_x, m_y, m_xy); + fixVanishingPoint(m_vy, m_o, m_y, m_x, m_xy); + fixSidePoints(); + } else + if (&point == &m_yz) { + fixVanishingPoint(m_vy, m_o, m_y, m_z, m_yz); + fixVanishingPoint(m_vz, m_o, m_z, m_y, m_yz); + fixSidePoints(); + } else + if (&point == &m_zx) { + fixVanishingPoint(m_vz, m_o, m_z, m_x, m_zx); + fixVanishingPoint(m_vx, m_o, m_x, m_z, m_zx); + fixSidePoints(); + } else + if (&point == &m_vx) { + fixAxisPoint(m_x, m_vx, old); + fixSidePoints(); + } else + if (&point == &m_vy) { + fixAxisPoint(m_y, m_vy, old); + fixSidePoints(); + } else + if (&point == &m_vz) { + fixAxisPoint(m_z, m_vz, old); + fixSidePoints(); + } + } + + void getGuidelines( + const TPointD &position, + const TAffine &toTool, + TGuidelineList &outGuidelines ) const override + { + addGuideline(position, toTool, m_vx, outGuidelines); + addGuideline(position, toTool, m_vy, outGuidelines); + addGuideline(position, toTool, m_vz, outGuidelines); + } + + void drawGrid( + const TAssistantPoint &vx, + const TAssistantPoint &vy, + const TPointD &y ) const + { + double alpha = getDrawingGridAlpha(); + const TPointD &o = m_o.position; + + if (!vx.visible && !vy.visible) { + TAssistantLine::drawGrid( + o, o + vx.position, o, y, false, false, false, alpha ); + return; + } + + if (!vx.visible) { + TAssistantLine::drawGrid( + vy.position, vy.position + vx.position, o, y, false, false, true, alpha ); + return; + } + + TPointD p = y; + if (vy.visible) { + const TPointD &a = vx.position; + const TPointD &b = o; + const TPointD da = y - a; + const TPointD db = vy.position - vx.position; + lineCross(p, a, b, da, db); + } + + TAssistantVanishingPoint::drawPerspectiveGrid(vx.position, o, p, alpha); + } + + void drawVanishingPoint(const TAssistantPoint &v, double pixelSize, double alpha) const { + if (!v.visible) + return; + const TPointD &p = v.position; + TPointD dx(20.0*pixelSize, 0.0); + TPointD dy(0.0, 10.0*pixelSize); + drawSegment(p-dx-dy, p+dx+dy, pixelSize, alpha); + drawSegment(p-dx+dy, p+dx-dy, pixelSize, alpha); + } + + void drawBox(double alpha) const { + double pixelSize = sqrt(tglGetPixelSize2()); + TPointD xyz; + fixSidePoint(xyz, m_xy.position, m_zx.position, m_vz, m_vy); + drawSegment(xyz, m_xy.position, pixelSize, alpha); + drawSegment(xyz, m_yz.position, pixelSize, alpha); + drawSegment(xyz, m_zx.position, pixelSize, alpha); + drawSegment(m_xy.position, m_x.position, pixelSize, alpha); + drawSegment(m_xy.position, m_y.position, pixelSize, alpha); + drawSegment(m_yz.position, m_y.position, pixelSize, alpha); + drawSegment(m_yz.position, m_z.position, pixelSize, alpha); + drawSegment(m_zx.position, m_z.position, pixelSize, alpha); + drawSegment(m_zx.position, m_x.position, pixelSize, alpha); + drawSegment(m_o.position, m_x.position, pixelSize, alpha); + drawSegment(m_o.position, m_y.position, pixelSize, alpha); + drawSegment(m_o.position, m_z.position, pixelSize, alpha); + } + + void draw(TToolViewer*, bool enabled) const override { + double pixelSize = sqrt(tglGetPixelSize2()); + double alpha = getDrawingAlpha(enabled); + drawVanishingPoint(m_vx, pixelSize, alpha); + drawVanishingPoint(m_vy, pixelSize, alpha); + drawVanishingPoint(m_vz, pixelSize, alpha); + if (getGridXY()) { + drawGrid(m_vx, m_vy, m_y.position); + drawGrid(m_vy, m_vx, m_x.position); + } + if (getGridYZ()) { + drawGrid(m_vy, m_vz, m_z.position); + drawGrid(m_vz, m_vy, m_y.position); + } + if (getGridZX()) { + drawGrid(m_vz, m_vx, m_x.position); + drawGrid(m_vx, m_vz, m_z.position); + } + if (getShowBox()) + drawBox(alpha); + } + + void drawEdit(TToolViewer *viewer) const override { + if (!getShowBox()) drawBox(getDrawingAlpha(false)); + TAssistant::drawEdit(viewer); + } +}; + + +//***************************************************************************************** +// Registration +//***************************************************************************************** + +static TAssistantTypeT<TAssistantPerspective> assistantPerspective("assistantPerspective"); diff --git a/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp b/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp index 61b0792..23e8aa3 100644 --- a/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp +++ b/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp @@ -1,375 +1,365 @@ // TnzTools includes -#include <tools/assistant.h> -#include <tools/assistants/guidelineline.h> +#include "assistantvanishingpoint.h" -// TnzCore includes -#include <tgl.h> //***************************************************************************************** // TAssistantVanishingPoint implementation //***************************************************************************************** -class TAssistantVanishingPoint final : public TAssistant { - Q_DECLARE_TR_FUNCTIONS(TAssistantVanishingPoint) -public: - const TStringId m_idPassThrough; - const TStringId m_idGrid; - const TStringId m_idPerspective; - -protected: - TAssistantPoint &m_center; - TAssistantPoint &m_a0; - TAssistantPoint &m_a1; - TAssistantPoint &m_b0; - TAssistantPoint &m_b1; - TAssistantPoint &m_grid0; - TAssistantPoint &m_grid1; - -public: - TAssistantVanishingPoint(TMetaObject &object): - TAssistant(object), - m_idPassThrough("passThrough"), - m_idGrid("grid"), - m_idPerspective("perspective"), - m_center( addPoint("center", TAssistantPoint::CircleCross) ), - m_a0 ( addPoint("a0", TAssistantPoint::Circle, TPointD(-50.0, 0.0)) ), - m_a1 ( addPoint("a1", TAssistantPoint::Circle, TPointD(-75.0, 0.0)) ), - m_b0 ( addPoint("b0", TAssistantPoint::Circle, TPointD( 50.0, 0.0)) ), - m_b1 ( addPoint("b1", TAssistantPoint::Circle, TPointD( 75.0, 0.0)) ), - m_grid0 ( addPoint("grid0", TAssistantPoint::CircleDoubleDots, TPointD( 0.0,-50.0)) ), - m_grid1 ( addPoint("grid1", TAssistantPoint::CircleDots, TPointD( 25.0,-50.0)) ) - { - addProperty( new TBoolProperty(m_idPassThrough.str(), getPassThrough()) ); - addProperty( new TBoolProperty(m_idGrid.str(), getGrid()) ); - addProperty( new TBoolProperty(m_idPerspective.str(), getPerspective()) ); - } - static QString getLocalName() +TAssistantVanishingPoint::TAssistantVanishingPoint(TMetaObject &object): + TAssistant(object), + m_idPassThrough("passThrough"), + m_idGrid("grid"), + m_idPerspective("perspective"), + m_center( addPoint("center", TAssistantPoint::CircleCross) ), + m_a0 ( addPoint("a0", TAssistantPoint::Circle, TPointD(-50.0, 0.0)) ), + m_a1 ( addPoint("a1", TAssistantPoint::Circle, TPointD(-75.0, 0.0)) ), + m_b0 ( addPoint("b0", TAssistantPoint::Circle, TPointD( 50.0, 0.0)) ), + m_b1 ( addPoint("b1", TAssistantPoint::Circle, TPointD( 75.0, 0.0)) ), + m_grid0 ( addPoint("grid0", TAssistantPoint::CircleDoubleDots, TPointD( 0.0,-50.0)) ), + m_grid1 ( addPoint("grid1", TAssistantPoint::CircleDots, TPointD( 25.0,-50.0)) ) +{ + addProperty( new TBoolProperty(m_idPassThrough.str(), getPassThrough()) ); + addProperty( new TBoolProperty(m_idGrid.str(), getGrid()) ); + addProperty( new TBoolProperty(m_idPerspective.str(), getPerspective()) ); +} + + +QString TAssistantVanishingPoint::getLocalName() { return tr("Vanishing Point"); } - void updateTranslation() const override { - TAssistant::updateTranslation(); - setTranslation(m_idPassThrough, tr("Pass Through")); - setTranslation(m_idGrid, tr("Grid")); - setTranslation(m_idPerspective, tr("Perspective")); - } - inline bool getPassThrough() const - { return data()[m_idPassThrough].getBool(); } - inline bool getGrid() const - { return data()[m_idGrid].getBool(); } - inline bool getPerspective() const - { return data()[m_idPerspective].getBool(); } +void TAssistantVanishingPoint::updateTranslation() const { + TAssistant::updateTranslation(); + setTranslation(m_idPassThrough, tr("Pass Through")); + setTranslation(m_idGrid, tr("Grid")); + setTranslation(m_idPerspective, tr("Perspective")); +} - void onDataChanged(const TVariant &value) override { - TAssistant::onDataChanged(value); - m_grid0.visible = m_grid1.visible = getGrid(); - } -private: - void fixCenter() { - if ( !(m_a0.position == m_a1.position) - && !(m_b0.position == m_b1.position) ) - { - const TPointD &a = m_a0.position; - const TPointD &b = m_b0.position; - const TPointD da = m_a1.position - a; - const TPointD db = m_b1.position - b; - const TPointD ab = b - a; - double k = db.x*da.y - db.y*da.x; - if (fabs(k) > TConsts::epsilon) { - double lb = (da.x*ab.y - da.y*ab.x)/k; - m_center.position.x = lb*db.x + b.x; - m_center.position.y = lb*db.y + b.y; - } +void TAssistantVanishingPoint::onDataChanged(const TVariant &value) { + TAssistant::onDataChanged(value); + m_grid0.visible = m_grid1.visible = getGrid(); +} + + +void TAssistantVanishingPoint::fixCenter() { + if ( !(m_a0.position == m_a1.position) + && !(m_b0.position == m_b1.position) ) + { + const TPointD &a = m_a0.position; + const TPointD &b = m_b0.position; + const TPointD da = m_a1.position - a; + const TPointD db = m_b1.position - b; + const TPointD ab = b - a; + double k = db.x*da.y - db.y*da.x; + if (fabs(k) > TConsts::epsilon) { + double lb = (da.x*ab.y - da.y*ab.x)/k; + m_center.position.x = lb*db.x + b.x; + m_center.position.y = lb*db.y + b.y; } } +} - void fixSidePoint(TAssistantPoint &p0, TAssistantPoint &p1, TPointD previousP0) { - if (p0.position != m_center.position && p0.position != p1.position) { - TPointD dp0 = p0.position - m_center.position; - TPointD dp1 = p1.position - previousP0; - double l0 = norm(dp0); - double l1 = norm(dp1); - if (l0 > TConsts::epsilon && l0 + l1 > TConsts::epsilon) - p1.position = m_center.position + dp0*((l0 + l1)/l0); - } + +void TAssistantVanishingPoint::fixSidePoint(TAssistantPoint &p0, TAssistantPoint &p1, TPointD previousP0) { + if (p0.position != m_center.position && p0.position != p1.position) { + TPointD dp0 = p0.position - m_center.position; + TPointD dp1 = p1.position - previousP0; + double l0 = norm(dp0); + double l1 = norm(dp1); + if (l0 > TConsts::epsilon && l0 + l1 > TConsts::epsilon) + p1.position = m_center.position + dp0*((l0 + l1)/l0); } +} - void fixSidePoint(TAssistantPoint &p0, TAssistantPoint &p1) - { fixSidePoint(p0, p1, p0.position); } - void fixGrid1(const TPointD &previousCenter, const TPointD &previousGrid0) { - TPointD dx = previousCenter - previousGrid0; - double l = norm2(dx); - if (l <= TConsts::epsilon*TConsts::epsilon) return; - dx = dx*(1.0/sqrt(l)); - TPointD dy(-dx.y, dx.x); +void TAssistantVanishingPoint::fixSidePoint(TAssistantPoint &p0, TAssistantPoint &p1) + { fixSidePoint(p0, p1, p0.position); } - TPointD d = m_grid1.position - previousGrid0; - double x = (dx*d); - double y = (dy*d); - dx = m_center.position - m_grid0.position; - l = norm2(dx); - if (l <= TConsts::epsilon*TConsts::epsilon) return; - dx = dx*(1.0/sqrt(l)); - dy = TPointD(-dx.y, dx.x); +void TAssistantVanishingPoint::fixGrid1(const TPointD &previousCenter, const TPointD &previousGrid0) { + TPointD dx = previousCenter - previousGrid0; + double l = norm2(dx); + if (l <= TConsts::epsilon*TConsts::epsilon) return; + dx = dx*(1.0/sqrt(l)); + TPointD dy(-dx.y, dx.x); - m_grid1.position = m_grid0.position + dx*x + dy*y; - } + TPointD d = m_grid1.position - previousGrid0; + double x = (dx*d); + double y = (dy*d); -public: - void onFixPoints() override { + dx = m_center.position - m_grid0.position; + l = norm2(dx); + if (l <= TConsts::epsilon*TConsts::epsilon) return; + dx = dx*(1.0/sqrt(l)); + dy = TPointD(-dx.y, dx.x); + + m_grid1.position = m_grid0.position + dx*x + dy*y; +} + + +void TAssistantVanishingPoint::onFixPoints() { + fixSidePoint(m_a0, m_a1); + fixSidePoint(m_b0, m_b1); + fixCenter(); +} + + +void TAssistantVanishingPoint::onMovePoint(TAssistantPoint &point, const TPointD &position) { + TPointD previousCenter = m_center.position; + TPointD previous = point.position; + point.position = position; + if (&point == &m_center) { fixSidePoint(m_a0, m_a1); fixSidePoint(m_b0, m_b1); + } else + if (&point == &m_a0) { + fixSidePoint(m_a0, m_a1, previous); + fixSidePoint(m_b0, m_b1); + } else + if (&point == &m_b0) { + fixSidePoint(m_a0, m_a1); + fixSidePoint(m_b0, m_b1, previous); + } else + if (&point == &m_a1) { fixCenter(); + fixSidePoint(m_a0, m_a1); + fixSidePoint(m_b0, m_b1); + } else + if (&point == &m_b1) { + fixCenter(); + fixSidePoint(m_b0, m_b1); + fixSidePoint(m_a0, m_a1); } - void onMovePoint(TAssistantPoint &point, const TPointD &position) override { - TPointD previousCenter = m_center.position; - TPointD previous = point.position; - point.position = position; - if (&point == &m_center) { - fixSidePoint(m_a0, m_a1); - fixSidePoint(m_b0, m_b1); - } else - if (&point == &m_a0) { - fixSidePoint(m_a0, m_a1, previous); - fixSidePoint(m_b0, m_b1); - } else - if (&point == &m_b0) { - fixSidePoint(m_a0, m_a1); - fixSidePoint(m_b0, m_b1, previous); - } else - if (&point == &m_a1) { - fixCenter(); - fixSidePoint(m_a0, m_a1); - fixSidePoint(m_b0, m_b1); - } else - if (&point == &m_b1) { - fixCenter(); - fixSidePoint(m_b0, m_b1); - fixSidePoint(m_a0, m_a1); - } - - if (&point == &m_grid0) { - fixGrid1(previousCenter, previous); - } else - if (&point != &m_grid1) { - fixGrid1(previousCenter, m_grid0.position); - } + if (&point == &m_grid0) { + fixGrid1(previousCenter, previous); + } else + if (&point != &m_grid1) { + fixGrid1(previousCenter, m_grid0.position); } - - void getGuidelines( - const TPointD &position, - const TAffine &toTool, - TGuidelineList &outGuidelines ) const override - { - if (getPassThrough()) { - outGuidelines.push_back(TGuidelineP( - new TGuidelineInfiniteLine( - getEnabled(), - getMagnetism(), - toTool * m_center.position, - position ))); - } else { - outGuidelines.push_back(TGuidelineP( - new TGuidelineRay( - getEnabled(), - getMagnetism(), - toTool * m_center.position, - position ))); - } +} + + +void TAssistantVanishingPoint::getGuidelines( + const TPointD &position, + const TAffine &toTool, + TGuidelineList &outGuidelines ) const +{ + if (getPassThrough()) { + outGuidelines.push_back(TGuidelineP( + new TGuidelineInfiniteLine( + getEnabled(), + getMagnetism(), + toTool * m_center.position, + position ))); + } else { + outGuidelines.push_back(TGuidelineP( + new TGuidelineRay( + getEnabled(), + getMagnetism(), + toTool * m_center.position, + position ))); } - - void drawSimpleGrid() const { - double alpha = getDrawingGridAlpha(); - const TPointD &p = m_center.position; - double pixelSize = sqrt(tglGetPixelSize2()); - double minStep = 5.0*pixelSize; - - // calculate rays count and step - TPointD d0 = m_grid0.position - p; - TPointD d1 = m_grid1.position - p; - TPointD dp = d0; - double l = norm(d0); - if (l <= TConsts::epsilon) return; - if (norm2(d1) <= TConsts::epsilon*TConsts::epsilon) return; - double a0 = atan(d0); - double a1 = atan(d1); - double da = fabs(a1 - a0); - if (da > M_PI) da = M_PI - da; - if (da < TConsts::epsilon) da = TConsts::epsilon; - double count = M_2PI/da; - if (count > 1e6) return; - double radiusPart = minStep/(da*l); - if (radiusPart > 1.0) return; - int raysCount = (int)round(count); - double step = M_2PI/(double)raysCount; - - // common data about viewport - const TRectD oneBox(-1.0, -1.0, 1.0, 1.0); - TAffine4 modelview, projection; - glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a); - glGetDoublev(GL_PROJECTION_MATRIX, projection.a); - TAffine matrix = (projection*modelview).get2d(); - TAffine matrixInv = matrix.inv(); - - // calculate range - if (!(matrixInv*oneBox).contains(p)) { - TPointD corners[4] = { - TPointD(oneBox.x0, oneBox.y0), - TPointD(oneBox.x0, oneBox.y1), - TPointD(oneBox.x1, oneBox.y0), - TPointD(oneBox.x1, oneBox.y1) }; - double angles[4]; - double a0 = 0.0, a1 = 0.0, da = 0.0; - for(int i = 0; i < 4; ++i) { - angles[i] = atan(matrixInv*corners[i] - p) + M_2PI; - for(int j = 0; j < i; ++j) { - double d = fabs(angles[i] - angles[j]); - if (d > M_PI) d = M_2PI - d; - if (d > da) da = d, a0 = angles[i], a1 = angles[j]; - } +} + + +void TAssistantVanishingPoint::drawSimpleGrid( + const TPointD ¢er, + const TPointD &grid0, + const TPointD &grid1, + double alpha ) +{ + double pixelSize = sqrt(tglGetPixelSize2()); + double minStep = 5.0*pixelSize; + + // calculate rays count and step + TPointD d0 = grid0 - center; + TPointD d1 = grid1 - center; + TPointD dp = d0; + double l = norm(d0); + if (l <= TConsts::epsilon) return; + if (norm2(d1) <= TConsts::epsilon*TConsts::epsilon) return; + double a0 = atan(d0); + double a1 = atan(d1); + double da = fabs(a1 - a0); + if (da > M_PI) da = M_PI - da; + if (da < TConsts::epsilon) da = TConsts::epsilon; + double count = M_2PI/da; + if (count > 1e6) return; + double radiusPart = minStep/(da*l); + if (radiusPart > 1.0) return; + int raysCount = (int)round(count); + double step = M_2PI/(double)raysCount; + + // common data about viewport + const TRectD oneBox(-1.0, -1.0, 1.0, 1.0); + TAffine4 modelview, projection; + glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a); + glGetDoublev(GL_PROJECTION_MATRIX, projection.a); + TAffine matrix = (projection*modelview).get2d(); + TAffine matrixInv = matrix.inv(); + + // calculate range + if (!(matrixInv*oneBox).contains(center)) { + TPointD corners[4] = { + TPointD(oneBox.x0, oneBox.y0), + TPointD(oneBox.x0, oneBox.y1), + TPointD(oneBox.x1, oneBox.y0), + TPointD(oneBox.x1, oneBox.y1) }; + double angles[4]; + double a0 = 0.0, a1 = 0.0, da = 0.0; + for(int i = 0; i < 4; ++i) { + angles[i] = atan(matrixInv*corners[i] - center) + M_2PI; + for(int j = 0; j < i; ++j) { + double d = fabs(angles[i] - angles[j]); + if (d > M_PI) d = M_2PI - d; + if (d > da) da = d, a0 = angles[i], a1 = angles[j]; } - if (a1 < a0) std::swap(a1, a0); - if (a1 - a0 > M_PI) { std::swap(a1, a0); a1 += M_2PI; } - double a = atan(dp) + M_2PI; - a0 = ceil ((a0 - a)/step)*step + a; - a1 = floor((a1 - a)/step)*step + a; - - double s = sin(a0 - a); - double c = cos(a0 - a); - dp = TPointD(c*dp.x - s*dp.y, s*dp.x + c*dp.y); - raysCount = (int)round((a1 - a0)/step); - } - - // draw rays - double s = sin(step); - double c = cos(step); - for(int i = 0; i < raysCount; ++i) { - TPointD p0 = matrix*(p + dp*radiusPart); - TPointD p1 = matrix*(p + dp); - if (TGuidelineLineBase::truncateRay(oneBox, p0, p1)) - drawSegment(matrixInv*p0, matrixInv*p1, pixelSize, alpha); - dp = TPointD(c*dp.x - s*dp.y, s*dp.x + c*dp.y); } + if (a1 < a0) std::swap(a1, a0); + if (a1 - a0 > M_PI) { std::swap(a1, a0); a1 += M_2PI; } + double a = atan(dp) + M_2PI; + a0 = ceil ((a0 - a)/step)*step + a; + a1 = floor((a1 - a)/step)*step + a; + + double s = sin(a0 - a); + double c = cos(a0 - a); + dp = TPointD(c*dp.x - s*dp.y, s*dp.x + c*dp.y); + raysCount = (int)round((a1 - a0)/step); } - void drawPerspectiveGrid() const { - // initial calculations - double alpha = getDrawingGridAlpha(); - const TPointD ¢er = m_center.position; - double pixelSize = sqrt(tglGetPixelSize2()); - double minStep = 5.0*pixelSize; - - TPointD step = m_grid1.position - m_grid0.position; - double stepLen2 = norm2(step); - double stepLen = sqrt(stepLen2); - if (stepLen <= minStep) return; - TPointD stepProj = step*(1.0/stepLen2); - - TPointD dp = center - m_grid0.position; - double startX = dp*stepProj; - TPointD zeroPoint = m_grid0.position + step*startX; - TPointD cz = zeroPoint - center; - double czLen2 = norm2(cz); - double czLen = sqrt(czLen2); - if (czLen <= TConsts::epsilon) return; - TPointD zeroProj = cz*(1.0/czLen2); - - double smallK = minStep/stepLen; - TPointD smallGrid0 = center - dp*smallK; - TPointD smallStep = step*smallK; - TPointD smallStepProj = stepProj*(1/smallK); - - // common data about viewport - const TRectD oneBox(-1.0, -1.0, 1.0, 1.0); - TAffine4 modelview, projection; - glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a); - glGetDoublev(GL_PROJECTION_MATRIX, projection.a); - TAffine matrix = (projection*modelview).get2d(); - TAffine matrixInv = matrix.inv(); - - // calculate bounds - bool found = false; - double minx = 0.0, maxx = 0.0; - TPointD p0 = matrix*(smallGrid0); - TPointD p1 = matrix*(smallGrid0 + smallStep); - if (TGuidelineLineBase::truncateInfiniteLine(oneBox, p0, p1)) { - p0 = matrixInv*p0; - p1 = matrixInv*p1; - minx = (p0 - smallGrid0)*smallStepProj; - maxx = (p1 - smallGrid0)*smallStepProj; - if (maxx < minx) std::swap(maxx, minx); - found = true; - } - if (!oneBox.contains(matrix*center)) { - TPointD corners[4] = { - TPointD(oneBox.x0, oneBox.y0), - TPointD(oneBox.x0, oneBox.y1), - TPointD(oneBox.x1, oneBox.y0), - TPointD(oneBox.x1, oneBox.y1) }; - for(int i = 0; i < 4; ++i) { - TPointD p = matrixInv*corners[i] - center; - double k = p*zeroProj; - if (k < TConsts::epsilon) continue; - double x = startX + (p*stepProj)/k; - if (!found || x < minx) minx = x; - if (!found || x > maxx) maxx = x; - found = true; - } - if (maxx <= minx) return; - } - - // draw grid - if (maxx - minx > 1e6) return; - for(double x = ceil(minx); x < maxx; ++x) { - TPointD p = smallGrid0 + smallStep*x - center; - TPointD p0 = matrix*(center + p); - TPointD p1 = matrix*(center + p*2.0); - if (TGuidelineLineBase::truncateRay(oneBox, p0, p1)) - drawSegment(matrixInv*p0, matrixInv*p1, pixelSize, alpha); - } - - // draw horizon - p0 = matrix*(center); - p1 = matrix*(center + step); - if (TGuidelineLineBase::truncateInfiniteLine(oneBox, p0, p1)) + // draw rays + double s = sin(step); + double c = cos(step); + for(int i = 0; i < raysCount; ++i) { + TPointD p0 = matrix*(center + dp*radiusPart); + TPointD p1 = matrix*(center + dp); + if (TGuidelineLineBase::truncateRay(oneBox, p0, p1)) drawSegment(matrixInv*p0, matrixInv*p1, pixelSize, alpha); + dp = TPointD(c*dp.x - s*dp.y, s*dp.x + c*dp.y); } - - void draw(TToolViewer *viewer, bool enabled) const override { - double pixelSize = sqrt(tglGetPixelSize2()); - const TPointD &p = m_center.position; - TPointD dx(20.0*pixelSize, 0.0); - TPointD dy(0.0, 10.0*pixelSize); - double alpha = getDrawingAlpha(enabled); - drawSegment(p-dx-dy, p+dx+dy, pixelSize, alpha); - drawSegment(p-dx+dy, p+dx-dy, pixelSize, alpha); - if (getGrid()) { - if (getPerspective()) - drawPerspectiveGrid(); - else - drawSimpleGrid(); +} + + +void TAssistantVanishingPoint::drawPerspectiveGrid( + const TPointD ¢er, + const TPointD &grid0, + const TPointD &grid1, + double alpha ) +{ + // initial calculations + double pixelSize = sqrt(tglGetPixelSize2()); + double minStep = 5.0*pixelSize; + + TPointD ox = grid1 - grid0; + double lx = norm2(ox); + if (!(lx > TConsts::epsilon*TConsts::epsilon)) return; + + // common data about viewport + const TRectD oneBox(-1, -1, 1, 1); + TAffine4 modelview, projection; + glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a); + glGetDoublev(GL_PROJECTION_MATRIX, projection.a); + TAffine matrix = (projection*modelview).get2d(); + TAffine matrixInv = matrix.inv(); + + // draw horizon + TPointD p0 = matrix*center; + TPointD p1 = matrix*(center + ox); + if (TGuidelineLineBase::truncateInfiniteLine(oneBox, p0, p1)) + drawSegment(matrixInv*p0, matrixInv*p1, pixelSize, alpha); + + // build grid matrix + TPointD dg = grid0 - center; + lx = sqrt(lx); + ox *= 1/lx; + TPointD oy(-ox.y, ox.x); + double baseY = dg*oy; + if (!(fabs(baseY) > TConsts::epsilon)) return; + if (baseY < 0) { oy = -oy; baseY = -baseY; } + double baseX = dg*ox/baseY; + double stepX = lx/baseY; + double k = stepX*4/minStep; + double dk = 1/k; + TAffine gridMatrix( ox.x, oy.x, center.x, + ox.y, oy.y, center.y ); + matrix = matrix*gridMatrix; + matrixInv = matrix.inv(); + + const TRectD bounds = matrixInv*oneBox; + + // top bound + double x1 = bounds.y1*k - 1; + if (x1 < TConsts::epsilon) return; + double x0 = -x1; + + // angle bounds + double y; + y = bounds.x0 < 0 ? bounds.y0 : bounds.y1; + if (y > TConsts::epsilon) x0 = std::max(x0, bounds.x0/y); + y = bounds.x1 < 0 ? bounds.y1 : bounds.y0; + if (y > TConsts::epsilon) x1 = std::min(x1, bounds.x1/y); + + // delta bounds + if (bounds.x0 < 0) + x0 = std::max( x0, (1 - sqrt(1 - 4*bounds.x0*dk))*k/2 ); + if (bounds.x1 > 0) + x1 = std::min( x1, (sqrt(1 + 4*bounds.x1*dk) - 1)*k/2 ); + + // draw grid + double i0 = ceil((x0 - baseX)/stepX); + x0 = baseX + stepX*i0; + for(double x = x0; x < x1; x += stepX) { + double l = dk*(fabs(x) + 1); + TPointD p0 = gridMatrix*TPointD(x*l, l); + TPointD p1 = gridMatrix*TPointD(x*l*2, l*2); + drawSegment(p0, p1, pixelSize, 0, alpha); + if (bounds.y1 > l*2 + TConsts::epsilon) { + TPointD p2 = gridMatrix*TPointD(x*bounds.y1, bounds.y1); + drawSegment(p1, p2, pixelSize, alpha); } } - - void drawEdit(TToolViewer *viewer) const override { - double pixelSize = sqrt(tglGetPixelSize2()); - drawSegment(m_center.position, m_a1.position, pixelSize); - drawSegment(m_center.position, m_b1.position, pixelSize); - TAssistant::drawEdit(viewer); +} + + +void TAssistantVanishingPoint::draw(TToolViewer*, bool enabled) const { + double pixelSize = sqrt(tglGetPixelSize2()); + const TPointD &p = m_center.position; + TPointD dx(20.0*pixelSize, 0.0); + TPointD dy(0.0, 10.0*pixelSize); + double alpha = getDrawingAlpha(enabled); + drawSegment(p-dx-dy, p+dx+dy, pixelSize, alpha); + drawSegment(p-dx+dy, p+dx-dy, pixelSize, alpha); + if (getGrid()) { + const TPointD &p0 = m_grid0.position; + const TPointD &p1 = m_grid1.position; + double gridAlpha = getDrawingGridAlpha(); + if (getPerspective()) + drawPerspectiveGrid(p, p0, p1, gridAlpha); + else + drawSimpleGrid(p, p0, p1, gridAlpha); } -}; +} + + +void TAssistantVanishingPoint::drawEdit(TToolViewer *viewer) const { + double pixelSize = sqrt(tglGetPixelSize2()); + drawSegment(m_center.position, m_a1.position, pixelSize); + drawSegment(m_center.position, m_b1.position, pixelSize); + TAssistant::drawEdit(viewer); +} + //***************************************************************************************** // Registration //***************************************************************************************** + static TAssistantTypeT<TAssistantVanishingPoint> assistantVanishingPoint("assistantVanishingPoint"); diff --git a/toonz/sources/tnztools/assistants/assistantvanishingpoint.h b/toonz/sources/tnztools/assistants/assistantvanishingpoint.h new file mode 100644 index 0000000..b7b276c --- /dev/null +++ b/toonz/sources/tnztools/assistants/assistantvanishingpoint.h @@ -0,0 +1,83 @@ +#pragma once + +#ifndef ASSISTANTVANISHINGPOINT_INCLUDED +#define ASSISTANTVANISHINGPOINT_INCLUDED + + +// TnzTools includes +#include <tools/assistant.h> +#include <tools/assistants/guidelineline.h> + +// TnzCore includes +#include <tgl.h> + + +//***************************************************************************************** +// TAssistantVanishingPoint definition +//***************************************************************************************** + +class TAssistantVanishingPoint final : public TAssistant { + Q_DECLARE_TR_FUNCTIONS(TAssistantVanishingPoint) +public: + const TStringId m_idPassThrough; + const TStringId m_idGrid; + const TStringId m_idPerspective; + +protected: + TAssistantPoint &m_center; + TAssistantPoint &m_a0; + TAssistantPoint &m_a1; + TAssistantPoint &m_b0; + TAssistantPoint &m_b1; + TAssistantPoint &m_grid0; + TAssistantPoint &m_grid1; + +public: + TAssistantVanishingPoint(TMetaObject &object); + + static QString getLocalName(); + + void updateTranslation() const override; + + inline bool getPassThrough() const + { return data()[m_idPassThrough].getBool(); } + inline bool getGrid() const + { return data()[m_idGrid].getBool(); } + inline bool getPerspective() const + { return data()[m_idPerspective].getBool(); } + + void onDataChanged(const TVariant &value) override; + +private: + void fixCenter(); + void fixSidePoint(TAssistantPoint &p0, TAssistantPoint &p1, TPointD previousP0); + void fixSidePoint(TAssistantPoint &p0, TAssistantPoint &p1); + void fixGrid1(const TPointD &previousCenter, const TPointD &previousGrid0); + +public: + void onFixPoints() override; + void onMovePoint(TAssistantPoint &point, const TPointD &position) override; + + void getGuidelines( + const TPointD &position, + const TAffine &toTool, + TGuidelineList &outGuidelines ) const override; + + static void drawSimpleGrid( + const TPointD ¢er, + const TPointD &grid0, + const TPointD &grid1, + double alpha ); + + static void drawPerspectiveGrid( + const TPointD ¢er, + const TPointD &grid0, + const TPointD &grid1, + double alpha ); + + void draw(TToolViewer *viewer, bool enabled) const override; + void drawEdit(TToolViewer *viewer) const override; +}; + + +#endif diff --git a/toonz/sources/tnztools/assistants/replicatoraffine.cpp b/toonz/sources/tnztools/assistants/replicatoraffine.cpp index f637c90..02c36cc 100644 --- a/toonz/sources/tnztools/assistants/replicatoraffine.cpp +++ b/toonz/sources/tnztools/assistants/replicatoraffine.cpp @@ -249,6 +249,8 @@ public: void getPoints(const TAffine &toTool, PointList &points) const override { points.reserve(points.size() * getMultipler()); int pointsCount = (int)points.size(); + int i0 = getSkipFirst(); + int i1 = pointsCount - getSkipLast(); TAffine aff = getAffine(toTool); struct { @@ -262,7 +264,7 @@ public: TAffine a; for(int j = 0; j < t[i].count; ++j) { a = t[i].aff * a; - transformPoints(a, points, pointsCount); + transformPoints(a, points, i0, i1); } } } @@ -285,7 +287,7 @@ public: { getCountInv(), aff.inv(), pressureInv }, }; - TModifierClone *modifier = new TModifierClone(); + TModifierClone *modifier = new TModifierClone(true, getSkipFirst(), getSkipLast()); for(int i = 0; i < 2; ++i) { TTrackTransform tt; for(int j = 0; j < t[i].count; ++j) { diff --git a/toonz/sources/tnztools/assistants/replicatorgrid.cpp b/toonz/sources/tnztools/assistants/replicatorgrid.cpp index b276024..7928f2f 100644 --- a/toonz/sources/tnztools/assistants/replicatorgrid.cpp +++ b/toonz/sources/tnztools/assistants/replicatorgrid.cpp @@ -177,6 +177,8 @@ public: void getPoints(const TAffine &toTool, PointList &points) const override { points.reserve(points.size() * getMultipler()); int pointsCount = (int)points.size(); + int i0 = getSkipFirst(); + int i1 = pointsCount - getSkipLast(); TPointD c = toTool*m_center.position; TPointD da = toTool*m_a.position - c; @@ -212,22 +214,22 @@ public: transformPoints( TAffine( 1, 0, o.x, 0, 1, o.y ), - points, pointsCount ); + points, i0, i1 ); if (mirrorA) transformPoints( TAffine( ma.a11, ma.a12, ma.a13 + o.x, ma.a21, ma.a22, ma.a23 + o.y ), - points, pointsCount ); + points, i0, i1 ); if (mirrorB) transformPoints( TAffine( mb.a11, mb.a12, mb.a13 + o.x, mb.a21, mb.a22, mb.a23 + o.y ), - points, pointsCount ); + points, i0, i1 ); if (mirrorA && mirrorB) transformPoints( TAffine( mc.a11, mc.a12, mc.a13 + o.x, mc.a21, mc.a22, mc.a23 + o.y ), - points, pointsCount ); + points, i0, i1 ); } } @@ -263,7 +265,7 @@ public: int a0 = -getCountAInv(); int b0 = -getCountBInv(); - TModifierClone *modifier = new TModifierClone(); + TModifierClone *modifier = new TModifierClone(true, getSkipFirst(), getSkipLast()); for(int ib = b0; ib < b1; ++ib) for(int ia = a0; ia < a1; ++ia) { TPointD o = da*ia + db*ib; diff --git a/toonz/sources/tnztools/assistants/replicatorjitter.cpp b/toonz/sources/tnztools/assistants/replicatorjitter.cpp index befb329..433a432 100644 --- a/toonz/sources/tnztools/assistants/replicatorjitter.cpp +++ b/toonz/sources/tnztools/assistants/replicatorjitter.cpp @@ -16,9 +16,10 @@ class TReplicatorJitter final : public TReplicator { Q_DECLARE_TR_FUNCTIONS(TReplicatorJitter) public: - const TStringId m_idSkipFirst; const TStringId m_idPeriod; const TStringId m_idAmplitude; + const TStringId m_idKeepFirstPoint; + const TStringId m_idKeepLastPoint; protected: TAssistantPoint &m_center; @@ -26,13 +27,12 @@ protected: public: TReplicatorJitter(TMetaObject &object): TReplicator(object), - m_idSkipFirst("skipFirst"), m_idPeriod("period"), - m_idAmplitude("m_idAmplitude"), + m_idAmplitude("amplitude"), + m_idKeepFirstPoint("keepFirstPoint"), + m_idKeepLastPoint("keepLastPoint"), m_center( addPoint("center", TAssistantPoint::CircleCross) ) { - addProperty( createSpinProperty(m_idSkipFirst, getSkipFirst(), 0) ); - TDoubleProperty *p; p = new TDoubleProperty(m_idPeriod.str(), 0.0, 1000, getPeriod()); @@ -42,6 +42,9 @@ public: p = new TDoubleProperty(m_idAmplitude.str(), 0.0, 1000, getAmplitude()); p->setNonLinearSlider(); addProperty(p); + + addProperty( new TBoolProperty(m_idKeepFirstPoint.str(), getKeepFirstPoint()) ); + addProperty( new TBoolProperty(m_idKeepLastPoint.str(), getKeepLastPoint()) ); } @@ -51,22 +54,23 @@ public: void updateTranslation() const override { TReplicator::updateTranslation(); - setTranslation(m_idSkipFirst, tr("Skip First Tracks")); setTranslation(m_idPeriod, tr("Period")); setTranslation(m_idAmplitude, tr("Amplitude")); + setTranslation(m_idKeepFirstPoint, tr("Fix First Point")); + setTranslation(m_idKeepLastPoint, tr("Fix Last Point")); } - inline int getSkipFirst() const - { return (int)data()[m_idSkipFirst].getDouble(); } inline double getPeriod() const { return data()[m_idPeriod].getDouble(); } inline double getAmplitude() const { return data()[m_idAmplitude].getDouble(); } + inline bool getKeepFirstPoint() const + { return data()[m_idKeepFirstPoint].getBool(); } + inline bool getKeepLastPoint() const + { return data()[m_idKeepLastPoint].getBool(); } protected: - inline void setSkipFirst(int x) - { if (getSkipFirst() != (double)x) data()[m_idSkipFirst].setDouble((double)x); } inline void setPeriod(double x) { if (getPeriod() != x) data()[m_idPeriod].setDouble(x); } inline void setAmplitude(double x) @@ -95,19 +99,22 @@ protected: public: void getPoints(const TAffine &toTool, PointList &points) const override { - int skipFirst = getSkipFirst(); - if (skipFirst < 0) skipFirst = 0; - if (skipFirst >= (int)points.size()) return; + int pointsCount = (int)points.size(); + int i0 = getSkipFirst(); + int i1 = pointsCount - getSkipLast(); + if (i0 < 0) i0 = 0; + if (i1 > pointsCount) i1 = pointsCount; + if (i0 >= i1) return; double scale = getScale(toTool); double period = getPeriod()*scale; double amplitude = getAmplitude()*scale; - if (!(period > TConsts::epsilon && amplitude > TConsts::epsilon)) { + if (period > TConsts::epsilon && amplitude > TConsts::epsilon) { int seedX = 0; int seedY = 7722441; - for(PointList::iterator i = points.begin() + skipFirst; i != points.end(); ++i) { - i->x += TModifierJitter::func(seedX, 0)*amplitude; - i->y += TModifierJitter::func(seedY, 0)*amplitude; + for(int i = i0; i < i1; ++i) { + points[i].x += TModifierJitter::func(seedX, points[i].x/period)*amplitude; + points[i].y += TModifierJitter::func(seedY, points[i].y/period)*amplitude; ++seedX, ++seedY; } } @@ -122,7 +129,10 @@ public: outModifiers.push_back(new TModifierJitter( getPeriod()*scale, getAmplitude()*scale, - getSkipFirst() )); + getSkipFirst(), + getSkipLast(), + getKeepFirstPoint(), + getKeepLastPoint() )); } diff --git a/toonz/sources/tnztools/assistants/replicatormirror.cpp b/toonz/sources/tnztools/assistants/replicatormirror.cpp index 0c608c8..04ad2cb 100644 --- a/toonz/sources/tnztools/assistants/replicatormirror.cpp +++ b/toonz/sources/tnztools/assistants/replicatormirror.cpp @@ -117,15 +117,19 @@ public: { return 2; } - void getPoints(const TAffine &toTool, PointList &points) const override - { transformPoints(getAffine(toTool), points, (int)points.size()); } + void getPoints(const TAffine &toTool, PointList &points) const override { + int pointsCount = (int)points.size(); + int i0 = getSkipFirst(); + int i1 = pointsCount - getSkipLast(); + transformPoints(getAffine(toTool), points, i0, i1); + } void getModifiers( const TAffine &toTool, TInputModifier::List &outModifiers ) const override { - TModifierClone *modifier = new TModifierClone(); + TModifierClone *modifier = new TModifierClone(true, getSkipFirst(), getSkipLast()); modifier->transforms.push_back(TTrackTransform( getAffine(toTool), getPressure() )); outModifiers.push_back(modifier); diff --git a/toonz/sources/tnztools/assistants/replicatorstar.cpp b/toonz/sources/tnztools/assistants/replicatorstar.cpp index f9821d7..e60f10b 100644 --- a/toonz/sources/tnztools/assistants/replicatorstar.cpp +++ b/toonz/sources/tnztools/assistants/replicatorstar.cpp @@ -116,6 +116,8 @@ public: void getPoints(const TAffine &toTool, PointList &points) const override { points.reserve(points.size() * getMultipler()); int pointsCount = (int)points.size(); + int i0 = getSkipFirst(); + int i1 = pointsCount - getSkipLast(); int count = getCount(); bool mirror = getMirror(); @@ -131,12 +133,12 @@ public: TAffine t0 = t1.inv(); TRotation r(360.0/getCount()); - + for(int i = 0; i < count; ++i) { if (i) - transformPoints(t1*t0, points, pointsCount); + transformPoints(t1*t0, points, i0, i1); if (mirror) { - transformPoints(t2*t0, points, pointsCount); + transformPoints(t2*t0, points, i0, i1); t2 *= r; } t1 *= r; @@ -163,7 +165,7 @@ public: TAffine t0 = t1.inv(); TRotation r(360.0/getCount()); - TModifierClone *modifier = new TModifierClone(); + TModifierClone *modifier = new TModifierClone(true, getSkipFirst(), getSkipLast()); for(int i = 0; i < count; ++i) { if (i) modifier->transforms.push_back(TTrackTransform(t1*t0)); diff --git a/toonz/sources/tnztools/editassistantstool.cpp b/toonz/sources/tnztools/editassistantstool.cpp index 2c98f9e..67cd997 100644 --- a/toonz/sources/tnztools/editassistantstool.cpp +++ b/toonz/sources/tnztools/editassistantstool.cpp @@ -502,7 +502,7 @@ protected: m_currentImage.set(m_readImage); if (index < (*m_reader)->size()) if (const TMetaObjectPC &obj = (**m_reader)[index]) - if (const TAssistant *assistant = obj->getHandler<TAssistant>()) { + if (const TAssistantBase *assistant = obj->getHandler<TAssistantBase>()) { assistant->deselectAll(); m_currentAssistant.set(obj); m_currentAssistantIndex = index; @@ -806,7 +806,7 @@ public: TAssistant::scanAssistants( this, // tool &position, 1, // pointer positions - nullptr, // out guidelines + &m_currentGuidelines, // out guidelines true, // draw false, // enabled only false, // mark enabled diff --git a/toonz/sources/tnztools/modifiers/modifierclone.cpp b/toonz/sources/tnztools/modifiers/modifierclone.cpp index 05d96b8..52db00a 100644 --- a/toonz/sources/tnztools/modifiers/modifierclone.cpp +++ b/toonz/sources/tnztools/modifiers/modifierclone.cpp @@ -16,8 +16,8 @@ TTrackPoint TModifierClone::Interpolator::interpolate(double index) // TModifierClone implementation //***************************************************************************************** -TModifierClone::TModifierClone(bool keepOriginals): - keepOriginals(keepOriginals) { } +TModifierClone::TModifierClone(bool keepOriginals, int skipFirst, int skipLast): + keepOriginals(keepOriginals), skipFirst(skipFirst), skipLast(skipLast) { } void TModifierClone::modifyTrack(const TTrack &track, TTrackList &outTracks) @@ -73,3 +73,15 @@ void TModifierClone::modifyTrack(const TTrack &track, track.resetChanges(); } +void TModifierClone::modifyTracks( + const TTrackList &tracks, + TTrackList &outTracks ) +{ + int cnt = (int)tracks.size(); + int i0 = skipFirst; + int i1 = cnt - skipLast; + for(int i = 0; i < cnt; ++i) + if (i0 <= i && i < i1) modifyTrack(*tracks[i], outTracks); + else TInputModifier::modifyTrack(*tracks[i], outTracks); +} + diff --git a/toonz/sources/tnztools/modifiers/modifierjitter.cpp b/toonz/sources/tnztools/modifiers/modifierjitter.cpp index 292bb82..61a5c72 100644 --- a/toonz/sources/tnztools/modifiers/modifierjitter.cpp +++ b/toonz/sources/tnztools/modifiers/modifierjitter.cpp @@ -81,20 +81,46 @@ static inline unsigned int trackSeedY(const TTrack &track) { //***************************************************************************************** -TModifierJitter::Interpolator::Interpolator(TTrack &track, double period, double amplitude): +TModifierJitter::Interpolator::Interpolator( + TTrack &track, + double period, + double amplitude, + bool keepFirstPoint, + bool keepLastPoint +): TTrackInterpolator(track), seedX(trackSeedX(track)), seedY(trackSeedY(track)), frequency(fabs(period) > TConsts::epsilon ? 1/period : 0), - amplitude(fabs(period) > TConsts::epsilon ? amplitude : 0) + amplitude(fabs(period) > TConsts::epsilon ? amplitude : 0), + keepFirstPoint(keepFirstPoint), + keepLastPoint(keepLastPoint) { } TTrackPoint TModifierJitter::Interpolator::interpolateFromOriginal(double originalIndex) { TTrackPoint p = track.calcPointFromOriginal(originalIndex); + double a = amplitude; double l = p.length*frequency; - p.position.x += Jitter::func(seedX, l)*amplitude; - p.position.y += Jitter::func(seedY, l)*amplitude; + if (frequency && a && track.original && (keepFirstPoint || keepLastPoint)) { + double ll = track.original->back().length*frequency; + if (l < 0) l = 0; + if (l > ll) l = ll; + if (ll < TConsts::epsilon) { + a = 0; + } else + if (keepFirstPoint && keepLastPoint && ll < 2) { + a *= 0.5 - 0.5*cos(l/ll*M_2PI); + } else + if (keepFirstPoint && l < 1) { + a *= 0.5 - 0.5*cos(l*M_PI); + } else + if (keepLastPoint && (ll - l) < 1) { + a *= 0.5 - 0.5*cos((ll - l)*M_PI); + } + } + p.position.x += Jitter::func(seedX, l)*a; + p.position.y += Jitter::func(seedY, l)*a; return p; } @@ -109,8 +135,21 @@ TTrackPoint TModifierJitter::Interpolator::interpolate(double index) //***************************************************************************************** -TModifierJitter::TModifierJitter(double period, double amplitude, int skipFirst): - period(period), amplitude(amplitude), skipFirst(skipFirst) { } +TModifierJitter::TModifierJitter( + double period, + double amplitude, + int skipFirst, + int skipLast, + bool keepFirstPoint, + bool keepLastPoint +): + period(period), + amplitude(amplitude), + skipFirst(skipFirst), + skipLast(skipLast), + keepFirstPoint(keepFirstPoint), + keepLastPoint(keepLastPoint) +{ } void TModifierJitter::modifyTrack(const TTrack &track, @@ -120,7 +159,7 @@ void TModifierJitter::modifyTrack(const TTrack &track, Handler *handler = new Handler(); track.handler = handler; handler->track = new TTrack(track); - new Interpolator(*handler->track, period, amplitude); + new Interpolator(*handler->track, period, amplitude, keepFirstPoint, keepLastPoint); } Handler *handler = dynamic_cast<Handler*>(track.handler.getPointer()); @@ -137,8 +176,14 @@ void TModifierJitter::modifyTrack(const TTrack &track, if (!intr) return; + bool preview = intr->keepLastPoint && intr->frequency && intr->amplitude; + int start = track.size() - track.pointsAdded; if (start < 0) start = 0; + if (preview) { + double l = track[start].length - 1/intr->frequency; + start = track.floorIndex( track.indexByLength(l) ); + } // process sub-track subTrack.truncate(start); @@ -146,7 +191,13 @@ void TModifierJitter::modifyTrack(const TTrack &track, subTrack.push_back(intr->interpolateFromOriginal(i), false); // fit points - subTrack.fix_to(track.fixedSize()); + if (track.fixedFinished() || !preview) { + subTrack.fix_to(track.fixedSize()); + } else + if (track.fixedSize()) { + double l = track[track.fixedSize() - 1].length - 1/intr->frequency; + subTrack.fix_to(subTrack.floorIndex( track.indexByLength(l) )); + } track.resetChanges(); } @@ -154,15 +205,15 @@ void TModifierJitter::modifyTrack(const TTrack &track, void TModifierJitter::modifyTracks( - const TTrackList &tracks, - TTrackList &outTracks ) + const TTrackList &tracks, + TTrackList &outTracks ) { - int cnt = std::min( std::max(0, skipFirst), (int)tracks.size() ); - TTrackList::const_iterator split = tracks.begin() + cnt; - for(TTrackList::const_iterator i = tracks.begin(); i != split; ++i) - TInputModifier::modifyTrack(**i, outTracks); - for(TTrackList::const_iterator i = split; i != tracks.end(); ++i) - modifyTrack(**i, outTracks); + int cnt = (int)tracks.size(); + int i0 = skipFirst; + int i1 = cnt - skipLast; + for(int i = 0; i < cnt; ++i) + if (i0 <= i && i < i1) modifyTrack(*tracks[i], outTracks); + else TInputModifier::modifyTrack(*tracks[i], outTracks); } diff --git a/toonz/sources/tnztools/replicator.cpp b/toonz/sources/tnztools/replicator.cpp index 6e63cb6..b7c9d27 100644 --- a/toonz/sources/tnztools/replicator.cpp +++ b/toonz/sources/tnztools/replicator.cpp @@ -24,7 +24,22 @@ const int TReplicator::multiplierLimit = 256; //************************************************************************ TReplicator::TReplicator(TMetaObject &object): - TAssistantBase(object) { } + TAssistantBase(object), + m_idSkipFirst("skipFirst"), + m_idSkipLast("skipLast") +{ + addProperty( createSpinProperty(m_idSkipFirst, getSkipFirst(), 0) ); + addProperty( createSpinProperty(m_idSkipLast, getSkipLast(), 0) ); +} + +//--------------------------------------------------------------------------------------------------- + +void +TReplicator::updateTranslation() const { + TAssistantBase::updateTranslation(); + setTranslation(m_idSkipFirst, tr("Skip First Tracks")); + setTranslation(m_idSkipLast, tr("Skip Last Tracks")); +} //--------------------------------------------------------------------------------------------------- @@ -57,9 +72,13 @@ TReplicator::createCountProperty(const TStringId &id, int def, int min, int max) //--------------------------------------------------------------------------------------------------- void -TReplicator::transformPoints(const TAffine &aff, PointList &points, int count) { - points.reserve(points.size() + count); - for(int i = 0; i < count; ++i) +TReplicator::transformPoints(const TAffine &aff, PointList &points, int i0, int i1) { + int cnt = (int)points.size(); + if (i0 < 0) i0 = 0; + if (i1 > cnt) i1 = cnt; + if (i0 >= i1) return; + points.reserve(points.size() + i1 - i0); + for(int i = i0; i < i1; ++i) points.push_back(aff*points[i]); } diff --git a/toonz/sources/toonzqt/intfield.cpp b/toonz/sources/toonzqt/intfield.cpp index e9ad097..918f28e 100644 --- a/toonz/sources/toonzqt/intfield.cpp +++ b/toonz/sources/toonzqt/intfield.cpp @@ -309,8 +309,13 @@ IntField::IntField(QWidget *parent, bool isMaxRangeLimited, bool isRollerHide, if (isSpinnerHide) enableSpinner(false); - layout->addWidget(m_inc); + // TODO: + // Commonly in OpenToonz spin-buttons has been placed in that order: [+][-] + // This seems unusual behavior. + // And in this particular case buttons has been placed in another order: [-][+] + // We need to know what is better layout->addWidget(m_dec); + layout->addWidget(m_inc); m_slider = new QSlider(Qt::Horizontal, this); ret = ret && connect(m_slider, SIGNAL(valueChanged(int)), this,