diff --git a/toonz/sources/common/tcore/tmathutil.cpp b/toonz/sources/common/tcore/tmathutil.cpp index 4ce50a9..1d31cab 100644 --- a/toonz/sources/common/tcore/tmathutil.cpp +++ b/toonz/sources/common/tcore/tmathutil.cpp @@ -738,6 +738,114 @@ int rootFinding(const std::vector &in_poly, std::vector &sol) { //----------------------------------------------------------------------------- +int solveEquation2(std::complex *roots, double a, double b, double c) { + if (isAlmostZero(a)) { + if (isAlmostZero(b)) return 0; + roots[0] = -c/b; + return 1; + } + + double D = b*b - a*c*4; + double k = 0.5/a; + std::complex d = D < 0 + ? std::complex(0, sqrt(-D)) + : std::complex(sqrt(D)); + roots[0] = (-b - d)*k; + roots[1] = (-b + d)*k; + return 2; +} + +//----------------------------------------------------------------------------- + +int solveEquation3(std::complex *roots, double a, double b, double c, double d) { + if (isAlmostZero(a)) + return solveEquation2(roots, b, c, d); + + // x = y - b/(3*a) + // y*y*y + p*y + q = 0 + double p = (3*a*c - b*b)/(3*a*a); + double q = (27*a*a*d - 9*a*b*c + 2*b*b*b)/(27*a*a*a); + + double Q = p*p*p/27 + q*q/4; + std::complex Qs = Q < 0 + ? std::complex(0, sqrt(-Q)) + : std::complex(sqrt(Q)); + std::complex A = pow(-q/2 + Qs, 1.0/3); + std::complex B = pow(-q/2 - Qs, 1.0/3); + + // choose complimentary B for A (A*B must be equal -p/3) + std::complex rot(-0.5, sqrt(3.0)/2); + if (!isAlmostZero(A*B + p/3)) B *= rot; + if (!isAlmostZero(A*B + p/3)) B *= rot; + + std::complex Y = (A - B)*std::complex(0, sqrt(3.0)); + std::complex y0 = A + B; + std::complex y1 = (-y0 - Y)/2.0; + std::complex y2 = (-y0 + Y)/2.0; + + double dd = b/(3*a); + roots[0] = y0 - dd; + roots[1] = y1 - dd; + roots[2] = y2 - dd; + return 3; +} + +//----------------------------------------------------------------------------- + +int solveEquation4(std::complex *roots, double a, double b, double c, double d, double e) { + if (isAlmostZero(a)) + return solveEquation3(roots, b, c, d, e); + + // x = y - b/(4*a) + // y^4 + p*y^2 + q*y + r = 0 + double dd = b/(4*a); + double p = (8*a*c - 3*b*b)/(8*a*a); + double q = (8*a*a*d - 4*a*b*c + b*b*b)/(8*a*a*a); + double r = (256*a*a*a*e - 64*a*a*b*d + 16*a*b*b*c - 3*b*b*b*b)/(256*a*a*a*a); + + if (isAlmostZero(q)) { + // biquadratic equation + // y^4 + p*y^2 + r = 0 + // handling this special case will give us more accurate results + std::complex y[2]; + solveEquation2(y, 1, p, r); + y[0] = sqrt(y[0]); + y[1] = sqrt(y[1]); + roots[0] = -y[0] - dd; + roots[1] = y[0] - dd; + roots[2] = -y[1] - dd; + roots[3] = y[1] - dd; + return 4; + } + + // solve cubic equation + // z*z*z + (p/2)*z*z + ((p*p - 4*r)/16)*z - q*q/64 = 0 + double pp = p/2; + double qq = (p*p - 4*r)/16; + double rr = -q*q/64; + std::complex z[3]; + solveEquation3(z, 1, pp, qq, rr); + + z[0] = sqrt(z[0]); + z[1] = sqrt(z[1]); + z[2] = sqrt(z[2]); + + // we need to find signs combination where following is valid: + // (+-z0)*(+-z1)*(+-z2) = -q/8 + // magnitudes are always equals, we need to check signs only + std::complex zzz = z[0]*z[1]*z[2]; + if ((zzz.real() > 0) == (q > 0)) + z[0] = -z[0]; + + roots[0] = z[0] - z[1] - z[2] - dd; + roots[1] = -z[0] + z[1] - z[2] - dd; + roots[2] = -z[0] - z[1] + z[2] - dd; + roots[3] = z[0] + z[1] + z[2] - dd; + return 4; +} + +//----------------------------------------------------------------------------- + /* */ int numberOfRootsInInterval(int order, const double *polyH, double min, diff --git a/toonz/sources/include/tgeometry.h b/toonz/sources/include/tgeometry.h index 7654e26..f5c9a81 100644 --- a/toonz/sources/include/tgeometry.h +++ b/toonz/sources/include/tgeometry.h @@ -39,10 +39,10 @@ class TPointT { public: T x, y; - TPointT() : x(0), y(0){}; - TPointT(T _x, T _y) : x(_x), y(_y){}; - TPointT(const TPointT &point) : x(point.x), y(point.y){}; - explicit TPointT(const TPoint4T &point); + 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 &point); inline TPointT &operator=(const TPointT &a) { x = a.x; @@ -77,11 +77,11 @@ public: T a[4]; }; - TPoint4T(): + inline TPoint4T(): x(), y(), z(), w() { }; - TPoint4T(T x, T y, T z, T w): + inline TPoint4T(T x, T y, T z, T w): x(x), y(y), z(z), w(w) { }; - explicit TPoint4T(const TPointT &p, T w = (T)1): + inline explicit TPoint4T(const TPointT &p, T w = (T)1): x(p.x), y(p.y), z(), w(w) { }; }; @@ -223,7 +223,7 @@ inline double tdistance2(const TPointD &p1, const TPointD &p2) { } inline bool operator==(const TPointD &p0, const TPointD &p1) { - return tdistance2(p0, p1) < TConsts::epsilon * TConsts::epsilon; + return tdistance2(p0, p1) <= TConsts::epsilon * TConsts::epsilon; } inline bool operator!=(const TPointD &p0, const TPointD &p1) { return !(p0 == p1); @@ -1474,7 +1474,7 @@ public: inline int operator-(const Iterator &i) const { assert(m_flip == i.m_flip && m_begin == i.m_begin && m_end == i.m_end && m_prebegin == i.m_prebegin); - int ii = m_current - i.m_current; + int ii = (int)(m_current - i.m_current); return ii < 0 ? ii + size() : ii; } diff --git a/toonz/sources/include/tmathutil.h b/toonz/sources/include/tmathutil.h index df56563..15fec88 100644 --- a/toonz/sources/include/tmathutil.h +++ b/toonz/sources/include/tmathutil.h @@ -7,6 +7,7 @@ #include "texception.h" #include +#include #ifndef __sgi #include @@ -128,6 +129,48 @@ DVAPI int rootFinding(const std::vector &poly, //----------------------------------------------------------------------------- /*! + Find complex roots of a quadratic equation + a*x^2 + b*x + c = 0 + \ret count of roots found: + 0 if a and b are almost zero + 1 if a is almost zero + 2 if a is not almost zero + roots may have duplicates, count depends only from type of equation + */ +DVAPI int solveEquation2(std::complex *roots, double a, double b, double c); + +//----------------------------------------------------------------------------- + +/*! + Find complex roots of a cubic equation: + a*x^3 + b*x^2 + c*x + d = 0 + \ret count of roots found: + 0 if a, b and c are almost zero + 1 if a and b are almost zero + 2 if a is almost zero + 3 if a is not almost zero + roots may have duplicates, count depends only from type of equation + */ +DVAPI int solveEquation3(std::complex *roots, double a, double b, double c, double d); + +//----------------------------------------------------------------------------- + +/*! + Find complex roots of a power of four equation: + a*x^4 + b*x^3 + c*x^2 + d*x + e = 0 + \ret count of roots found: + 0 if a, b, c and d are almost zero + 1 if a, b and c are almost zero + 2 if a and b are almost zero + 3 if a is almost zero + 4 if a is not almost zero + roots may have duplicates, count depends only from type of equation + */ +DVAPI int solveEquation4(std::complex *roots, double a, double b, double c, double d, double e); + +//----------------------------------------------------------------------------- + +/*! Check if val is nearest to epsilon. \par val value to test \par eps tolerance required @@ -140,6 +183,19 @@ inline bool isAlmostZero(double val, double eps = TConsts::epsilon) { //----------------------------------------------------------------------------- /*! + Check if complex val is nearest to epsilon. + \par val value to test + \par eps tolerance required + \ret true if value is nearest to zero + */ +inline bool isAlmostZero(std::complex val, double eps = TConsts::epsilon) { + return val.real()*val.real() + val.imag()*val.imag() < eps*eps; +} + + +//----------------------------------------------------------------------------- + +/*! Find number of roots of a poly in interval min max. \par order degree of polynomious \par coeff of poly in crescent order diff --git a/toonz/sources/include/tools/assistant.h b/toonz/sources/include/tools/assistant.h index c2b2322..14931aa 100644 --- a/toonz/sources/include/tools/assistant.h +++ b/toonz/sources/include/tools/assistant.h @@ -66,8 +66,15 @@ public: virtual TTrackPoint transformPoint(const TTrackPoint &point) const { return point; } + + // this function uses in calcTrackWeight to select best guideline, + // does not use for drawing + virtual TPointD nearestPoint(const TPointD &point) const + { return transformPoint(TTrackPoint(point)).position; } + virtual void draw(bool active, bool enabled) const { } + void draw(bool active = false) const { draw(active, true); } @@ -304,6 +311,7 @@ protected: 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; diff --git a/toonz/sources/include/tools/assistants/guidelineellipse.h b/toonz/sources/include/tools/assistants/guidelineellipse.h index c8b1607..faefda3 100644 --- a/toonz/sources/include/tools/assistants/guidelineellipse.h +++ b/toonz/sources/include/tools/assistants/guidelineellipse.h @@ -28,17 +28,27 @@ class DVAPI TGuidelineEllipse : public TGuideline { public: const TAffine matrix; const TAffine matrixInv; + const double Rx; + const double Ry; TGuidelineEllipse( bool enabled, double magnetism, - TAffine matrix ); + const TAffine &matrix ); TGuidelineEllipse( bool enabled, double magnetism, - TAffine matrix, - TAffine matrixInv ); + const TAffine &matrix, + const TAffine &matrixInv ); + + TGuidelineEllipse( + bool enabled, + double magnetism, + const TAffine &matrix, + const TAffine &matrixInv, + double Rx, + double Ry ); //! returns false when ellipse is invisible static bool truncateEllipse( @@ -49,6 +59,7 @@ public: static int calcSegmentsCount(const TAffine &ellipseMatrix, double pixelSize); TTrackPoint transformPoint(const TTrackPoint &point) const override; + TPointD nearestPoint(const TPointD &point) const override; void draw(bool active, bool enabled) const override; }; diff --git a/toonz/sources/include/tools/inputmanager.h b/toonz/sources/include/tools/inputmanager.h index 7d9a58d..323dba1 100644 --- a/toonz/sources/include/tools/inputmanager.h +++ b/toonz/sources/include/tools/inputmanager.h @@ -47,104 +47,6 @@ typedef TSmartPointerT TInputModifierP; //***************************************************************************************** -// TInputSavePoint definition -//***************************************************************************************** - -class DVAPI TInputSavePoint { -public: - class DVAPI Holder { - private: - TInputSavePoint *m_savePoint; - bool m_lock = false; - - public: - inline explicit Holder(TInputSavePoint *savePoint = NULL, bool lock = true): - m_savePoint(), m_lock() - { set(savePoint, lock); } - inline Holder(const Holder &other): - m_savePoint(), m_lock() - { *this = other; } - inline ~Holder() - { reset(); } - - inline Holder& operator= (const Holder &other) - { set(other.m_savePoint, other.m_lock); return *this; } - - inline operator bool () const - { return assigned(); } - - inline void set(TInputSavePoint *savePoint, bool lock) { - if (m_savePoint != savePoint) { - if (m_savePoint) { - if (m_lock) m_savePoint->unlock(); - m_savePoint->release(); - } - m_savePoint = savePoint; - m_lock = lock; - if (m_savePoint) { - m_savePoint->hold(); - if (m_lock) savePoint->lock(); - } - } else - if (m_lock != lock) { - if (m_savePoint) { - if (lock) m_savePoint->lock(); - else m_savePoint->unlock(); - } - m_lock = lock; - } - } - - inline void reset() - { set(NULL, false); } - inline void setLock(bool lock) - { set(m_savePoint, lock); } - inline void lock() - { setLock(true); } - inline void unlock() - { setLock(false); } - - inline TInputSavePoint* savePoint() const - { return m_savePoint; } - inline bool assigned() const - { return savePoint(); } - inline bool locked() const - { return m_savePoint && m_lock; } - inline bool available() const - { return m_savePoint && m_savePoint->available; } - inline bool isFree() const - { return !m_savePoint || m_savePoint->isFree(); } - }; - - typedef std::vector List; - -private: - int m_refCount; - int m_lockCount; - - inline void hold() - { ++m_refCount; } - inline void release() - { if ((--m_refCount) <= 0) delete this; } - inline void lock() - { ++m_lockCount; } - inline void unlock() - { --m_lockCount; } - -public: - bool available; - - inline explicit TInputSavePoint(bool available = false): - m_refCount(), m_lockCount(), available(available) { } - inline bool isFree() const - { return m_lockCount <= 0; } - - static inline Holder create(bool available = false) - { return Holder(new TInputSavePoint(available)); } -}; - - -//***************************************************************************************** // TInputModifier definition //***************************************************************************************** @@ -164,11 +66,9 @@ public: virtual void modifyTrack( const TTrack &track, - const TInputSavePoint::Holder &savePoint, TTrackList &outTracks ); virtual void modifyTracks( const TTrackList &tracks, - const TInputSavePoint::Holder &savePoint, TTrackList &outTracks ); virtual void modifyHover( @@ -221,30 +121,11 @@ public: virtual void inputHoverEvent(const TInputManager &manager); - /*! paint single track-point at the top painting level */ - virtual void inputPaintTrackPoint(const TTrackPoint &point, const TTrack &track, bool firstTrack); - - /*! create new painting level and return true, or do nothing and return false - was: ------O-------O------ - become: ------O-------O------O */ - virtual bool inputPaintPush() { return false; } - /*! paint several track-points at the top painting level - was: ------O-------O------ - become: ------O-------O------------ */ + virtual void inputPaintTracksBegin() { } + virtual void inputPaintTrackPoint(const TTrackPoint &point, const TTrack &track, bool firstTrack, bool preview); + virtual void inputPaintTracksEnd() { } virtual void inputPaintTracks(const TTrackList &tracks); - /*! try to merge N top painting levels and return count of levels that actually merged - was: ------O-------O------O------ - become (N = 2): ------O--------------------- */ - virtual int inputPaintApply(int count) { return 0; } - /*! reset top level to initial state - was: ------O-------O------O------ - become: ------O-------O------O */ - virtual void inputPaintCancel() { } - /*! cancel and pop N painting levels - was: ------O-------O------O------ - become (N = 2): ------O------- */ - virtual void inputPaintPop(int count) { } - + virtual void inputInvalidateRect(const TRectD &bounds) { } virtual TTool* inputGetTool() { return nullptr; }; @@ -256,26 +137,15 @@ public: //***************************************************************************************** class DVAPI TInputManager { -public: - class TrackHandler: public TTrackHandler { - public: - std::vector saves; - TrackHandler(TTrack &original, int keysCount = 0): - TTrackHandler(original), saves(keysCount, 0) - { } - }; - private: TTimerTicks m_lastTicks; TInputHandler *m_handler; TInputModifier::List m_modifiers; std::vector m_tracks; std::vector m_hovers; - TInputSavePoint::List m_savePoints; TRectD m_prevBounds; TRectD m_nextBounds; bool m_started; - int m_savePointsSent; static TInputState::TouchId m_lastTouchId; @@ -294,8 +164,6 @@ private: return m_lastTicks = ticks; } - void paintRollbackTo(int saveIndex, TTrackList &subTracks); - void paintApply(int count, TTrackList &subTracks); void paintTracks(); int trackCompare( diff --git a/toonz/sources/include/tools/modifiers/modifierassistants.h b/toonz/sources/include/tools/modifiers/modifierassistants.h index 3dcd35d..0ba8059 100644 --- a/toonz/sources/include/tools/modifiers/modifierassistants.h +++ b/toonz/sources/include/tools/modifiers/modifierassistants.h @@ -29,7 +29,6 @@ public: class DVAPI Modifier: public TTrackModifier { public: bool initialized; - TInputSavePoint::Holder savePoint; TGuidelineList guidelines; Modifier(TTrackHandler &handler); @@ -52,7 +51,6 @@ public: void modifyTrack( const TTrack &track, - const TInputSavePoint::Holder &savePoint, TTrackList &outTracks ) override; TRectD calcDrawBounds(const TTrackList &tracks, const THoverList &hovers) override; diff --git a/toonz/sources/include/tools/modifiers/modifierline.h b/toonz/sources/include/tools/modifiers/modifierline.h index a6966a2..495f711 100644 --- a/toonz/sources/include/tools/modifiers/modifierline.h +++ b/toonz/sources/include/tools/modifiers/modifierline.h @@ -33,14 +33,12 @@ public: bool fixAngle; double maxPressure; - TInputSavePoint::Holder savePoint; - + TTrackPoint calcPoint(double originalIndex) override; }; void modifyTrack( const TTrack &track, - const TInputSavePoint::Holder &savePoint, TTrackList &outTracks ) override; }; diff --git a/toonz/sources/include/tools/modifiers/modifiersegmentation.h b/toonz/sources/include/tools/modifiers/modifiersegmentation.h index af4278c..48d8872 100644 --- a/toonz/sources/include/tools/modifiers/modifiersegmentation.h +++ b/toonz/sources/include/tools/modifiers/modifiersegmentation.h @@ -27,18 +27,21 @@ class DVAPI TModifierSegmentation: public TInputModifier { private: TPointD m_step; + int m_maxLevel; - void addSegments(TTrack &track, const TTrackPoint &p0, const TTrackPoint &p1, int level = 0); + void addSegments(TTrack &track, const TTrackPoint &p0, const TTrackPoint &p1, int maxLevel); public: - TModifierSegmentation(const TPointD &step = TPointD(1.0, 1.0)); + TModifierSegmentation(const TPointD &step = TPointD(1.0, 1.0), int level = 10); void setStep(const TPointD &step); - const TPointD& getStep() const { return m_step; } + inline const TPointD& getStep() const { return m_step; } + + void setMaxLevel(int maxLevel); + inline int getMaxLevel() const { return m_maxLevel; } void modifyTrack( const TTrack &track, - const TInputSavePoint::Holder &savePoint, TTrackList &outTracks ) override; }; diff --git a/toonz/sources/include/tools/modifiers/modifiersmooth.h b/toonz/sources/include/tools/modifiers/modifiersmooth.h new file mode 100644 index 0000000..6968d38 --- /dev/null +++ b/toonz/sources/include/tools/modifiers/modifiersmooth.h @@ -0,0 +1,54 @@ +#pragma once + +#ifndef MODIFIERSMOOTH_INCLUDED +#define MODIFIERSMOOTH_INCLUDED + +// TnzTools includes +#include + + +#undef DVAPI +#undef DVVAR +#ifdef TNZTOOLS_EXPORTS +#define DVAPI DV_EXPORT_API +#define DVVAR DV_EXPORT_VAR +#else +#define DVAPI DV_IMPORT_API +#define DVVAR DV_IMPORT_VAR +#endif + + +//=================================================================== + +//***************************************************************************************** +// TModifierSmooth definition +//***************************************************************************************** + +class DVAPI TModifierSmooth: public TInputModifier { +public: + class DVAPI Modifier: public TTrackModifier { + public: + const int radius; + TTrack *smoothedTrack; + + Modifier(TTrackHandler &handler, int radius); + TTrackPoint calcPoint(double originalIndex) override; + }; + +private: + int m_radius; + +public: + TModifierSmooth(int radius = 10); + + void setRadius(int radius); + int getRadius() const { return m_radius; } + + void modifyTrack( + const TTrack &track, + TTrackList &outTracks ) override; +}; + + +#endif + diff --git a/toonz/sources/include/tools/modifiers/modifiertangents.h b/toonz/sources/include/tools/modifiers/modifiertangents.h index 8ead779..a7e6232 100644 --- a/toonz/sources/include/tools/modifiers/modifiertangents.h +++ b/toonz/sources/include/tools/modifiers/modifiertangents.h @@ -31,17 +31,15 @@ public: explicit Modifier(TTrackHandler &handler): TTrackModifier(handler) { } - TInputSavePoint::Holder savePoint; TTrackTangentList tangents; TTrackPoint calcPoint(double originalIndex) override; }; - TTrackTangent calcLastTangent(const TTrack &track) const; + TTrackTangent calcTangent(const TTrack &track, int index) const; void modifyTrack( const TTrack &track, - const TInputSavePoint::Holder &savePoint, TTrackList &outTracks ) override; }; diff --git a/toonz/sources/include/tools/modifiers/modifiertest.h b/toonz/sources/include/tools/modifiers/modifiertest.h index 91911af..e410547 100644 --- a/toonz/sources/include/tools/modifiers/modifiertest.h +++ b/toonz/sources/include/tools/modifiers/modifiertest.h @@ -50,7 +50,6 @@ public: TModifierTest(int count, double radius); void modifyTrack(const TTrack &track, - const TInputSavePoint::Holder &savePoint, TTrackList &outTracks) override; }; diff --git a/toonz/sources/include/tools/track.h b/toonz/sources/include/tools/track.h index eb12057..c9268d4 100644 --- a/toonz/sources/include/tools/track.h +++ b/toonz/sources/include/tools/track.h @@ -183,10 +183,12 @@ public: mutable TTrackToolHandlerP toolHandler; mutable int pointsRemoved; mutable int pointsAdded; + mutable int fixedPointsAdded; private: TTrackPointList m_points; const TTrackPoint m_none; + int m_pointsFixed; public: @@ -208,7 +210,7 @@ public: inline TTimerTicks ticks() const { return keyHistory.ticks(); } inline bool changed() const - { return pointsAdded != 0 || pointsRemoved != 0; } + { return pointsRemoved || pointsAdded || fixedPointsAdded; } const TTrack* root() const; int level() const; @@ -236,6 +238,10 @@ public: inline int size() const { return (int)m_points.size(); } + inline int fixedSize() const + { return m_pointsFixed; } + inline int previewSize() const + { return size() - fixedSize(); } inline bool empty() const { return m_points.empty(); } inline const TTrackPoint& front() const @@ -244,6 +250,8 @@ public: { return point(size() - 1); } inline bool finished() const { return !m_points.empty() && back().final; } + inline bool fixedFinished() const + { return finished() && !previewSize(); } inline const TTrackPoint& operator[] (int index) const { return point(index); } inline const TTrackPointList& points() const @@ -253,14 +261,21 @@ public: { pointsRemoved = 0; } inline void resetAdded() const { pointsAdded = 0; } + inline void resetFixedAdded() const + { fixedPointsAdded = 0; } inline void resetChanges() const - { resetRemoved(); resetAdded(); } + { resetRemoved(); resetAdded(); resetFixedAdded(); } - void push_back(const TTrackPoint &point); + void push_back(const TTrackPoint &point, bool fixed); void pop_back(int count = 1); + void fix_points(int count = 1); inline void truncate(int count) { pop_back(size() - count); } + inline void fix_to(int count) + { fix_points(count - fixedSize()); } + inline void fix_all() + { fix_to(size()); } inline const TTrackPoint& current() const { return point(size() - pointsAdded); } @@ -381,9 +396,9 @@ public: return TTrackPoint( interpolationSpline(p0.position , p1.position , t0.position , t1.position , l), interpolationLinear(p0.pressure , p1.pressure , l), - //interpolationSpline(p0.pressure , p1.pressure , t0.pressure , t1.pressure , l), + //interpolationSpline(p0.pressure , p1.pressure , t0.pressure , t1.pressure , l), interpolationLinear(p0.tilt , p1.tilt , l), - //interpolationSpline(p0.tilt , p1.tilt , t0.tilt , t1.tilt , l), + //interpolationSpline(p0.tilt , p1.tilt , t0.tilt , t1.tilt , l), interpolationLinear(p0.originalIndex , p1.originalIndex , l), interpolationLinear(p0.time , p1.time , l), interpolationLinear(p0.length , p1.length , l), diff --git a/toonz/sources/include/toonz/rasterstrokegenerator.h b/toonz/sources/include/toonz/rasterstrokegenerator.h index e2c7857..1de01d3 100644 --- a/toonz/sources/include/toonz/rasterstrokegenerator.h +++ b/toonz/sources/include/toonz/rasterstrokegenerator.h @@ -70,7 +70,7 @@ public: void generateStroke(bool isPencil, bool isStraight = false) const; // Ritorna m_points - std::vector getPointsSequence() { return m_points; } + const std::vector& getPointsSequence() { return m_points; } void setPointsSequence(const std::vector &points) { m_points = points; } diff --git a/toonz/sources/tnztools/CMakeLists.txt b/toonz/sources/tnztools/CMakeLists.txt index 101eb4e..2675853 100644 --- a/toonz/sources/tnztools/CMakeLists.txt +++ b/toonz/sources/tnztools/CMakeLists.txt @@ -54,7 +54,7 @@ set(HEADERS ../include/tools/modifiers/modifiertangents.h ../include/tools/modifiers/modifiertest.h ../include/tools/modifiers/modifiersegmentation.h - ../include/tools/modifiers/modifierassistants.h + ../include/tools/modifiers/modifiersmooth.h ../include/tools/assistants/guidelineline.h ../include/tools/assistants/guidelineellipse.h ) @@ -130,6 +130,7 @@ set(SOURCES modifiers/modifiertangents.cpp modifiers/modifiertest.cpp modifiers/modifiersegmentation.cpp + modifiers/modifiersmooth.cpp assistants/guidelineline.cpp assistants/guidelineellipse.cpp assistants/assistantvanishingpoint.cpp diff --git a/toonz/sources/tnztools/assistant.cpp b/toonz/sources/tnztools/assistant.cpp index 5e0ec9c..826ab4e 100644 --- a/toonz/sources/tnztools/assistant.cpp +++ b/toonz/sources/tnztools/assistant.cpp @@ -78,14 +78,15 @@ TGuideline::calcTrackWeight(const TTrack &track, const TAffine &toScreen, bool & double weight = length*logNormalDistribuitionUnscaled(midStepLength, snapLenght, snapScale); sumWeight += weight; - TTrackPoint ntp = transformPoint(mid); - double deviation = tdistance(toScreen*ntp.position, p); + double deviation = tdistance( + toScreen*mid.position, + toScreen*nearestPoint(mid.position) ); sumDeviation += weight*deviation; } prev = p; if (sumLength >= maxLength) - { outLongEnough = true; break; } + { outLongEnough = i < track.fixedSize(); break; } } return sumWeight > TConsts::epsilon ? sumDeviation/sumWeight @@ -429,6 +430,14 @@ TAssistant::drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize, //--------------------------------------------------------------------------------------------------- void +TAssistant::drawMark(const TPointD &p, const TPointD &normal, double pixelSize, double alpha) const { + TPointD d = normal*5*pixelSize; + drawSegment(p - d,p + d, pixelSize, alpha); +} + +//--------------------------------------------------------------------------------------------------- + +void TAssistant::drawDot(const TPointD &p, double alpha) const { double colorBlack[4] = { 0.0, 0.0, 0.0, alpha }; double colorWhite[4] = { 1.0, 1.0, 1.0, alpha }; diff --git a/toonz/sources/tnztools/assistants/assistantellipse.cpp b/toonz/sources/tnztools/assistants/assistantellipse.cpp index 236c6a7..db6055e 100644 --- a/toonz/sources/tnztools/assistants/assistantellipse.cpp +++ b/toonz/sources/tnztools/assistants/assistantellipse.cpp @@ -249,11 +249,9 @@ private: void drawRuler(const TAffine &ellipseMatrix, double pixelSize) const { double minStep = 10.0*pixelSize; - double alpha = getDrawingGridAlpha(); + double alpha = getDrawingAlpha(); - 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; + TAffine em = ellipseMatrix; TAffine ellipseMatrixInv = ellipseMatrix.inv(); TPointD g0 = ellipseMatrixInv*m_grid0.position; TPointD g1 = ellipseMatrixInv*m_grid1.position; @@ -262,22 +260,55 @@ private: 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 x = begin; fabs(x) < fabs(end); x *= k) - drawDot(ellipseMatrix * TPointD(cos(x), (ga0 < 0.0 ? -1.0 : 1.0)*sin(x))); + 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); + } + } } 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) - drawDot(ellipseMatrix * TPointD(cos(a), sin(a))); + 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); + } + } } } diff --git a/toonz/sources/tnztools/assistants/assistantline.cpp b/toonz/sources/tnztools/assistants/assistantline.cpp index a306f7b..d87134f 100644 --- a/toonz/sources/tnztools/assistants/assistantline.cpp +++ b/toonz/sources/tnztools/assistants/assistantline.cpp @@ -159,13 +159,14 @@ public: private: void drawRuler(const TPointD &a, const TPointD &b, double pixelSize, bool perspective) const { double minStep = 10.0*pixelSize; - double alpha = getDrawingGridAlpha(); + 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); @@ -176,13 +177,13 @@ private: 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) - drawDot(a + direction*(xa0 + x), alpha); + 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) - drawDot(a + direction*x, alpha); + drawMark(a + direction*x, normal, pixelSize, alpha); } } @@ -318,7 +319,14 @@ public: success = TGuidelineLineBase::truncateRay(oneBox, bb, aa); else success = TGuidelineLineBase::truncateInfiniteLine(oneBox, aa, bb); - if (!success) return; + + 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; diff --git a/toonz/sources/tnztools/assistants/guidelineellipse.cpp b/toonz/sources/tnztools/assistants/guidelineellipse.cpp index 51014c2..c18934f 100644 --- a/toonz/sources/tnztools/assistants/guidelineellipse.cpp +++ b/toonz/sources/tnztools/assistants/guidelineellipse.cpp @@ -3,32 +3,112 @@ #include // TnzCore includes -#include "tgl.h" +#include +#include + + + +// returns a point nearest to the ellipse with center in zero +static TPointD findNearestPoint(const TPointD &p, double Rx, double Ry) { + Rx = fabs(Rx); + Ry = fabs(Ry); + TPointD ep; + + if (isAlmostZero(Rx)) { + ep.y = p.y < -Ry ? -Ry + : p.y > Ry ? Ry : p.y; + return ep; + } + + if (isAlmostZero(Ry)) { + ep.x = p.x < -Rx ? -Rx + : p.x > Rx ? Rx : p.x; + return ep; + } + + double k = 1/Rx; + double x0 = p.x*k; + double y0 = p.y*k; + k *= Ry; + k *= k; // k = (Ry/Rx)^2 + double l = k - 1; + + double a = l*l; + double b = 2*l*x0; + double c = x0*x0 + y0*y0*k - l*l; + double d = -b; + double e = -x0*x0; + + double dist = INFINITY; + std::complex roots[4]; + int cnt = solveEquation4(roots, a, b, c, d, e); + for(int i = 0; i < cnt; ++i) { + if (!isAlmostZero(roots[i].imag())) + continue; + + double x = roots[i].real(); + double y; + if (isAlmostZero(fabs(x) - 1)) { + y = 0; + } else + if (fabs(x) < 1) { + y = sqrt(k*(1 - x*x)); + if (y0 < 0) y = -y; + } else { + continue; + } + + double dd = (x0-x)*(x0-x) + (y0-y)*(y0-y); + if (dd < dist) + { ep.x = x*Rx; ep.y = y*Rx; dist = dd; } + } + return ep; +} + //***************************************************************************************** // TGuidelineEllipse implementation //***************************************************************************************** + TGuidelineEllipse::TGuidelineEllipse( bool enabled, double magnetism, - TAffine matrix + const TAffine &matrix, + const TAffine &matrixInv, + double Rx, + double Ry ): TGuideline(enabled, magnetism), matrix(matrix), - matrixInv(matrix.inv()) { } + matrixInv(matrixInv), + Rx(Rx), + Ry(Ry) + { } TGuidelineEllipse::TGuidelineEllipse( bool enabled, double magnetism, - TAffine matrix, - TAffine matrixInv + const TAffine &matrix, + const TAffine &matrixInv ): - TGuideline(enabled, magnetism), - matrix(matrix), - matrixInv(matrixInv) { } + TGuidelineEllipse( + enabled, magnetism, matrix, matrixInv, + sqrt(matrix.a11*matrix.a11 + matrix.a21*matrix.a21), + sqrt(matrix.a12*matrix.a12 + matrix.a22*matrix.a22) ) + { } + + +TGuidelineEllipse::TGuidelineEllipse( + bool enabled, + double magnetism, + const TAffine &matrix +): + TGuidelineEllipse(enabled, magnetism, matrix, matrix.inv()) + { } + TTrackPoint @@ -43,6 +123,18 @@ TGuidelineEllipse::transformPoint(const TTrackPoint &point) const } +TPointD +TGuidelineEllipse::nearestPoint(const TPointD &point) const +{ + TPointD p = matrixInv*point; + p = findNearestPoint(TPointD(p.x*Rx, p.y*Ry), Rx, Ry); + if (!isAlmostZero(Rx)) p.x /= Rx; + if (!isAlmostZero(Ry)) p.y /= Ry; + return matrix*p; +} + + + bool TGuidelineEllipse::truncateEllipse( TAngleRangeSet &ranges, diff --git a/toonz/sources/tnztools/assistants/guidelineline.cpp b/toonz/sources/tnztools/assistants/guidelineline.cpp index 9018ccb..cc8707a 100644 --- a/toonz/sources/tnztools/assistants/guidelineline.cpp +++ b/toonz/sources/tnztools/assistants/guidelineline.cpp @@ -73,12 +73,14 @@ TGuidelineLineBase::truncateInfiniteLine(const TRectD &bounds, TPointD &p0, TPoi bool TGuidelineLineBase::truncateRay(const TRectD &bounds, TPointD &p0, TPointD &p1) { if (bounds.isEmpty()) return false; - TRectD b(bounds); - if (p0.x <= p1.x) { if (b.x0 < p0.x) b.x0 = p0.x; } - else { if (b.x1 > p0.x) b.x1 = p0.x; } - if (p0.y <= p1.y) { if (b.y0 < p0.y) b.y0 = p0.y; } - else { if (b.y1 > p0.y) b.y1 = p0.y; } + // restrict bounds by p0 + // slightly expand this restriction to include vert and hor rays + TRectD b(bounds); + if (p0.x <= p1.x) { if (b.x0 < p0.x - TConsts::epsilon) b.x0 = p0.x - TConsts::epsilon; } + else { if (b.x1 > p0.x + TConsts::epsilon) b.x1 = p0.x + TConsts::epsilon; } + if (p0.y <= p1.y) { if (b.y0 < p0.y - TConsts::epsilon) b.y0 = p0.y - TConsts::epsilon; } + else { if (b.y1 > p0.y + TConsts::epsilon) b.y1 = p0.y + TConsts::epsilon; } if (b.isEmpty()) return false; return truncateInfiniteLine(b, p0, p1); @@ -86,7 +88,13 @@ TGuidelineLineBase::truncateRay(const TRectD &bounds, TPointD &p0, TPointD &p1) bool TGuidelineLineBase::truncateLine(const TRectD &bounds, TPointD &p0, TPointD &p1) { - TRectD b = bounds * boundingBox(p0, p1); + // restrict bounds by p0 and p1 + // slightly expand this restriction to include vert and hor lines + TRectD b( std::min(p0.x, p1.x) - TConsts::epsilon, + std::min(p0.y, p1.y) - TConsts::epsilon, + std::max(p0.x, p1.x) + TConsts::epsilon, + std::max(p0.y, p1.y) + TConsts::epsilon ); + b *= bounds; if (b.isEmpty()) return false; return truncateInfiniteLine(b, p0, p1); } diff --git a/toonz/sources/tnztools/bluredbrush.cpp b/toonz/sources/tnztools/bluredbrush.cpp index 44f5214..61ee125 100644 --- a/toonz/sources/tnztools/bluredbrush.cpp +++ b/toonz/sources/tnztools/bluredbrush.cpp @@ -197,7 +197,14 @@ BluredBrush::~BluredBrush() {} //---------------------------------------------------------------------------------- -void BluredBrush::addPoint(const TThickPoint &p, double opacity) { +void BluredBrush::addPoint(const TThickPoint &p, double opacity, bool keepDistance) { + if (keepDistance) { + double dist = norm2(p - m_lastPoint); + double d = 0.12 * m_lastPoint.thick; + if (dist < d*d) + return; + } + double radius = p.thick * 0.5; double brushRadius = m_size * 0.5; double scaleFactor = radius / brushRadius; diff --git a/toonz/sources/tnztools/bluredbrush.h b/toonz/sources/tnztools/bluredbrush.h index 3f7a32c..650a001 100644 --- a/toonz/sources/tnztools/bluredbrush.h +++ b/toonz/sources/tnztools/bluredbrush.h @@ -34,7 +34,7 @@ public: bool doDynamicOpacity); ~BluredBrush(); - void addPoint(const TThickPoint &p, double opacity); + void addPoint(const TThickPoint &p, double opacity, bool keepDistance = false); void addArc(const TThickPoint &pa, const TThickPoint &pb, const TThickPoint &pc, double opacityA, double opacityC); TRect getBoundFromPoints(const std::vector &points) const; diff --git a/toonz/sources/tnztools/fullcolorbrushtool.cpp b/toonz/sources/tnztools/fullcolorbrushtool.cpp index cdc7a4c..a2f3c28 100644 --- a/toonz/sources/tnztools/fullcolorbrushtool.cpp +++ b/toonz/sources/tnztools/fullcolorbrushtool.cpp @@ -306,17 +306,17 @@ bool FullColorBrushTool::askWrite(const TRect &rect) { bool FullColorBrushTool::preLeftButtonDown() { m_modifierAssistants->drawOnly = !FullcolorAssistants; - m_inputmanager.drawPreview = false; //! m_modifierAssistants->drawOnly; + m_inputmanager.drawPreview = false; //!m_modifierAssistants->drawOnly; m_inputmanager.clearModifiers(); m_inputmanager.addModifier(TInputModifierP(m_modifierTangents.getPointer())); m_inputmanager.addModifier( TInputModifierP(m_modifierAssistants.getPointer())); - m_inputmanager.addModifier( - TInputModifierP(m_modifierSegmentation.getPointer())); #ifndef NDEBUG m_inputmanager.addModifier(TInputModifierP(m_modifierTest.getPointer())); #endif + m_inputmanager.addModifier( + TInputModifierP(m_modifierSegmentation.getPointer())); touchImage(); @@ -505,9 +505,13 @@ void FullColorBrushTool::inputSetBusy(bool busy) { void FullColorBrushTool::inputPaintTrackPoint(const TTrackPoint &point, const TTrack &track, - bool firstTrack) { + bool firstTrack, + bool preview) +{ // get raster - if (!m_started || !getViewer()) return; + if (!m_started || !getViewer() || preview) + return; + TRasterImageP ri = (TRasterImageP)getImage(true); if (!ri) return; TRasterP ras = ri->getRaster(); diff --git a/toonz/sources/tnztools/fullcolorbrushtool.h b/toonz/sources/tnztools/fullcolorbrushtool.h index 239ea92..cdb8450 100644 --- a/toonz/sources/tnztools/fullcolorbrushtool.h +++ b/toonz/sources/tnztools/fullcolorbrushtool.h @@ -81,7 +81,7 @@ public: const TInputState &state) override; void inputSetBusy(bool busy) override; void inputPaintTrackPoint(const TTrackPoint &point, const TTrack &track, - bool firstTrack) override; + bool firstTrack, bool preview) override; void inputInvalidateRect(const TRectD &bounds) override; TTool *inputGetTool() override { return this; }; diff --git a/toonz/sources/tnztools/inputmanager.cpp b/toonz/sources/tnztools/inputmanager.cpp index bc00bb9..06b7f8d 100644 --- a/toonz/sources/tnztools/inputmanager.cpp +++ b/toonz/sources/tnztools/inputmanager.cpp @@ -1,5 +1,6 @@ +#include #include // TnzCore includes @@ -29,7 +30,6 @@ TInputModifier::setManager(TInputManager *manager) { void TInputModifier::modifyTrack( const TTrack &track, - const TInputSavePoint::Holder &savePoint, TTrackList &outTracks ) { if (!track.handler) { @@ -51,7 +51,8 @@ TInputModifier::modifyTrack( TTrack &subTrack = **ti; subTrack.truncate(start); for(int i = start; i < track.size(); ++i) - subTrack.push_back( subTrack.modifier->calcPoint(i) ); + subTrack.push_back( subTrack.modifier->calcPoint(i), false ); + subTrack.fix_to(track.fixedSize()); } track.resetChanges(); } @@ -60,11 +61,10 @@ TInputModifier::modifyTrack( void TInputModifier::modifyTracks( const TTrackList &tracks, - const TInputSavePoint::Holder &savePoint, TTrackList &outTracks ) { for(TTrackList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) - modifyTrack(**i, savePoint, outTracks); + modifyTrack(**i, outTracks); } @@ -127,9 +127,9 @@ TInputModifier::draw(const TTrackList &tracks, const std::vector &hover bool TInputHandler::inputKeyEvent( bool press, - TInputState::Key key, + TInputState::Key, QKeyEvent *event, - const TInputManager &manager ) + const TInputManager& ) { return press && event && inputKeyDown(event); } @@ -138,7 +138,7 @@ TInputHandler::inputKeyEvent( void TInputHandler::inputButtonEvent( bool press, - TInputState::DeviceId device, + TInputState::DeviceId, TInputState::Button button, const TInputManager &manager ) { @@ -155,8 +155,8 @@ TInputHandler::inputHoverEvent(const TInputManager &manager) { void -TInputHandler::inputPaintTrackPoint(const TTrackPoint &point, const TTrack &track, bool firstTrack) { - if (firstTrack) { +TInputHandler::inputPaintTrackPoint(const TTrackPoint &point, const TTrack &track, bool firstTrack, bool preview) { + if (firstTrack && !preview) { if (track.pointsAdded == track.size()) inputLeftButtonDown(point, track); else @@ -164,14 +164,25 @@ TInputHandler::inputPaintTrackPoint(const TTrackPoint &point, const TTrack &trac inputLeftButtonUp(point, track); else inputLeftButtonDrag(point, track); - } + } } void TInputHandler::inputPaintTracks(const TTrackList &tracks) { + // prepare tracks counters + for(TTrackList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) { + (*i)->pointsAdded = (*i)->fixedPointsAdded + (*i)->previewSize(); + (*i)->resetRemoved(); + } + + // begin paint + bool preview = false; + bool paintStarted = false; + // paint track points in chronological order while(true) { + // find earlier not painted point TTrackP track; TTimerTicks minTicks = 0; double minTimeOffset = 0.0; @@ -187,10 +198,27 @@ TInputHandler::inputPaintTracks(const TTrackList &tracks) { } } } - if (!track) break; - inputPaintTrackPoint(track->current(), *track, track == tracks.front()); + + if (!track) + break; // all tracks are painted + + if (track->pointsAdded <= track->previewSize()) + preview = true; + if (!paintStarted) + { inputPaintTracksBegin(); paintStarted = true; } + inputPaintTrackPoint(track->current(), *track, track == tracks.front(), preview); + + // update counters --track->pointsAdded; + if (!preview) { + assert(track->fixedPointsAdded > 0); + --track->fixedPointsAdded; + } } + + // end paint + if (paintStarted) + inputPaintTracksEnd(); } @@ -205,162 +233,53 @@ TInputManager::TInputManager(): m_tracks(1), m_hovers(1), m_started(), - m_savePointsSent(), drawPreview() { } void -TInputManager::paintRollbackTo(int saveIndex, TTrackList &subTracks) { - if (saveIndex >= (int)m_savePoints.size()) - return; - - int level = saveIndex + 1; - if (level <= m_savePointsSent) { - if (m_handler) { - if (level < m_savePointsSent) - m_handler->inputPaintPop(m_savePointsSent - level); - m_handler->inputPaintCancel(); - } - m_savePointsSent = level; - } - - for(TTrackList::const_iterator i = subTracks.begin(); i != subTracks.end(); ++i) { - TTrack &track = **i; - if (TrackHandler *handler = dynamic_cast(track.handler.getPointer())) { - handler->saves.resize(level); - int cnt = handler->saves[saveIndex]; - track.resetRemoved(); - track.pointsAdded = track.size() - cnt; - } - } - for(int i = level; i < (int)m_savePoints.size(); ++i) - m_savePoints[i].savePoint()->available = false; - m_savePoints.resize(level); -} - - -void -TInputManager::paintApply(int count, TTrackList &subTracks) { - if (count <= 0) - return; - - int level = (int)m_savePoints.size() - count; - bool resend = true; - - if (level < m_savePointsSent) { - // apply - int applied = m_handler ? m_handler->inputPaintApply(m_savePointsSent - level) : false; - applied = std::max(0, std::min(m_savePointsSent - level, applied)); - m_savePointsSent -= applied; - if (m_savePointsSent == level) resend = false; - } - - if (level < m_savePointsSent) { - // rollback - if (m_handler) m_handler->inputPaintPop(m_savePointsSent - level); - m_savePointsSent = level; +TInputManager::paintTracks() { + // run modifiers + for(int i = 0; i < (int)m_modifiers.size(); ++i) { + m_tracks[i+1].clear(); + m_modifiers[i]->modifyTracks(m_tracks[i], m_tracks[i+1]); } + TTrackList &subTracks = m_tracks.back(); - // remove keypoints - for(TTrackList::const_iterator i = subTracks.begin(); i != subTracks.end(); ++i) { - TTrack &track = **i; - if (TrackHandler *handler = dynamic_cast(track.handler.getPointer())) { - if (resend) { - track.resetRemoved(); - track.pointsAdded = track.size() - handler->saves[m_savePointsSent]; - } - handler->saves.resize(level); - } + + // begin painting if need + bool changed = false; + for(TTrackList::const_iterator i = subTracks.begin(); i != subTracks.end(); ++i) + if ((*i)->changed()) + { changed = true; break; } + if (!m_started && changed) { + m_started = true; + if (m_handler) m_handler->inputSetBusy(true); } - for(int i = level; i < (int)m_savePoints.size(); ++i) - m_savePoints[i].savePoint()->available = false; - m_savePoints.resize(level); -} - -void -TInputManager::paintTracks() { + + // paint tracks + if (changed && m_handler) + m_handler->inputPaintTracks(subTracks); + + + // end painting if all tracks are finished bool allFinished = true; for(TTrackList::const_iterator i = m_tracks.front().begin(); i != m_tracks.front().end(); ++i) - if (!(*i)->finished()) + if (!(*i)->fixedFinished()) { allFinished = false; break; } - - while(true) { - // run modifiers - TInputSavePoint::Holder newSavePoint = TInputSavePoint::create(true); - for(int i = 0; i < (int)m_modifiers.size(); ++i) { - m_tracks[i+1].clear(); - m_modifiers[i]->modifyTracks(m_tracks[i], newSavePoint, m_tracks[i+1]); - } - TTrackList &subTracks = m_tracks.back(); - - // is paint started? - if (!m_started && !subTracks.empty()) { - m_started = true; - if (m_handler) m_handler->inputSetBusy(true); - } - - // create handlers + if (allFinished) for(TTrackList::const_iterator i = subTracks.begin(); i != subTracks.end(); ++i) - if (!(*i)->handler) - (*i)->handler = new TrackHandler(**i, (int)m_savePoints.size()); - - if (!m_savePoints.empty()) { - // rollback - int rollbackIndex = (int)m_savePoints.size(); - for(TTrackList::const_iterator i = subTracks.begin(); i != subTracks.end(); ++i) { - TTrack &track = **i; - if (track.pointsRemoved > 0) { - int count = track.size() - track.pointsAdded; - if (TrackHandler *handler = dynamic_cast(track.handler.getPointer())) - while(rollbackIndex > 0 && (rollbackIndex >= (int)m_savePoints.size() || handler->saves[rollbackIndex] > count)) - --rollbackIndex; - } - } - paintRollbackTo(rollbackIndex, subTracks); - - // apply - int applyCount = 0; - while(applyCount < (int)m_savePoints.size() && m_savePoints[(int)m_savePoints.size() - applyCount - 1].isFree()) - ++applyCount; - paintApply(applyCount, subTracks); - } - - // send to handler - if (m_savePointsSent == (int)m_savePoints.size() && !subTracks.empty() && m_handler) - m_handler->inputPaintTracks(subTracks); - for(TTrackList::const_iterator i = subTracks.begin(); i != subTracks.end(); ++i) - (*i)->resetChanges(); - - // is paint finished? - newSavePoint.unlock(); - if (newSavePoint.isFree()) { - newSavePoint.savePoint()->available = false; - if (allFinished) { - paintApply((int)m_savePoints.size(), subTracks); - // send to tool final - if (!subTracks.empty()) { - if (m_handler) m_handler->inputPaintTracks(subTracks); - for(TTrackList::const_iterator i = subTracks.begin(); i != subTracks.end(); ++i) - (*i)->resetChanges(); - } - for(std::vector::iterator i = m_tracks.begin(); i != m_tracks.end(); ++i) - i->clear(); - if (m_started) { - if (m_handler) m_handler->inputSetBusy(false); - m_started = false; - } - } - break; + if (!(*i)->fixedFinished()) + { allFinished = false; break; } + + if (allFinished) { + if (m_started) { + if (m_handler) m_handler->inputSetBusy(false); + m_started = false; } - - // create save point - if (m_handler && m_handler->inputPaintPush()) ++m_savePointsSent; - m_savePoints.push_back(newSavePoint); - for(TTrackList::const_iterator i = subTracks.begin(); i != subTracks.end(); ++i) - if (TrackHandler *handler = dynamic_cast((*i)->handler.getPointer())) - handler->saves.push_back((*i)->size()); + for(int i = 0; i < (int)m_tracks.size(); ++i) + m_tracks[i].clear(); } } @@ -444,14 +363,16 @@ TInputManager::addTrackPoint( double time, bool final ) { - track->push_back( TTrackPoint( - position, - pressure, - tilt, - (double)track->size(), - time, - 0.0, // length will calculated inside of TTrack::push_back - final )); + track->push_back( + TTrackPoint( + position, + pressure, + tilt, + (double)track->size(), + time, + 0.0, // length will calculated inside of TTrack::push_back + final ), + true ); } @@ -502,15 +423,9 @@ TInputManager::finishTracks() { void TInputManager::reset() { - // forget about handler paint stack + // forget about handler busy state // assuime it was already reset by outside m_started = false; - m_savePointsSent = 0; - - // reset save point - for(int i = 0; i < (int)m_savePoints.size(); ++i) - m_savePoints[i].savePoint()->available = false; - m_savePoints.clear(); // reset tracks for(int i = 0; i < (int)m_tracks.size(); ++i) @@ -660,16 +575,13 @@ TInputManager::calcDrawBounds() { for(int i = 0; i < (int)m_modifiers.size(); ++i) bounds += m_modifiers[i]->calcDrawBounds(m_tracks[i], m_hovers[i]); - if (m_savePointsSent < (int)m_savePoints.size()) { + if (drawPreview) { for(TTrackList::const_iterator ti = getOutputTracks().begin(); ti != getOutputTracks().end(); ++ti) { TTrack &track = **ti; - if (TrackHandler *handler = dynamic_cast(track.handler.getPointer())) { - int start = handler->saves[m_savePointsSent] - 1; - if (start < 0) start = 0; - if (start + 1 < track.size()) - for(int i = start + 1; i < track.size(); ++i) - bounds += boundingBox(track[i-1].position, track[i].position); - } + int start = std::max(0, track.fixedSize() - track.fixedPointsAdded); + if (start + 1 < track.size()) + for(int i = start + 1; i < track.size(); ++i) + bounds += boundingBox(track[i-1].position, track[i].position); } } @@ -685,53 +597,69 @@ TInputManager::draw() { m_prevBounds = m_nextBounds; m_nextBounds = TRectD(); - // paint not sent sub-tracks - if ( debugInputManager || (drawPreview && m_savePointsSent < (int)m_savePoints.size()) ) { + // paint not fixed parts of tracks + if (drawPreview) { glPushAttrib(GL_ALL_ATTRIB_BITS); tglEnableBlending(); tglEnableLineSmooth(true, 1.0); double pixelSize = sqrt(tglGetPixelSize2()); - double colorBlack[4] = { 0.0, 0.0, 0.0, 1.0 }; - double colorWhite[4] = { 1.0, 1.0, 1.0, 1.0 }; + double colorBlack[4] = { 0.0, 0.0, 0.0, 0.5 }; + double colorWhite[4] = { 1.0, 1.0, 1.0, 0.5 }; for(TTrackList::const_iterator ti = getOutputTracks().begin(); ti != getOutputTracks().end(); ++ti) { TTrack &track = **ti; - if (TrackHandler *handler = dynamic_cast(track.handler.getPointer())) { - int start = debugInputManager ? 0 : handler->saves[m_savePointsSent] - 1; - if (start < 0) start = 0; - if (start + 1 < track.size()) { - //int level = m_savePointsSent; - //colorBlack[3] = (colorWhite[3] = 0.8); - double radius = 2.0; - for(int i = start + 1; i < track.size(); ++i) { - //while(level < (int)handler->saves.size() && handler->saves[level] <= i) - // colorBlack[3] = (colorWhite[3] *= 0.8), ++level; - - const TPointD &a = track[i-1].position; - const TPointD &b = track[i].position; - TPointD d = b - a; - + int start = std::max(0, track.fixedSize() - track.fixedPointsAdded); + const TPointD *a = &track[start].position; + for(int i = start + 1; i < track.size(); ++i) { + const TPointD *b = &track[i].position; + TPointD d = *b - *a; + double k = norm2(d); + if (k > TConsts::epsilon*TConsts::epsilon) { + k = 0.5*pixelSize/sqrt(k); + d = TPointD(-k*d.y, k*d.x); + glColor4dv(colorWhite); + tglDrawSegment(*a - d, *b - d); + glColor4dv(colorBlack); + tglDrawSegment(*a + d, *b + d); + a = b; + } + } + } + glPopAttrib(); + } + + // paint all tracks modifications for debug + if (debugInputManager) { + double pixelSize = sqrt(tglGetPixelSize2()); + double color[4] = { 0.0, 0.0, 0.0, 0.5 }; + int cnt = m_tracks.size(); + for(int li = 0; li < cnt; ++li) { + HSV2RGB(240 + li*120.0/(cnt-1), 1, 1, &color[0], &color[1], &color[2]); + glColor4dv(color); + for(TTrackList::const_iterator ti = m_tracks[li].begin(); ti != m_tracks[li].end(); ++ti) { + assert(*ti); + const TTrack &track = **ti; + int radius = 0; + const TPointD *a = &track[0].position; + for(int i = 0; i < track.size(); ++i) { + const TPointD *b = &track[i].position; + + if (i) { + TPointD d = *b - *a; double k = norm2(d); - if (k > TConsts::epsilon*TConsts::epsilon) { - k = 0.5*pixelSize/sqrt(k); - d = TPointD(-k*d.y, k*d.x); - glColor4dv(colorWhite); - tglDrawSegment(a - d, b - d); - glColor4dv(colorBlack); - tglDrawSegment(a + d, b + d); - radius = 2.0; + if (k > 4*pixelSize*pixelSize) { + tglDrawSegment(*a, *b); + radius = 0; + a = b; } else { - radius += 2.0; - } - - if (debugInputManager) { - glColor4d(0.0, 0.0, 0.0, 0.25); - tglDrawCircle(b, radius*pixelSize); + ++radius; } } + + if (radius && li == 0) + tglDrawCircle(*b, radius*pixelSize*3); } } } - glPopAttrib(); } // paint modifiers diff --git a/toonz/sources/tnztools/modifiers/modifierassistants.cpp b/toonz/sources/tnztools/modifiers/modifierassistants.cpp index 1a62e44..597e97d 100644 --- a/toonz/sources/tnztools/modifiers/modifierassistants.cpp +++ b/toonz/sources/tnztools/modifiers/modifierassistants.cpp @@ -113,64 +113,64 @@ TModifierAssistants::scanAssistants( void TModifierAssistants::modifyTrack( const TTrack &track, - const TInputSavePoint::Holder &savePoint, TTrackList &outTracks ) { if (!track.handler) { track.handler = new TTrackHandler(track); Modifier *modifier = new Modifier(*track.handler); - if (!drawOnly) - scanAssistants(&track[0].position, 1, &modifier->guidelines, false, true); - track.handler->tracks.push_back(new TTrack(modifier)); - - if ((int)modifier->guidelines.size() > 1) { - modifier->savePoint = savePoint; - outTracks.push_back(track.handler->tracks.front()); - return; - } } outTracks.push_back(track.handler->tracks.front()); TTrack &subTrack = *track.handler->tracks.front(); - if (!track.changed()) return; - if (Modifier *modifier = dynamic_cast(subTrack.modifier.getPointer())) { - // remove points - int start = track.size() - track.pointsAdded; - if (start < 0) start = 0; - - if ((int)modifier->guidelines.size() > 1 && modifier->savePoint.available()) { - // select guideline - bool longEnough = false; - if (TInputManager *manager = getManager()) - if (TInputHandler *handler = manager->getHandler()) - if (TTool *tool = handler->inputGetTool()) - if (TToolViewer *viewer = tool->getViewer()) { - TAffine trackToScreen = tool->getMatrix(); - if (tool->getToolType() & TTool::LevelTool) - if (TObjectHandle *objHandle = TTool::getApplication()->getCurrentObject()) - if (!objHandle->isSpline()) - trackToScreen *= TScale(viewer->getDpiScale().x, viewer->getDpiScale().y); - trackToScreen *= viewer->get3dViewMatrix().get2d().inv(); - TGuidelineP guideline = TGuideline::findBest(modifier->guidelines, track, trackToScreen, longEnough); - if (guideline != modifier->guidelines.front()) - for(int i = 1; i < (int)modifier->guidelines.size(); ++i) - if (modifier->guidelines[i] == guideline) { - std::swap(modifier->guidelines[i], modifier->guidelines.front()); - start = 0; - break; - } - } - modifier->savePoint.setLock(!longEnough); - } else { - modifier->savePoint.reset(); - } + if (!track.changed()) + return; + Modifier *modifier = dynamic_cast(subTrack.modifier.getPointer()); + if (!modifier) + return; + + // remove points + int start = track.size() - track.pointsAdded; + if (start < 0) start = 0; + + if (!drawOnly && start <= 0) { + modifier->guidelines.clear(); + scanAssistants(&track[0].position, 1, &modifier->guidelines, false, true); + } + + bool fixed = subTrack.fixedSize() || modifier->guidelines.size() <= 1; - // add points - subTrack.truncate(start); - for(int i = start; i < track.size(); ++i) - subTrack.push_back( modifier->calcPoint(i) ); + // select guideline + if (!fixed) + if (TInputManager *manager = getManager()) + if (TInputHandler *handler = manager->getHandler()) + if (TTool *tool = handler->inputGetTool()) + if (TToolViewer *viewer = tool->getViewer()) { + TAffine trackToScreen = tool->getMatrix(); + if (tool->getToolType() & TTool::LevelTool) + if (TObjectHandle *objHandle = TTool::getApplication()->getCurrentObject()) + if (!objHandle->isSpline()) + trackToScreen *= TScale(viewer->getDpiScale().x, viewer->getDpiScale().y); + trackToScreen *= viewer->get3dViewMatrix().get2d(); + TGuidelineP guideline = TGuideline::findBest(modifier->guidelines, track, trackToScreen, fixed); + if (guideline != modifier->guidelines.front()) + for(int i = 1; i < (int)modifier->guidelines.size(); ++i) + if (modifier->guidelines[i] == guideline) { + std::swap(modifier->guidelines[i], modifier->guidelines.front()); + start = 0; + break; + } } + + // add points + subTrack.truncate(start); + for(int i = start; i < track.size(); ++i) + subTrack.push_back( modifier->calcPoint(i), false ); + + // fix points + if (fixed || track.fixedFinished()) + subTrack.fix_to(track.fixedSize()); + track.resetChanges(); } diff --git a/toonz/sources/tnztools/modifiers/modifierline.cpp b/toonz/sources/tnztools/modifiers/modifierline.cpp index ed923ce..e678914 100644 --- a/toonz/sources/tnztools/modifiers/modifierline.cpp +++ b/toonz/sources/tnztools/modifiers/modifierline.cpp @@ -31,12 +31,10 @@ TTrackPoint TModifierLine::Modifier::calcPoint(double originalIndex) { } void TModifierLine::modifyTrack(const TTrack &track, - const TInputSavePoint::Holder &savePoint, TTrackList &outTracks) { if (!track.handler) { track.handler = new TTrackHandler(track); Modifier *modifier = new Modifier(*track.handler); - modifier->savePoint = savePoint; track.handler->tracks.push_back(new TTrack(modifier)); } @@ -66,7 +64,6 @@ void TModifierLine::modifyTrack(const TTrack &track, } modifier->maxPressure = maxPressure; modifier->fixAngle = fixAngle; - if (track.finished()) modifier->savePoint.reset(); subTrack.truncate(0); @@ -75,7 +72,7 @@ void TModifierLine::modifyTrack(const TTrack &track, p.originalIndex = 0; p.pressure = maxPressure; p.tilt = TPointD(); - subTrack.push_back(p); + subTrack.push_back(p, false); } if (track.size() > 1) { @@ -84,8 +81,11 @@ void TModifierLine::modifyTrack(const TTrack &track, p.pressure = maxPressure; p.tilt = TPointD(); if (fixAngle) calcFixedAngle(subTrack.front(), p); - subTrack.push_back(p); + subTrack.push_back(p, false); } + if (track.fixedFinished()) + subTrack.fix_all(); + track.resetChanges(); } diff --git a/toonz/sources/tnztools/modifiers/modifiersegmentation.cpp b/toonz/sources/tnztools/modifiers/modifiersegmentation.cpp index 447c9e4..ba1232e 100644 --- a/toonz/sources/tnztools/modifiers/modifiersegmentation.cpp +++ b/toonz/sources/tnztools/modifiers/modifiersegmentation.cpp @@ -9,8 +9,9 @@ //***************************************************************************************** -TModifierSegmentation::TModifierSegmentation(const TPointD &step) - { setStep(step); } +TModifierSegmentation::TModifierSegmentation(const TPointD &step, int maxLevel): + m_maxLevel(0) + { setStep(step); setMaxLevel(maxLevel); } void @@ -21,30 +22,35 @@ TModifierSegmentation::setStep(const TPointD &step) { void +TModifierSegmentation::setMaxLevel(int maxLevel) { + m_maxLevel = std::max(0, maxLevel); +} + + +void TModifierSegmentation::addSegments( TTrack &track, const TTrackPoint &p0, const TTrackPoint &p1, int level) { - static const int maxRecursion = 10; TPointD d = p1.position - p0.position; - if (level >= maxRecursion || (fabs(d.x) <= m_step.x && fabs(d.y) <= m_step.y)) { - track.push_back(p1); + if (level <= 0 || (fabs(d.x) <= m_step.x && fabs(d.y) <= m_step.y)) { + track.push_back(p1, false); return; } + --level; TTrackPoint p = track.modifier->calcPoint(0.5*(p0.originalIndex + p1.originalIndex)); - addSegments(track, p0, p, level + 1); - addSegments(track, p, p1, level + 1); + addSegments(track, p0, p, level); + addSegments(track, p, p1, level); } void TModifierSegmentation::modifyTrack( const TTrack &track, - const TInputSavePoint::Holder &savePoint, TTrackList &outTracks ) { if (!track.handler) { @@ -66,16 +72,21 @@ TModifierSegmentation::modifyTrack( // remove points int start = track.size() - track.pointsAdded; if (start < 0) start = 0; - int subStart = subTrack.floorIndex(subTrack.indexByOriginalIndex(start-1)) + 1; + int subStart = start > 0 ? subTrack.floorIndex(subTrack.indexByOriginalIndex(start-1)) + 1 : 0; subTrack.truncate(subStart); // add points TTrackPoint p0 = subTrack.modifier->calcPoint(start - 1); for(int i = start; i < track.size(); ++i) { TTrackPoint p1 = subTrack.modifier->calcPoint(i); - addSegments(subTrack, p0, p1); + addSegments(subTrack, p0, p1, m_maxLevel); p0 = p1; } + // fix points + if (track.fixedSize()) + subTrack.fix_to( + subTrack.floorIndex(subTrack.indexByOriginalIndex(track.fixedSize() - 1)) + 1 ); + track.resetChanges(); } diff --git a/toonz/sources/tnztools/modifiers/modifiersmooth.cpp b/toonz/sources/tnztools/modifiers/modifiersmooth.cpp new file mode 100644 index 0000000..449623f --- /dev/null +++ b/toonz/sources/tnztools/modifiers/modifiersmooth.cpp @@ -0,0 +1,119 @@ + + +#include +#include + + +//***************************************************************************************** +// TModifierSmooth::Modifier implementation +//***************************************************************************************** + + +TModifierSmooth::Modifier::Modifier(TTrackHandler &handler, int radius): + TTrackModifier(handler), + radius(std::max(radius, 0)), + smoothedTrack() +{ } + + +TTrackPoint +TModifierSmooth::Modifier::calcPoint(double originalIndex) { + return smoothedTrack + ? smoothedTrack->interpolateLinear(originalIndex) + : TTrackModifier::calcPoint(originalIndex); +} + + +//***************************************************************************************** +// TModifierSmooth implementation +//***************************************************************************************** + + +TModifierSmooth::TModifierSmooth(int radius): m_radius() + { setRadius(radius); } + + +void +TModifierSmooth::setRadius(int radius) + { m_radius = std::max(0, radius); } + + +void +TModifierSmooth::modifyTrack( + const TTrack &track, + TTrackList &outTracks ) +{ + if (!m_radius) { + TInputModifier::modifyTrack(track, outTracks); + return; + } + + if (!track.handler) { + track.handler = new TTrackHandler(track); + Modifier *modifier = new Modifier(*track.handler, m_radius); + modifier->smoothedTrack = new TTrack(modifier); + track.handler->tracks.push_back(modifier->smoothedTrack); + } + + if (track.handler->tracks.empty()) + return; + + TTrack &subTrack = *track.handler->tracks.front(); + outTracks.push_back(track.handler->tracks.front()); + + if (!track.changed()) + return; + + Modifier *modifier = dynamic_cast(subTrack.modifier.getPointer()); + if (!modifier) + return; + int radius = modifier->radius; + + // remove points + int start = std::max(0, track.size() - track.pointsAdded); + subTrack.truncate(start); + + // add points + // extra points will be added at the beginning and at the end + int size = track.size() + 2*radius; + double k = 1.0/(2*radius + 1); + TTrackTangent accum; + for(int i = start - 2*radius; i < size; ++i) { + + const TTrackPoint &p1 = track[i]; + accum.position += p1.position; + accum.pressure += p1.pressure; + accum.tilt += p1.tilt; + if (i < start) + continue; + + int oi = track.clampIndex(i - radius); + const TTrackPoint &p = track[oi]; + subTrack.push_back( + TTrackPoint( + accum.position*k, + accum.pressure*k, + accum.tilt*k, + oi, + p.time, + 0, + p.final ), + false ); + + const TTrackPoint &p0 = track[i - 2*radius]; + accum.position -= p0.position; + accum.pressure -= p0.pressure; + accum.tilt -= p0.tilt; + } + + // fix points + if (track.fixedFinished()) { + subTrack.fix_all(); + } else + if (track.fixedSize()) { + subTrack.fix_to(track.fixedSize()); + } + + track.resetChanges(); +} + diff --git a/toonz/sources/tnztools/modifiers/modifiertangents.cpp b/toonz/sources/tnztools/modifiers/modifiertangents.cpp index f71c56e..c7f11e0 100644 --- a/toonz/sources/tnztools/modifiers/modifiertangents.cpp +++ b/toonz/sources/tnztools/modifiers/modifiertangents.cpp @@ -12,23 +12,31 @@ TTrackPoint TModifierTangents::Modifier::calcPoint(double originalIndex) { double frac; int i0 = original.floorIndex(originalIndex, &frac); - int i1 = original.ceilIndex(originalIndex); + int i1 = i0 + 1; TTrackPoint p; - if (i0 >= 0) { - // calculate tangent length to make monotonic subdivisions, - // (because we don't have valid input time) - const TTrackPoint &p0 = original[i0]; - const TTrackPoint &p1 = original[i1]; - TTrackTangent t0 = i0 < (int)tangents.size() ? tangents[i0] : TTrackTangent(); - TTrackTangent t1 = i1 < (int)tangents.size() ? tangents[i1] : TTrackTangent(); - double l = fabs(p1.length - p0.length); - t0.position.x *= l; - t0.position.y *= l; - t1.position.x *= l; - t1.position.y *= l; - p = TTrack::interpolationSpline(p0, p1, t0, t1, frac); - } + + // calculate tangent length to make monotonic subdivisions, + // (because we don't have valid input time) + const TTrackPoint &p0 = original[i0]; + const TTrackPoint &p1 = original[i1]; + TTrackTangent t0 = i0 >= 0 && i0 < (int)tangents.size() ? tangents[i0] : TTrackTangent(); + TTrackTangent t1 = i1 >= 0 && i1 < (int)tangents.size() ? tangents[i1] : TTrackTangent(); + double l = p1.length - p0.length; + + t0.position.x *= l; + t0.position.y *= l; + t0.pressure *= l; + t0.tilt.x *= l; + t0.tilt.y *= l; + + t1.position.x *= l; + t1.position.y *= l; + t1.pressure *= l; + t1.tilt.x *= l; + t1.tilt.y *= l; + + p = TTrack::interpolationSpline(p0, p1, t0, t1, frac); p.originalIndex = originalIndex; return p; } @@ -40,25 +48,32 @@ TModifierTangents::Modifier::calcPoint(double originalIndex) { TTrackTangent -TModifierTangents::calcLastTangent(const TTrack &track) const { - if (track.size() < 2) return TTrackTangent(); - const TTrackPoint &p0 = track[track.size() - 2]; - const TTrackPoint &p1 = track.back(); - - // calculate tangent (with normalized position) - // with valid points time normalization does not required - double dl = p1.length - p0.length; +TModifierTangents::calcTangent(const TTrack &track, int index) const { + if (index <= 0 || index >= track.size() - 1) + return TTrackTangent(); + + const TTrackPoint &p0 = track[index-1]; + const TTrackPoint &p2 = track[index+1]; + + // calculate tangent length by time + // for that we need know time of actual user input + // instead of time when message dispatched + //double k = p2.time - p0.time; + + // calculate tangent based on length, util we have no valid times + double k = p2.length - p0.length; + + k = k > TConsts::epsilon ? 1/k : 0; return TTrackTangent( - dl > TConsts::epsilon ? (p1.position - p0.position)*(1.0/dl) : TPointD(), - p1.pressure - p0.pressure, - p1.tilt - p0.tilt ); + (p2.position - p0.position)*k, + (p2.pressure - p0.pressure)*k, + (p2.tilt - p0.tilt )*k ); } void TModifierTangents::modifyTrack( const TTrack &track, - const TInputSavePoint::Holder &savePoint, TTrackList &outTracks ) { if (!track.handler) { @@ -77,83 +92,30 @@ TModifierTangents::modifyTrack( return; outTracks.push_back(track.handler->tracks.front()); - - if ( !track.changed() - && track.size() == subTrack.size() - && track.size() == (int)modifier->tangents.size() ) - return; - - if (!track.changed() && subTrack.size() == track.size() - 1) { - // add temporary point - modifier->tangents.push_back(calcLastTangent(track)); - subTrack.push_back(track.back()); - } else { - // apply permanent changes - - // remove points - int start = track.size() - track.pointsAdded; - if (start < 0) start = 0; - if (start > 1) --start; - subTrack.truncate(start); - TTrackTangent lastTangent = - start < (int)modifier->tangents.size() ? modifier->tangents[start] - : modifier->tangents.empty() ? TTrackTangent() - : modifier->tangents.back(); - modifier->tangents.resize(start, lastTangent); - - // add first point - int index = start; - if (index == 0) { - modifier->tangents.push_back(TTrackTangent()); - subTrack.push_back(track.back()); - ++index; - } - - // add points with tangents - if (track.size() > 2) { - while(index < track.size() - 1) { - const TTrackPoint &p0 = track[index-1]; - const TTrackPoint &p1 = track[index]; - const TTrackPoint &p2 = track[index+1]; - - // calculate tangents length by time - // for that we need read time of user input - // instead of time when message dispatched - //double dt = p2.time - p0.time; - //double k = dt > TConsts::epsilon ? (p1.time - p0.time)/dt : 0.0; - //TTrackTangent tangent( - // (p2.position - p0.position)*k, - // (p2.pressure - p0.pressure)*k, - // (p2.tilt - p0.tilt)*k ); - - // calculate tangent (with normalized position) - TPointD d = p2.position - p0.position; - double k = norm2(d); - k = k > TConsts::epsilon*TConsts::epsilon ? 1.0/sqrt(k) : 0.0; - TTrackTangent tangent( - d*k, - (p2.pressure - p0.pressure)*0.5, - (p2.tilt - p0.tilt)*0.5 ); - - modifier->tangents.push_back(tangent); - subTrack.push_back(p1); - ++index; - } - } - - track.resetChanges(); - - // release previous key point - modifier->savePoint.reset(); - - if (track.finished()) { - // finish - modifier->tangents.push_back(calcLastTangent(track)); - subTrack.push_back(track.back()); - } else { - // save key point - modifier->savePoint = savePoint; - } + if (!track.changed()) + return; + + // update subTrack + int start = track.size() - track.pointsAdded; + if (start > 1) --start; + if (start < 0) start = 0; + subTrack.truncate(start); + for(int i = start; i < track.size(); ++i) + subTrack.push_back(track[i], false); + + // update tangents + modifier->tangents.resize(start); + for(int i = start; i < track.size(); ++i) + modifier->tangents.push_back(calcTangent(track, i)); + + // fix points + if (track.fixedFinished()) { + subTrack.fix_all(); + } else + if (track.fixedSize()) { + subTrack.fix_to(std::max(track.fixedSize() - 1, 1)); } + + track.resetChanges(); } diff --git a/toonz/sources/tnztools/modifiers/modifiertest.cpp b/toonz/sources/tnztools/modifiers/modifiertest.cpp index 2033a2b..7801f3b 100644 --- a/toonz/sources/tnztools/modifiers/modifiertest.cpp +++ b/toonz/sources/tnztools/modifiers/modifiertest.cpp @@ -52,7 +52,6 @@ TModifierTest::TModifierTest(int count, double radius) : count(count), radius(radius) {} void TModifierTest::modifyTrack(const TTrack &track, - const TInputSavePoint::Holder &savePoint, TTrackList &outTracks) { const double segmentSize = 2.0 * M_PI / 10.0; @@ -68,7 +67,7 @@ void TModifierTest::modifyTrack(const TTrack &track, Handler *handler = dynamic_cast(track.handler.getPointer()); if (!handler) { - TInputModifier::modifyTrack(track, savePoint, outTracks); + TInputModifier::modifyTrack(track, outTracks); return; } @@ -124,14 +123,19 @@ void TModifierTest::modifyTrack(const TTrack &track, double end = 1.0 - 0.5 * step; for (double frac = step; frac < end; frac += step) subTrack.push_back( - subTrack.modifier->calcPoint((double)i - 1.0 + frac)); + subTrack.modifier->calcPoint((double)i - 1.0 + frac), false); } } - subTrack.push_back(subTrack.modifier->calcPoint(i)); + subTrack.push_back(subTrack.modifier->calcPoint(i), false); } + + // fix points + if (track.fixedSize()) + subTrack.fix_to( + subTrack.floorIndex(subTrack.indexByOriginalIndex(track.fixedSize() - 1)) + 1 ); } track.resetChanges(); } -#endif \ No newline at end of file +#endif diff --git a/toonz/sources/tnztools/toonzrasterbrushtool.cpp b/toonz/sources/tnztools/toonzrasterbrushtool.cpp index f199b1a..539368a 100644 --- a/toonz/sources/tnztools/toonzrasterbrushtool.cpp +++ b/toonz/sources/tnztools/toonzrasterbrushtool.cpp @@ -6,7 +6,6 @@ #include "tools/toolhandle.h" #include "tools/toolutils.h" #include "tools/tooloptions.h" -#include "bluredbrush.h" // TnzQt includes #include "toonzqt/dvdialog.h" @@ -21,7 +20,6 @@ #include "toonz/txsheet.h" #include "toonz/tstageobject.h" #include "toonz/tstageobjectspline.h" -#include "toonz/rasterstrokegenerator.h" #include "toonz/ttileset.h" #include "toonz/txshsimplelevel.h" #include "toonz/toonzimageutils.h" @@ -57,6 +55,7 @@ TEnv::DoubleVar RasterBrushHardness("RasterBrushHardness", 100); TEnv::DoubleVar RasterBrushModifierSize("RasterBrushModifierSize", 0); TEnv::StringVar RasterBrushPreset("RasterBrushPreset", ""); TEnv::IntVar BrushLockAlpha("InknpaintBrushLockAlpha", 0); +TEnv::IntVar RasterBrushAssistants("RasterBrushAssistants", 0); //------------------------------------------------------------------- #define CUSTOM_WSTR L"" @@ -401,19 +400,6 @@ namespace { //------------------------------------------------------------------- -void addStrokeToImage(TTool::Application *application, const TVectorImageP &vi, - TStroke *stroke, bool breakAngles, bool frameCreated, - bool levelCreated, TXshSimpleLevel *sLevel = NULL, - TFrameId id = TFrameId::NO_FRAME) { - QMutexLocker lock(vi->getMutex()); - addStroke(application, vi.getPointer(), stroke, breakAngles, frameCreated, - levelCreated, sLevel, id); - // la notifica viene gia fatta da addStroke! - // getApplication()->getCurrentTool()->getTool()->notifyImageChanged(); -} - -//--------------------------------------------------------------------------------------------------------- - enum DrawOrder { OverAll = 0, UnderAll, PaletteOrder }; void getAboveStyleIdSet(int styleId, TPaletteP palette, @@ -450,27 +436,27 @@ public: , m_points(points) , m_styleId(styleId) , m_selective(selective) + , m_isPaletteOrder(isPaletteOrder) , m_isPencil(isPencil) , m_isStraight(isStraight) - , m_isPaletteOrder(isPaletteOrder) , m_modifierLockAlpha(lockAlpha) {} void redo() const override { insertLevelAndFrameIfNeeded(); TToonzImageP image = getImage(); TRasterCM32P ras = image->getRaster(); - RasterStrokeGenerator m_rasterTrack( + RasterStrokeGenerator rasterTrack( ras, BRUSH, NONE, m_styleId, m_points[0], m_selective, 0, m_modifierLockAlpha, !m_isPencil, m_isPaletteOrder); if (m_isPaletteOrder) { QSet aboveStyleIds; getAboveStyleIdSet(m_styleId, image->getPalette(), aboveStyleIds); - m_rasterTrack.setAboveStyleIds(aboveStyleIds); + rasterTrack.setAboveStyleIds(aboveStyleIds); } - m_rasterTrack.setPointsSequence(m_points); - m_rasterTrack.generateStroke(m_isPencil, m_isStraight); + rasterTrack.setPointsSequence(m_points); + rasterTrack.generateStroke(m_isPencil, m_isStraight); image->setSavebox(image->getSavebox() + - m_rasterTrack.getBBox(m_rasterTrack.getPointsSequence())); + rasterTrack.getBBox(rasterTrack.getPointsSequence())); ToolUtils::updateSaveBox(); TTool::getApplication()->getCurrentXsheet()->notifyXsheetChanged(); notifyImageChanged(); @@ -506,8 +492,8 @@ public: , m_styleId(styleId) , m_drawOrder(drawOrder) , m_maxThick(maxThick) - , m_isStraight(isStraight) , m_hardness(hardness) + , m_isStraight(isStraight) , m_modifierLockAlpha(lockAlpha) {} void redo() const override { @@ -632,15 +618,6 @@ double computeThickness(double pressure, const TDoublePairProperty &property) { return (thick0 + (thick1 - thick0) * t) * 0.5; } -//--------------------------------------------------------------------------------------------------------- - -int computeThickness(double pressure, const TIntPairProperty &property) { - double t = pressure * pressure * pressure; - int thick0 = property.getValue().first; - int thick1 = property.getValue().second; - return tround(thick0 + (thick1 - thick0) * t); -} - } // namespace //-------------------------------------------------------------------------------------------------- @@ -878,18 +855,15 @@ ToonzRasterBrushTool::ToonzRasterBrushTool(std::string name, int targetType) , m_pencil("Pencil", false) , m_pressure("Pressure", true) , m_modifierSize("ModifierSize", -3, 3, 0, true) - , m_rasterTrack(0) - , m_styleId(0) - , m_bluredBrush(0) - , m_active(false) + , m_modifierLockAlpha("Lock Alpha", false) + , m_assistants("Assistants", true) + , m_targetType(targetType) , m_enabled(false) , m_isPrompting(false) , m_firstTime(true) , m_presetsLoaded(false) - , m_targetType(targetType) - , m_workingFrameId(TFrameId()) , m_notifier(0) - , m_modifierLockAlpha("Lock Alpha", false) { +{ bind(targetType); m_rasThickness.setNonLinearSlider(); @@ -901,6 +875,7 @@ ToonzRasterBrushTool::ToonzRasterBrushTool(std::string name, int targetType) m_prop[0].bind(m_modifierSize); m_prop[0].bind(m_modifierLockAlpha); m_prop[0].bind(m_pencil); + m_prop[0].bind(m_assistants); m_pencil.setId("PencilMode"); m_drawOrder.addValue(L"Over All"); @@ -915,6 +890,21 @@ ToonzRasterBrushTool::ToonzRasterBrushTool(std::string name, int targetType) m_preset.addValue(CUSTOM_WSTR); m_pressure.setId("PressureSensitivity"); m_modifierLockAlpha.setId("LockAlpha"); + + m_inputmanager.setHandler(this); + m_modifierLine = new TModifierLine(); + m_modifierTangents = new TModifierTangents(); + m_modifierAssistants = new TModifierAssistants(); + m_modifierSegmentation = new TModifierSegmentation(); + m_modifierSmoothSegmentation = new TModifierSegmentation(TPointD(1, 1), 3); + for(int i = 0; i < 3; ++i) + m_modifierSmooth[i] = new TModifierSmooth(); +#ifndef NDEBUG + m_modifierTest = new TModifierTest(5, 40); +#endif + + m_inputmanager.addModifier( + TInputModifierP(m_modifierAssistants.getPointer())); } //------------------------------------------------------------------------------------------------------- @@ -1051,6 +1041,8 @@ void ToonzRasterBrushTool::drawEmptyCircle(TPointD pos, int thick, if (!isPencil) tglDrawCircle(pos, (thick + 1) * 0.5); else { + pos.x = floor(pos.x) + 0.5; + pos.y = floor(pos.y) + 0.5; int x = 0, y = tround((thick * 0.5) - 0.5); int d = 3 - 2 * (int)(thick * 0.5); bool horizontal = true, isDecimal = thick % 2 != 0; @@ -1109,70 +1101,7 @@ void ToonzRasterBrushTool::updateTranslation() { m_pencil.setQStringName(tr("Pencil")); m_pressure.setQStringName(tr("Pressure")); m_modifierLockAlpha.setQStringName(tr("Lock Alpha")); -} - -//--------------------------------------------------------------------------------------------------- - -void ToonzRasterBrushTool::updateWorkAndBackupRasters(const TRect &rect) { - TToonzImageP ti = TImageP(getImage(false, 1)); - if (!ti) return; - - TRasterCM32P ras = ti->getRaster(); - - if (m_isMyPaintStyleSelected) { - const int denominator = 8; - TRect enlargedRect = rect + m_lastRect; - int dx = (enlargedRect.getLx() - 1) / denominator + 1; - int dy = (enlargedRect.getLy() - 1) / denominator + 1; - - if (m_lastRect.isEmpty()) { - enlargedRect.x0 -= dx; - enlargedRect.y0 -= dy; - enlargedRect.x1 += dx; - enlargedRect.y1 += dy; - - TRect _rect = enlargedRect * ras->getBounds(); - if (_rect.isEmpty()) return; - - m_workRas->extract(_rect)->copy(ras->extract(_rect)); - m_backupRas->extract(_rect)->copy(ras->extract(_rect)); - } else { - if (enlargedRect.x0 < m_lastRect.x0) enlargedRect.x0 -= dx; - if (enlargedRect.y0 < m_lastRect.y0) enlargedRect.y0 -= dy; - if (enlargedRect.x1 > m_lastRect.x1) enlargedRect.x1 += dx; - if (enlargedRect.y1 > m_lastRect.y1) enlargedRect.y1 += dy; - - TRect _rect = enlargedRect * ras->getBounds(); - if (_rect.isEmpty()) return; - - TRect _lastRect = m_lastRect * ras->getBounds(); - QList rects = ToolUtils::splitRect(_rect, _lastRect); - for (int i = 0; i < rects.size(); i++) { - m_workRas->extract(rects[i])->copy(ras->extract(rects[i])); - m_backupRas->extract(rects[i])->copy(ras->extract(rects[i])); - } - } - - m_lastRect = enlargedRect; - return; - } - - TRect _rect = rect * ras->getBounds(); - TRect _lastRect = m_lastRect * ras->getBounds(); - - if (_rect.isEmpty()) return; - - if (m_lastRect.isEmpty()) { - m_workRas->extract(_rect)->clear(); - m_backupRas->extract(_rect)->copy(ras->extract(_rect)); - return; - } - - QList rects = ToolUtils::splitRect(_rect, _lastRect); - for (int i = 0; i < rects.size(); i++) { - m_workRas->extract(rects[i])->clear(); - m_backupRas->extract(rects[i])->copy(ras->extract(rects[i])); - } + m_assistants.setQStringName(tr("Assistants")); } //--------------------------------------------------------------------------------------------------- @@ -1205,18 +1134,8 @@ void ToonzRasterBrushTool::onActivate() { //-------------------------------------------------------------------------------------------------- void ToonzRasterBrushTool::onDeactivate() { - /*--- - * ドラッグ中にツールが切り替わった場合に備え、onDeactivateにもMouseReleaseと同じ処理を行う - * ---*/ - if (m_tileSaver) { - bool isValid = m_enabled && m_active; - m_enabled = false; - m_active = false; - if (isValid) { - finishRasterBrush(m_mousePos, - 1); /*-- 最後のストロークの筆圧は1とする --*/ - } - } + m_inputmanager.finishTracks(); + m_enabled = false; m_workRas = TRaster32P(); m_backupRas = TRasterCM32P(); } @@ -1229,16 +1148,34 @@ bool ToonzRasterBrushTool::askRead(const TRect &rect) { return askWrite(rect); } bool ToonzRasterBrushTool::askWrite(const TRect &rect) { if (rect.isEmpty()) return true; - m_strokeRect += rect; - m_strokeSegmentRect += rect; + m_painting.myPaint.strokeSegmentRect += rect; updateWorkAndBackupRasters(rect); - m_tileSaver->save(rect); + m_painting.tileSaver->save(rect); return true; } //-------------------------------------------------------------------------------------------------- bool ToonzRasterBrushTool::preLeftButtonDown() { + int smoothRadius = (int)round(m_smooth.getValue()); + m_modifierAssistants->drawOnly = !RasterBrushAssistants; + m_inputmanager.drawPreview = false; //!m_modifierAssistants->drawOnly; + + m_inputmanager.clearModifiers(); + m_inputmanager.addModifier(TInputModifierP(m_modifierTangents.getPointer())); + if (smoothRadius > 0) { + m_inputmanager.addModifier(TInputModifierP(m_modifierSmoothSegmentation.getPointer())); + for(int i = 0; i < 3; ++i) { + m_modifierSmooth[i]->setRadius(smoothRadius); + m_inputmanager.addModifier(TInputModifierP(m_modifierSmooth[i].getPointer())); + } + } + m_inputmanager.addModifier(TInputModifierP(m_modifierAssistants.getPointer())); +#ifndef NDEBUG + m_inputmanager.addModifier(TInputModifierP(m_modifierTest.getPointer())); +#endif + m_inputmanager.addModifier(TInputModifierP(m_modifierSegmentation.getPointer())); + touchImage(); if (m_isFrameCreated) { setWorkAndBackupImages(); @@ -1250,654 +1187,339 @@ bool ToonzRasterBrushTool::preLeftButtonDown() { return true; } -//-------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------- -void ToonzRasterBrushTool::leftButtonDown(const TPointD &pos, +void ToonzRasterBrushTool::handleMouseEvent(MouseEventType type, + const TPointD &pos, const TMouseEvent &e) { - TTool::Application *app = TTool::getApplication(); - if (!app) return; - - int col = app->getCurrentColumn()->getColumnIndex(); - m_enabled = col >= 0 || app->getCurrentFrame()->isEditingLevel(); - // todo: gestire autoenable - if (!m_enabled) return; - - TPointD centeredPos = getCenteredCursorPos(pos); - - m_currentColor = TPixel32::Black; - m_active = !!getImage(true); - if (!m_active) { - m_active = !!touchImage(); - } - if (!m_active) return; - - if (m_active) { - // nel caso che il colore corrente sia un cleanup/studiopalette color - // oppure il colore di un colorfield - m_styleId = app->getCurrentLevelStyleIndex(); - TColorStyle *cs = app->getCurrentLevelStyle(); - if (cs) { - TRasterStyleFx *rfx = cs ? cs->getRasterStyleFx() : 0; - m_active = cs != 0 && (cs->isStrokeStyle() || (rfx && rfx->isInkStyle())); - m_currentColor = cs->getAverageColor(); - m_currentColor.m = 255; - } else { - m_styleId = 1; - m_currentColor = TPixel32::Black; - } - } - - // Modifier to do straight line - if (e.isShiftPressed()) { - m_isStraight = true; - m_firstPoint = pos; - m_lastPoint = pos; + TTimerTicks t = TToolTimer::ticks(); + bool alt = e.getModifiersMask() & TMouseEvent::ALT_KEY; + bool shift = e.getModifiersMask() & TMouseEvent::SHIFT_KEY; + bool control = e.getModifiersMask() & TMouseEvent::CTRL_KEY; + + if (shift && type == ME_DOWN && e.button() == Qt::LeftButton && !m_painting.active) { + m_modifierAssistants->drawOnly = true; + m_inputmanager.clearModifiers(); + m_inputmanager.addModifier(TInputModifierP(m_modifierLine.getPointer())); + m_inputmanager.addModifier(TInputModifierP(m_modifierAssistants.getPointer())); + m_inputmanager.addModifier(TInputModifierP(m_modifierSegmentation.getPointer())); + m_inputmanager.drawPreview = true; } - double pressure; - if (m_isMyPaintStyleSelected) // mypaint brush case - pressure = m_pressure.getValue() && e.isTablet() ? e.m_pressure : 0.5; - else - pressure = m_pressure.getValue() ? e.m_pressure : 1.0; - - // assert(0<=m_styleId && m_styleId<2); - TImageP img = getImage(true); - TToonzImageP ri(img); - TRasterCM32P ras = ri->getRaster(); - if (ras) { - TPointD rasCenter = ras->getCenterD(); - m_tileSet = new TTileSetCM32(ras->getSize()); - m_tileSaver = new TTileSaverCM32(ras, m_tileSet); - double maxThick = m_rasThickness.getValue().second; - double thickness = (m_pressure.getValue()) - ? computeThickness(pressure, m_rasThickness) * 2 - : maxThick; - - /*--- ストロークの最初にMaxサイズの円が描かれてしまう不具合を防止する - * ---*/ - if (m_pressure.getValue() && e.m_pressure == 1.0 && !m_isStraight) - thickness = m_rasThickness.getValue().first; - - TPointD halfThick(maxThick * 0.5, maxThick * 0.5); - TRectD invalidateRect(centeredPos - halfThick, centeredPos + halfThick); - TPointD dpi; - ri->getDpi(dpi.x, dpi.y); - TRectD previousTipRect(m_brushPos - halfThick, m_brushPos + halfThick); - if (dpi.x > Stage::inch || dpi.y > Stage::inch) - previousTipRect *= dpi.x / Stage::inch; - invalidateRect += previousTipRect; - - // if the drawOrder mode = "Palette Order", - // get styleId list which is above the current style in the palette - DrawOrder drawOrder = (DrawOrder)m_drawOrder.getIndex(); - QSet aboveStyleIds; - if (drawOrder == PaletteOrder) { - getAboveStyleIdSet(m_styleId, ri->getPalette(), aboveStyleIds); - } - - // mypaint brush case - if (m_isMyPaintStyleSelected) { - TPointD point(centeredPos + rasCenter); - - updateCurrentStyle(); - if (!(m_workRas && m_backupRas)) setWorkAndBackupImages(); - m_workRas->lock(); - mypaint::Brush mypaintBrush; - TMyPaintBrushStyle *mypaintStyle = - dynamic_cast(app->getCurrentLevelStyle()); - { // applyToonzBrushSettings - mypaintBrush.fromBrush(mypaintStyle->getBrush()); - double modifierSize = m_modifierSize.getValue() * log(2.0); - float baseSize = - mypaintBrush.getBaseValue(MYPAINT_BRUSH_SETTING_RADIUS_LOGARITHMIC); - mypaintBrush.setBaseValue(MYPAINT_BRUSH_SETTING_RADIUS_LOGARITHMIC, - baseSize + modifierSize); - } - m_toonz_brush = - new MyPaintToonzBrush(m_workRas, *this, mypaintBrush, true); - m_strokeRect.empty(); - m_strokeSegmentRect.empty(); - m_toonz_brush->beginStroke(); - m_toonz_brush->strokeTo(point, pressure, TPointD(), restartBrushTimer()); - TRect updateRect = m_strokeSegmentRect * ras->getBounds(); - if (!updateRect.isEmpty()) { - // ras->extract(updateRect)->copy(m_workRas->extract(updateRect)); - m_toonz_brush->updateDrawing(ri->getRaster(), m_backupRas, m_strokeRect, - m_styleId, m_modifierLockAlpha.getValue()); - } - m_lastRect = m_strokeRect; - - TPointD thickOffset(m_maxCursorThick * 0.5, m_maxCursorThick * 0.5); - invalidateRect = convert(m_strokeSegmentRect) - rasCenter; - invalidateRect += - TRectD(centeredPos - thickOffset, centeredPos + thickOffset); - invalidateRect += - TRectD(m_brushPos - thickOffset, m_brushPos + thickOffset); - } else if (m_hardness.getValue() == 100 || m_pencil.getValue()) { - /*-- Pencilモードでなく、Hardness=100 の場合のブラシサイズを1段階下げる - * --*/ - if (!m_pencil.getValue() && !m_isStraight) thickness -= 1.0; - - TThickPoint thickPoint(centeredPos + convert(ras->getCenter()), - thickness); - m_rasterTrack = new RasterStrokeGenerator( - ras, BRUSH, NONE, m_styleId, thickPoint, drawOrder != OverAll, 0, - m_modifierLockAlpha.getValue(), !m_pencil.getValue(), - drawOrder == PaletteOrder); - - if (drawOrder == PaletteOrder) - m_rasterTrack->setAboveStyleIds(aboveStyleIds); - - m_tileSaver->save(m_rasterTrack->getLastRect()); - if (!m_isStraight) - m_rasterTrack->generateLastPieceOfStroke(m_pencil.getValue()); - - std::vector pts; - if (m_smooth.getValue() == 0) { - pts.push_back(thickPoint); - } else { - m_smoothStroke.beginStroke(m_smooth.getValue()); - m_smoothStroke.addPoint(thickPoint); - m_smoothStroke.getSmoothPoints(pts); - } - } else { - m_points.clear(); - TThickPoint point(centeredPos + rasCenter, thickness); - m_points.push_back(point); - m_bluredBrush = new BluredBrush(m_workRas, maxThick, m_brushPad, false); - - if (drawOrder == PaletteOrder) - m_bluredBrush->setAboveStyleIds(aboveStyleIds); - - m_strokeRect = m_bluredBrush->getBoundFromPoints(m_points); - updateWorkAndBackupRasters(m_strokeRect); - m_tileSaver->save(m_strokeRect); - m_bluredBrush->addPoint(point, 1); - if (!m_isStraight) - m_bluredBrush->updateDrawing(ri->getRaster(), m_backupRas, m_strokeRect, - m_styleId, drawOrder, - m_modifierLockAlpha.getValue()); - m_lastRect = m_strokeRect; - - std::vector pts; - if (m_smooth.getValue() == 0) { - pts.push_back(point); - } else { - m_smoothStroke.beginStroke(m_smooth.getValue()); - m_smoothStroke.addPoint(point); - m_smoothStroke.getSmoothPoints(pts); - } - } - /*-- 作業中のFidを登録 --*/ - m_workingFrameId = getFrameId(); + if (alt != m_inputmanager.state.isKeyPressed(TKey::alt)) + m_inputmanager.keyEvent(alt, TKey::alt, t, nullptr); + if (shift != m_inputmanager.state.isKeyPressed(TKey::shift)) + m_inputmanager.keyEvent(shift, TKey::shift, t, nullptr); + if (control != m_inputmanager.state.isKeyPressed(TKey::control)) + m_inputmanager.keyEvent(control, TKey::control, t, nullptr); - invalidate(invalidateRect.enlarge(2)); + if (type == ME_MOVE) { + THoverList hovers(1, pos); + m_inputmanager.hoverEvent(hovers); + } else { + m_inputmanager.trackEvent(e.isTablet(), 0, pos, + e.isTablet() ? &e.m_pressure : nullptr, nullptr, + type == ME_UP, t); + m_inputmanager.processTracks(); } - // updating m_brushPos is needed to refresh viewer properly - m_mousePos = pos; - m_brushPos = getCenteredCursorPos(pos); } -//------------------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------- +void ToonzRasterBrushTool::leftButtonDown(const TPointD &pos, + const TMouseEvent &e) { + handleMouseEvent(ME_DOWN, pos, e); +} void ToonzRasterBrushTool::leftButtonDrag(const TPointD &pos, - const TMouseEvent &e) { - TRectD invalidateRect; - m_lastPoint = pos; - - if (!m_enabled || !m_active) { - m_mousePos = pos; - m_brushPos = getCenteredCursorPos(pos); - return; - } + const TMouseEvent &e) { + handleMouseEvent(ME_DRAG, pos, e); +} +void ToonzRasterBrushTool::leftButtonUp(const TPointD &pos, + const TMouseEvent &e) { + handleMouseEvent(ME_UP, pos, e); +} +void ToonzRasterBrushTool::mouseMove(const TPointD &pos, const TMouseEvent &e) { + handleMouseEvent(ME_MOVE, pos, e); +} - TPointD centeredPos = getCenteredCursorPos(m_lastPoint); +//-------------------------------------------------------------------------------------------------- - double pressure; - if (m_isMyPaintStyleSelected) // mypaint brush case - pressure = m_pressure.getValue() && e.isTablet() ? e.m_pressure : 0.5; - else - pressure = m_pressure.getValue() ? e.m_pressure : 1.0; - - TToonzImageP ti = TImageP(getImage(true)); - TPointD rasCenter = ti->getRaster()->getCenterD(); - double maxThickness = m_rasThickness.getValue().second; - double thickness = (m_pressure.getValue()) - ? computeThickness(pressure, m_rasThickness) * 2 - : maxThickness; - - if (m_maxPressure < pressure) m_maxPressure = pressure; - - if (m_isStraight) { - invalidateRect = TRectD(m_firstPoint, m_lastPoint).enlarge(2); - if (e.isCtrlPressed()) { - double distance = (m_brushPos.x - m_maxCursorThick + 1) * 0.5; - TRectD brushRect = - TRectD(TPointD(m_brushPos.x - distance, m_brushPos.y - distance), - TPointD(m_brushPos.x + distance, m_brushPos.y + distance)); - invalidateRect += (brushRect); - double denominator = m_lastPoint.x - m_firstPoint.x; - if (denominator == 0) denominator == 0.001; - double slope = ((m_lastPoint.y - m_firstPoint.y) / denominator); - double angle = std::atan(slope) * (180 / 3.14159); - if (abs(angle) > 67.5) - m_lastPoint.x = m_firstPoint.x; - else if (abs(angle) < 22.5) - m_lastPoint.y = m_firstPoint.y; - else { - double xDistance = m_lastPoint.x - m_firstPoint.x; - double yDistance = m_lastPoint.y - m_firstPoint.y; - if (abs(xDistance) > abs(yDistance)) { - if (abs(yDistance) == yDistance) - m_lastPoint.y = m_firstPoint.y + abs(xDistance); - else - m_lastPoint.y = m_firstPoint.y - abs(xDistance); - } else { - if (abs(xDistance) == xDistance) - m_lastPoint.x = m_firstPoint.x + abs(yDistance); - else - m_lastPoint.x = m_firstPoint.x - abs(yDistance); - } - } - } - m_mousePos = pos; - m_brushPos = getCenteredCursorPos(pos); - invalidate(invalidateRect); +void ToonzRasterBrushTool::inputSetBusy(bool busy) { + if (m_painting.active == busy) return; - } + + if (busy) { + // begin painting + + TTool::Application *app = TTool::getApplication(); + if (!app) return; - if (m_isMyPaintStyleSelected) { - TRasterP ras = ti->getRaster(); - TPointD point(centeredPos + rasCenter); - - m_strokeSegmentRect.empty(); - m_toonz_brush->strokeTo(point, pressure, TPointD(), restartBrushTimer()); - TRect updateRect = m_strokeSegmentRect * ras->getBounds(); - if (!updateRect.isEmpty()) { - // ras->extract(updateRect)->copy(m_workRaster->extract(updateRect)); - m_toonz_brush->updateDrawing(ras, m_backupRas, m_strokeSegmentRect, - m_styleId, m_modifierLockAlpha.getValue()); - } - m_lastRect = m_strokeRect; - - TPointD thickOffset(m_maxCursorThick * 0.5, m_maxCursorThick * 0.5); - invalidateRect = convert(m_strokeSegmentRect) - rasCenter; - invalidateRect += - TRectD(centeredPos - thickOffset, centeredPos + thickOffset); - invalidateRect += - TRectD(m_brushPos - thickOffset, m_brushPos + thickOffset); - } else if (m_rasterTrack && - (m_hardness.getValue() == 100 || m_pencil.getValue())) { - /*-- Pencilモードでなく、Hardness=100 の場合のブラシサイズを1段階下げる - * --*/ - if (!m_pencil.getValue()) thickness -= 1.0; - - TThickPoint thickPoint(centeredPos + rasCenter, thickness); - std::vector pts; - if (m_smooth.getValue() == 0) { - pts.push_back(thickPoint); + int col = app->getCurrentColumn()->getColumnIndex(); + m_enabled = col >= 0 || app->getCurrentFrame()->isEditingLevel(); + // todo: gestire autoenable + if (!m_enabled) return; + + m_painting.active = !!getImage(true); + if (!m_painting.active) + m_painting.active = !!touchImage(); + if (!m_painting.active) + return; + + // nel caso che il colore corrente sia un cleanup/studiopalette color + // oppure il colore di un colorfield + updateCurrentStyle(); + if (TColorStyle *cs = app->getCurrentLevelStyle()) { + m_painting.styleId = app->getCurrentLevelStyleIndex(); + TRasterStyleFx *rfx = cs->getRasterStyleFx(); + if (!cs->isStrokeStyle() && (!rfx || !rfx->isInkStyle())) + { m_painting.active = false; return; } } else { - m_smoothStroke.addPoint(thickPoint); - m_smoothStroke.getSmoothPoints(pts); + m_painting.styleId = 1; } - for (size_t i = 0; i < pts.size(); ++i) { - const TThickPoint &thickPoint = pts[i]; - m_rasterTrack->add(thickPoint); - m_tileSaver->save(m_rasterTrack->getLastRect()); - m_rasterTrack->generateLastPieceOfStroke(m_pencil.getValue()); - std::vector brushPoints = m_rasterTrack->getPointsSequence(); - int m = (int)brushPoints.size(); - std::vector points; - if (m == 3) { - points.push_back(brushPoints[0]); - points.push_back(brushPoints[1]); - } else { - points.push_back(brushPoints[m - 4]); - points.push_back(brushPoints[m - 3]); - points.push_back(brushPoints[m - 2]); - } - invalidateRect += ToolUtils::getBounds(points, maxThickness) - rasCenter; + + m_painting.frameId = getFrameId(); + + TImageP img = getImage(true); + TToonzImageP ri(img); + TRasterCM32P ras = ri->getRaster(); + if (!ras) + { m_painting.active = false; return; } + + m_painting.tileSet = new TTileSetCM32(ras->getSize()); + m_painting.tileSaver = new TTileSaverCM32(ras, m_painting.tileSet); + m_painting.affectedRect.empty(); + setWorkAndBackupImages(); + + if (m_isMyPaintStyleSelected) { + // init myPaint drawing + + m_painting.myPaint.isActive = true; + m_painting.myPaint.strokeSegmentRect.empty(); + + m_workRas->lock(); + + // prepare base brush + if ( TMyPaintBrushStyle *mypaintStyle = dynamic_cast(app->getCurrentLevelStyle()) ) + m_painting.myPaint.baseBrush.fromBrush( mypaintStyle->getBrush() ); else + m_painting.myPaint.baseBrush.fromDefaults(); + double modifierSize = m_modifierSize.getValue() * log(2.0); + float baseSize = m_painting.myPaint.baseBrush.getBaseValue( MYPAINT_BRUSH_SETTING_RADIUS_LOGARITHMIC ); + m_painting.myPaint.baseBrush.setBaseValue( MYPAINT_BRUSH_SETTING_RADIUS_LOGARITHMIC, baseSize + modifierSize ); + } else + if (m_hardness.getValue() == 100 || m_pencil.getValue()) { + // init pencil drawing + + m_painting.pencil.isActive = true; + m_painting.pencil.realPencil = m_pencil.getValue(); + } else { + // init blured brush drawing (regular drawing) + + m_painting.blured.isActive = true; } + } else { - // antialiased brush - assert(m_workRas.getPointer() && m_backupRas.getPointer()); - TThickPoint thickPoint(centeredPos + rasCenter, thickness); - std::vector pts; - if (m_smooth.getValue() == 0) { - pts.push_back(thickPoint); - } else { - m_smoothStroke.addPoint(thickPoint); - m_smoothStroke.getSmoothPoints(pts); + // finish painting + + if (m_painting.myPaint.isActive) { + // finish myPaint drawing + m_workRas->unlock(); } - for (size_t i = 0; i < pts.size(); ++i) { - TThickPoint old = m_points.back(); - - const TThickPoint &point = pts[i]; - TThickPoint mid((old + point) * 0.5, (point.thick + old.thick) * 0.5); - m_points.push_back(mid); - m_points.push_back(point); - - TRect bbox; - int m = (int)m_points.size(); - std::vector points; - if (m == 3) { - // ho appena cominciato. devo disegnare un segmento - TThickPoint pa = m_points.front(); - points.push_back(pa); - points.push_back(mid); - bbox = m_bluredBrush->getBoundFromPoints(points); - updateWorkAndBackupRasters(bbox + m_lastRect); - m_tileSaver->save(bbox); - m_bluredBrush->addArc(pa, (mid + pa) * 0.5, mid, 1, 1); - m_lastRect += bbox; - } else { - points.push_back(m_points[m - 4]); - points.push_back(old); - points.push_back(mid); - bbox = m_bluredBrush->getBoundFromPoints(points); - updateWorkAndBackupRasters(bbox + m_lastRect); - m_tileSaver->save(bbox); - m_bluredBrush->addArc(m_points[m - 4], old, mid, 1, 1); - m_lastRect += bbox; - } - invalidateRect += ToolUtils::getBounds(points, maxThickness) - rasCenter; - m_bluredBrush->updateDrawing(ti->getRaster(), m_backupRas, bbox, - m_styleId, m_drawOrder.getIndex(), - m_modifierLockAlpha.getValue()); - m_strokeRect += bbox; + delete m_painting.tileSaver; + m_painting.tileSaver = nullptr; + + // add undo record + TFrameId frameId = m_painting.frameId.isEmptyFrame() ? getCurrentFid() : m_painting.frameId; + if (m_painting.tileSet->getTileCount() > 0) { + TTool::Application *app = TTool::getApplication(); + TXshLevel *level = app->getCurrentLevel()->getLevel(); + TXshSimpleLevelP simLevel = level->getSimpleLevel(); + TRasterCM32P ras = TToonzImageP( getImage(true) )->getRaster(); + TRasterCM32P subras = ras->extract(m_painting.affectedRect)->clone(); + TUndoManager::manager()->add(new MyPaintBrushUndo( + m_painting.tileSet, simLevel.getPointer(), frameId, m_isFrameCreated, + m_isLevelCreated, subras, m_painting.affectedRect.getP00())); + // MyPaintBrushUndo will delete tileSet by it self + } else { + // delete tileSet here because MyPaintBrushUndo will not do it + delete m_painting.tileSet; } - } + m_painting.tileSet = nullptr; - // clear & draw brush tip when drawing smooth stroke - if (m_smooth.getValue() != 0) { - TPointD halfThick(m_maxThick * 0.5, m_maxThick * 0.5); - invalidateRect += TRectD(m_brushPos - halfThick, m_brushPos + halfThick); - invalidateRect += TRectD(centeredPos - halfThick, centeredPos + halfThick); - } + /*-- 作業中のフレームをリセット --*/ + m_painting.frameId = TFrameId(); - m_mousePos = pos; - m_brushPos = getCenteredCursorPos(pos); - invalidate(invalidateRect.enlarge(2)); -} - -//--------------------------------------------------------------------------------------------------------------- - -void ToonzRasterBrushTool::leftButtonUp(const TPointD &pos, - const TMouseEvent &e) { - bool isValid = m_enabled && m_active; - m_enabled = false; - m_active = false; - if (!isValid) { - return; + m_painting.myPaint.isActive = false; + m_painting.pencil.isActive = false; + m_painting.blured.isActive = false; + m_painting.active = false; + + /*-- FIdを指定して、描画中にフレームが動いても、 + 描画開始時のFidのサムネイルが更新されるようにする。--*/ + notifyImageChanged(frameId); + ToolUtils::updateSaveBox(); } - TPointD centeredPos; - if (m_isStraight) - centeredPos = getCenteredCursorPos(m_lastPoint); - else - centeredPos = getCenteredCursorPos(pos); - - double pressure; - if (m_isMyPaintStyleSelected) // mypaint brush case - pressure = m_pressure.getValue() && e.isTablet() ? e.m_pressure : 0.5; - else - pressure = m_pressure.getValue() ? e.m_pressure : 1.0; - - if (m_isStraight && m_maxPressure > 0.0) pressure = m_maxPressure; - - finishRasterBrush(centeredPos, pressure); - int tc = ToonzCheck::instance()->getChecks(); - if (tc & ToonzCheck::eGap || tc & ToonzCheck::eAutoclose || m_isStraight) - invalidate(); - - m_isStraight = false; - m_maxPressure = -1.0; } -//--------------------------------------------------------------------------------------------------------------- -/*! - * ドラッグ中にツールが切り替わった場合に備え、onDeactivate時とMouseRelease時にと同じ終了処理を行う - */ -void ToonzRasterBrushTool::finishRasterBrush(const TPointD &pos, - double pressureVal) { - TToonzImageP ti = TImageP(getImage(true)); - - if (!ti) return; - - TPointD rasCenter = ti->getRaster()->getCenterD(); - TTool::Application *app = TTool::getApplication(); - TXshLevel *level = app->getCurrentLevel()->getLevel(); - TXshSimpleLevelP simLevel = level->getSimpleLevel(); - - /*-- - * 描画中にカレントフレームが変わっても、描画開始時のFidに対してUndoを記録する - * --*/ - TFrameId frameId = - m_workingFrameId.isEmptyFrame() ? getCurrentFid() : m_workingFrameId; - if (m_isMyPaintStyleSelected) { - TRasterCM32P ras = ti->getRaster(); - TPointD point(pos + rasCenter); - double pressure = m_pressure.getValue() ? pressureVal : 0.5; - - m_strokeSegmentRect.empty(); - m_toonz_brush->strokeTo(point, pressure, TPointD(), restartBrushTimer()); - m_toonz_brush->endStroke(); - TRect updateRect = m_strokeSegmentRect * ras->getBounds(); - if (!updateRect.isEmpty()) { - // ras->extract(updateRect)->copy(m_workRaster->extract(updateRect)); - m_toonz_brush->updateDrawing(ras, m_backupRas, m_strokeSegmentRect, - m_styleId, m_modifierLockAlpha.getValue()); - } - TPointD thickOffset(m_maxCursorThick * 0.5, - m_maxCursorThick * 0.5); // TODO - TRectD invalidateRect = convert(m_strokeSegmentRect) - rasCenter; - invalidateRect += TRectD(pos - thickOffset, pos + thickOffset); - invalidate(invalidateRect.enlarge(2.0)); - - if (m_toonz_brush) { - delete m_toonz_brush; - m_toonz_brush = 0; - } - - m_lastRect.empty(); - m_workRas->unlock(); - - if (m_tileSet->getTileCount() > 0) { - TRasterCM32P subras = ras->extract(m_strokeRect)->clone(); - TUndoManager::manager()->add(new MyPaintBrushUndo( - m_tileSet, simLevel.getPointer(), frameId, m_isFrameCreated, - m_isLevelCreated, subras, m_strokeRect.getP00())); - } - - } else if (m_rasterTrack && - (m_hardness.getValue() == 100 || m_pencil.getValue())) { - double thickness = m_pressure.getValue() - ? computeThickness(pressureVal, m_rasThickness) * 2 - : m_rasThickness.getValue().second; - - if (!m_isStraight) { - // ストロークの最初にMaxサイズの円が描かれてしまう不具合を防止する - if (m_pressure.getValue() && pressureVal == 1.0) - thickness = m_rasThickness.getValue().first; +//-------------------------------------------------------------------------------------------------- - // Pencilモードでなく、Hardness=100 の場合のブラシサイズを1段階下げる - if (!m_pencil.getValue()) thickness -= 1.0; - } +void ToonzRasterBrushTool::inputPaintTrackPoint(const TTrackPoint &point, const TTrack &track, bool firstTrack, bool preview) { + if (!m_painting.active || preview) + return; + + TImageP img = getImage(true); + TToonzImageP ri(img); + TRasterCM32P ras = ri->getRaster(); + if (!ras) + return; + TPointD rasCenter = ras->getCenterD(); + TPointD fixedPosition = getCenteredCursorPos(point.position); + - TRectD invalidateRect; - TThickPoint thickPoint(pos + rasCenter, thickness); - std::vector pts; - if (m_smooth.getValue() == 0 || m_isStraight) { - pts.push_back(thickPoint); - } else { - m_smoothStroke.addPoint(thickPoint); - m_smoothStroke.endStroke(); - m_smoothStroke.getSmoothPoints(pts); + TRectD invalidateRect; + bool firstPoint = track.size() == track.pointsAdded; + bool lastPoint = track.pointsAdded == 1 && track.finished(); + + // first point must be without handler, following points must be with handler + // other behaviour is possible bug and must be ignored + assert(firstPoint == !track.toolHandler); + if (firstPoint != !track.toolHandler) + return; + + if (m_painting.myPaint.isActive) { + // mypaint case + + // init brush + MyPaintStroke *handler; + if (firstPoint) { + handler = new MyPaintStroke(m_workRas, *this, m_painting.myPaint.baseBrush, false); + handler->brush.beginStroke(); + track.toolHandler = handler; } - for (size_t i = 0; i < pts.size(); ++i) { - const TThickPoint &thickPoint = pts[i]; - m_rasterTrack->add(thickPoint); - m_tileSaver->save(m_rasterTrack->getLastRect(m_isStraight)); - m_rasterTrack->generateLastPieceOfStroke(m_pencil.getValue(), true, - m_isStraight); - - std::vector brushPoints = m_rasterTrack->getPointsSequence(); - int m = (int)brushPoints.size(); - std::vector points; - if (m_isStraight) { - points.push_back(brushPoints[0]); - points.push_back(brushPoints[2]); - } else if (m == 3) { - points.push_back(brushPoints[0]); - points.push_back(brushPoints[1]); - } else { - points.push_back(brushPoints[m - 4]); - points.push_back(brushPoints[m - 3]); - points.push_back(brushPoints[m - 2]); + handler = dynamic_cast(track.toolHandler.getPointer()); + if (!handler) return; + + // paint stroke + m_painting.myPaint.strokeSegmentRect.empty(); + handler->brush.strokeTo( fixedPosition + rasCenter, point.pressure, + point.tilt, point.time - track.previous().time ); + if (lastPoint) + handler->brush.endStroke(); + + // update affected area + TRect updateRect = m_painting.myPaint.strokeSegmentRect * ras->getBounds(); + m_painting.affectedRect += updateRect; + if (!updateRect.isEmpty()) + handler->brush.updateDrawing( ras, m_backupRas, m_painting.myPaint.strokeSegmentRect, + m_painting.styleId, m_modifierLockAlpha.getValue() ); + + // determine invalidate rect + invalidateRect += convert(m_painting.myPaint.strokeSegmentRect) - rasCenter; + } else + if (m_painting.pencil.isActive) { + // pencil case + + // Pencilモードでなく、Hardness=100 の場合のブラシサイズを1段階下げる + double thickness = computeThickness(point.pressure, m_rasThickness)*2; + //if (!m_painting.pencil.realPencil && !m_modifierLine->getManager()) + // thickness -= 1.0; + TThickPoint thickPoint(fixedPosition + rasCenter, thickness); + + // init brush + PencilStroke *handler; + if (firstPoint) { + DrawOrder drawOrder = (DrawOrder)m_drawOrder.getIndex(); + handler = new PencilStroke( ras, BRUSH, NONE, m_painting.styleId, thickPoint, drawOrder != OverAll, 0, + m_modifierLockAlpha.getValue(), !m_painting.pencil.realPencil, + drawOrder == PaletteOrder ); + + // if the drawOrder mode = "Palette Order", + // get styleId list which is above the current style in the palette + if (drawOrder == PaletteOrder) { + QSet aboveStyleIds; + getAboveStyleIdSet(m_painting.styleId, ri->getPalette(), aboveStyleIds); + handler->brush.setAboveStyleIds(aboveStyleIds); } - double maxThickness = m_rasThickness.getValue().second; - invalidateRect += ToolUtils::getBounds(points, maxThickness) - rasCenter; - } - invalidate(invalidateRect.enlarge(2)); - - if (m_tileSet->getTileCount() > 0) { - TUndoManager::manager()->add(new RasterBrushUndo( - m_tileSet, m_rasterTrack->getPointsSequence(), - m_rasterTrack->getStyleId(), m_rasterTrack->isSelective(), - simLevel.getPointer(), frameId, m_pencil.getValue(), m_isFrameCreated, - m_isLevelCreated, m_rasterTrack->isPaletteOrder(), - m_rasterTrack->isAlphaLocked(), m_isStraight)); + track.toolHandler = handler; } - delete m_rasterTrack; - m_rasterTrack = 0; - } else { - double maxThickness = m_rasThickness.getValue().second; - double thickness = (m_pressure.getValue()) - ? computeThickness(pressureVal, m_rasThickness) * 2 - : maxThickness; - TPointD rasCenter = ti->getRaster()->getCenterD(); - TRectD invalidateRect; - TThickPoint thickPoint(pos + rasCenter, thickness); - std::vector pts; - if (m_smooth.getValue() == 0 || m_isStraight) { - pts.push_back(thickPoint); - } else { - m_smoothStroke.addPoint(thickPoint); - m_smoothStroke.endStroke(); - m_smoothStroke.getSmoothPoints(pts); - } - // we need to skip the for-loop here if pts.size() == 0 or else - // (pts.size() - 1) becomes ULLONG_MAX since size_t is unsigned - if (pts.size() > 0) { - // this doesn't get run for a straight line - for (size_t i = 0; i < pts.size() - 1; ++i) { - TThickPoint old = m_points.back(); - - const TThickPoint &point = pts[i]; - TThickPoint mid((old + point) * 0.5, (point.thick + old.thick) * 0.5); - m_points.push_back(mid); - m_points.push_back(point); - - TRect bbox; - int m = (int)m_points.size(); - std::vector points; - if (m == 3) { - // ho appena cominciato. devo disegnare un segmento - TThickPoint pa = m_points.front(); - points.push_back(pa); - points.push_back(mid); - bbox = m_bluredBrush->getBoundFromPoints(points); - updateWorkAndBackupRasters(bbox + m_lastRect); - m_tileSaver->save(bbox); - m_bluredBrush->addArc(pa, (mid + pa) * 0.5, mid, 1, 1); - m_lastRect += bbox; - } else { - points.push_back(m_points[m - 4]); - points.push_back(old); - points.push_back(mid); - bbox = m_bluredBrush->getBoundFromPoints(points); - updateWorkAndBackupRasters(bbox + m_lastRect); - m_tileSaver->save(bbox); - m_bluredBrush->addArc(m_points[m - 4], old, mid, 1, 1); - m_lastRect += bbox; - } - - invalidateRect += - ToolUtils::getBounds(points, maxThickness) - rasCenter; - - m_bluredBrush->updateDrawing(ti->getRaster(), m_backupRas, bbox, - m_styleId, m_drawOrder.getIndex(), - m_modifierLockAlpha.getValue()); - m_strokeRect += bbox; - } - if (!m_isStraight && m_points.size() > 1) { - TThickPoint point = pts.back(); - m_points.push_back(point); - } - int m = m_points.size(); - std::vector points; - if (!m_isStraight && m_points.size() > 1) { - points.push_back(m_points[m - 3]); - points.push_back(m_points[m - 2]); - points.push_back(m_points[m - 1]); - } else { - const TThickPoint point = m_points[0]; - TThickPoint mid((thickPoint + point) * 0.5, - (point.thick + thickPoint.thick) * 0.5); - m_points.push_back(mid); - m_points.push_back(thickPoint); - points.push_back(m_points[0]); - points.push_back(m_points[1]); - points.push_back(m_points[2]); + handler = dynamic_cast(track.toolHandler.getPointer()); + if (!handler) return; + + // paint stroke + if (!firstPoint) + handler->brush.add(thickPoint); + + // update affected area + TRect strokeRect = handler->brush.getLastRect() * ras->getBounds(); + m_painting.tileSaver->save( strokeRect ); + m_painting.affectedRect += strokeRect; + handler->brush.generateLastPieceOfStroke( m_painting.pencil.realPencil ); + + // determine invalidate rect + invalidateRect += convert(strokeRect) - rasCenter; + } else + if (m_painting.blured.isActive) { + // blured brush case (aka regular brush) + + double maxThick = m_rasThickness.getValue().second; + DrawOrder drawOrder = (DrawOrder)m_drawOrder.getIndex(); + + // init brush + BluredStroke *handler; + if (firstPoint) { + handler = new BluredStroke(m_workRas, maxThick, m_brushPad, false); + // if the drawOrder mode = "Palette Order", + // get styleId list which is above the current style in the palette + if (drawOrder == PaletteOrder) { + QSet aboveStyleIds; + getAboveStyleIdSet(m_painting.styleId, ri->getPalette(), aboveStyleIds); + handler->brush.setAboveStyleIds(aboveStyleIds); } - TRect bbox = m_bluredBrush->getBoundFromPoints(points); - updateWorkAndBackupRasters(bbox); - m_tileSaver->save(bbox); - m_bluredBrush->addArc(points[0], points[1], points[2], 1, 1); - m_bluredBrush->updateDrawing(ti->getRaster(), m_backupRas, bbox, - m_styleId, m_drawOrder.getIndex(), - m_modifierLockAlpha.getValue()); - - invalidateRect += ToolUtils::getBounds(points, maxThickness) - rasCenter; - - m_lastRect += bbox; - m_strokeRect += bbox; - } - if (!invalidateRect.isEmpty()) invalidate(invalidateRect.enlarge(2)); - m_lastRect.empty(); - - delete m_bluredBrush; - m_bluredBrush = 0; - - if (m_tileSet->getTileCount() > 0) { - TUndoManager::manager()->add(new RasterBluredBrushUndo( - m_tileSet, m_points, m_styleId, (DrawOrder)m_drawOrder.getIndex(), - m_modifierLockAlpha.getValue(), simLevel.getPointer(), frameId, - m_rasThickness.getValue().second, m_hardness.getValue() * 0.01, - m_isFrameCreated, m_isLevelCreated, m_isStraight)); + track.toolHandler = handler; } + handler = dynamic_cast(track.toolHandler.getPointer()); + if (!handler) return; + + // paint stroke + double radius = computeThickness(point.pressure, m_rasThickness); + TThickPoint thickPoint(fixedPosition + rasCenter, radius*2); + TRect strokeRect( tfloor(thickPoint.x - maxThick - 0.999), + tfloor(thickPoint.y - maxThick - 0.999), + tceil(thickPoint.x + maxThick + 0.999), + tceil(thickPoint.y + maxThick + 0.999) ); + strokeRect *= ras->getBounds(); + m_painting.affectedRect += strokeRect; + updateWorkAndBackupRasters(m_painting.affectedRect); + m_painting.tileSaver->save(strokeRect); + handler->brush.addPoint(thickPoint, 1, !lastPoint); + handler->brush.updateDrawing( ras, m_backupRas, strokeRect, + m_painting.styleId, drawOrder, + m_modifierLockAlpha.getValue() ); + + invalidateRect += convert(strokeRect) - rasCenter; } - delete m_tileSaver; - - m_tileSaver = 0; - - /*-- FIdを指定して、描画中にフレームが動いても、 -   描画開始時のFidのサムネイルが更新されるようにする。--*/ - notifyImageChanged(frameId); - m_strokeRect.empty(); - - ToolUtils::updateSaveBox(); - - /*-- 作業中のフレームをリセット --*/ - m_workingFrameId = TFrameId(); + // invalidate rect + if (firstTrack) { + // for the first track we will paint cursor circle + // here we will invalidate rects for it + double thickness = m_isMyPaintStyleSelected ? m_maxCursorThick : m_maxThick*0.5; + TPointD thickOffset(thickness + 1, thickness + 1); + invalidateRect += TRectD(m_brushPos - thickOffset, m_brushPos + thickOffset); + invalidateRect += TRectD(fixedPosition - thickOffset, fixedPosition + thickOffset); + m_mousePos = point.position; + m_brushPos = fixedPosition; + } + + if (!invalidateRect.isEmpty()) + invalidate(invalidateRect.enlarge(2)); } + //--------------------------------------------------------------------------------------------------------------- -// 明日はここをMyPaintのときにカーソルを消せるように修正する!!!!!! -void ToonzRasterBrushTool::mouseMove(const TPointD &pos, const TMouseEvent &e) { - qApp->processEvents(QEventLoop::ExcludeUserInputEvents); +// 明日はここをMyPaintのときにカーソルを消せるように修正する!!!!!! +void ToonzRasterBrushTool::inputMouseMove(const TPointD &position, const TInputState &state) { struct Locals { ToonzRasterBrushTool *m_this; @@ -1909,18 +1531,7 @@ void ToonzRasterBrushTool::mouseMove(const TPointD &pos, const TMouseEvent &e) { TTool::getApplication()->getCurrentTool()->notifyToolChanged(); } - void addMinMax(TDoublePairProperty &prop, double add) { - if (add == 0.0) return; - const TDoublePairProperty::Range &range = prop.getRange(); - - TDoublePairProperty::Value value = prop.getValue(); - value.first = tcrop(value.first + add, range.first, range.second); - value.second = tcrop(value.second + add, range.first, range.second); - - setValue(prop, value); - } - - void addMinMaxSeparate(TDoublePairProperty &prop, double min, double max) { + void addMinMax(TDoublePairProperty &prop, double min, double max) { if (min == 0.0 && max == 0.0) return; const TDoublePairProperty::Range &range = prop.getRange(); @@ -1933,40 +1544,34 @@ void ToonzRasterBrushTool::mouseMove(const TPointD &pos, const TMouseEvent &e) { setValue(prop, value); } - } locals = {this}; - // if (e.isAltPressed() && !e.isCtrlPressed()) { - // const TPointD &diff = pos - m_mousePos; - // double add = (fabs(diff.x) > fabs(diff.y)) ? diff.x : diff.y; - - // locals.addMinMax( - // TToonzImageP(getImage(false, 1)) ? m_rasThickness : m_thickness, add); - //} else - double thickness = (m_isMyPaintStyleSelected) ? (double)(m_maxCursorThick + 1) : m_maxThick; TPointD halfThick(thickness * 0.5, thickness * 0.5); TRectD invalidateRect(m_brushPos - halfThick, m_brushPos + halfThick); - if (e.isCtrlPressed() && e.isAltPressed() && !e.isShiftPressed() && - Preferences::instance()->useCtrlAltToResizeBrushEnabled()) { + if ( Preferences::instance()->useCtrlAltToResizeBrushEnabled() + && state.isKeyPressed(TKey::control) + && state.isKeyPressed(TKey::alt) + && !state.isKeyPressed(TKey::shift) ) + { // Resize the brush if CTRL+ALT is pressed and the preference is enabled. - const TPointD &diff = pos - m_mousePos; + const TPointD &diff = position - m_mousePos; double max = diff.x / 2; double min = diff.y / 2; - locals.addMinMaxSeparate(m_rasThickness, min, max); + locals.addMinMax(m_rasThickness, min, max); double radius = m_rasThickness.getValue().second * 0.5; invalidateRect += TRectD(m_brushPos - TPointD(radius, radius), m_brushPos + TPointD(radius, radius)); } else { - m_mousePos = pos; - m_brushPos = getCenteredCursorPos(pos); + m_mousePos = position; + m_brushPos = getCenteredCursorPos(position); - invalidateRect += TRectD(pos - halfThick, pos + halfThick); + invalidateRect += TRectD(position - halfThick, position + halfThick); } invalidate(invalidateRect.enlarge(2)); @@ -1980,10 +1585,7 @@ void ToonzRasterBrushTool::mouseMove(const TPointD &pos, const TMouseEvent &e) { //------------------------------------------------------------------------------------------------------------- void ToonzRasterBrushTool::draw() { - if (m_isStraight) { - tglDrawSegment(m_firstPoint, m_lastPoint); - invalidate(TRectD(m_firstPoint, m_lastPoint).enlarge(2)); - } + m_inputmanager.draw(); /*--ショートカットでのツール切り替え時に赤点が描かれるのを防止する--*/ if (m_minThick == 0 && m_maxThick == 0 && @@ -2032,25 +1634,9 @@ void ToonzRasterBrushTool::draw() { //-------------------------------------------------------------------------------------------------------------- void ToonzRasterBrushTool::onEnter() { - TImageP img = getImage(false); - m_minThick = m_rasThickness.getValue().first; m_maxThick = m_rasThickness.getValue().second; updateCurrentStyle(); - - Application *app = getApplication(); - - m_styleId = app->getCurrentLevelStyleIndex(); - TColorStyle *cs = app->getCurrentLevelStyle(); - if (cs) { - TRasterStyleFx *rfx = cs->getRasterStyleFx(); - m_active = cs->isStrokeStyle() || (rfx && rfx->isInkStyle()); - m_currentColor = cs->getAverageColor(); - m_currentColor.m = 255; - } else { - m_currentColor = TPixel32::Black; - } - m_active = img; } //---------------------------------------------------------------------------------------------------------- @@ -2074,7 +1660,6 @@ TPropertyGroup *ToonzRasterBrushTool::getProperties(int idx) { void ToonzRasterBrushTool::onImageChanged() { if (!isEnabled()) return; - setWorkAndBackupImages(); } @@ -2086,22 +1671,57 @@ void ToonzRasterBrushTool::setWorkAndBackupImages() { TRasterP ras = ti->getRaster(); TDimension dim = ras->getSize(); - double hardness = m_hardness.getValue() * 0.01; - if (!m_isMyPaintStyleSelected && hardness == 1.0 && - ras->getPixelSize() == 4) { - m_workRas = TRaster32P(); - m_backupRas = TRasterCM32P(); + if (!m_workRas || m_workRas->getLx() < dim.lx || m_workRas->getLy() < dim.ly) + m_workRas = TRaster32P(dim); + if (!m_backupRas || m_backupRas->getLx() < dim.lx || m_backupRas->getLy() < dim.ly) + m_backupRas = TRasterCM32P(dim); + + m_workBackupRect.empty(); +} + +//--------------------------------------------------------------------------------------------------- + +void ToonzRasterBrushTool::updateWorkAndBackupRasters(const TRect &rect) { + TToonzImageP ti = TImageP(getImage(false, 1)); + if (!ti) return; + TRasterCM32P ras = ti->getRaster(); + + // work and backup rect will additionaly enlarged to 1/8 of it size + // in each affected direction to predict future possible enlargements in this direction + const int denominator = 8; + TRect enlargedRect = rect + m_workBackupRect; + int dx = (enlargedRect.getLx() - 1) / denominator + 1; + int dy = (enlargedRect.getLy() - 1) / denominator + 1; + + if (m_workBackupRect.isEmpty()) { + enlargedRect.x0 -= dx; + enlargedRect.y0 -= dy; + enlargedRect.x1 += dx; + enlargedRect.y1 += dy; + + enlargedRect *= ras->getBounds(); + if (enlargedRect.isEmpty()) return; + + m_workRas->extract(enlargedRect)->copy(ras->extract(enlargedRect)); + m_backupRas->extract(enlargedRect)->copy(ras->extract(enlargedRect)); } else { - if (!m_workRas || m_workRas->getLx() > dim.lx || - m_workRas->getLy() > dim.ly) - m_workRas = TRaster32P(dim); - if (!m_backupRas || m_backupRas->getLx() > dim.lx || - m_backupRas->getLy() > dim.ly) - m_backupRas = TRasterCM32P(dim); - - m_strokeRect.empty(); - m_lastRect.empty(); + if (enlargedRect.x0 < m_workBackupRect.x0) enlargedRect.x0 -= dx; + if (enlargedRect.y0 < m_workBackupRect.y0) enlargedRect.y0 -= dy; + if (enlargedRect.x1 > m_workBackupRect.x1) enlargedRect.x1 += dx; + if (enlargedRect.y1 > m_workBackupRect.y1) enlargedRect.y1 += dy; + + enlargedRect *= ras->getBounds(); + if (enlargedRect.isEmpty()) return; + + TRect lastRect = m_workBackupRect * ras->getBounds(); + QList rects = ToolUtils::splitRect(enlargedRect, lastRect); + for (int i = 0; i < rects.size(); i++) { + m_workRas->extract(rects[i])->copy(ras->extract(rects[i])); + m_backupRas->extract(rects[i])->copy(ras->extract(rects[i])); + } } + + m_workBackupRect = enlargedRect; } //------------------------------------------------------------------ @@ -2131,6 +1751,7 @@ bool ToonzRasterBrushTool::onPropertyChanged(std::string propertyName) { RasterBrushHardness = m_hardness.getValue(); RasterBrushModifierSize = m_modifierSize.getValue(); BrushLockAlpha = m_modifierLockAlpha.getValue(); + RasterBrushAssistants = m_assistants.getValue(); // Recalculate/reset based on changed settings if (propertyName == m_rasThickness.getName()) { @@ -2138,8 +1759,6 @@ bool ToonzRasterBrushTool::onPropertyChanged(std::string propertyName) { m_maxThick = m_rasThickness.getValue().second; } - if (propertyName == m_hardness.getName()) setWorkAndBackupImages(); - if (propertyName == m_hardness.getName() || propertyName == m_rasThickness.getName()) { m_brushPad = getBrushPad(m_rasThickness.getValue().second, @@ -2202,13 +1821,12 @@ void ToonzRasterBrushTool::loadPreset() { m_pressure.setValue(preset.m_pressure); m_modifierSize.setValue(preset.m_modifierSize); m_modifierLockAlpha.setValue(preset.m_modifierLockAlpha); + m_assistants.setValue(preset.m_assistants); // Recalculate based on updated presets m_minThick = m_rasThickness.getValue().first; m_maxThick = m_rasThickness.getValue().second; - setWorkAndBackupImages(); - m_brushPad = ToolUtils::getBrushPad(preset.m_max, preset.m_hardness * 0.01); } catch (...) { } @@ -2230,6 +1848,7 @@ void ToonzRasterBrushTool::addPreset(QString name) { preset.m_pressure = m_pressure.getValue(); preset.m_modifierSize = m_modifierSize.getValue(); preset.m_modifierLockAlpha = m_modifierLockAlpha.getValue(); + preset.m_assistants = m_assistants.getValue(); // Pass the preset to the manager m_presetsManager.addPreset(preset); @@ -2268,13 +1887,12 @@ void ToonzRasterBrushTool::loadLastBrush() { m_smooth.setValue(BrushSmooth); m_modifierSize.setValue(RasterBrushModifierSize); m_modifierLockAlpha.setValue(BrushLockAlpha ? 1 : 0); + m_assistants.setValue(RasterBrushAssistants ? 1 : 0); // Recalculate based on prior values m_minThick = m_rasThickness.getValue().first; m_maxThick = m_rasThickness.getValue().second; - setWorkAndBackupImages(); - m_brushPad = getBrushPad(m_rasThickness.getValue().second, m_hardness.getValue() * 0.01); } @@ -2289,20 +1907,13 @@ bool ToonzRasterBrushTool::isPencilModeActive() { //------------------------------------------------------------------ void ToonzRasterBrushTool::onColorStyleChanged() { - // in case the style switched while drawing - if (m_tileSaver) { - bool isValid = m_enabled && m_active; - m_enabled = false; - if (isValid) { - finishRasterBrush(m_mousePos, 1); - } - } + m_inputmanager.finishTracks(); + m_enabled = false; TTool::Application *app = getApplication(); TMyPaintBrushStyle *mpbs = dynamic_cast(app->getCurrentLevelStyle()); m_isMyPaintStyleSelected = (mpbs) ? true : false; - setWorkAndBackupImages(); getApplication()->getCurrentTool()->notifyToolChanged(); } @@ -2374,8 +1985,8 @@ BrushData::BrushData() , m_opacityMax(0.0) , m_pencil(false) , m_pressure(false) - , m_modifierSize(0.0) , m_drawOrder(0) + , m_modifierSize(0.0) , m_modifierOpacity(0.0) , m_modifierEraser(0.0) , m_modifierLockAlpha(0.0) diff --git a/toonz/sources/tnztools/toonzrasterbrushtool.h b/toonz/sources/tnztools/toonzrasterbrushtool.h index 3ac8fae..681230c 100644 --- a/toonz/sources/tnztools/toonzrasterbrushtool.h +++ b/toonz/sources/tnztools/toonzrasterbrushtool.h @@ -3,15 +3,28 @@ #ifndef TOONZRASTERBRUSHTOOL_H #define TOONZRASTERBRUSHTOOL_H -#include "tgeometry.h" -#include "tproperty.h" -#include "trasterimage.h" -#include "ttoonzimage.h" -#include "tstroke.h" -#include "toonz/strokegenerator.h" - -#include "tools/tool.h" -#include "tools/cursors.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#ifndef NDEBUG +#include +#endif + +#include "bluredbrush.h" #include "mypainttoonzbrush.h" #include @@ -118,7 +131,9 @@ private: // Toonz Raster Brush Tool declaration //************************************************************************ -class ToonzRasterBrushTool final : public TTool, public RasterController { +class ToonzRasterBrushTool final : public TTool, + public RasterController, + public TInputHandler { Q_DECLARE_TR_FUNCTIONS(ToonzRasterBrushTool) void updateCurrentStyle(); @@ -142,6 +157,14 @@ public: void leftButtonUp(const TPointD &pos, const TMouseEvent &e) override; void mouseMove(const TPointD &pos, const TMouseEvent &e) override; + void inputMouseMove(const TPointD &position, + const TInputState &state) override; + void inputSetBusy(bool busy) override; + void inputPaintTrackPoint(const TTrackPoint &point, const TTrack &track, + bool firstTrack, bool preview) override; + void inputInvalidateRect(const TRectD &bounds) override { invalidate(bounds); } + TTool *inputGetTool() override { return this; }; + void draw() override; void onEnter() override; @@ -162,7 +185,6 @@ public: void loadLastBrush(); - void finishRasterBrush(const TPointD &pos, double pressureVal); // return true if the pencil mode is active in the Brush / PaintBrush / Eraser // Tools. bool isPencilModeActive() override; @@ -172,7 +194,92 @@ public: bool askWrite(const TRect &rect) override; bool isMyPaintStyleSelected() { return m_isMyPaintStyleSelected; } +private: + enum MouseEventType { ME_DOWN, ME_DRAG, ME_UP, ME_MOVE }; + void handleMouseEvent(MouseEventType type, const TPointD &pos, + const TMouseEvent &e); + protected: + TInputManager m_inputmanager; + TSmartPointerT m_modifierLine; + TSmartPointerT m_modifierTangents; + TSmartPointerT m_modifierAssistants; + TSmartPointerT m_modifierSegmentation; + TSmartPointerT m_modifierSmoothSegmentation; + TSmartPointerT m_modifierSmooth[3]; +#ifndef NDEBUG + TSmartPointerT m_modifierTest; +#endif + + class MyPaintStroke: public TTrackToolHandler { + public: + MyPaintToonzBrush brush; + + inline MyPaintStroke( + const TRaster32P &ras, + RasterController &controller, + const mypaint::Brush &brush, + bool interpolation = false + ): + brush(ras, controller, brush, interpolation) + { } + }; + + class PencilStroke: public TTrackToolHandler { + public: + RasterStrokeGenerator brush; + + inline PencilStroke( const TRasterCM32P &raster, Tasks task, + ColorType colorType, int styleId, const TThickPoint &p, + bool selective, int selectedStyle, bool lockAlpha, + bool keepAntialias, bool isPaletteOrder = false + ): + brush(raster, task, colorType, styleId, p, selective, selectedStyle, lockAlpha, keepAntialias, isPaletteOrder) + { } + }; + + class BluredStroke: public TTrackToolHandler { + public: + BluredBrush brush; + + inline BluredStroke( const TRaster32P &ras, int size, + const QRadialGradient &gradient, bool doDynamicOpacity + ): + brush(ras, size, gradient, doDynamicOpacity) + { } + }; + + struct Painting { + // initial painting input + bool active = false; + int styleId = 0; + bool smooth = false; + // 作業中のFrameIdをクリック時に保存し、マウスリリース時(Undoの登録時)に別のフレームに 移動していたときの不具合を修正する。 + TFrameId frameId; + + // common variables + TTileSetCM32 *tileSet = nullptr; + TTileSaverCM32 *tileSaver = nullptr; + TRect affectedRect; + + SmoothStroke smoothStroke; + + struct Pencil { + bool isActive = false; + bool realPencil = false; + } pencil; + + struct Blured { + bool isActive = false; + } blured; + + struct MyPaint { + bool isActive = false; + mypaint::Brush baseBrush; + TRect strokeSegmentRect; + } myPaint; + } m_painting; + TPropertyGroup m_prop[2]; TDoublePairProperty m_rasThickness; @@ -184,12 +291,8 @@ protected: TBoolProperty m_pressure; TDoubleProperty m_modifierSize; TBoolProperty m_modifierLockAlpha; + TBoolProperty m_assistants; - RasterStrokeGenerator *m_rasterTrack; - TTileSetCM32 *m_tileSet; - TTileSaverCM32 *m_tileSaver; - TPixel32 m_currentColor; - int m_styleId; double m_minThick, m_maxThick; int m_targetType; @@ -197,44 +300,27 @@ protected: m_mousePos, //!< Current mouse position, in world coordinates. m_brushPos; //!< World position the brush will be painted at. - BluredBrush *m_bluredBrush; QRadialGradient m_brushPad; TRasterCM32P m_backupRas; TRaster32P m_workRas; - - std::vector m_points; - TRect m_strokeRect, m_lastRect, - m_strokeSegmentRect; // used with mypaint brush - - SmoothStroke m_smoothStroke; + TRect m_workBackupRect; BrushPresetManager m_presetsManager; //!< Manager for presets of this tool instance - bool m_active, m_enabled, + bool m_enabled, m_isPrompting, //!< Whether the tool is prompting for spline //! substitution. m_firstTime, m_presetsLoaded; - /*--- -作業中のFrameIdをクリック時に保存し、マウスリリース時(Undoの登録時)に別のフレームに -移動していたときの不具合を修正する。---*/ - TFrameId m_workingFrameId; - ToonzRasterBrushToolNotifier *m_notifier; bool m_isMyPaintStyleSelected = false; - MyPaintToonzBrush *m_toonz_brush = 0; QElapsedTimer m_brushTimer; int m_minCursorThick, m_maxCursorThick; bool m_propertyUpdating = false; - bool m_isStraight = false; - TPointD m_firstPoint; - TPointD m_lastPoint; - double m_maxPressure = -1.0; - protected: static void drawLine(const TPointD &point, const TPointD ¢re, bool horizontal, bool isDecimal); diff --git a/toonz/sources/tnztools/track.cpp b/toonz/sources/tnztools/track.cpp index 8d048d1..5af347a 100644 --- a/toonz/sources/tnztools/track.cpp +++ b/toonz/sources/tnztools/track.cpp @@ -42,7 +42,9 @@ TTrack::TTrack( hasPressure(hasPressure), hasTilt(hasTilt), pointsRemoved(), - pointsAdded() + pointsAdded(), + fixedPointsAdded(), + m_pointsFixed() { } TTrack::TTrack(const TTrackModifierP &modifier): @@ -55,7 +57,9 @@ TTrack::TTrack(const TTrackModifierP &modifier): hasTilt(modifier->original.hasTilt), modifier(modifier), pointsRemoved(), - pointsAdded() + pointsAdded(), + fixedPointsAdded(), + m_pointsFixed() { } const TTrack* @@ -82,7 +86,7 @@ TTrack::floorIndex(double index, double *outFrac) const { } void -TTrack::push_back(const TTrackPoint &point) { +TTrack::push_back(const TTrackPoint &point, bool fixed) { m_points.push_back(point); if (size() > 1) { const TTrackPoint &prev = *(m_points.rbegin() + 1); @@ -100,12 +104,14 @@ TTrack::push_back(const TTrackPoint &point) { p.length = prev.length + sqrt(d.x*d.x + d.y*d.y); } ++pointsAdded; + if (fixed) fix_all(); } void TTrack::pop_back(int count) { if (count > size()) count = size(); if (count <= 0) return; + assert(size() - count >= m_pointsFixed); m_points.resize(size() - count); if (pointsAdded > count) { pointsAdded -= count; return; } @@ -114,6 +120,15 @@ TTrack::pop_back(int count) { pointsRemoved += count; } +void +TTrack::fix_points(int count) { + count = std::min(count, previewSize()); + assert(count >= 0); + if (count <= 0) return; + m_pointsFixed += count; + fixedPointsAdded += count; +} + TTrackPoint TTrack::calcPoint(double index) const {