From fa009d0ee1b6707dab1da06514a1db1482e007a4 Mon Sep 17 00:00:00 2001 From: Ivan Mahonin Date: Tue, 22 Aug 2023 22:37:02 +0700 Subject: [PATCH] Assistants for ToonzVectorBrush --- toonz/sources/include/tgeometry.h | 14 +- toonz/sources/include/tools/assistant.h | 12 + toonz/sources/include/tools/inputmanager.h | 6 +- .../tools/modifiers/modifierassistants.h | 20 +- .../include/tools/modifiers/modifierline.h | 11 +- .../tools/modifiers/modifiersegmentation.h | 6 +- .../tools/modifiers/modifiersimplify.h | 42 + .../include/tools/modifiers/modifiersmooth.h | 16 +- .../tools/modifiers/modifiertangents.h | 12 +- .../include/tools/modifiers/modifiertest.h | 29 +- toonz/sources/include/tools/tool.h | 12 + toonz/sources/include/tools/track.h | 128 +- toonz/sources/include/toonz/strokegenerator.h | 20 +- toonz/sources/tnztools/CMakeLists.txt | 10 +- toonz/sources/tnztools/assistant.cpp | 92 ++ toonz/sources/tnztools/editassistantstool.cpp | 59 +- toonz/sources/tnztools/fullcolorbrushtool.cpp | 33 +- toonz/sources/tnztools/fullcolorbrushtool.h | 7 +- toonz/sources/tnztools/inputmanager.cpp | 49 +- .../tnztools/modifiers/modifierassistants.cpp | 171 +-- .../tnztools/modifiers/modifierline.cpp | 70 +- .../modifiers/modifiersegmentation.cpp | 27 +- .../tnztools/modifiers/modifiersimplify.cpp | 75 + .../tnztools/modifiers/modifiersmooth.cpp | 90 +- .../tnztools/modifiers/modifiertangents.cpp | 59 +- .../tnztools/modifiers/modifiertest.cpp | 159 ++- toonz/sources/tnztools/tool.cpp | 5 + .../sources/tnztools/toonzrasterbrushtool.cpp | 165 +-- toonz/sources/tnztools/toonzrasterbrushtool.h | 51 +- .../sources/tnztools/toonzvectorbrushtool.cpp | 1221 ++++++++--------- toonz/sources/tnztools/toonzvectorbrushtool.h | 135 +- toonz/sources/tnztools/track.cpp | 64 +- toonz/sources/toonz/sceneviewer.cpp | 20 + toonz/sources/toonz/sceneviewer.h | 2 + toonz/sources/toonz/sceneviewerevents.cpp | 6 + toonz/sources/toonzlib/strokegenerator.cpp | 71 +- 36 files changed, 1523 insertions(+), 1446 deletions(-) create mode 100644 toonz/sources/include/tools/modifiers/modifiersimplify.h create mode 100644 toonz/sources/tnztools/modifiers/modifiersimplify.cpp diff --git a/toonz/sources/include/tgeometry.h b/toonz/sources/include/tgeometry.h index f5c9a81a6..34b9902d3 100644 --- a/toonz/sources/include/tgeometry.h +++ b/toonz/sources/include/tgeometry.h @@ -200,6 +200,16 @@ inline TPointD normalize(const TPointD &p) { return (1.0 / n) * p; } +/*! +\relates TPointT +This helper function returns the normalized version of the specified point +or zero if it is not possible +*/ +inline TPointD normalizeOrZero(const TPointD &p) { + double n = norm2(p); + return fabs(n) > TConsts::epsilon*TConsts::epsilon ? p*(1/sqrt(n)) : TPointD(); +} + /*! \relates TPointT This helper function converts a TPoint (TPointT) into a TPointD @@ -1426,9 +1436,9 @@ class DVAPI TAngleRangeSet { inline Range range() const { return Range(a0(), a1()); } inline int size() const - { return (m_end - m_begin)/2 + 1; } + { return (int)(m_end - m_begin)/2 + 1; } inline int index() const - { return (m_current - m_begin)/2; } + { return (int)(m_current - m_begin)/2; } inline int reverseIndex() const { int i = index(); return i == 0 ? 0 : size() - i; } inline bool lapped() const diff --git a/toonz/sources/include/tools/assistant.h b/toonz/sources/include/tools/assistant.h index 14931aaa3..5be247d42 100644 --- a/toonz/sources/include/tools/assistant.h +++ b/toonz/sources/include/tools/assistant.h @@ -39,6 +39,7 @@ class TProperty; class TPropertyGroup; +class TTool; class TToolViewer; class TAssistant; class TAssistantPoint; @@ -340,6 +341,17 @@ class DVAPI TAssistant : public TMetaObjectHandler { double &outK, double &outMin, double &outMax ); + + static bool scanAssistants( + TTool *tool, + const TPointD *positions, + int positionsCount, + TGuidelineList *outGuidelines, + bool draw, + bool enabledOnly, + bool markEnabled, + bool drawGuidelines, + TImage *skipImage ); }; diff --git a/toonz/sources/include/tools/inputmanager.h b/toonz/sources/include/tools/inputmanager.h index 323dba14d..3dfcc5132 100644 --- a/toonz/sources/include/tools/inputmanager.h +++ b/toonz/sources/include/tools/inputmanager.h @@ -231,8 +231,10 @@ class DVAPI TInputManager { TInputState::DeviceId deviceId, TInputState::TouchId touchId, const TPointD &position, - const double *pressure, - const TPointD *tilt, + const double pressure, + const TPointD &tilt, + bool hasPressure, + bool hasTilt, bool final, TTimerTicks ticks ); bool keyEvent( diff --git a/toonz/sources/include/tools/modifiers/modifierassistants.h b/toonz/sources/include/tools/modifiers/modifierassistants.h index 0ba8059d6..35a286a3a 100644 --- a/toonz/sources/include/tools/modifiers/modifierassistants.h +++ b/toonz/sources/include/tools/modifiers/modifierassistants.h @@ -26,13 +26,16 @@ class DVAPI TModifierAssistants: public TInputModifier { public: - class DVAPI Modifier: public TTrackModifier { + typedef TSubTrackHandler Handler; + class DVAPI Interpolator: public TTrackInterpolator { public: - bool initialized; + const double magnetism; TGuidelineList guidelines; - - Modifier(TTrackHandler &handler); - TTrackPoint calcPoint(double originalIndex) override; + inline Interpolator(TTrack &track, double magnetism): + TTrackInterpolator(track), + magnetism(magnetism > 0 ? (magnetism < 1 ? magnetism : 1) : 0) + { } + TTrackPoint interpolate(double index) override; }; private: @@ -41,13 +44,14 @@ class DVAPI TModifierAssistants: public TInputModifier { int positionsCount, TGuidelineList *outGuidelines, bool draw, - bool enabledOnly ) const; + bool enabledOnly, + bool drawGuidelines ) const; public: - bool drawOnly; + double magnetism; double sensitiveLength; - explicit TModifierAssistants(bool drawOnly = false); + explicit TModifierAssistants(double magnetism = 1); void modifyTrack( const TTrack &track, diff --git a/toonz/sources/include/tools/modifiers/modifierline.h b/toonz/sources/include/tools/modifiers/modifierline.h index 495f711c9..622e2ae25 100644 --- a/toonz/sources/include/tools/modifiers/modifierline.h +++ b/toonz/sources/include/tools/modifiers/modifierline.h @@ -24,17 +24,12 @@ // TModifierLine definition //***************************************************************************************** -class TModifierLine: public TInputModifier { +class DVAPI TModifierLine: public TInputModifier { public: - class Modifier: public TTrackModifier { + class DVAPI Handler: public TSubTrackHandler { public: - explicit Modifier(TTrackHandler &handler): - TTrackModifier(handler), fixAngle(), maxPressure() { } - - bool fixAngle; double maxPressure; - - TTrackPoint calcPoint(double originalIndex) override; + inline Handler(): maxPressure() { } }; void modifyTrack( diff --git a/toonz/sources/include/tools/modifiers/modifiersegmentation.h b/toonz/sources/include/tools/modifiers/modifiersegmentation.h index 48d88727d..acfb76b40 100644 --- a/toonz/sources/include/tools/modifiers/modifiersegmentation.h +++ b/toonz/sources/include/tools/modifiers/modifiersegmentation.h @@ -25,6 +25,10 @@ //***************************************************************************************** class DVAPI TModifierSegmentation: public TInputModifier { +public: + typedef TSubTrackHandler Handler; + typedef TTrackIntrOrig Interpolator; + private: TPointD m_step; int m_maxLevel; @@ -32,7 +36,7 @@ class DVAPI TModifierSegmentation: public TInputModifier { void addSegments(TTrack &track, const TTrackPoint &p0, const TTrackPoint &p1, int maxLevel); public: - TModifierSegmentation(const TPointD &step = TPointD(1.0, 1.0), int level = 10); + explicit TModifierSegmentation(const TPointD &step = TPointD(1.0, 1.0), int level = 10); void setStep(const TPointD &step); inline const TPointD& getStep() const { return m_step; } diff --git a/toonz/sources/include/tools/modifiers/modifiersimplify.h b/toonz/sources/include/tools/modifiers/modifiersimplify.h new file mode 100644 index 000000000..155dd4a9e --- /dev/null +++ b/toonz/sources/include/tools/modifiers/modifiersimplify.h @@ -0,0 +1,42 @@ +#pragma once + +#ifndef MODIFIERSIMPLIFY_INCLUDED +#define MODIFIERSIMPLIFY_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 + + +//=================================================================== + +//***************************************************************************************** +// TModifierSimplify definition +//***************************************************************************************** + +class DVAPI TModifierSimplify: public TInputModifier { +public: + typedef TSubTrackHandler Handler; + typedef TTrackIntrOrig Interpolator; + + double step; + + explicit TModifierSimplify(double step = 1.0); + + void modifyTrack( + const TTrack &track, + TTrackList &outTracks ) override; +}; + + +#endif diff --git a/toonz/sources/include/tools/modifiers/modifiersmooth.h b/toonz/sources/include/tools/modifiers/modifiersmooth.h index 6968d38ae..e814b732c 100644 --- a/toonz/sources/include/tools/modifiers/modifiersmooth.h +++ b/toonz/sources/include/tools/modifiers/modifiersmooth.h @@ -26,23 +26,15 @@ class DVAPI TModifierSmooth: public TInputModifier { public: - class DVAPI Modifier: public TTrackModifier { + class DVAPI Handler: public TSubTrackHandler { public: const int radius; - TTrack *smoothedTrack; - - Modifier(TTrackHandler &handler, int radius); - TTrackPoint calcPoint(double originalIndex) override; + inline explicit Handler(int radius): radius(radius) { } }; -private: - int m_radius; - -public: - TModifierSmooth(int radius = 10); + int radius; - void setRadius(int radius); - int getRadius() const { return m_radius; } + explicit TModifierSmooth(int radius = 10); void modifyTrack( const TTrack &track, diff --git a/toonz/sources/include/tools/modifiers/modifiertangents.h b/toonz/sources/include/tools/modifiers/modifiertangents.h index a7e6232f1..960564e4b 100644 --- a/toonz/sources/include/tools/modifiers/modifiertangents.h +++ b/toonz/sources/include/tools/modifiers/modifiertangents.h @@ -26,17 +26,15 @@ class DVAPI TModifierTangents: public TInputModifier { public: - class DVAPI Modifier: public TTrackModifier { + typedef TSubTrackHandler Handler; + class DVAPI Interpolator: public TTrackInterpolator { public: - explicit Modifier(TTrackHandler &handler): - TTrackModifier(handler) { } - TTrackTangentList tangents; - - TTrackPoint calcPoint(double originalIndex) override; + using TTrackInterpolator::TTrackInterpolator; + TTrackPoint interpolate(double index) override; }; - TTrackTangent calcTangent(const TTrack &track, int index) const; + static TTrackTangent calcTangent(const TTrack &track, int index); void modifyTrack( const TTrack &track, diff --git a/toonz/sources/include/tools/modifiers/modifiertest.h b/toonz/sources/include/tools/modifiers/modifiertest.h index e4105471a..4732695e8 100644 --- a/toonz/sources/include/tools/modifiers/modifiertest.h +++ b/toonz/sources/include/tools/modifiers/modifiertest.h @@ -26,28 +26,31 @@ class DVAPI TModifierTest : public TInputModifier { public: - class DVAPI Handler : public TTrackHandler { + class DVAPI Handler : public TMultiTrackHandler { public: + const double radius; std::vector angles; - Handler(const TTrack &original) : TTrackHandler(original) {} + inline explicit Handler(double radius): + radius(std::max(TConsts::epsilon, radius)) { } }; - class DVAPI Modifier : public TTrackModifier { + class DVAPI Interpolator : public TTrackInterpolator { public: - double angle; - double radius; - double speed; - - Modifier(TTrackHandler &handler, double angle, double radius, - double speed = 0.25); - TTrackPoint calcPoint(double originalIndex) override; + const double angle; + const double radius; + const double speed; + inline Interpolator(TTrack &track, double angle, double radius, double speed): + TTrackInterpolator(track), angle(angle), radius(radius), speed(speed) { } + TTrackPoint interpolateFromOriginal(double originalIndex); + TTrackPoint interpolate(double index) override; }; public: - const int count; - const double radius; + int count; + double radius; + double speed; - TModifierTest(int count, double radius); + TModifierTest(int count = 3, double radius = 40, double speed = 0.25); void modifyTrack(const TTrack &track, TTrackList &outTracks) override; diff --git a/toonz/sources/include/tools/tool.h b/toonz/sources/include/tools/tool.h index fde613ef8..2be17c8bb 100644 --- a/toonz/sources/include/tools/tool.h +++ b/toonz/sources/include/tools/tool.h @@ -300,6 +300,17 @@ class DVAPI TTool { AllTargets = 0xffffffff, }; + enum ToolHints //! Misc flags related with tool + { HintNone = 0, + HintAssistants = 1 << 0, //!< Draw asistants when tool active + HintGuidelines = 1 << 1, //!< Draw asistant guidelines + HintAssistantsEnabled = 1 << 2, //!< Tool will use assisnats + + HintAssistantsAll = HintAssistants + | HintGuidelines + | HintAssistantsEnabled, + }; + public: static TTool *getTool(std::string toolName, ToolTargetType targetType); @@ -346,6 +357,7 @@ least virtual ToolType getToolType() const = 0; ToolTargetType getTargetType() const { return (ToolTargetType)m_targetType; } + virtual unsigned int getToolHints() const; std::string getName() const { return m_name; } diff --git a/toonz/sources/include/tools/track.h b/toonz/sources/include/tools/track.h index c9268d416..a8177b33d 100644 --- a/toonz/sources/include/tools/track.h +++ b/toonz/sources/include/tools/track.h @@ -37,13 +37,15 @@ class TTrack; class TTrackPoint; class TTrackTangent; class TTrackHandler; -class TTrackToolHandler; -class TTrackModifier; +class TSubTrackHandler; +class TMultiTrackHandler; +class TTrackInterpolator; typedef TSmartPointerT TTrackP; typedef TSmartPointerT TTrackHandlerP; -typedef TSmartPointerT TTrackToolHandlerP; -typedef TSmartPointerT TTrackModifierP; +typedef TSmartPointerT TSubTrackHandlerP; +typedef TSmartPointerT TMultiTrackHandlerP; +typedef TSmartPointerT TTrackInterpolatorP; typedef std::vector TTrackPointList; typedef std::vector TTrackTangentList; @@ -52,13 +54,6 @@ typedef std::vector TTrackList; //=================================================================== -//***************************************************************************************** -// TTrackToolHandler definition -//***************************************************************************************** - -class DVAPI TTrackToolHandler : public TSmartObject { }; - - //***************************************************************************************** // export template implementations for win32 //***************************************************************************************** @@ -66,8 +61,9 @@ class DVAPI TTrackToolHandler : public TSmartObject { }; #ifdef _WIN32 template class DVAPI TSmartPointerT; template class DVAPI TSmartPointerT; -template class DVAPI TSmartPointerT; -template class DVAPI TSmartPointerT; +template class DVAPI TSmartPointerT; +template class DVAPI TSmartPointerT; +template class DVAPI TSmartPointerT; #endif @@ -133,28 +129,38 @@ class DVAPI TTrackTangent { // TTrackHandler definition //***************************************************************************************** -class DVAPI TTrackHandler : public TSmartObject { +class DVAPI TTrackHandler : public TSmartObject { }; + + +//***************************************************************************************** +// TSubTrackHandler definition +//***************************************************************************************** + +class DVAPI TSubTrackHandler: public TTrackHandler { public: - const TTrack &original; - std::vector tracks; - TTrackHandler(const TTrack &original): - original(original) { } + TTrackP track; }; //***************************************************************************************** -// TTrackModifier definition +// TMultiTrackHandler definition //***************************************************************************************** -class DVAPI TTrackModifier : public TSmartObject { +class DVAPI TMultiTrackHandler: public TTrackHandler { public: - TTrackHandler &handler; - const TTrack &original; - const double timeOffset; + std::vector tracks; +}; + + +//***************************************************************************************** +// TTrackInterpolator definition +//***************************************************************************************** - explicit TTrackModifier(TTrackHandler &handler, double timeOffset = 0.0): - handler(handler), original(handler.original), timeOffset(timeOffset) { } - virtual TTrackPoint calcPoint(double originalIndex); +class DVAPI TTrackInterpolator : public TSmartObject { +public: + TTrack &track; + inline explicit TTrackInterpolator(TTrack &track); + virtual TTrackPoint interpolate(double index) = 0; }; @@ -177,15 +183,19 @@ class DVAPI TTrack : public TSmartObject { const TInputState::ButtonHistory::Holder buttonHistory; const bool hasPressure; const bool hasTilt; - const TTrackModifierP modifier; + const TTrack* const original; + const double timeOffset; + const double rootTimeOffset; + mutable TTrackHandlerP handler; - mutable TTrackToolHandlerP toolHandler; mutable int pointsRemoved; mutable int pointsAdded; mutable int fixedPointsAdded; private: + friend class TTrackInterpolator; + TTrackInterpolatorP interpolator; TTrackPointList m_points; const TTrackPoint m_none; int m_pointsFixed; @@ -198,15 +208,17 @@ class DVAPI TTrack : public TSmartObject { const TInputState::KeyHistory::Holder &keyHistory = TInputState::KeyHistory::Holder(), const TInputState::ButtonHistory::Holder &buttonHistory = TInputState::ButtonHistory::Holder(), bool hasPressure = false, - bool hasTilt = false + bool hasTilt = false, + double timeOffset = 0 ); - explicit TTrack(const TTrackModifierP &modifier); + explicit TTrack(const TTrack &original, double timeOffset = 0); + + const TTrackInterpolatorP& getInterpolator() const + { return interpolator; } + void removeInterpolator() + { interpolator.reset(); } - inline const TTrack* original() const - { return modifier ? &modifier->original : NULL; } - inline double timeOffset() const - { return modifier ? modifier->timeOffset : 0.0; } inline TTimerTicks ticks() const { return keyHistory.ticks(); } inline bool changed() const @@ -217,12 +229,14 @@ class DVAPI TTrack : public TSmartObject { inline int clampIndex(int index) const { return std::min(std::max(index, 0), size() - 1); } + inline double clampIndexFloat(double index) const + { return std::min(std::max(index, 0.0), (double)(size() - 1)); } inline int floorIndexNoClamp(double index) const { return (int)floor(index + TConsts::epsilon); } inline int floorIndex(double index) const { return clampIndex(floorIndexNoClamp(index)); } inline int ceilIndexNoClamp(double index) const - { return (int)ceil(index - TConsts::epsilon); } + { return floorIndexNoClamp(index) + 1; } inline int ceilIndex(double index) const { return clampIndex(ceilIndexNoClamp(index)); } @@ -287,15 +301,15 @@ class DVAPI TTrack : public TSmartObject { inline TInputState::KeyState::Holder getKeyState(double time) const { return keyHistory.get(time); } inline TInputState::KeyState::Holder getKeyState(const TTrackPoint &point) const - { return getKeyState(timeOffset() + point.time); } + { return getKeyState(rootTimeOffset + point.time); } inline TInputState::KeyState::Holder getCurrentKeyState() const - { return getKeyState(timeOffset() + current().time); } + { return getKeyState(rootTimeOffset + current().time); } inline TInputState::ButtonState::Holder getButtonState(double time) const { return buttonHistory.get(time); } inline TInputState::ButtonState::Holder getButtonState(const TTrackPoint &point) const - { return getButtonState(timeOffset() + point.time); } + { return getButtonState(rootTimeOffset + point.time); } inline TInputState::ButtonState::Holder getCurrentButtonState() const - { return getButtonState(timeOffset() + current().time); } + { return getButtonState(rootTimeOffset + current().time); } private: template @@ -345,11 +359,25 @@ class DVAPI TTrack : public TSmartObject { return interpolationLinear(p0.length, p1.length, frac); } - TTrackPoint calcPoint(double index) const; + inline TTrackPoint calcPoint(double index) const + { return interpolator ? interpolator->interpolate(index) : interpolateLinear(index); } TPointD calcTangent(double index, double distance = 0.1) const; double rootIndexByIndex(double index) const; TTrackPoint calcRootPoint(double index) const; + inline TTrackPoint pointFromOriginal(const TTrackPoint &originalPoint, double originalIndex) const { + TTrackPoint p = originalPoint; + p.originalIndex = original ? original->clampIndexFloat(originalIndex) : originalIndex; + p.time -= timeOffset; + return p; + } + + inline TTrackPoint pointFromOriginal(int originalIndex) const + { return original ? pointFromOriginal(original->point(originalIndex), originalIndex) : TTrackPoint(); } + + inline TTrackPoint calcPointFromOriginal(double originalIndex) const + { return original ? pointFromOriginal(original->calcPoint(originalIndex), originalIndex) : TTrackPoint(); } + inline TTrackPoint interpolateLinear(double index) const { double frac; const TTrackPoint &p0 = floorPoint(index, &frac); @@ -407,4 +435,24 @@ class DVAPI TTrack : public TSmartObject { }; + +//***************************************************************************************** +// TTrackInterpolator implemantation +//***************************************************************************************** + +inline TTrackInterpolator::TTrackInterpolator(TTrack &track): + track(track) { track.interpolator = this; } + + +//***************************************************************************************** +// TTrackIntrOrig definition +//***************************************************************************************** + +class DVAPI TTrackIntrOrig : public TTrackInterpolator { +public: + using TTrackInterpolator::TTrackInterpolator; + TTrackPoint interpolate(double index) override; +}; + + #endif diff --git a/toonz/sources/include/toonz/strokegenerator.h b/toonz/sources/include/toonz/strokegenerator.h index bc96dc7df..1d253f0e2 100644 --- a/toonz/sources/include/toonz/strokegenerator.h +++ b/toonz/sources/include/toonz/strokegenerator.h @@ -39,6 +39,9 @@ class DVAPI StrokeGenerator { //! Ultimo punto del frammento visualizzato TPointD m_p0, /*! Ultimo punto del frammento visualizzato*/ m_p1; + + //! mark that stroke must be looped + bool m_loop; //! Visualizza i frammenti /*! @@ -66,10 +69,19 @@ class DVAPI StrokeGenerator { di 4*pixelSize2 \param point TThickPoint da aggiungere al vettore - \param pixelSize2 Dimensione pixel + \param pixelSize2 Size of pixel, use 0 to guarantee that new point will be added + \returns true if point was actually added */ - void add(const TThickPoint &point, double pixelSize2); - + bool add(const TThickPoint &point, double pixelSize2); + + //! Remove last point (keep in mind that not each 'add' call produces new point) + void pop(); + + //! Mark/unmark track as looped + void setLoop(bool loop = true); + + inline bool getLoop() const { return m_loop; } + TPointD getFirstPoint(); // returns the first point //! Filtra i punti di m_points @@ -107,7 +119,7 @@ onlyLastPoint elementi di m_points \param onlyLastPoints Numero elementi sulla base dei quali creare la stroke */ - TStroke *makeStroke(double error, UINT onlyLastPoints = 0) const; + TStroke *makeStroke(double error, UINT onlyLastPoints = 0, bool useLoop = false) const; }; //=============================================================== diff --git a/toonz/sources/tnztools/CMakeLists.txt b/toonz/sources/tnztools/CMakeLists.txt index 267585371..9ca30f7cc 100644 --- a/toonz/sources/tnztools/CMakeLists.txt +++ b/toonz/sources/tnztools/CMakeLists.txt @@ -51,10 +51,11 @@ set(HEADERS ../include/tools/assistant.h ../include/tools/modifiers/modifierassistants.h ../include/tools/modifiers/modifierline.h - ../include/tools/modifiers/modifiertangents.h - ../include/tools/modifiers/modifiertest.h ../include/tools/modifiers/modifiersegmentation.h + ../include/tools/modifiers/modifiersimplify.h ../include/tools/modifiers/modifiersmooth.h + ../include/tools/modifiers/modifiertangents.h + ../include/tools/modifiers/modifiertest.h ../include/tools/assistants/guidelineline.h ../include/tools/assistants/guidelineellipse.h ) @@ -127,10 +128,11 @@ set(SOURCES assistant.cpp modifiers/modifierassistants.cpp modifiers/modifierline.cpp - modifiers/modifiertangents.cpp - modifiers/modifiertest.cpp modifiers/modifiersegmentation.cpp + modifiers/modifiersimplify.cpp modifiers/modifiersmooth.cpp + modifiers/modifiertangents.cpp + modifiers/modifiertest.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 826ab4ef3..d43d4c865 100644 --- a/toonz/sources/tnztools/assistant.cpp +++ b/toonz/sources/tnztools/assistant.cpp @@ -1,12 +1,24 @@ #include +#include + +#include +#include +#include +#include +#include +#include +#include + #include #include #include #include + + #ifdef MACOSX const double line_width_scale = 1.5; #else @@ -594,3 +606,83 @@ TAssistant::calcPerspectiveStep( outMax = (dx1 > 0.0 ? maxX : minX) - x0; return true; } + + +bool +TAssistant::scanAssistants( + TTool *tool, + const TPointD *positions, + int positionsCount, + TGuidelineList *outGuidelines, + bool draw, + bool enabledOnly, + bool markEnabled, + bool drawGuidelines, + TImage *skipImage ) +{ + TGuidelineList guidelines; + if (drawGuidelines && !outGuidelines) + outGuidelines = &guidelines; + + bool found = false; + bool findGuidelines = (positions && positionsCount > 0 && outGuidelines); + if (!findGuidelines) drawGuidelines = false; + bool doSomething = findGuidelines || draw; + + if (tool) + if (TToolViewer *viewer = tool->getViewer()) + if (TApplication *application = tool->getApplication()) + if (TXshLevelHandle *levelHandle = application->getCurrentLevel()) + if (TXshLevel *level = levelHandle->getLevel()) + if (TXshSimpleLevel *simpleLevel = level->getSimpleLevel()) + if (TFrameHandle *frameHandle = application->getCurrentFrame()) + if (TXsheetHandle *XsheetHandle = application->getCurrentXsheet()) + if (TXsheet *Xsheet = XsheetHandle->getXsheet()) + { + TPointD dpiScale = getCurrentDpiScale(simpleLevel, tool->getCurrentFid()); + int frame = frameHandle->getFrame(); + int count = Xsheet->getColumnCount(); + TAffine worldToTrack; + if ( tool->getToolType() & TTool::LevelTool + && !application->getCurrentObject()->isSpline() ) + { + worldToTrack.a11 /= dpiScale.x; + worldToTrack.a22 /= dpiScale.y; + } + + for(int i = 0; i < count; ++i) + if (TXshColumn *column = Xsheet->getColumn(i)) + if (column->isCamstandVisible()) + if (column->isPreviewVisible()) + if (TImageP image = Xsheet->getCell(frame, i).getImage(false)) + if (image != skipImage) + if (image->getType() == TImage::META) + if (TMetaImage *metaImage = dynamic_cast(image.getPointer())) + { + TAffine imageToTrack = worldToTrack * tool->getColumnMatrix(i); + if (draw) { glPushMatrix(); tglMultMatrix(imageToTrack); } + + TMetaImage::Reader reader(*metaImage); + for(TMetaObjectListCW::iterator i = reader->begin(); i != reader->end(); ++i) + if (*i) + if (const TAssistant *assistant = (*i)->getHandler()) + if (!enabledOnly || assistant->getEnabled()) + { + found = true; + if (findGuidelines) + for(int i = 0; i < positionsCount; ++i) + assistant->getGuidelines(positions[i], imageToTrack, *outGuidelines); + if (draw) assistant->draw(viewer, assistant->getEnabled() && markEnabled); + if (!doSomething) return true; + } + + if (draw) glPopMatrix(); + } + } + + if (drawGuidelines) + for(TGuidelineList::const_iterator i = outGuidelines->begin(); i != outGuidelines->end(); ++i) + (*i)->draw(); + + return found; +} diff --git a/toonz/sources/tnztools/editassistantstool.cpp b/toonz/sources/tnztools/editassistantstool.cpp index 25299d937..ed217b90c 100644 --- a/toonz/sources/tnztools/editassistantstool.cpp +++ b/toonz/sources/tnztools/editassistantstool.cpp @@ -200,6 +200,8 @@ class EditAssistantsTool final : public TTool { ToolType getToolType() const override { return TTool::LevelWriteTool; } + unsigned int getToolHints() const override + { return TTool::getToolHints() & ~HintAssistantsAll; } int getCursorId() const override { return ToolCursor::StrokeSelectCursor; } void onImageChanged() override { @@ -599,56 +601,21 @@ class EditAssistantsTool final : public TTool { { assistant->drawEdit(getViewer()); assistant->getGuidelines( - m_currentPosition + m_currentPointOffset, + position, TAffine(), m_currentGuidelines ); } - // draw assistans from other layers - TImage *currentImage = getImage(false); - if (TToolViewer *viewer = getViewer()) - if (TApplication *application = getApplication()) - if (TXshLevelHandle *levelHandle = application->getCurrentLevel()) - if (TXshLevel *level = levelHandle->getLevel()) - if (TXshSimpleLevel *simpleLevel = level->getSimpleLevel()) - if (TFrameHandle *frameHandle = application->getCurrentFrame()) - if (TXsheetHandle *XsheetHandle = application->getCurrentXsheet()) - if (TXsheet *Xsheet = XsheetHandle->getXsheet()) - { - TPointD dpiScale = getCurrentDpiScale(simpleLevel, getCurrentFid()); - int frame = frameHandle->getFrame(); - int count = Xsheet->getColumnCount(); - TAffine worldToTrack; - worldToTrack.a11 /= dpiScale.x; - worldToTrack.a22 /= dpiScale.y; - - for(int i = 0; i < count; ++i) - if (TXshColumn *column = Xsheet->getColumn(i)) - if (column->isCamstandVisible()) - if (column->isPreviewVisible()) - if (TImageP image = Xsheet->getCell(frame, i).getImage(false)) - if (image->getType() == TImage::META) - if (image != currentImage) - if (TMetaImage *metaImage = dynamic_cast(image.getPointer())) - { - TAffine imageToTrack = worldToTrack * getColumnMatrix(i); - glPushMatrix(); tglMultMatrix(imageToTrack); - - TMetaImage::Reader reader(*metaImage); - for(TMetaObjectListCW::iterator i = reader->begin(); i != reader->end(); ++i) - if (*i) - if (const TAssistant *assistant = (*i)->getHandler()) { - assistant->getGuidelines(position, imageToTrack, m_currentGuidelines); - assistant->draw(viewer, false); - } - - glPopMatrix(); - } - } - - // draw guidelines - for(TGuidelineList::const_iterator i = m_currentGuidelines.begin(); i != m_currentGuidelines.end(); ++i) - (*i)->draw(); + // draw assistans and guidelines from other layers + TAssistant::scanAssistants( + this, // tool + &position, 1, // pointer positions + nullptr, // out guidelines + true, // draw + false, // enabled only + false, // mark enabled + true, // draw guidelines + nullptr ); // skip image } }; diff --git a/toonz/sources/tnztools/fullcolorbrushtool.cpp b/toonz/sources/tnztools/fullcolorbrushtool.cpp index a2f3c283b..d764b8277 100644 --- a/toonz/sources/tnztools/fullcolorbrushtool.cpp +++ b/toonz/sources/tnztools/fullcolorbrushtool.cpp @@ -55,7 +55,7 @@ TEnv::DoubleVar FullcolorModifierSize("FullcolorModifierSize", 0); TEnv::DoubleVar FullcolorModifierOpacity("FullcolorModifierOpacity", 100); TEnv::IntVar FullcolorModifierEraser("FullcolorModifierEraser", 0); TEnv::IntVar FullcolorModifierLockAlpha("FullcolorModifierLockAlpha", 0); -TEnv::IntVar FullcolorAssistants("FullcolorAssistants", 0); +TEnv::IntVar FullcolorAssistants("FullcolorAssistants", 1); TEnv::StringVar FullcolorBrushPreset("FullcolorBrushPreset", ""); //---------------------------------------------------------------------------------- @@ -150,7 +150,7 @@ FullColorBrushTool::FullColorBrushTool(std::string name) m_inputmanager.setHandler(this); #ifndef NDEBUG - m_modifierTest = new TModifierTest(5, 40); + m_modifierTest = new TModifierTest(); #endif m_modifierLine = new TModifierLine(); m_modifierTangents = new TModifierTangents(); @@ -305,8 +305,8 @@ bool FullColorBrushTool::askWrite(const TRect &rect) { //-------------------------------------------------------------------------------------------------- bool FullColorBrushTool::preLeftButtonDown() { - m_modifierAssistants->drawOnly = !FullcolorAssistants; - m_inputmanager.drawPreview = false; //!m_modifierAssistants->drawOnly; + m_modifierAssistants->magnetism = m_assistants.getValue() ? 1 : 0; + m_inputmanager.drawPreview = false; //!m_modifierAssistants->drawOnly; m_inputmanager.clearModifiers(); m_inputmanager.addModifier(TInputModifierP(m_modifierTangents.getPointer())); @@ -342,7 +342,7 @@ void FullColorBrushTool::handleMouseEvent(MouseEventType type, bool control = e.getModifiersMask() & TMouseEvent::CTRL_KEY; if (shift && type == ME_DOWN && e.button() == Qt::LeftButton && !m_started) { - m_modifierAssistants->drawOnly = true; + m_modifierAssistants->magnetism = 0; m_inputmanager.clearModifiers(); m_inputmanager.addModifier(TInputModifierP(m_modifierLine.getPointer())); m_inputmanager.addModifier( @@ -363,9 +363,14 @@ void FullColorBrushTool::handleMouseEvent(MouseEventType type, 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); + bool isMyPaint = getApplication()->getCurrentLevelStyle()->getTagId() == 4001; + int deviceId = e.isTablet() ? 1 : 0; + double defPressure = isMyPaint ? 0.5 : 1.0; + bool hasPressure = e.isTablet(); + double pressure = hasPressure ? e.m_pressure : defPressure; + bool final = type == ME_UP; + m_inputmanager.trackEvent( + deviceId, 0, pos, pressure, TPointD(), hasPressure, false, final, t); m_inputmanager.processTracks(); } } @@ -519,20 +524,24 @@ void FullColorBrushTool::inputPaintTrackPoint(const TTrackPoint &point, // init brush TrackHandler *handler; - if (track.size() == track.pointsAdded && !track.toolHandler && m_workRaster) { + if (track.size() == track.pointsAdded && !track.handler && m_workRaster) { mypaint::Brush mypaintBrush; applyToonzBrushSettings(mypaintBrush); handler = new TrackHandler(m_workRaster, *this, mypaintBrush); handler->brush.beginStroke(); - track.toolHandler = handler; + track.handler = handler; } - handler = dynamic_cast(track.toolHandler.getPointer()); + handler = dynamic_cast(track.handler.getPointer()); if (!handler) return; + bool isMyPaint = getApplication()->getCurrentLevelStyle()->getTagId() == 4001; + double defPressure = isMyPaint ? 0.5 : 1.0; + double pressure = m_enabledPressure ? point.pressure : defPressure; + // paint stroke m_strokeSegmentRect.empty(); handler->brush.strokeTo(point.position + rasCenter, - m_enabledPressure ? point.pressure : 0.5, point.tilt, + pressure, point.tilt, point.time - track.previous().time); if (track.pointsAdded == 1 && track.finished()) handler->brush.endStroke(); diff --git a/toonz/sources/tnztools/fullcolorbrushtool.h b/toonz/sources/tnztools/fullcolorbrushtool.h index cdb8450c8..dd9b831de 100644 --- a/toonz/sources/tnztools/fullcolorbrushtool.h +++ b/toonz/sources/tnztools/fullcolorbrushtool.h @@ -42,7 +42,7 @@ class FullColorBrushTool final : public TTool, public TInputHandler { Q_DECLARE_TR_FUNCTIONS(FullColorBrushTool) public: - class TrackHandler : public TTrackToolHandler { + class TrackHandler : public TTrackHandler { public: MyPaintToonzBrush brush; @@ -59,7 +59,10 @@ class FullColorBrushTool final : public TTool, public: FullColorBrushTool(std::string name); - ToolType getToolType() const override { return TTool::LevelWriteTool; } + ToolType getToolType() const override + { return TTool::LevelWriteTool; } + unsigned int getToolHints() const override + { return TTool::getToolHints() & ~HintAssistantsAll; } ToolOptionsBox *createOptionsBox() override; diff --git a/toonz/sources/tnztools/inputmanager.cpp b/toonz/sources/tnztools/inputmanager.cpp index 06b7f8d2d..ee5c10c26 100644 --- a/toonz/sources/tnztools/inputmanager.cpp +++ b/toonz/sources/tnztools/inputmanager.cpp @@ -33,27 +33,28 @@ TInputModifier::modifyTrack( TTrackList &outTracks ) { if (!track.handler) { - track.handler = new TTrackHandler(track); - track.handler->tracks.push_back( - new TTrack( - new TTrackModifier(*track.handler) )); + TSubTrackHandler *handler = new TSubTrackHandler(); + track.handler = handler; + handler->track = new TTrack(track); + new TTrackIntrOrig(*handler->track); } - outTracks.insert( - outTracks.end(), - track.handler->tracks.begin(), - track.handler->tracks.end() ); - if (!track.changed()) + TSubTrackHandler *handler = dynamic_cast(track.handler.getPointer()); + if (!handler) return; + + outTracks.push_back(handler->track); + TTrack &subTrack = *handler->track; + if (!track.changed()) + return; + int start = std::max(0, track.size() - track.pointsAdded); - for(TTrackList::const_iterator ti = track.handler->tracks.begin(); ti != track.handler->tracks.end(); ++ti) { - TTrack &subTrack = **ti; - subTrack.truncate(start); - for(int i = start; i < track.size(); ++i) - subTrack.push_back( subTrack.modifier->calcPoint(i), false ); - subTrack.fix_to(track.fixedSize()); - } + subTrack.truncate(start); + for(int i = start; i < track.size(); ++i) + subTrack.push_back(subTrack.pointFromOriginal(i), false); + subTrack.fix_to(track.fixedSize()); + track.resetChanges(); } @@ -190,7 +191,7 @@ TInputHandler::inputPaintTracks(const TTrackList &tracks) { const TTrack &t = **i; if (t.pointsAdded > 0) { TTimerTicks ticks = t.ticks(); - double timeOffset = t.timeOffset() + t.current().time; + double timeOffset = t.rootTimeOffset + t.current().time; if (!track || (ticks - minTicks)*TToolTimer::frequency + timeOffset - minTimeOffset < 0.0) { track = *i; minTicks = ticks; @@ -485,21 +486,23 @@ TInputManager::trackEvent( TInputState::DeviceId deviceId, TInputState::TouchId touchId, const TPointD &position, - const double *pressure, - const TPointD *tilt, + const double pressure, + const TPointD &tilt, + bool hasPressure, + bool hasTilt, bool final, TTimerTicks ticks ) { ticks = fixTicks(ticks); - TTrackP track = getTrack(deviceId, touchId, ticks, (bool)pressure, (bool)tilt); + TTrackP track = getTrack(deviceId, touchId, ticks, hasPressure, hasTilt); if (!track->finished()) { ticks = fixTicks(ticks); - double time = (double)(ticks - track->ticks())*TToolTimer::step - track->timeOffset(); + double time = (double)(ticks - track->ticks())*TToolTimer::step - track->rootTimeOffset; addTrackPoint( track, position, - pressure ? *pressure : 0.5, - tilt ? *tilt : TPointD(), + pressure, + tilt, time, final ); } diff --git a/toonz/sources/tnztools/modifiers/modifierassistants.cpp b/toonz/sources/tnztools/modifiers/modifierassistants.cpp index 597e97dd8..361344683 100644 --- a/toonz/sources/tnztools/modifiers/modifierassistants.cpp +++ b/toonz/sources/tnztools/modifiers/modifierassistants.cpp @@ -20,20 +20,15 @@ //***************************************************************************************** -// TModifierAssistants::Modifier implementation +// TModifierAssistants::Interpolator implementation //***************************************************************************************** -TModifierAssistants::Modifier::Modifier(TTrackHandler &handler): - TTrackModifier(handler), - initialized() -{ } - - TTrackPoint -TModifierAssistants::Modifier::calcPoint(double originalIndex) { - TTrackPoint p = TTrackModifier::calcPoint(originalIndex); - return guidelines.empty() ? p : guidelines.front()->smoothTransformPoint(p); +TModifierAssistants::Interpolator::interpolate(double index) { + TTrackPoint p = track.original ? track.calcPointFromOriginal(index) + : track.interpolateLinear(index); + return guidelines.empty() ? p : guidelines.front()->smoothTransformPoint(p, magnetism); } @@ -42,8 +37,8 @@ TModifierAssistants::Modifier::calcPoint(double originalIndex) { //***************************************************************************************** -TModifierAssistants::TModifierAssistants(bool drawOnly): - drawOnly(drawOnly), +TModifierAssistants::TModifierAssistants(double magnetism): + magnetism(magnetism), sensitiveLength(50.0) { } @@ -53,60 +48,22 @@ TModifierAssistants::scanAssistants( int positionsCount, TGuidelineList *outGuidelines, bool draw, - bool enabledOnly ) const + bool enabledOnly, + bool drawGuidelines ) const { - bool found = false; if (TInputManager *manager = getManager()) if (TInputHandler *handler = manager->getHandler()) - if (TTool *tool = handler->inputGetTool()) - if (TToolViewer *viewer = tool->getViewer()) - if (TApplication *application = tool->getApplication()) - if (TXshLevelHandle *levelHandle = application->getCurrentLevel()) - if (TXshLevel *level = levelHandle->getLevel()) - if (TXshSimpleLevel *simpleLevel = level->getSimpleLevel()) - if (TFrameHandle *frameHandle = application->getCurrentFrame()) - if (TXsheetHandle *XsheetHandle = application->getCurrentXsheet()) - if (TXsheet *Xsheet = XsheetHandle->getXsheet()) - { - TPointD dpiScale = getCurrentDpiScale(simpleLevel, tool->getCurrentFid()); - bool findGuidelines = (positions && positionsCount > 0 && outGuidelines); - bool doSomething = findGuidelines || draw; - - int frame = frameHandle->getFrame(); - int count = Xsheet->getColumnCount(); - TAffine worldToTrack; - worldToTrack.a11 /= dpiScale.x; - worldToTrack.a22 /= dpiScale.y; - - for(int i = 0; i < count; ++i) - if (TXshColumn *column = Xsheet->getColumn(i)) - if (column->isCamstandVisible()) - if (column->isPreviewVisible()) - if (TImageP image = Xsheet->getCell(frame, i).getImage(false)) - if (image->getType() == TImage::META) - if (TMetaImage *metaImage = dynamic_cast(image.getPointer())) - { - TAffine imageToTrack = worldToTrack * tool->getColumnMatrix(i); - if (draw) { glPushMatrix(); tglMultMatrix(imageToTrack); } - - TMetaImage::Reader reader(*metaImage); - for(TMetaObjectListCW::iterator i = reader->begin(); i != reader->end(); ++i) - if (*i) - if (const TAssistant *assistant = (*i)->getHandler()) - if (!enabledOnly || assistant->getEnabled()) - { - found = true; - if (findGuidelines) - for(int i = 0; i < positionsCount; ++i) - assistant->getGuidelines(positions[i], imageToTrack, *outGuidelines); - if (draw) assistant->draw(viewer, !drawOnly); - if (!doSomething) return true; - } - - if (draw) glPopMatrix(); - } - } - return found; + return TAssistant::scanAssistants( + handler->inputGetTool(), + positions, + positionsCount, + outGuidelines, + draw, + enabledOnly, + magnetism > 0, + drawGuidelines, + nullptr ); + return false; } @@ -116,29 +73,35 @@ TModifierAssistants::modifyTrack( TTrackList &outTracks ) { if (!track.handler) { - track.handler = new TTrackHandler(track); - Modifier *modifier = new Modifier(*track.handler); - track.handler->tracks.push_back(new TTrack(modifier)); + Handler *handler = new Handler(); + track.handler = handler; + handler->track = new TTrack(track); + new Interpolator(*handler->track, magnetism); } - - outTracks.push_back(track.handler->tracks.front()); - TTrack &subTrack = *track.handler->tracks.front(); - if (!track.changed()) + + Handler *handler = dynamic_cast(track.handler.getPointer()); + if (!handler) + return; + + outTracks.push_back(handler->track); + TTrack &subTrack = *handler->track; + Interpolator *intr = dynamic_cast(subTrack.getInterpolator().getPointer()); + if (!intr) return; - Modifier *modifier = dynamic_cast(subTrack.modifier.getPointer()); - if (!modifier) + + if (!track.changed()) 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); + if (intr->magnetism && start <= 0) { + intr->guidelines.clear(); + scanAssistants(&track[0].position, 1, &intr->guidelines, false, true, false); } - bool fixed = subTrack.fixedSize() || modifier->guidelines.size() <= 1; + bool fixed = subTrack.fixedSize() || intr->guidelines.size() <= 1; // select guideline if (!fixed) @@ -152,11 +115,11 @@ TModifierAssistants::modifyTrack( 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()); + TGuidelineP guideline = TGuideline::findBest(intr->guidelines, track, trackToScreen, fixed); + if (guideline != intr->guidelines.front()) + for(int i = 1; i < (int)intr->guidelines.size(); ++i) + if (intr->guidelines[i] == guideline) { + std::swap(intr->guidelines[i], intr->guidelines.front()); start = 0; break; } @@ -165,7 +128,7 @@ TModifierAssistants::modifyTrack( // add points subTrack.truncate(start); for(int i = start; i < track.size(); ++i) - subTrack.push_back( modifier->calcPoint(i), false ); + subTrack.push_back( intr->interpolate(i), false ); // fix points if (fixed || track.fixedFinished()) @@ -177,7 +140,7 @@ TModifierAssistants::modifyTrack( TRectD TModifierAssistants::calcDrawBounds(const TTrackList&, const THoverList&) { - if (scanAssistants(NULL, 0, NULL, false, false)) + if (scanAssistants(NULL, 0, NULL, false, false, false)) return TConsts::infiniteRectD; return TRectD(); } @@ -185,16 +148,20 @@ TModifierAssistants::calcDrawBounds(const TTrackList&, const THoverList&) { void TModifierAssistants::drawTrack(const TTrack &track) { - if (!track.handler) return; - TTrack &subTrack = *track.handler->tracks.front(); - if (Modifier *modifier = dynamic_cast(subTrack.modifier.getPointer())) { - const TGuidelineList &guidelines = modifier->guidelines; - if (!guidelines.empty()) { - guidelines.front()->draw(true); - for(TGuidelineList::const_iterator i = guidelines.begin() + 1; i != guidelines.end(); ++i) - (*i)->draw(); - } - } + Handler *handler = dynamic_cast(track.handler.getPointer()); + if (!handler) return; + + TTrack &subTrack = *handler->track; + Interpolator *intr = dynamic_cast(subTrack.getInterpolator().getPointer()); + if (!intr) return; + + const TGuidelineList &guidelines = intr->guidelines; + if (guidelines.empty()) + return; + + guidelines.front()->draw(true); + for(TGuidelineList::const_iterator i = guidelines.begin() + 1; i != guidelines.end(); ++i) + (*i)->draw(); } @@ -205,21 +172,17 @@ TModifierAssistants::draw(const TTrackList &tracks, const THoverList &hovers) { if (tracks.empty()) // hide hovers if track exists allHovers.insert(allHovers.end(), hovers.begin(), hovers.end()); for(TTrackList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) - if ((*i)->handler && !(*i)->handler->tracks.empty() && !(*i)->handler->tracks.front()->empty()) - allHovers.push_back( (*i)->handler->tracks.front()->back().position ); + if (Handler *handler = dynamic_cast((*i)->handler.getPointer())) + allHovers.push_back( handler->track->back().position ); - // draw assistants - TGuidelineList guidelines; + // draw assistants and guidelines scanAssistants( allHovers.empty() ? NULL : &allHovers.front(), (int)allHovers.size(), - &guidelines, + nullptr, true, - false ); - - // draw guidelines - for(TGuidelineList::const_iterator i = guidelines.begin(); i != guidelines.end(); ++i) - (*i)->draw(false, !drawOnly); + false, + true ); // draw tracks TInputModifier::drawTracks(tracks); diff --git a/toonz/sources/tnztools/modifiers/modifierline.cpp b/toonz/sources/tnztools/modifiers/modifierline.cpp index e67891440..382589a75 100644 --- a/toonz/sources/tnztools/modifiers/modifierline.cpp +++ b/toonz/sources/tnztools/modifiers/modifierline.cpp @@ -2,10 +2,13 @@ #include + + //***************************************************************************************** // TModifierLine implementation //***************************************************************************************** + static inline void calcFixedAngle(const TTrackPoint &p0, TTrackPoint &p1) { TPointD p = p1.position - p0.position; double l = sqrt(p.x * p.x + p.y * p.y); @@ -20,70 +23,51 @@ static inline void calcFixedAngle(const TTrackPoint &p0, TTrackPoint &p1) { p1.position = p0.position + p; } -TTrackPoint TModifierLine::Modifier::calcPoint(double originalIndex) { - if (original.empty()) return TTrackPoint(); - if (original.size() < 2) return original.front(); - TTrackPoint p0 = original.front(); - TTrackPoint p1 = original.back(); - if (fixAngle) calcFixedAngle(p0, p1); - return TTrack::interpolationLinear(p0, p1, - originalIndex / (original.size() - 1)); -} void TModifierLine::modifyTrack(const TTrack &track, TTrackList &outTracks) { if (!track.handler) { - track.handler = new TTrackHandler(track); - Modifier *modifier = new Modifier(*track.handler); - track.handler->tracks.push_back(new TTrack(modifier)); + Handler *handler = new Handler(); + track.handler = handler; + handler->track = new TTrack(track); } - if (!track.changed()) return; - if (track.handler->tracks.empty()) return; - - TTrack &subTrack = *track.handler->tracks.front(); - Modifier *modifier = dynamic_cast(subTrack.modifier.getPointer()); - if (!modifier) { - track.resetChanges(); + Handler *handler = dynamic_cast(track.handler.getPointer()); + if (!handler) + return; + + outTracks.push_back(handler->track); + TTrack &subTrack = *handler->track; + + if (!track.changed()) return; - } - bool fixAngle = track.getKeyState(track.back()).isPressed(TKey::control); - outTracks.push_back(track.handler->tracks.front()); + subTrack.truncate(0); + // calc max pressure int i1 = track.size(); int i0 = i1 - track.pointsAdded; - double maxPressure = modifier->maxPressure; + double maxPressure = handler->maxPressure; if (track.pointsRemoved) { maxPressure = 0; i0 = 0; } - for (int i = i0; i < i1; ++i) { + for(int i = i0; i < i1; ++i) { double p = track[i].pressure; if (maxPressure < p) maxPressure = p; } - modifier->maxPressure = maxPressure; - modifier->fixAngle = fixAngle; - - subTrack.truncate(0); - - if (track.size() > 0) { - TTrackPoint p = track.front(); - p.originalIndex = 0; - p.pressure = maxPressure; - p.tilt = TPointD(); - subTrack.push_back(p, false); - } - + handler->maxPressure = maxPressure; + + if (track.size() > 0) + subTrack.push_back(subTrack.pointFromOriginal(0), false); + if (track.size() > 1) { - TTrackPoint p = track.back(); - p.originalIndex = track.size() - 1; - p.pressure = maxPressure; - p.tilt = TPointD(); - if (fixAngle) calcFixedAngle(subTrack.front(), p); + TTrackPoint p = subTrack.pointFromOriginal(track.size() - 1); + if (track.getKeyState(track.back()).isPressed(TKey::control)) + calcFixedAngle(subTrack.front(), p); subTrack.push_back(p, false); } - + if (track.fixedFinished()) subTrack.fix_all(); diff --git a/toonz/sources/tnztools/modifiers/modifiersegmentation.cpp b/toonz/sources/tnztools/modifiers/modifiersegmentation.cpp index ba1232e4b..4718afc78 100644 --- a/toonz/sources/tnztools/modifiers/modifiersegmentation.cpp +++ b/toonz/sources/tnztools/modifiers/modifiersegmentation.cpp @@ -42,7 +42,7 @@ TModifierSegmentation::addSegments( } --level; - TTrackPoint p = track.modifier->calcPoint(0.5*(p0.originalIndex + p1.originalIndex)); + TTrackPoint p = track.calcPointFromOriginal(0.5*(p0.originalIndex + p1.originalIndex)); addSegments(track, p0, p, level); addSegments(track, p, p1, level); } @@ -54,21 +54,22 @@ TModifierSegmentation::modifyTrack( TTrackList &outTracks ) { if (!track.handler) { - track.handler = new TTrackHandler(track); - track.handler->tracks.push_back( - new TTrack( - new TTrackModifier(*track.handler) )); + Handler *handler = new Handler(); + track.handler = handler; + handler->track = new TTrack(track); + new Interpolator(*handler->track); } - if (track.handler->tracks.empty()) + Handler *handler = dynamic_cast(track.handler.getPointer()); + if (!handler) return; - - TTrack &subTrack = *track.handler->tracks.front(); - outTracks.push_back(track.handler->tracks.front()); - + + outTracks.push_back(handler->track); + TTrack &subTrack = *handler->track; + if (!track.changed()) return; - + // remove points int start = track.size() - track.pointsAdded; if (start < 0) start = 0; @@ -76,9 +77,9 @@ TModifierSegmentation::modifyTrack( subTrack.truncate(subStart); // add points - TTrackPoint p0 = subTrack.modifier->calcPoint(start - 1); + TTrackPoint p0 = subTrack.pointFromOriginal(start - 1); for(int i = start; i < track.size(); ++i) { - TTrackPoint p1 = subTrack.modifier->calcPoint(i); + TTrackPoint p1 = subTrack.pointFromOriginal(i); addSegments(subTrack, p0, p1, m_maxLevel); p0 = p1; } diff --git a/toonz/sources/tnztools/modifiers/modifiersimplify.cpp b/toonz/sources/tnztools/modifiers/modifiersimplify.cpp new file mode 100644 index 000000000..612af0b51 --- /dev/null +++ b/toonz/sources/tnztools/modifiers/modifiersimplify.cpp @@ -0,0 +1,75 @@ + + +#include +#include + + +//***************************************************************************************** +// TModifierSimplify implementation +//***************************************************************************************** + + +TModifierSimplify::TModifierSimplify(double step): + step(step) { } + + +void +TModifierSimplify::modifyTrack( + const TTrack &track, + TTrackList &outTracks ) +{ + if (!track.handler) { + Handler *handler = new Handler(); + track.handler = handler; + handler->track = new TTrack(track); + new Interpolator(*handler->track); + } + + Handler *handler = dynamic_cast(track.handler.getPointer()); + if (!handler) + return; + + outTracks.push_back(handler->track); + TTrack &subTrack = *handler->track; + + if (!track.changed()) + return; + + // remove points + int start = track.size() - track.pointsAdded; + if (start < 0) start = 0; + int subStart = subTrack.floorIndex(subTrack.indexByOriginalIndex(start)); + if (subStart < 0) subStart = 0; + start = track.floorIndex(subTrack[subStart].originalIndex); + if (start < 0) start = 0; + subTrack.truncate(subStart); + + // add points + double step2 = step*step; + TTrackPoint p0 = subTrack.back(); + for(int i = start; i < track.size(); ++i) { + const TTrackPoint &p1 = subTrack.pointFromOriginal(i); + if (!subTrack.empty() && tdistance2(p1.position, p0.position) < step2) { + if (p0.pressure < p1.pressure) p0.pressure = p1.pressure; + if (i == track.size() - 1) p0.position = p1.position; + p0.tilt = p1.tilt; + p0.time = p1.time; + p0.final = p1.final; + subTrack.pop_back(); + subTrack.push_back(p0, false); + } else { + p0 = p1; + subTrack.push_back(p0, false); + } + } + + // fix points + if (track.fixedFinished()) + subTrack.fix_all(); + else + if (track.fixedSize()) + subTrack.fix_to( + subTrack.floorIndex( subTrack.indexByOriginalIndex(track.fixedSize()-1) )); + + track.resetChanges(); +} diff --git a/toonz/sources/tnztools/modifiers/modifiersmooth.cpp b/toonz/sources/tnztools/modifiers/modifiersmooth.cpp index 449623f56..ac0b21281 100644 --- a/toonz/sources/tnztools/modifiers/modifiersmooth.cpp +++ b/toonz/sources/tnztools/modifiers/modifiersmooth.cpp @@ -4,38 +4,14 @@ #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); } +TModifierSmooth::TModifierSmooth(int radius): + radius(radius) { } void @@ -43,32 +19,25 @@ TModifierSmooth::modifyTrack( const TTrack &track, TTrackList &outTracks ) { - if (!m_radius) { - TInputModifier::modifyTrack(track, outTracks); - return; - } + int radius = abs(this->radius); - 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 && radius) { + Handler *handler = new Handler(radius); + track.handler = handler; + handler->track = new TTrack(track); } - if (track.handler->tracks.empty()) - return; - - TTrack &subTrack = *track.handler->tracks.front(); - outTracks.push_back(track.handler->tracks.front()); - + Handler *handler = dynamic_cast(track.handler.getPointer()); + if (!handler) + return TInputModifier::modifyTrack(track, outTracks); + + radius = handler->radius; + outTracks.push_back(handler->track); + TTrack &subTrack = *handler->track; + 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); @@ -87,18 +56,23 @@ TModifierSmooth::modifyTrack( 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 ); + double originalIndex; + if (i <= radius) { + originalIndex = i/(double)(radius + 1); + } else + if (i >= size - radius - 1) { + originalIndex = track.size() - 1 - (size - i - 1)/(double)(radius + 1); + } else { + originalIndex = i - radius; + } + + TTrackPoint p = subTrack.pointFromOriginal(i - radius); + p.position = accum.position*k; + p.pressure = accum.pressure*k; + p.tilt = accum.tilt*k; + p.originalIndex = originalIndex; + p.final = p.final && i == size - 1; + subTrack.push_back(p, false); const TTrackPoint &p0 = track[i - 2*radius]; accum.position -= p0.position; diff --git a/toonz/sources/tnztools/modifiers/modifiertangents.cpp b/toonz/sources/tnztools/modifiers/modifiertangents.cpp index c7f11e09d..e1cfcd755 100644 --- a/toonz/sources/tnztools/modifiers/modifiertangents.cpp +++ b/toonz/sources/tnztools/modifiers/modifiertangents.cpp @@ -4,22 +4,22 @@ //***************************************************************************************** -// TModifierTangents::Modifier implementation +// TModifierTangents::Interpolator implementation //***************************************************************************************** TTrackPoint -TModifierTangents::Modifier::calcPoint(double originalIndex) { +TModifierTangents::Interpolator::interpolate(double index) { double frac; - int i0 = original.floorIndex(originalIndex, &frac); + int i0 = track.floorIndex(index, &frac); int i1 = i0 + 1; TTrackPoint p; // 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]; + const TTrackPoint &p0 = track[i0]; + const TTrackPoint &p1 = track[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; @@ -36,9 +36,7 @@ TModifierTangents::Modifier::calcPoint(double originalIndex) { t1.tilt.x *= l; t1.tilt.y *= l; - p = TTrack::interpolationSpline(p0, p1, t0, t1, frac); - p.originalIndex = originalIndex; - return p; + return TTrack::interpolationSpline(p0, p1, t0, t1, frac); } @@ -48,7 +46,7 @@ TModifierTangents::Modifier::calcPoint(double originalIndex) { TTrackTangent -TModifierTangents::calcTangent(const TTrack &track, int index) const { +TModifierTangents::calcTangent(const TTrack &track, int index) { if (index <= 0 || index >= track.size() - 1) return TTrackTangent(); @@ -60,7 +58,7 @@ TModifierTangents::calcTangent(const TTrack &track, int index) const { // instead of time when message dispatched //double k = p2.time - p0.time; - // calculate tangent based on length, util we have no valid times + // calculate tangent based on length, until we have no valid times double k = p2.length - p0.length; k = k > TConsts::epsilon ? 1/k : 0; @@ -77,36 +75,39 @@ TModifierTangents::modifyTrack( TTrackList &outTracks ) { if (!track.handler) { - track.handler = new TTrackHandler(track); - track.handler->tracks.push_back( - new TTrack( - new Modifier(*track.handler) )); + Handler *handler = new Handler(); + track.handler = handler; + handler->track = new TTrack(track); + new Interpolator(*handler->track); } - - if (track.handler->tracks.empty()) + + Handler *handler = dynamic_cast(track.handler.getPointer()); + if (!handler) return; - - TTrack &subTrack = *track.handler->tracks.front(); - Modifier *modifier = dynamic_cast(subTrack.modifier.getPointer()); - if (!modifier) + + outTracks.push_back(handler->track); + TTrack &subTrack = *handler->track; + Interpolator *intr = dynamic_cast(subTrack.getInterpolator().getPointer()); + if (!intr) return; - - outTracks.push_back(track.handler->tracks.front()); + 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); + int tangentStart = start - 1; + if (tangentStart < 0) tangentStart = 0; + intr->tangents.resize(tangentStart); + for(int i = tangentStart; i < track.size(); ++i) + intr->tangents.push_back(calcTangent(track, i)); + + // update subTrack + subTrack.truncate(start); for(int i = start; i < track.size(); ++i) - modifier->tangents.push_back(calcTangent(track, i)); + subTrack.push_back(subTrack.pointFromOriginal(i), false); // fix points if (track.fixedFinished()) { diff --git a/toonz/sources/tnztools/modifiers/modifiertest.cpp b/toonz/sources/tnztools/modifiers/modifiertest.cpp index 7801f3bf2..ba3379261 100644 --- a/toonz/sources/tnztools/modifiers/modifiertest.cpp +++ b/toonz/sources/tnztools/modifiers/modifiertest.cpp @@ -6,111 +6,118 @@ // std includes #include + //***************************************************************************************** -// TModifierTest::Modifier implementation +// TModifierTest::Interpolator implementation //***************************************************************************************** -TModifierTest::Modifier::Modifier(TTrackHandler &handler, double angle, - double radius, double speed) - : TTrackModifier(handler), angle(angle), radius(radius), speed(speed) {} - -TTrackPoint TModifierTest::Modifier::calcPoint(double originalIndex) { - TTrackPoint p = TTrackModifier::calcPoint(originalIndex); - - if (p.length > TConsts::epsilon) { - double frac; - int i0 = original.floorIndex(originalIndex, &frac); - int i1 = original.ceilIndex(originalIndex); - if (i0 < 0) return p; - - if (Handler *handler = dynamic_cast(&this->handler)) { - double angle = this->angle + speed * TTrack::interpolationLinear( - handler->angles[i0], - handler->angles[i1], frac); - double radius = 2.0 * this->radius * p.pressure; - double s = sin(angle); - double c = cos(angle); - - TPointD tangent = - original.calcTangent(originalIndex, fabs(2.0 * this->radius / speed)); - p.position.x -= tangent.y * s * radius; - p.position.y += tangent.x * s * radius; - p.pressure *= 1.0 - 0.5 * c; - } - } else { - p.pressure = 0.0; - } +TTrackPoint TModifierTest::Interpolator::interpolateFromOriginal(double originalIndex) { + Handler *handler = track.original + ? dynamic_cast(track.original->handler.getPointer()) + : nullptr; + if (!handler) + return track.interpolateLinear(track.indexByOriginalIndex(originalIndex)); + + TTrackPoint p = track.calcPointFromOriginal(originalIndex); + + double frac; + int i0 = track.original->floorIndex(originalIndex, &frac); + int i1 = track.original->ceilIndex(originalIndex); + + double angle = TTrack::interpolationLinear( + handler->angles[i0], handler->angles[i1], frac ); + double a = angle*speed + this->angle; + + double kr = 1 - 1/(angle + 1); + double s = sin(a); + double r = radius*p.pressure*kr*s; + + double d = fabs(2.0*radius); + if (fabs(speed) > TConsts::epsilon) + d /= fabs(speed); + + TPointD tangent = track.original->calcTangent(originalIndex, d); + p.position.x -= tangent.y * r; + p.position.y += tangent.x * r; + p.pressure *= fabs(s); return p; } + +TTrackPoint TModifierTest::Interpolator::interpolate(double index) { + return interpolateFromOriginal(track.originalIndexByIndex(index)); +} + + //***************************************************************************************** // TModifierTest implementation //***************************************************************************************** -TModifierTest::TModifierTest(int count, double radius) - : count(count), radius(radius) {} +TModifierTest::TModifierTest(int count, double radius, double speed) + : count(count), radius(radius), speed(speed) {} void TModifierTest::modifyTrack(const TTrack &track, TTrackList &outTracks) { const double segmentSize = 2.0 * M_PI / 10.0; - if (!track.handler) { - if (track.getKeyState(track.front().time).isPressed(TKey(Qt::Key_Alt))) { - // TModifierTest::Handler for spiro - track.handler = new Handler(track); - for (int i = 0; i < count; ++i) - track.handler->tracks.push_back(new TTrack(new Modifier( - *track.handler, i * 2.0 * M_PI / (double)count, radius, 0.25))); + if ( !track.handler + && track.getKeyState(track.front().time).isPressed(TKey::alt) ) + { + Handler *handler = new Handler(this->radius); + track.handler = handler; + for (int i = 0; i < count; ++i) { + handler->tracks.push_back(new TTrack(track)); + TTrack &subTrack = *handler->tracks.back(); + new Interpolator(subTrack, i*2*M_PI/(double)count, radius, 0.25); } } - - Handler *handler = dynamic_cast(track.handler.getPointer()); - if (!handler) { - TInputModifier::modifyTrack(track, outTracks); + + Handler *handler = dynamic_cast(track.handler.getPointer()); + if (!handler) + return TInputModifier::modifyTrack(track, outTracks); + + outTracks.insert(outTracks.end(), handler->tracks.begin(), handler->tracks.end()); + if (!track.changed()) return; - } - - outTracks.insert(outTracks.end(), track.handler->tracks.begin(), - track.handler->tracks.end()); - if (!track.changed()) return; + double radius = handler->radius; int start = track.size() - track.pointsAdded; if (start < 0) start = 0; // remove angles - double lastAngle = start < (int)handler->angles.size() - ? handler->angles[start] - : handler->angles.empty() ? 0.0 - : handler->angles.back(); - handler->angles.resize(start, lastAngle); - + handler->angles.resize(start); + // add angles - for (int i = start; i < track.size(); ++i) { - if (i > 0) { - double dl = track[i].length - track[i - 1].length; - double da = track[i].pressure > TConsts::epsilon - ? dl / (radius * track[i].pressure) - : 0.0; - handler->angles.push_back(handler->angles[i - 1] + da); + for(int i = start; i < track.size(); ++i) { + if (i) { + const TTrackPoint &p0 = track[i - 1]; + const TTrackPoint &p1 = track[i]; + double dl = p1.length - p0.length; + double da = p1.pressure > TConsts::epsilon + ? dl / (radius * p1.pressure) + : 0.0; + handler->angles.push_back(handler->angles.back() + da); } else { handler->angles.push_back(0.0); } } - + // process sub-tracks - for (TTrackList::const_iterator ti = handler->tracks.begin(); - ti != handler->tracks.end(); ++ti) { - TTrack &subTrack = **ti; + for(TTrackList::const_iterator ti = handler->tracks.begin(); ti != handler->tracks.end(); ++ti) { + TTrack &subTrack = **ti; + Interpolator *intr = dynamic_cast(subTrack.getInterpolator().getPointer()); + if (!intr) + continue; + double currentSegmentSize = segmentSize; - if (const Modifier *modifier = - dynamic_cast(subTrack.modifier.getPointer())) - if (fabs(modifier->speed) > TConsts::epsilon) - currentSegmentSize = segmentSize / fabs(modifier->speed); + if (fabs(intr->speed) > TConsts::epsilon) + currentSegmentSize /= fabs(intr->speed); // remove points - 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 @@ -123,18 +130,20 @@ 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), false); + intr->interpolateFromOriginal(i - 1 + frac), false ); } } - subTrack.push_back(subTrack.modifier->calcPoint(i), false); + subTrack.push_back(intr->interpolateFromOriginal(i), false); } // fix points + if (track.fixedFinished()) + subTrack.fix_all(); if (track.fixedSize()) subTrack.fix_to( subTrack.floorIndex(subTrack.indexByOriginalIndex(track.fixedSize() - 1)) + 1 ); } - + track.resetChanges(); } diff --git a/toonz/sources/tnztools/tool.cpp b/toonz/sources/tnztools/tool.cpp index 38434c5db..0f730feef 100644 --- a/toonz/sources/tnztools/tool.cpp +++ b/toonz/sources/tnztools/tool.cpp @@ -179,6 +179,11 @@ TTool::TTool(std::string name) //------------------------------------------------------------------- +unsigned int TTool::getToolHints() const + { return HintAssistants | HintGuidelines; } + +//------------------------------------------------------------------- + TTool *TTool::getTool(std::string toolName, ToolTargetType targetType) { if (!toolTable) return 0; diff --git a/toonz/sources/tnztools/toonzrasterbrushtool.cpp b/toonz/sources/tnztools/toonzrasterbrushtool.cpp index 539368a79..b4ba5d5f9 100644 --- a/toonz/sources/tnztools/toonzrasterbrushtool.cpp +++ b/toonz/sources/tnztools/toonzrasterbrushtool.cpp @@ -55,7 +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); +TEnv::IntVar RasterBrushAssistants("RasterBrushAssistants", 1); //------------------------------------------------------------------- #define CUSTOM_WSTR L"" @@ -720,124 +720,6 @@ static void Smooth(std::vector &points, const int radius, } } -//-------------------------------------------------------------------------------------------------- - -void SmoothStroke::beginStroke(int smooth) { - m_smooth = smooth; - m_outputIndex = 0; - m_readIndex = -1; - m_rawPoints.clear(); - m_outputPoints.clear(); - m_resampledIndex = 0; - m_resampledPoints.clear(); -} - -//-------------------------------------------------------------------------------------------------- - -void SmoothStroke::addPoint(const TThickPoint &point) { - if (m_rawPoints.size() > 0 && m_rawPoints.back().x == point.x && - m_rawPoints.back().y == point.y) { - return; - } - m_rawPoints.push_back(point); - generatePoints(); -} - -//-------------------------------------------------------------------------------------------------- - -void SmoothStroke::endStroke() { - generatePoints(); - // force enable the output all segments - m_outputIndex = m_outputPoints.size() - 1; -} - -//-------------------------------------------------------------------------------------------------- - -void SmoothStroke::clearPoints() { - m_outputIndex = 0; - m_readIndex = -1; - m_outputPoints.clear(); - m_rawPoints.clear(); - m_resampledIndex = 0; - m_resampledPoints.clear(); -} - -//-------------------------------------------------------------------------------------------------- - -void SmoothStroke::getSmoothPoints(std::vector &smoothPoints) { - int n = m_outputPoints.size(); - for (int i = m_readIndex + 1; i <= m_outputIndex && i < n; ++i) { - smoothPoints.push_back(m_outputPoints[i]); - } - m_readIndex = m_outputIndex; -} - -//-------------------------------------------------------------------------------------------------- - -void SmoothStroke::generatePoints() { - int n = (int)m_rawPoints.size(); - if (n == 0) { - return; - } - - // if m_smooth = 0, then skip whole smoothing process - if (m_smooth == 0) { - for (int i = m_outputIndex; i < (int)m_outputPoints.size(); ++i) { - if (m_outputPoints[i] != m_rawPoints[i]) { - break; - } - ++m_outputIndex; - } - m_outputPoints = m_rawPoints; - return; - } - - std::vector smoothedPoints = m_resampledPoints; - // Add more stroke samples before applying the smoothing - // This is because the raw inputs points are too few to support smooth result, - // especially on stroke ends - - int resampleStartId = m_resampledIndex; - for (int i = resampleStartId; i < n - 1; ++i) { - const TThickPoint &p1 = m_rawPoints[i]; - const TThickPoint &p2 = m_rawPoints[i + 1]; - const TThickPoint &p0 = i - 1 >= 0 ? m_rawPoints[i - 1] : p1; - const TThickPoint &p3 = i + 2 < n ? m_rawPoints[i + 2] : p2; - - std::vector tmpResampled; - tmpResampled.push_back(p1); - // define subsample amount according to distance between points - int samples = std::min((int)tdistance(p1, p2), 8); - if (samples >= 1) - CatmullRomInterpolate(p0, p1, p2, p3, samples, tmpResampled); - - if (i + 2 < n) { - m_resampledIndex = i + 1; - std::copy(tmpResampled.begin(), tmpResampled.end(), - std::back_inserter(m_resampledPoints)); - } - std::copy(tmpResampled.begin(), tmpResampled.end(), - std::back_inserter(smoothedPoints)); - } - smoothedPoints.push_back(m_rawPoints.back()); - // Apply the 1D box filter - // Multiple passes result in better quality and fix the stroke ends break - // issue - // level is passed to define range where the points are smoothed - for (int level = 2; level >= 0; --level) { - Smooth(smoothedPoints, m_smooth, m_readIndex, level); - } - // Compare the new smoothed stroke with old one - // Enable the output for unchanged parts - int outputNum = (int)m_outputPoints.size(); - for (int i = m_outputIndex; i < outputNum; ++i) { - if (m_outputPoints[i] != smoothedPoints[i]) { - break; - } - ++m_outputIndex; - } - m_outputPoints = smoothedPoints; -} //=================================================================== // @@ -900,7 +782,7 @@ ToonzRasterBrushTool::ToonzRasterBrushTool(std::string name, int targetType) for(int i = 0; i < 3; ++i) m_modifierSmooth[i] = new TModifierSmooth(); #ifndef NDEBUG - m_modifierTest = new TModifierTest(5, 40); + m_modifierTest = new TModifierTest(); #endif m_inputmanager.addModifier( @@ -1158,15 +1040,15 @@ bool ToonzRasterBrushTool::askWrite(const TRect &rect) { bool ToonzRasterBrushTool::preLeftButtonDown() { int smoothRadius = (int)round(m_smooth.getValue()); - m_modifierAssistants->drawOnly = !RasterBrushAssistants; - m_inputmanager.drawPreview = false; //!m_modifierAssistants->drawOnly; + m_modifierAssistants->magnetism = m_assistants.getValue() ? 1 : 0; + 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_modifierSmooth[i]->radius = smoothRadius; m_inputmanager.addModifier(TInputModifierP(m_modifierSmooth[i].getPointer())); } } @@ -1198,7 +1080,7 @@ void ToonzRasterBrushTool::handleMouseEvent(MouseEventType type, bool control = e.getModifiersMask() & TMouseEvent::CTRL_KEY; if (shift && type == ME_DOWN && e.button() == Qt::LeftButton && !m_painting.active) { - m_modifierAssistants->drawOnly = true; + m_modifierAssistants->magnetism = 0; m_inputmanager.clearModifiers(); m_inputmanager.addModifier(TInputModifierP(m_modifierLine.getPointer())); m_inputmanager.addModifier(TInputModifierP(m_modifierAssistants.getPointer())); @@ -1217,9 +1099,13 @@ void ToonzRasterBrushTool::handleMouseEvent(MouseEventType type, 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); + int deviceId = e.isTablet() ? 1 : 0; + double defPressure = m_isMyPaintStyleSelected ? 0.5 : 1.0; + bool hasPressure = e.isTablet(); + double pressure = hasPressure ? e.m_pressure : defPressure; + bool final = type == ME_UP; + m_inputmanager.trackEvent( + deviceId, 0, pos, pressure, TPointD(), hasPressure, false, final, t); m_inputmanager.processTracks(); } } @@ -1383,10 +1269,13 @@ void ToonzRasterBrushTool::inputPaintTrackPoint(const TTrackPoint &point, const // 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) + assert(firstPoint == !track.handler); + if (firstPoint != !track.handler) return; + double defPressure = m_painting.myPaint.isActive ? 0.5 : 1.0; + double pressure = m_pressure.getValue() ? point.pressure : defPressure; + if (m_painting.myPaint.isActive) { // mypaint case @@ -1395,14 +1284,14 @@ void ToonzRasterBrushTool::inputPaintTrackPoint(const TTrackPoint &point, const if (firstPoint) { handler = new MyPaintStroke(m_workRas, *this, m_painting.myPaint.baseBrush, false); handler->brush.beginStroke(); - track.toolHandler = handler; + track.handler = handler; } - handler = dynamic_cast(track.toolHandler.getPointer()); + handler = dynamic_cast(track.handler.getPointer()); if (!handler) return; // paint stroke m_painting.myPaint.strokeSegmentRect.empty(); - handler->brush.strokeTo( fixedPosition + rasCenter, point.pressure, + handler->brush.strokeTo( fixedPosition + rasCenter, pressure, point.tilt, point.time - track.previous().time ); if (lastPoint) handler->brush.endStroke(); @@ -1421,7 +1310,7 @@ void ToonzRasterBrushTool::inputPaintTrackPoint(const TTrackPoint &point, const // pencil case // Pencilモードでなく、Hardness=100 の場合のブラシサイズを1段階下げる - double thickness = computeThickness(point.pressure, m_rasThickness)*2; + double thickness = computeThickness(pressure, m_rasThickness)*2; //if (!m_painting.pencil.realPencil && !m_modifierLine->getManager()) // thickness -= 1.0; TThickPoint thickPoint(fixedPosition + rasCenter, thickness); @@ -1441,9 +1330,9 @@ void ToonzRasterBrushTool::inputPaintTrackPoint(const TTrackPoint &point, const getAboveStyleIdSet(m_painting.styleId, ri->getPalette(), aboveStyleIds); handler->brush.setAboveStyleIds(aboveStyleIds); } - track.toolHandler = handler; + track.handler = handler; } - handler = dynamic_cast(track.toolHandler.getPointer()); + handler = dynamic_cast(track.handler.getPointer()); if (!handler) return; // paint stroke @@ -1476,13 +1365,13 @@ void ToonzRasterBrushTool::inputPaintTrackPoint(const TTrackPoint &point, const getAboveStyleIdSet(m_painting.styleId, ri->getPalette(), aboveStyleIds); handler->brush.setAboveStyleIds(aboveStyleIds); } - track.toolHandler = handler; + track.handler = handler; } - handler = dynamic_cast(track.toolHandler.getPointer()); + handler = dynamic_cast(track.handler.getPointer()); if (!handler) return; // paint stroke - double radius = computeThickness(point.pressure, m_rasThickness); + double radius = computeThickness(pressure, m_rasThickness); TThickPoint thickPoint(fixedPosition + rasCenter, radius*2); TRect strokeRect( tfloor(thickPoint.x - maxThick - 0.999), tfloor(thickPoint.y - maxThick - 0.999), diff --git a/toonz/sources/tnztools/toonzrasterbrushtool.h b/toonz/sources/tnztools/toonzrasterbrushtool.h index 681230c93..65bc306fa 100644 --- a/toonz/sources/tnztools/toonzrasterbrushtool.h +++ b/toonz/sources/tnztools/toonzrasterbrushtool.h @@ -91,42 +91,6 @@ class BrushPresetManager { void removePreset(const std::wstring &name); }; -//************************************************************************ -// Smooth Stroke declaration -// Brush stroke smoothing buffer. -//************************************************************************ -class SmoothStroke { -public: - SmoothStroke() {} - ~SmoothStroke() {} - - // begin stroke - // smooth is smooth strength, from 0 to 100 - void beginStroke(int smooth); - // add stroke point - void addPoint(const TThickPoint &point); - // end stroke - void endStroke(); - // Get generated stroke points which has been smoothed. - // Both addPoint() and endStroke() generate new smoothed points. - // This method will removed generated points - void getSmoothPoints(std::vector &smoothPoints); - // Remove all points - used for straight lines - void clearPoints(); - -private: - void generatePoints(); - -private: - int m_smooth; - int m_outputIndex; - int m_readIndex; - std::vector m_rawPoints; - std::vector m_outputPoints; - - int m_resampledIndex; - std::vector m_resampledPoints; -}; //************************************************************************ // Toonz Raster Brush Tool declaration //************************************************************************ @@ -142,8 +106,11 @@ class ToonzRasterBrushTool final : public TTool, public: ToonzRasterBrushTool(std::string name, int targetType); - ToolType getToolType() const override { return TTool::LevelWriteTool; } - + ToolType getToolType() const override + { return TTool::LevelWriteTool; } + unsigned int getToolHints() const override + { return TTool::getToolHints() & ~HintAssistantsAll; } + ToolOptionsBox *createOptionsBox() override; void updateTranslation() override; @@ -211,7 +178,7 @@ class ToonzRasterBrushTool final : public TTool, TSmartPointerT m_modifierTest; #endif - class MyPaintStroke: public TTrackToolHandler { + class MyPaintStroke: public TTrackHandler { public: MyPaintToonzBrush brush; @@ -225,7 +192,7 @@ class ToonzRasterBrushTool final : public TTool, { } }; - class PencilStroke: public TTrackToolHandler { + class PencilStroke: public TTrackHandler { public: RasterStrokeGenerator brush; @@ -238,7 +205,7 @@ class ToonzRasterBrushTool final : public TTool, { } }; - class BluredStroke: public TTrackToolHandler { + class BluredStroke: public TTrackHandler { public: BluredBrush brush; @@ -262,8 +229,6 @@ class ToonzRasterBrushTool final : public TTool, TTileSaverCM32 *tileSaver = nullptr; TRect affectedRect; - SmoothStroke smoothStroke; - struct Pencil { bool isActive = false; bool realPencil = false; diff --git a/toonz/sources/tnztools/toonzvectorbrushtool.cpp b/toonz/sources/tnztools/toonzvectorbrushtool.cpp index 2f46d026e..1e01069af 100644 --- a/toonz/sources/tnztools/toonzvectorbrushtool.cpp +++ b/toonz/sources/tnztools/toonzvectorbrushtool.cpp @@ -58,6 +58,7 @@ TEnv::IntVar V_BrushPressureSensitivity("InknpaintBrushPressureSensitivity", 1); TEnv::IntVar V_VectorBrushFrameRange("VectorBrushFrameRange", 0); TEnv::IntVar V_VectorBrushSnap("VectorBrushSnap", 0); TEnv::IntVar V_VectorBrushSnapSensitivity("VectorBrushSnapSensitivity", 0); +TEnv::IntVar V_VectorBrushAssistants("VectorBrushAssistants", 1); TEnv::StringVar V_VectorBrushPreset("VectorBrushPreset", ""); //------------------------------------------------------------------- @@ -474,8 +475,9 @@ void getAboveStyleIdSet(int styleId, TPaletteP palette, //========================================================================================================= double computeThickness(double pressure, const TDoublePairProperty &property, - bool isPath) { + bool enablePressure, bool isPath ) { if (isPath) return 0.0; + if (!enablePressure) return property.getValue().second*0.5; double t = pressure * pressure * pressure; double thick0 = property.getValue().first; double thick1 = property.getValue().second; @@ -499,24 +501,32 @@ ToonzVectorBrushTool::ToonzVectorBrushTool(std::string name, int targetType) , m_preset("Preset:") , m_breakAngles("Break", true) , m_pressure("Pressure", true) + , m_snap("Snap", false) + , m_frameRange("Range:") + , m_snapSensitivity("Sensitivity:") , m_capStyle("Cap") , m_joinStyle("Join") , m_miterJoinLimit("Miter:", 0, 100, 4) - , m_rasterTrack(0) - , m_styleId(0) - , m_modifiedRegion() - , m_bluredBrush(0) - , m_active(false) - , m_enabled(false) - , m_isPrompting(false) + , m_assistants("Assistants", true) + , m_styleId() + , m_minThick() + , m_maxThick() + , m_col() + , m_firstFrame() + , m_veryFirstFrame() + , m_veryFirstCol() + , m_targetType(targetType) + , m_pixelSize() + , m_minDistance2() + , m_snapped() + , m_snappedSelf() + , m_active() , m_firstTime(true) + , m_isPath() + , m_presetsLoaded() , m_firstFrameRange(true) - , m_presetsLoaded(false) - , m_frameRange("Range:") - , m_snap("Snap", false) - , m_snapSensitivity("Sensitivity:") - , m_targetType(targetType) - , m_workingFrameId(TFrameId()) { + , m_propertyUpdating() +{ bind(targetType); m_thickness.setNonLinearSlider(); @@ -541,6 +551,8 @@ ToonzVectorBrushTool::ToonzVectorBrushTool(std::string name, int targetType) m_snapSensitivity.addValue(MEDIUM_WSTR); m_snapSensitivity.addValue(HIGH_WSTR); + m_prop[0].bind(m_assistants); + m_prop[0].bind(m_preset); m_preset.addValue(CUSTOM_WSTR); @@ -566,6 +578,23 @@ ToonzVectorBrushTool::ToonzVectorBrushTool(std::string name, int targetType) m_capStyle.setId("Cap"); m_joinStyle.setId("Join"); m_miterJoinLimit.setId("Miter"); + m_assistants.setId("Assistants"); + + 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(); + m_modifierSimplify = new TModifierSimplify(); +#ifndef NDEBUG + m_modifierTest = new TModifierTest(); +#endif + + m_inputmanager.addModifier( + TInputModifierP(m_modifierAssistants.getPointer())); } //------------------------------------------------------------------------------------------------------- @@ -593,6 +622,7 @@ void ToonzVectorBrushTool::updateTranslation() { m_frameRange.setQStringName(tr("Range:")); m_snap.setQStringName(tr("Snap")); m_snapSensitivity.setQStringName(""); + m_assistants.setQStringName(tr("Assistants")); m_frameRange.setItemUIName(L"Off", tr("Off")); m_frameRange.setItemUIName(LINEAR_WSTR, tr("Linear")); m_frameRange.setItemUIName(EASEIN_WSTR, tr("In")); @@ -638,421 +668,497 @@ void ToonzVectorBrushTool::onDeactivate() { * ---*/ // End current stroke. - if (m_active && m_enabled) { - leftButtonUp(m_lastDragPos, m_lastDragEvent); - } - - if (m_tileSaver && !m_isPath) { - m_enabled = false; - } - m_workRas = TRaster32P(); - m_backupRas = TRasterCM32P(); + m_inputmanager.finishTracks(); resetFrameRange(); } //-------------------------------------------------------------------------------------------------- -bool ToonzVectorBrushTool::preLeftButtonDown() { - if (getViewer() && getViewer()->getGuidedStrokePickerMode()) return false; +void ToonzVectorBrushTool::inputMouseMove( + const TPointD &position, const TInputState &state ) +{ + struct Locals { + ToonzVectorBrushTool *m_this; - touchImage(); - if (m_isFrameCreated) { - // When the xsheet frame is selected, whole viewer will be updated from - // SceneViewer::onXsheetChanged() on adding a new frame. - // We need to take care of a case when the level frame is selected. - if (m_application->getCurrentFrame()->isEditingLevel()) invalidate(); - } - return true; -} + void setValue(TDoublePairProperty &prop, + const TDoublePairProperty::Value &value) { + prop.setValue(value); -//-------------------------------------------------------------------------------------------------- + m_this->onPropertyChanged(prop.getName()); + TTool::getApplication()->getCurrentTool()->notifyToolChanged(); + } -void ToonzVectorBrushTool::leftButtonDown(const TPointD &pos, - const TMouseEvent &e) { - TTool::Application *app = TTool::getApplication(); - if (!app) return; + void addMinMax(TDoublePairProperty &prop, double min, double max) { + if (min == 0.0 && max == 0.0) return; + const TDoublePairProperty::Range &range = prop.getRange(); - if (getViewer() && getViewer()->getGuidedStrokePickerMode()) { - getViewer()->doPickGuideStroke(pos); - return; - } + TDoublePairProperty::Value value = prop.getValue(); + value.first += min; + value.second += max; + if (value.first > value.second) value.first = value.second; + value.first = tcrop(value.first, range.first, range.second); + value.second = tcrop(value.second, range.first, range.second); - int col = app->getCurrentColumn()->getColumnIndex(); - m_isPath = app->getCurrentObject()->isSpline(); - m_enabled = col >= 0 || m_isPath || app->getCurrentFrame()->isEditingLevel(); - // todo: gestire autoenable - if (!m_enabled) return; - if (!m_isPath) { - 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; - } + setValue(prop, value); } - } else { - m_currentColor = TPixel32::Red; - m_active = true; - } - TXshLevel *level = app->getCurrentLevel()->getLevel(); - if (level == NULL && !m_isPath) { - m_active = false; - return; - } + } locals = {this}; - // assert(0<=m_styleId && m_styleId<2); - m_track.clear(); - double thickness = (m_pressure.getValue() || m_isPath) - ? computeThickness(e.m_pressure, m_thickness, m_isPath) - : m_thickness.getValue().second * 0.5; - - /*--- ストロークの最初にMaxサイズの円が描かれてしまう不具合を防止する ---*/ - if (m_pressure.getValue() && e.m_pressure == 1.0) - thickness = m_thickness.getValue().first * 0.5; - m_currThickness = thickness; - m_smoothStroke.beginStroke(m_smooth.getValue()); - - if (m_foundFirstSnap) { - addTrackPoint(TThickPoint(m_firstSnapPoint, thickness), - getPixelSize() * getPixelSize()); - } else - addTrackPoint(TThickPoint(pos, thickness), getPixelSize() * getPixelSize()); - TRectD invalidateRect = m_track.getLastModifiedRegion(); - invalidate(invalidateRect.enlarge(2)); + TPointD halfThick(m_maxThick * 0.5, m_maxThick * 0.5); + TRectD invalidateRect(m_brushPos - halfThick, m_brushPos + halfThick); - // updating m_brushPos is needed to refresh viewer properly - m_brushPos = m_mousePos = pos; -} + bool alt = state.isKeyPressed(TInputState::Key::alt); + bool shift = state.isKeyPressed(TInputState::Key::shift); + bool control = state.isKeyPressed(TInputState::Key::control); + + if ( alt && control && !shift + && Preferences::instance()->useCtrlAltToResizeBrushEnabled() ) + { + // Resize the brush if CTRL+ALT is pressed and the preference is enabled. + const TPointD &diff = position - m_mousePos; + double max = diff.x / 2; + double min = diff.y / 2; -//------------------------------------------------------------------------------------------------------------- + locals.addMinMax(m_thickness, min, max); -void ToonzVectorBrushTool::leftButtonDrag(const TPointD &pos, - const TMouseEvent &e) { - if (!m_enabled || !m_active) { - m_brushPos = m_mousePos = pos; - return; + double radius = m_thickness.getValue().second * 0.5; + halfThick = TPointD(radius, radius); + } else { + m_brushPos = m_mousePos = position; } - - m_lastDragPos = pos; - m_lastDragEvent = e; - - double thickness = (m_pressure.getValue() || m_isPath) - ? computeThickness(e.m_pressure, m_thickness, m_isPath) - : m_thickness.getValue().second * 0.5; - - TRectD invalidateRect; - TPointD halfThick(m_maxThick * 0.5, m_maxThick * 0.5); - TPointD snapThick(6.0 * m_pixelSize, 6.0 * m_pixelSize); - - // In order to clear the previous brush tip + invalidateRect += TRectD(m_brushPos - halfThick, m_brushPos + halfThick); - // In order to clear the previous snap indicator - if (m_foundLastSnap) - invalidateRect += - TRectD(m_lastSnapPoint - snapThick, m_lastSnapPoint + snapThick); - - m_currThickness = thickness; - - m_mousePos = pos; - m_lastSnapPoint = pos; - m_foundLastSnap = false; - m_foundFirstSnap = false; - m_snapSelf = false; - m_altPressed = e.isAltPressed() && !e.isCtrlPressed(); - - checkStrokeSnapping(false, m_altPressed); - checkGuideSnapping(false, m_altPressed); - m_brushPos = m_lastSnapPoint; - - if (m_foundLastSnap) - invalidateRect += - TRectD(m_lastSnapPoint - snapThick, m_lastSnapPoint + snapThick); - - if (e.isShiftPressed() && e.isCtrlPressed()) { - TPointD m_firstPoint = m_track.getFirstPoint(); - - double denominator = m_lastSnapPoint.x - m_firstPoint.x; - if (denominator == 0) denominator = 0.001; - double slope = ((m_brushPos.y - m_firstPoint.y) / denominator); - double angle = std::atan(slope) * (180 / 3.14159); - if (abs(angle) > 67.5) - m_lastSnapPoint.x = m_firstPoint.x; - else if (abs(angle) < 22.5) - m_lastSnapPoint.y = m_firstPoint.y; - else { - double xDistance = m_lastSnapPoint.x - m_firstPoint.x; - double yDistance = m_lastSnapPoint.y - m_firstPoint.y; - if (abs(xDistance) > abs(yDistance)) { - if (abs(yDistance) == yDistance) - m_lastSnapPoint.y = m_firstPoint.y + abs(xDistance); - else - m_lastSnapPoint.y = m_firstPoint.y - abs(xDistance); - } else { - if (abs(xDistance) == xDistance) - m_lastSnapPoint.x = m_firstPoint.x + abs(yDistance); - else - m_lastSnapPoint.x = m_firstPoint.x - abs(yDistance); - } - } - - m_smoothStroke.clearPoints(); - m_track.add(TThickPoint(m_lastSnapPoint, thickness), - getPixelSize() * getPixelSize()); - m_track.removeMiddlePoints(); - invalidateRect += m_track.getModifiedRegion(); - } else if (e.isShiftPressed()) { - m_smoothStroke.clearPoints(); - m_track.add(TThickPoint(m_brushPos, thickness), - getPixelSize() * getPixelSize()); - m_track.removeMiddlePoints(); - invalidateRect += m_track.getModifiedRegion(); - } else if (m_dragDraw) { - addTrackPoint(TThickPoint(pos, thickness), getPixelSize() * getPixelSize()); - invalidateRect += m_track.getLastModifiedRegion(); + if (m_minThick == 0 && m_maxThick == 0) { + m_minThick = m_thickness.getValue().first; + m_maxThick = m_thickness.getValue().second; } - // In order to draw the current brush tip - invalidateRect += TRectD(m_brushPos - halfThick, m_brushPos + halfThick); + invalidate(invalidateRect.enlarge(2)); +} - if (!invalidateRect.isEmpty()) { - // for motion path, call the invalidate function directly to ignore dpi of - // the current level - if (m_isPath) - m_viewer->GLInvalidateRect(invalidateRect.enlarge(2)); - else - invalidate(invalidateRect.enlarge(2)); - } +//-------------------------------------------------------------------------------------------------- + +void ToonzVectorBrushTool::deleteStrokes(StrokeList &strokes) { + for(StrokeList::iterator i = strokes.begin(); i != strokes.end(); ++i) + delete *i; + strokes.clear(); } -//--------------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------- + +void ToonzVectorBrushTool::copyStrokes(StrokeList &dst, const StrokeList &src) { + deleteStrokes(dst); + dst.reserve(src.size()); + for(StrokeList::const_iterator i = src.begin(); i != src.end(); ++i) + dst.push_back(new TStroke(**i)); +} -void ToonzVectorBrushTool::leftButtonUp(const TPointD &pos, - const TMouseEvent &e) { - bool isValid = m_enabled && m_active; - m_enabled = false; +//-------------------------------------------------------------------------------------------------- - if (!isValid) { - // in case the current frame is moved to empty cell while dragging - if (!m_track.isEmpty()) { - m_track.clear(); - invalidate(); +void ToonzVectorBrushTool::inputSetBusy(bool busy) { + if (m_active == busy) return; + + if (busy) { + + // begin painting ////////////////////////// + + m_styleId = 0; + m_tracks.clear(); + + TTool::Application *app = TTool::getApplication(); + if (!app) + return; + + m_isPath = app->getCurrentObject()->isSpline(); + if (m_isPath) { + m_currentColor = TPixel32::Red; + m_active = true; + return; } - return; + + // todo: gestire autoenable + if ( app->getCurrentColumn()->getColumnIndex() < 0 + && !app->getCurrentFrame()->isEditingLevel() ) + return; + if (!getImage(true) || !touchImage()) + return; + if (!app->getCurrentLevel()->getLevel()) + return; + + // nel caso che il colore corrente sia un cleanup/studiopalette color + // oppure il colore di un colorfield + if (TColorStyle *cs = app->getCurrentLevelStyle()) { + TRasterStyleFx *rfx = cs->getRasterStyleFx(); + if (!cs->isStrokeStyle() && (!rfx || !rfx->isInkStyle())) + return; + m_styleId = app->getCurrentLevelStyleIndex(); + m_currentColor = cs->getAverageColor(); + m_currentColor.m = 255; + } else { + m_styleId = 1; + m_currentColor = TPixel32::Black; + } + + m_active = true; + + return; // painting has begun } - + + + // end painting ////////////////////////// + + m_active = false; + + // clear tracks automatically when return from this function + struct Cleanup { + ToonzVectorBrushTool &owner; + inline ~Cleanup() { owner.m_tracks.clear(); owner.invalidate(); } + } cleanup = {*this}; + + // remove empty tracks + for(TrackList::iterator i = m_tracks.begin(); i != m_tracks.end(); ) + if (i->isEmpty()) i = m_tracks.erase(i); else ++i; + + if (m_tracks.empty()) + return; + + // make motion path (if need) + if (m_isPath) { - double error = 20.0 * getPixelSize(); + double error = 20.0 * m_pixelSize; - TStroke *stroke; - if (e.isShiftPressed()) { - m_track.removeMiddlePoints(); - stroke = m_track.makeStroke(0); - } else { - flushTrackPoint(); - stroke = m_track.makeStroke(error); - } - int points = stroke->getControlPointCount(); + m_tracks.resize(1); + TStroke *stroke = m_tracks.front().makeStroke(error); TVectorImageP vi = getImage(true); - struct Cleanup { - ToonzVectorBrushTool *m_this; - ~Cleanup() { m_this->m_track.clear(), m_this->invalidate(); } - } cleanup = {this}; if (!isJustCreatedSpline(vi.getPointer())) { - m_isPrompting = true; - - QString question("Are you sure you want to replace the motion path?"); - int ret = - DVGui::MsgBox(question, QObject::tr("Yes"), QObject::tr("No"), 0); - - m_isPrompting = false; - - if (ret == 2 || ret == 0) return; + m_currentColor = TPixel32::Green; + invalidate(); + int ret = DVGui::MsgBox( + QString("Are you sure you want to replace the motion path?"), + QObject::tr("Yes"), QObject::tr("No"), 0 ); + if (ret != 1) + return; // 1 here means "Yes" button } QMutexLocker lock(vi->getMutex()); - TUndo *undo = - new UndoPath(getXsheet()->getStageObject(getObjectId())->getSpline()); + TUndo *undo = new UndoPath( + getXsheet()->getStageObject(getObjectId())->getSpline() ); - while (vi->getStrokeCount() > 0) vi->deleteStroke(0); + while(vi->getStrokeCount() > 0) vi->deleteStroke(0); vi->addStroke(stroke, false); notifyImageChanged(); TUndoManager::manager()->add(undo); - return; + return; // done with motion path } - + + // paint regular strokes + TVectorImageP vi = getImage(true); - if (m_track.isEmpty()) { - m_styleId = 0; - m_track.clear(); - return; - } - - if (vi && (m_snap.getValue() != m_altPressed) && m_foundLastSnap) { - addTrackPoint(TThickPoint(m_lastSnapPoint, m_currThickness), - getPixelSize() * getPixelSize()); - } - m_strokeIndex1 = -1; - m_strokeIndex2 = -1; - m_w1 = -1; - m_w2 = -2; - m_foundFirstSnap = false; - m_foundLastSnap = false; - - m_track.filterPoints(); - double error = 30.0 / (1 + 0.5 * m_accuracy.getValue()); - error *= getPixelSize(); - - TStroke *stroke; - if (e.isShiftPressed()) { - m_track.removeMiddlePoints(); - stroke = m_track.makeStroke(0); - } else { - flushTrackPoint(); - stroke = m_track.makeStroke(error); - } - stroke->setStyle(m_styleId); - { + QMutexLocker lock(vi->getMutex()); + TTool::Application *app = TTool::getApplication(); + + // prepare strokes + + StrokeList strokes; + strokes.reserve(m_tracks.size()); + for(TrackList::iterator i = m_tracks.begin(); i != m_tracks.end(); ++i) { + StrokeGenerator &track = *i; + + track.filterPoints(); + double error = 30.0/(1 + 0.5 * m_accuracy.getValue())*m_pixelSize; + TStroke *stroke = track.makeStroke(error); + + stroke->setStyle(m_styleId); + TStroke::OutlineOptions &options = stroke->outlineOptions(); - options.m_capStyle = m_capStyle.getIndex(); - options.m_joinStyle = m_joinStyle.getIndex(); - options.m_miterUpper = m_miterJoinLimit.getValue(); + options.m_capStyle = m_capStyle.getIndex(); + options.m_joinStyle = m_joinStyle.getIndex(); + options.m_miterUpper = m_miterJoinLimit.getValue(); + + if ( stroke->getControlPointCount() == 3 + && stroke->getControlPoint(0) != stroke->getControlPoint(2) ) + // gli stroke con solo 1 chunk vengono + // fatti dal tape tool...e devono venir + // riconosciuti come speciali di + // autoclose proprio dal fatto che + // hanno 1 solo chunk. + stroke->insertControlPoints(0.5); + + if (!m_frameRange.getIndex() && track.getLoop()) + stroke->setSelfLoop(true); + + strokes.push_back(stroke); } - m_styleId = 0; - QMutexLocker lock(vi->getMutex()); - if (stroke->getControlPointCount() == 3 && - stroke->getControlPoint(0) != - stroke->getControlPoint(2)) // gli stroke con solo 1 chunk vengono - // fatti dal tape tool...e devono venir - // riconosciuti come speciali di - // autoclose proprio dal fatto che - // hanno 1 solo chunk. - stroke->insertControlPoints(0.5); + // add stroke to image + if (m_frameRange.getIndex()) { + // frame range stroke if (m_firstFrameId == -1) { - m_firstStroke = new TStroke(*stroke); - m_firstFrameId = getFrameId(); - TTool::Application *application = TTool::getApplication(); - if (application) { - m_col = application->getCurrentColumn()->getColumnIndex(); - m_firstFrame = application->getCurrentFrame()->getFrame(); + // remember strokes for first srame + copyStrokes(m_firstStrokes, strokes); + m_firstFrameId = getFrameId(); + m_rangeTracks = m_tracks; + + if (app) { + m_col = app->getCurrentColumn()->getColumnIndex(); + m_firstFrame = app->getCurrentFrame()->getFrame(); } - m_rangeTrack = m_track; + if (m_firstFrameRange) { m_veryFirstCol = m_col; m_veryFirstFrame = m_firstFrame; m_veryFirstFrameId = m_firstFrameId; } - } else if (m_firstFrameId == getFrameId()) { - if (m_firstStroke) { - delete m_firstStroke; - m_firstStroke = 0; - } - m_firstStroke = new TStroke(*stroke); - m_rangeTrack = m_track; + } else + if (m_firstFrameId == getFrameId()) { + // painted of first frame agein, so + // just replace the remembered strokes for first frame + copyStrokes(m_firstStrokes, strokes); + m_rangeTracks = m_tracks; } else { + // paint frame range strokes TFrameId currentId = getFrameId(); - int curCol = 0, curFrame = 0; - TTool::Application *application = TTool::getApplication(); - if (application) { - curCol = application->getCurrentColumn()->getColumnIndex(); - curFrame = application->getCurrentFrame()->getFrame(); + int curCol = app ? app->getCurrentColumn()->getColumnIndex() : 0; + int curFrame = app ? app->getCurrentFrame()->getFrame() : 0; + + if (size_t count = std::min(m_firstStrokes.size(), strokes.size())) { + TUndoManager::manager()->beginBlock(); + for(size_t i = 0; i < count; ++i) + doFrameRangeStrokes( + m_firstFrameId, m_firstStrokes[i], getFrameId(), strokes[i], + m_frameRange.getIndex(), m_breakAngles.getValue(), false, false, + m_firstFrameRange ); + TUndoManager::manager()->endBlock(); } - bool success = doFrameRangeStrokes( - m_firstFrameId, m_firstStroke, getFrameId(), stroke, - m_frameRange.getIndex(), m_breakAngles.getValue(), false, false, - m_firstFrameRange); - if (e.isCtrlPressed()) { - if (application) { - if (m_firstFrameId > currentId) { - if (application->getCurrentFrame()->isEditingScene()) { - application->getCurrentColumn()->setColumnIndex(curCol); - application->getCurrentFrame()->setFrame(curFrame); - } else - application->getCurrentFrame()->setFid(currentId); + + if (m_inputmanager.state.isKeyPressed(TInputState::Key::control)) { + if (app && m_firstFrameId > currentId) { + if (app->getCurrentFrame()->isEditingScene()) { + app->getCurrentColumn()->setColumnIndex(curCol); + app->getCurrentFrame()->setFrame(curFrame); + } else { + app->getCurrentFrame()->setFid(currentId); } } + resetFrameRange(); - m_firstStroke = new TStroke(*stroke); - m_rangeTrack = m_track; + copyStrokes(m_firstStrokes, strokes); + m_rangeTracks = m_tracks; m_firstFrameId = currentId; m_firstFrameRange = false; - } - - if (application && !e.isCtrlPressed()) { - if (application->getCurrentFrame()->isEditingScene()) { - application->getCurrentColumn()->setColumnIndex(m_veryFirstCol); - application->getCurrentFrame()->setFrame(m_veryFirstFrame); - } else - application->getCurrentFrame()->setFid(m_veryFirstFrameId); - } - - if (!e.isCtrlPressed()) { + } else { + if (app) { + if (app->getCurrentFrame()->isEditingScene()) { + app->getCurrentColumn()->setColumnIndex(m_veryFirstCol); + app->getCurrentFrame()->setFrame(m_veryFirstFrame); + } else { + app->getCurrentFrame()->setFid(m_veryFirstFrameId); + } + } resetFrameRange(); } } - invalidate(); } else { - if (m_snapSelf) { - stroke->setSelfLoop(true); - m_snapSelf = false; + // regular paint strokes + TUndoManager::manager()->beginBlock(); + for(StrokeList::iterator i = strokes.begin(); i != strokes.end(); ++i) { + TStroke *stroke = *i; + addStrokeToImage(app, vi, stroke, m_breakAngles.getValue(), + false, false, m_isFrameCreated, m_isLevelCreated); + + if ((Preferences::instance()->getGuidedDrawingType() == 1 || + Preferences::instance()->getGuidedDrawingType() == 2) && + Preferences::instance()->getGuidedAutoInbetween()) + { + TFrameId fId = getFrameId(); + doGuidedAutoInbetween(fId, vi, stroke, m_breakAngles.getValue(), false, + false, false); + if (app->getCurrentFrame()->isEditingScene()) + app->getCurrentFrame()->setFrame( app->getCurrentFrame()->getFrameIndex() ); + else + app->getCurrentFrame()->setFid(fId); + } } + TUndoManager::manager()->endBlock(); + } + + deleteStrokes(strokes); +} - addStrokeToImage(getApplication(), vi, stroke, m_breakAngles.getValue(), - false, false, m_isFrameCreated, m_isLevelCreated); - TRectD bbox = stroke->getBBox().enlarge(2) + m_track.getModifiedRegion(); +//-------------------------------------------------------------------------------------------------- - invalidate(); // should use bbox? +void ToonzVectorBrushTool::inputPaintTracks(const TTrackList &tracks) { + if (tracks.empty()) return; - if ((Preferences::instance()->getGuidedDrawingType() == 1 || - Preferences::instance()->getGuidedDrawingType() == 2) && - Preferences::instance()->getGuidedAutoInbetween()) { - int fidx = getApplication()->getCurrentFrame()->getFrameIndex(); - TFrameId fId = getFrameId(); + TRectD invalidateRect; - doGuidedAutoInbetween(fId, vi, stroke, m_breakAngles.getValue(), false, - false, false); + size_t count = m_isPath ? 1 : tracks.size(); + m_tracks.resize(count); + for(size_t i = 0; i < count; ++i) { + const TTrack &track = *tracks[i]; + StrokeGenerator &gen = m_tracks[i]; + + while(track.pointsRemoved) { + gen.pop(); + --track.pointsRemoved; + } + + while(track.pointsAdded) { + const TTrackPoint &p = track.current(); + double t = computeThickness(p.pressure, m_thickness, m_pressure.getValue(), m_isPath); + gen.add(TThickPoint(p.position, t), 0); + --track.pointsAdded; + } + + bool loop = m_snappedSelf + && track.fixedFinished() + && !track.empty() + && areAlmostEqual(track.front().position, track.back().position); + gen.setLoop(loop); + + invalidateRect += gen.getLastModifiedRegion(); + if (!i) { + TPointD halfThick(m_maxThick * 0.5, m_maxThick * 0.5); + invalidateRect += TRectD(m_brushPos - halfThick, m_brushPos + halfThick); + m_brushPos = m_mousePos = track.current().position; + invalidateRect += TRectD(m_brushPos - halfThick, m_brushPos + halfThick); + } + } + + if (!invalidateRect.isEmpty()) { + if (m_isPath) { + if (getViewer()) getViewer()->GLInvalidateRect(invalidateRect); + } else { + invalidate(invalidateRect.enlarge(2)); + } + } +} - if (getApplication()->getCurrentFrame()->isEditingScene()) - getApplication()->getCurrentFrame()->setFrame(fidx); - else - getApplication()->getCurrentFrame()->setFid(fId); +//-------------------------------------------------------------------------------------------------- + +bool ToonzVectorBrushTool::preLeftButtonDown() { + if (getViewer() && getViewer()->getGuidedStrokePickerMode()) return false; + + m_pixelSize = getPixelSize(); + int smoothRadius = (int)round(m_smooth.getValue()); + m_modifierAssistants->magnetism = m_assistants.getValue() ? 1 : 0; + m_modifierSegmentation->setStep(TPointD(m_pixelSize, m_pixelSize)); + m_modifierSmoothSegmentation->setStep(TPointD(2*m_pixelSize, 2*m_pixelSize)); + m_modifierSimplify->step = 2*m_pixelSize; + m_inputmanager.drawPreview = false; + + 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]->radius = smoothRadius; + m_inputmanager.addModifier(TInputModifierP(m_modifierSmooth[i].getPointer())); } } - assert(stroke); - m_track.clear(); - m_altPressed = false; + m_inputmanager.addModifier(TInputModifierP(m_modifierAssistants.getPointer())); +#ifndef NDEBUG + m_inputmanager.addModifier(TInputModifierP(m_modifierTest.getPointer())); +#endif + m_inputmanager.addModifier(TInputModifierP(m_modifierSegmentation.getPointer())); + m_inputmanager.addModifier(TInputModifierP(m_modifierSimplify.getPointer())); + + touchImage(); + if (m_isFrameCreated) { + // When the xsheet frame is selected, whole viewer will be updated from + // SceneViewer::onXsheetChanged() on adding a new frame. + // We need to take care of a case when the level frame is selected. + if (m_application->getCurrentFrame()->isEditingLevel()) invalidate(); + } + return true; +} + +//-------------------------------------------------------------------------------------------------- + +void ToonzVectorBrushTool::handleMouseEvent(MouseEventType type, + const TPointD &pos, + const TMouseEvent &e) +{ + 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_active) { + m_modifierAssistants->magnetism = 0; + 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.addModifier(TInputModifierP(m_modifierSimplify.getPointer())); + m_inputmanager.drawPreview = true; + } + + 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); + + TPointD snappedPos = pos; + bool pickerMode = getViewer() && getViewer()->getGuidedStrokePickerMode(); + bool snapInvert = alt && (!control || type == ME_MOVE || type == ME_DOWN); + bool snapEnabled = !pickerMode && (snapInvert != m_snap.getValue()); + snap(pos, snapEnabled, m_active); + if (m_snapped) + snappedPos = m_snapPoint; + if (m_snappedSelf && type == ME_UP) + snappedPos = m_snapPointSelf; + + if (type == ME_MOVE) { + qApp->processEvents(QEventLoop::ExcludeUserInputEvents); + THoverList hovers(1, snappedPos); + m_inputmanager.hoverEvent(hovers); + } else + if (pickerMode) { + if (type == ME_DOWN) getViewer()->doPickGuideStroke(pos); + } else { + int deviceId = e.isTablet() ? 1 : 0; + bool hasPressure = e.isTablet(); + double pressure = hasPressure ? e.m_pressure : 1.0; + bool final = type == ME_UP; + m_inputmanager.trackEvent( + deviceId, 0, snappedPos, pressure, TPointD(), hasPressure, false, final, t); + m_inputmanager.processTracks(); + } +} + +//-------------------------------------------------------------------------------------------------- + +void ToonzVectorBrushTool::leftButtonDown(const TPointD &pos, + const TMouseEvent &e) { + handleMouseEvent(ME_DOWN, pos, e); +} +void ToonzVectorBrushTool::leftButtonDrag(const TPointD &pos, + const TMouseEvent &e) { + handleMouseEvent(ME_DRAG, pos, e); +} +void ToonzVectorBrushTool::leftButtonUp(const TPointD &pos, + const TMouseEvent &e) { + handleMouseEvent(ME_UP, pos, e); +} +void ToonzVectorBrushTool::mouseMove(const TPointD &pos, const TMouseEvent &e) { + handleMouseEvent(ME_MOVE, pos, e); } //-------------------------------------------------------------------------------------------------- bool ToonzVectorBrushTool::keyDown(QKeyEvent *event) { - if (event->key() == Qt::Key_Escape) { + if (event->key() == Qt::Key_Escape) resetFrameRange(); - } return false; } @@ -1070,14 +1176,14 @@ bool ToonzVectorBrushTool::doFrameRangeStrokes( TVectorImageP firstImage = new TVectorImage(); TVectorImageP lastImage = new TVectorImage(); - *first = *firstStroke; - *last = *lastStroke; - bool swapped = false; - if (firstFrameId > lastFrameId) { + bool swapped = firstFrameId > lastFrameId; + if (swapped) { std::swap(firstFrameId, lastFrameId); - *first = *lastStroke; - *last = *firstStroke; - swapped = true; + *first = *lastStroke; + *last = *firstStroke; + } else { + *first = *firstStroke; + *last = *lastStroke; } firstImage->addStroke(first, false); @@ -1256,307 +1362,142 @@ bool ToonzVectorBrushTool::doGuidedAutoInbetween( //-------------------------------------------------------------------------------------------------- -void ToonzVectorBrushTool::addTrackPoint(const TThickPoint &point, - double pixelSize2) { - m_smoothStroke.addPoint(point); - std::vector pts; - m_smoothStroke.getSmoothPoints(pts); - for (size_t i = 0; i < pts.size(); ++i) { - m_track.add(pts[i], pixelSize2); - } -} - -//-------------------------------------------------------------------------------------------------- - -void ToonzVectorBrushTool::flushTrackPoint() { - m_smoothStroke.endStroke(); - std::vector pts; - m_smoothStroke.getSmoothPoints(pts); - double pixelSize2 = getPixelSize() * getPixelSize(); - for (size_t i = 0; i < pts.size(); ++i) { - m_track.add(pts[i], pixelSize2); - } -} - -//--------------------------------------------------------------------------------------------------------------- - -void ToonzVectorBrushTool::mouseMove(const TPointD &pos, const TMouseEvent &e) { - qApp->processEvents(QEventLoop::ExcludeUserInputEvents); - - struct Locals { - ToonzVectorBrushTool *m_this; - - void setValue(TDoublePairProperty &prop, - const TDoublePairProperty::Value &value) { - prop.setValue(value); - - m_this->onPropertyChanged(prop.getName()); - 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) { - if (min == 0.0 && max == 0.0) return; - const TDoublePairProperty::Range &range = prop.getRange(); - - TDoublePairProperty::Value value = prop.getValue(); - value.first += min; - value.second += max; - if (value.first > value.second) value.first = value.second; - value.first = tcrop(value.first, range.first, range.second); - value.second = tcrop(value.second, range.first, range.second); - - setValue(prop, value); - } - - } locals = {this}; - - TPointD halfThick(m_maxThick * 0.5, m_maxThick * 0.5); - TRectD invalidateRect(m_brushPos - halfThick, m_brushPos + halfThick); - - if (e.isCtrlPressed() && e.isAltPressed() && !e.isShiftPressed() && - Preferences::instance()->useCtrlAltToResizeBrushEnabled()) { - // Resize the brush if CTRL+ALT is pressed and the preference is enabled. - const TPointD &diff = pos - m_mousePos; - double max = diff.x / 2; - double min = diff.y / 2; - - locals.addMinMaxSeparate(m_thickness, min, max); - - double radius = m_thickness.getValue().second * 0.5; - invalidateRect += TRectD(m_brushPos - TPointD(radius, radius), - m_brushPos + TPointD(radius, radius)); - - } else { - m_mousePos = pos; - m_brushPos = pos; - - TPointD snapThick(6.0 * m_pixelSize, 6.0 * m_pixelSize); - // In order to clear the previous snap indicator - if (m_foundFirstSnap) - invalidateRect += - TRectD(m_firstSnapPoint - snapThick, m_firstSnapPoint + snapThick); - - m_firstSnapPoint = pos; - m_foundFirstSnap = false; - m_altPressed = e.isAltPressed() && !e.isCtrlPressed(); - checkStrokeSnapping(true, m_altPressed); - checkGuideSnapping(true, m_altPressed); - m_brushPos = m_firstSnapPoint; - // In order to draw the snap indicator - if (m_foundFirstSnap) - invalidateRect += - TRectD(m_firstSnapPoint - snapThick, m_firstSnapPoint + snapThick); - - invalidateRect += TRectD(pos - halfThick, pos + halfThick); - } - - invalidate(invalidateRect.enlarge(2)); - - if (m_minThick == 0 && m_maxThick == 0) { - m_minThick = m_thickness.getValue().first; - m_maxThick = m_thickness.getValue().second; - } -} - -//------------------------------------------------------------------------------------------------------------- - -void ToonzVectorBrushTool::checkStrokeSnapping(bool beforeMousePress, - bool invertCheck) { - if (Preferences::instance()->getVectorSnappingTarget() == 1) return; - - TVectorImageP vi(getImage(false)); - bool checkSnap = m_snap.getValue(); - if (invertCheck) checkSnap = !checkSnap; - m_dragDraw = true; - if (vi && checkSnap) { +void ToonzVectorBrushTool::snap(const TPointD &pos, bool snapEnabled, bool withSelfSnap) { + bool oldSnapped = m_snapped; + bool oldSnappedSelf = m_snappedSelf; + TPointD oldPoint = m_snapPoint; + TPointD oldPointSelf = m_snapPointSelf; + + m_snapped = m_snappedSelf = false; + + if (snapEnabled) { // snapping is active double minDistance2 = m_minDistance2; - if (beforeMousePress) - m_strokeIndex1 = -1; - else - m_strokeIndex2 = -1; - int i, strokeNumber = vi->getStrokeCount(); - TStroke *stroke; - double distance2, outW; - bool snapFound = false; - TThickPoint point1; - - for (i = 0; i < strokeNumber; i++) { - stroke = vi->getStroke(i); - if (stroke->getNearestW(m_mousePos, outW, distance2) && - distance2 < minDistance2) { - minDistance2 = distance2; - beforeMousePress ? m_strokeIndex1 = i : m_strokeIndex2 = i; - if (areAlmostEqual(outW, 0.0, 1e-3)) - beforeMousePress ? m_w1 = 0.0 : m_w2 = 0.0; - else if (areAlmostEqual(outW, 1.0, 1e-3)) - beforeMousePress ? m_w1 = 1.0 : m_w2 = 1.0; - else - beforeMousePress ? m_w1 = outW : m_w2 = outW; + + // 0 - strokes, 1 - guides, 2 - all + int target = Preferences::instance()->getVectorSnappingTarget(); + + // snap to guides + if (target != 0) { + if (TToolViewer *viewer = getViewer()) { + // find nearest vertical guide + int cnt = viewer->getVGuideCount(); + for(int i = 0; i < cnt; ++i) { + double guide = viewer->getVGuide(i); + double d2 = guide - pos.y; + d2 *= d2; // we work with square of the distance + if (d2 < minDistance2) { + m_snapped = true; + m_snapPoint.x = pos.x; + m_snapPoint.y = guide; + minDistance2 = d2; + } + } - beforeMousePress ? point1 = stroke->getPoint(m_w1) - : point1 = stroke->getPoint(m_w2); - snapFound = true; - } - } - // compare to first point of current stroke - if (beforeMousePress && snapFound) { - m_firstSnapPoint = TPointD(point1.x, point1.y); - m_foundFirstSnap = true; - } else if (!beforeMousePress) { - if (!snapFound) { - TPointD tempPoint = m_track.getFirstPoint(); - double distanceFromStart = tdistance2(m_mousePos, tempPoint); - - if (distanceFromStart < m_minDistance2) { - point1 = tempPoint; - distance2 = distanceFromStart; - snapFound = true; - m_snapSelf = true; + // find nearest horizontal guide + cnt = viewer->getHGuideCount(); + for(int i = 0; i < cnt; ++i) { + double guide = viewer->getHGuide(i); + double d2 = guide - pos.x; + d2 *= d2; // we work with square of the distance + if (d2 < minDistance2) { + m_snapped = true; + m_snapPoint.x = guide; + m_snapPoint.y = pos.y; + minDistance2 = d2; + } } } - if (snapFound) { - m_lastSnapPoint = TPointD(point1.x, point1.y); - m_foundLastSnap = true; - if (distance2 < 2.0) m_dragDraw = false; - } } - } -} -//------------------------------------------------------------------------------------------------------------- - -void ToonzVectorBrushTool::checkGuideSnapping(bool beforeMousePress, - bool invertCheck) { - if (Preferences::instance()->getVectorSnappingTarget() == 0) return; - bool foundSnap; - TPointD snapPoint; - beforeMousePress ? foundSnap = m_foundFirstSnap : foundSnap = m_foundLastSnap; - beforeMousePress ? snapPoint = m_firstSnapPoint : snapPoint = m_lastSnapPoint; - - bool checkSnap = m_snap.getValue(); - if (invertCheck) checkSnap = !checkSnap; - - if (checkSnap) { - // check guide snapping - int vGuideCount = 0, hGuideCount = 0; - double guideDistance = sqrt(m_minDistance2); - TToolViewer *viewer = getViewer(); - if (viewer) { - vGuideCount = viewer->getVGuideCount(); - hGuideCount = viewer->getHGuideCount(); - } - double distanceToVGuide = -1.0, distanceToHGuide = -1.0; - double vGuide, hGuide; - bool useGuides = false; - if (vGuideCount) { - for (int j = 0; j < vGuideCount; j++) { - double guide = viewer->getVGuide(j); - double tempDistance = std::abs(guide - m_mousePos.y); - if (tempDistance < guideDistance && - (distanceToVGuide < 0 || tempDistance < distanceToVGuide)) { - distanceToVGuide = tempDistance; - vGuide = guide; - useGuides = true; + // snap to strokes + if (target != 1) { + if (TVectorImageP vi = getImage(false)) { + int count = vi->getStrokeCount(); + for(int i = 0; i < count; ++i) { + double w, d2; + TStroke *stroke = vi->getStroke(i); + if (!stroke->getNearestW(pos, w, d2) || d2 >= minDistance2) + continue; + minDistance2 = d2; + w = w > 0.001 ? (w < 0.999 ? w : 1.0) : 0.0; + m_snapped = true; + m_snapPoint = stroke->getPoint(w); } } - } - if (hGuideCount) { - for (int j = 0; j < hGuideCount; j++) { - double guide = viewer->getHGuide(j); - double tempDistance = std::abs(guide - m_mousePos.x); - if (tempDistance < guideDistance && - (distanceToHGuide < 0 || tempDistance < distanceToHGuide)) { - distanceToHGuide = tempDistance; - hGuide = guide; - useGuides = true; + + // finally snap to first point of track (self snap) + if (withSelfSnap && !m_tracks.empty() && !m_tracks.front().isEmpty()) { + TPointD p = m_tracks.front().getFirstPoint(); + double d2 = tdistance2(pos, p); + if (d2 < minDistance2) { + m_snappedSelf = true; + m_snapPointSelf = p; } } } - if (useGuides && foundSnap) { - double currYDistance = std::abs(snapPoint.y - m_mousePos.y); - double currXDistance = std::abs(snapPoint.x - m_mousePos.x); - double hypotenuse = - sqrt(pow(currYDistance, 2.0) + pow(currXDistance, 2.0)); - if ((distanceToVGuide >= 0 && distanceToVGuide < hypotenuse) || - (distanceToHGuide >= 0 && distanceToHGuide < hypotenuse)) { - useGuides = true; - m_snapSelf = false; - } else - useGuides = false; - } - if (useGuides) { - assert(distanceToHGuide >= 0 || distanceToVGuide >= 0); - if (distanceToHGuide < 0 || - (distanceToVGuide <= distanceToHGuide && distanceToVGuide >= 0)) { - snapPoint.y = vGuide; - snapPoint.x = m_mousePos.x; + } // snapping is active + + // invalidate rect + TRectD invalidateRect; + double radius = 8.0*m_pixelSize; + TPointD halfSize(radius, radius); - } else { - snapPoint.y = m_mousePos.y; - snapPoint.x = hGuide; - } - beforeMousePress ? m_foundFirstSnap = true : m_foundLastSnap = true; - beforeMousePress ? m_firstSnapPoint = snapPoint - : m_lastSnapPoint = snapPoint; - } + if ( oldSnapped != m_snapped + || !areAlmostEqual(oldPoint, m_snapPoint) ) + { + if (oldSnapped) invalidateRect += TRectD(oldPoint - halfSize, oldPoint + halfSize); + if (m_snapped) invalidateRect += TRectD(m_snapPoint - halfSize, m_snapPoint + halfSize); } + + if ( oldSnappedSelf != m_snappedSelf + || !areAlmostEqual(oldPointSelf, m_snapPointSelf) ) + { + if (oldSnappedSelf) invalidateRect += TRectD(oldPointSelf - halfSize, oldPointSelf + halfSize); + if (m_snappedSelf) invalidateRect += TRectD(m_snapPointSelf - halfSize, m_snapPointSelf + halfSize); + } + + if (!invalidateRect.isEmpty()) + invalidate(invalidateRect); } //------------------------------------------------------------------------------------------------------------- void ToonzVectorBrushTool::draw() { + m_pixelSize = getPixelSize(); + m_inputmanager.draw(); + /*--ショートカットでのツール切り替え時に赤点が描かれるのを防止する--*/ if (m_minThick == 0 && m_maxThick == 0 && !Preferences::instance()->getShow0ThickLines()) return; - TImageP img = getImage(false, 1); - - // Draw track - tglColor(m_isPrompting ? TPixel32::Green : m_currentColor); - m_track.drawAllFragments(); - - // snapping - TVectorImageP vi = img; - if (m_snap.getValue() != m_altPressed) { - m_pixelSize = getPixelSize(); - double thick = 6.0 * m_pixelSize; - if (m_foundFirstSnap) { - tglColor(TPixelD(0.1, 0.9, 0.1)); - tglDrawCircle(m_firstSnapPoint, thick); - } - - TThickPoint point2; - - if (m_foundLastSnap) { - tglColor(TPixelD(0.1, 0.9, 0.1)); - tglDrawCircle(m_lastSnapPoint, thick); - } + // draw track + tglColor(m_currentColor); + for(TrackList::iterator i = m_tracks.begin(); i != m_tracks.end(); ++i) + i->drawAllFragments(); + + // draw snapping + double snapMarkRadius = 6.0 * m_pixelSize; + if (m_snapped) { + tglColor(TPixelD(0.1, 0.9, 0.1)); + tglDrawCircle(m_snapPoint, snapMarkRadius); + } + if (m_snappedSelf) { + tglColor(TPixelD(0.9, 0.9, 0.1)); + tglDrawCircle(m_snapPointSelf, snapMarkRadius); } // frame range - if (m_firstStroke) { + for(TrackList::iterator i = m_rangeTracks.begin(); i != m_rangeTracks.end(); ++i) { + if (i->isEmpty()) continue; + TPointD offset1 = TPointD(5, 5); + TPointD offset2 = TPointD(-offset1.x, offset1.y); + TPointD point = i->getFirstPoint(); glColor3d(1.0, 0.0, 0.0); - m_rangeTrack.drawAllFragments(); + i->drawAllFragments(); glColor3d(0.0, 0.6, 0.0); - TPointD firstPoint = m_rangeTrack.getFirstPoint(); - TPointD topLeftCorner = TPointD(firstPoint.x - 5, firstPoint.y - 5); - TPointD topRightCorner = TPointD(firstPoint.x + 5, firstPoint.y - 5); - TPointD bottomLeftCorner = TPointD(firstPoint.x - 5, firstPoint.y + 5); - TPointD bottomRightCorner = TPointD(firstPoint.x + 5, firstPoint.y + 5); - tglDrawSegment(topLeftCorner, bottomRightCorner); - tglDrawSegment(topRightCorner, bottomLeftCorner); + tglDrawSegment(point - offset1, point + offset1); + tglDrawSegment(point - offset2, point + offset2); } if (getApplication()->getCurrentObject()->isSpline()) return; @@ -1584,24 +1525,8 @@ void ToonzVectorBrushTool::draw() { //-------------------------------------------------------------------------------------------------------------- void ToonzVectorBrushTool::onEnter() { - TImageP img = getImage(false); - m_minThick = m_thickness.getValue().first; m_maxThick = m_thickness.getValue().second; - - 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; } //---------------------------------------------------------------------------------------------------------- @@ -1615,19 +1540,15 @@ void ToonzVectorBrushTool::onLeave() { TPropertyGroup *ToonzVectorBrushTool::getProperties(int idx) { if (!m_presetsLoaded) initPresets(); - return &m_prop[idx]; } //------------------------------------------------------------------ void ToonzVectorBrushTool::resetFrameRange() { - m_rangeTrack.clear(); + m_rangeTracks.clear(); m_firstFrameId = -1; - if (m_firstStroke) { - delete m_firstStroke; - m_firstStroke = 0; - } + deleteStrokes(m_firstStrokes); m_firstFrameRange = true; } @@ -1687,6 +1608,7 @@ bool ToonzVectorBrushTool::onPropertyChanged(std::string propertyName) { V_VectorBrushSnap = m_snap.getValue(); int snapSensitivityIndex = m_snapSensitivity.getIndex(); V_VectorBrushSnapSensitivity = snapSensitivityIndex; + V_VectorBrushAssistants = m_assistants.getValue(); // Recalculate/reset based on changed settings m_minThick = m_thickness.getValue().first; @@ -1825,8 +1747,9 @@ void ToonzVectorBrushTool::loadLastBrush() { // Properties not tracked with preset m_frameRange.setIndex(V_VectorBrushFrameRange); - m_snap.setValue(V_VectorBrushSnap); + m_snap.setValue(V_VectorBrushSnap ? 1 : 0); m_snapSensitivity.setIndex(V_VectorBrushSnapSensitivity); + m_assistants.setValue(V_VectorBrushAssistants ? 1 : 0); // Recalculate based on prior values m_minThick = m_thickness.getValue().first; diff --git a/toonz/sources/tnztools/toonzvectorbrushtool.h b/toonz/sources/tnztools/toonzvectorbrushtool.h index 5ded2d6de..2aff0b422 100644 --- a/toonz/sources/tnztools/toonzvectorbrushtool.h +++ b/toonz/sources/tnztools/toonzvectorbrushtool.h @@ -3,17 +3,26 @@ #ifndef TOONZVECTORBRUSHTOOL_H #define TOONZVECTORBRUSHTOOL_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 "toonzrasterbrushtool.h" +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#ifndef NDEBUG +#include +#endif #include #include @@ -80,13 +89,18 @@ class VectorBrushPresetManager { // Brush Tool declaration //************************************************************************ -class ToonzVectorBrushTool final : public TTool { +class ToonzVectorBrushTool final : public TTool, + public TInputHandler +{ Q_DECLARE_TR_FUNCTIONS(ToonzVectorBrushTool) public: ToonzVectorBrushTool(std::string name, int targetType); - ToolType getToolType() const override { return TTool::LevelWriteTool; } + ToolType getToolType() const override + { return TTool::LevelWriteTool; } + unsigned int getToolHints() const override + { return TTool::getToolHints() & ~HintAssistantsAll; } ToolOptionsBox *createOptionsBox() override; @@ -102,6 +116,13 @@ class ToonzVectorBrushTool final : public TTool { void mouseMove(const TPointD &pos, const TMouseEvent &e) override; bool keyDown(QKeyEvent *event) override; + void inputMouseMove(const TPointD &position, + const TInputState &state) override; + void inputSetBusy(bool busy) override; + void inputPaintTracks(const TTrackList &tracks) override; + void inputInvalidateRect(const TRectD &bounds) override { invalidate(bounds); } + TTool *inputGetTool() override { return this; }; + void draw() override; void onEnter() override; @@ -128,21 +149,29 @@ class ToonzVectorBrushTool final : public TTool { // Tools. bool isPencilModeActive() override; - void addTrackPoint(const TThickPoint &point, double pixelSize2); - void flushTrackPoint(); bool doFrameRangeStrokes(TFrameId firstFrameId, TStroke *firstStroke, TFrameId lastFrameId, TStroke *lastStroke, int interpolationType, bool breakAngles, bool autoGroup = false, bool autoFill = false, bool drawFirstStroke = true, bool drawLastStroke = true, bool withUndo = true); - void checkGuideSnapping(bool beforeMousePress, bool invertCheck); - void checkStrokeSnapping(bool beforeMousePress, bool invertCheck); bool doGuidedAutoInbetween(TFrameId cFid, const TVectorImageP &cvi, TStroke *cStroke, bool breakAngles, bool autoGroup = false, bool autoFill = false, bool drawStroke = true); +protected: + typedef std::vector TrackList; + typedef std::vector StrokeList; + void deleteStrokes(StrokeList &strokes); + void copyStrokes(StrokeList &dst, const StrokeList &src); + + void snap(const TPointD &pos, bool snapEnabled, bool withSelfSnap = false); + + enum MouseEventType { ME_DOWN, ME_DRAG, ME_UP, ME_MOVE }; + void handleMouseEvent(MouseEventType type, const TPointD &pos, + const TMouseEvent &e); + protected: TPropertyGroup m_prop[2]; @@ -158,58 +187,48 @@ class ToonzVectorBrushTool final : public TTool { TEnumProperty m_capStyle; TEnumProperty m_joinStyle; TIntProperty m_miterJoinLimit; - - StrokeGenerator m_track; - StrokeGenerator m_rangeTrack; - RasterStrokeGenerator *m_rasterTrack; - TStroke *m_firstStroke; - TTileSetCM32 *m_tileSet; - TTileSaverCM32 *m_tileSaver; + TBoolProperty m_assistants; + + TInputManager m_inputmanager; + TSmartPointerT m_modifierLine; + TSmartPointerT m_modifierTangents; + TSmartPointerT m_modifierAssistants; + TSmartPointerT m_modifierSegmentation; + TSmartPointerT m_modifierSmoothSegmentation; + TSmartPointerT m_modifierSmooth[3]; + TSmartPointerT m_modifierSimplify; +#ifndef NDEBUG + TSmartPointerT m_modifierTest; +#endif + + TrackList m_tracks; + TrackList m_rangeTracks; + StrokeList m_firstStrokes; TFrameId m_firstFrameId, m_veryFirstFrameId; TPixel32 m_currentColor; - int m_styleId; + int m_styleId; // bwtodo: remove double m_minThick, m_maxThick; // for snapping and framerange - int m_strokeIndex1, m_strokeIndex2, m_col, m_firstFrame, m_veryFirstFrame, + int m_col, m_firstFrame, m_veryFirstFrame, m_veryFirstCol, m_targetType; - double m_w1, m_w2, m_pixelSize, m_currThickness, m_minDistance2; - bool m_foundFirstSnap = false, m_foundLastSnap = false, m_dragDraw = true, - m_altPressed = false, m_snapSelf = false; - TRectD m_modifiedRegion; - TPointD m_dpiScale, - m_mousePos, //!< Current mouse position, in world coordinates. - m_brushPos, //!< World position the brush will be painted at. - m_firstSnapPoint, m_lastSnapPoint; - - BluredBrush *m_bluredBrush; - QRadialGradient m_brushPad; - - TRasterCM32P m_backupRas; - TRaster32P m_workRas; - - std::vector m_points; - TRect m_strokeRect, m_lastRect; - - SmoothStroke m_smoothStroke; + double m_pixelSize, m_minDistance2; + + bool m_snapped; + bool m_snappedSelf; + TPointD m_snapPoint; + TPointD m_snapPointSelf; + + TPointD m_mousePos; //!< Current mouse position, in world coordinates. + TPointD m_brushPos; //!< World position the brush will be painted at. VectorBrushPresetManager m_presetsManager; //!< Manager for presets of this tool instance - bool m_active, m_enabled, - m_isPrompting, //!< Whether the tool is prompting for spline - //! substitution. - m_firstTime, m_isPath, m_presetsLoaded, m_firstFrameRange; - - /*--- - ƒFrameIdNbNɕۑA}EX[XiUndo̓o^jɕʂ̃t[ - ړĂƂ̕sCB---*/ - TFrameId m_workingFrameId; - - TPointD m_lastDragPos; //!< Position where mouse was last dragged. - TMouseEvent m_lastDragEvent; //!< Previous mouse-drag event. + bool m_active, m_firstTime, m_isPath, + m_presetsLoaded, m_firstFrameRange; - bool m_propertyUpdating = false; + bool m_propertyUpdating; }; #endif // TOONZVECTORBRUSHTOOL_H diff --git a/toonz/sources/tnztools/track.cpp b/toonz/sources/tnztools/track.cpp index 5af347aa1..2637216f3 100644 --- a/toonz/sources/tnztools/track.cpp +++ b/toonz/sources/tnztools/track.cpp @@ -11,14 +11,13 @@ TTrack::Id TTrack::m_lastId = 0; //***************************************************************************************** -// TTrackModifier implemantation +// TTrackIntrOrig implemantation //***************************************************************************************** TTrackPoint -TTrackModifier::calcPoint(double originalIndex) { - TTrackPoint p = original.calcPoint(originalIndex); - p.originalIndex = originalIndex; - return p; +TTrackIntrOrig::interpolate(double index) { + return track.original ? track.calcPointFromOriginal(track.originalIndexByIndex(index)) + : track.interpolateLinear(index); } @@ -32,7 +31,8 @@ TTrack::TTrack( const TInputState::KeyHistory::Holder &keyHistory, const TInputState::ButtonHistory::Holder &buttonHistory, bool hasPressure, - bool hasTilt + bool hasTilt, + double timeOffset ): id(++m_lastId), deviceId(deviceId), @@ -41,21 +41,26 @@ TTrack::TTrack( buttonHistory(buttonHistory), hasPressure(hasPressure), hasTilt(hasTilt), + original(), + timeOffset(timeOffset), + rootTimeOffset(timeOffset), pointsRemoved(), pointsAdded(), fixedPointsAdded(), m_pointsFixed() { } -TTrack::TTrack(const TTrackModifierP &modifier): +TTrack::TTrack(const TTrack &original, double timeOffset): id(++m_lastId), - deviceId(modifier->original.deviceId), - touchId(modifier->original.touchId), - keyHistory(modifier->original.keyHistory), - buttonHistory(modifier->original.buttonHistory), - hasPressure(modifier->original.hasPressure), - hasTilt(modifier->original.hasTilt), - modifier(modifier), + deviceId(original.deviceId), + touchId(original.touchId), + keyHistory(original.keyHistory), + buttonHistory(original.buttonHistory), + hasPressure(original.hasPressure), + hasTilt(original.hasTilt), + original(&original), + timeOffset(timeOffset), + rootTimeOffset(original.rootTimeOffset + timeOffset), pointsRemoved(), pointsAdded(), fixedPointsAdded(), @@ -64,15 +69,15 @@ TTrack::TTrack(const TTrackModifierP &modifier): const TTrack* TTrack::root() const - { return original() ? original()->root() : this; } + { return original ? original->root() : this; } int TTrack::level() const - { return original() ? original()->level() + 1 : 0; } + { return original ? original->level() + 1 : 0; } int TTrack::floorIndex(double index, double *outFrac) const { - int i = (int)floor(index + TConsts::epsilon); + int i = floorIndexNoClamp(index); if (i > size() - 1) { if (outFrac) *outFrac = 0.0; return size() - 1; @@ -87,10 +92,13 @@ TTrack::floorIndex(double index, double *outFrac) const { void TTrack::push_back(const TTrackPoint &point, bool fixed) { + assert(m_points.empty() || !m_points.back().final); m_points.push_back(point); - if (size() > 1) { + TTrackPoint &p = m_points.back(); + if (m_points.size() <= 1) { + p.length = 0; + } else { const TTrackPoint &prev = *(m_points.rbegin() + 1); - TTrackPoint &p = m_points.back(); // fix originalIndex if (p.originalIndex < prev.originalIndex) @@ -100,8 +108,7 @@ TTrack::push_back(const TTrackPoint &point, bool fixed) { p.time = std::max(p.time, prev.time + TToolTimer::step); // calculate length - TPointD d = p.position - prev.position; - p.length = prev.length + sqrt(d.x*d.x + d.y*d.y); + p.length = prev.length + tdistance(p.position, prev.position); } ++pointsAdded; if (fixed) fix_all(); @@ -130,13 +137,6 @@ TTrack::fix_points(int count) { } -TTrackPoint -TTrack::calcPoint(double index) const { - return modifier - ? modifier->calcPoint( originalIndexByIndex(index) ) - : interpolateLinear(index); -} - TPointD TTrack::calcTangent(double index, double distance) const { double minDistance = 10.0*TConsts::epsilon; @@ -150,14 +150,14 @@ TTrack::calcTangent(double index, double distance) const { double TTrack::rootIndexByIndex(double index) const { - return modifier - ? modifier->original.rootIndexByIndex( originalIndexByIndex(index) ) + return original + ? original->rootIndexByIndex( originalIndexByIndex(index) ) : index; } TTrackPoint TTrack::calcRootPoint(double index) const { - return modifier - ? modifier->original.calcRootPoint( originalIndexByIndex(index) ) + return original + ? original->calcRootPoint( originalIndexByIndex(index) ) : calcPoint(index); } diff --git a/toonz/sources/toonz/sceneviewer.cpp b/toonz/sources/toonz/sceneviewer.cpp index a7cfaa183..7b8dab6a7 100644 --- a/toonz/sources/toonz/sceneviewer.cpp +++ b/toonz/sources/toonz/sceneviewer.cpp @@ -23,6 +23,7 @@ #include "tools/toolhandle.h" #include "tools/toolcommandids.h" #include "tools/toolutils.h" +#include "tools/assistant.h" // TnzQt includes #include "toonzqt/icongenerator.h" @@ -1831,7 +1832,25 @@ void SceneViewer::drawOverlay() { !app->getCurrentObject()->isSpline()) glScaled(m_dpiScale.x, m_dpiScale.y, 1); m_pixelSize = sqrt(tglGetPixelSize2()) * getDevPixRatio(); + + // draw assistans and guidelines + m_toolHasAssistants = false; + unsigned int hints = tool->getToolHints(); + if (hints & TTool::HintAssistantsAll) { + m_toolHasAssistants = TAssistant::scanAssistants( + tool, // tool + &m_toolPos, 1, // pointer positions + nullptr, // out guidelines + true, // draw + false, // enabled only + hints & TTool::HintAssistantsEnabled, // mark enabled + true, // draw guidelines + nullptr ); // skip image + } + + // draw tool tool->draw(); + glPopMatrix(); // Used (only in the T_RGBPicker tool) to notify and set the currentColor // outside the draw() methods: @@ -3307,6 +3326,7 @@ TAffine SceneViewer::getNormalZoomScale() { //----------------------------------------------------------------------------- void SceneViewer::invalidateToolStatus() { + m_toolHasAssistants = false; TTool *tool = TApp::instance()->getCurrentTool()->getTool(); if (tool) { m_toolDisableReason = tool->updateEnabled(); diff --git a/toonz/sources/toonz/sceneviewer.h b/toonz/sources/toonz/sceneviewer.h index e93cfd3e1..dcb22d787 100644 --- a/toonz/sources/toonz/sceneviewer.h +++ b/toonz/sources/toonz/sceneviewer.h @@ -71,6 +71,8 @@ class SceneViewer final : public GLWidgetForHighDpi, double m_pressure; QPointF m_lastMousePos; QPointF m_pos; + TPointD m_toolPos; + bool m_toolHasAssistants = false; Qt::MouseButton m_mouseButton; bool m_foregroundDrawing; bool m_tabletEvent, m_tabletMove; diff --git a/toonz/sources/toonz/sceneviewerevents.cpp b/toonz/sources/toonz/sceneviewerevents.cpp index fd8d08539..d0a46dde7 100644 --- a/toonz/sources/toonz/sceneviewerevents.cpp +++ b/toonz/sources/toonz/sceneviewerevents.cpp @@ -656,6 +656,12 @@ void SceneViewer::onMove(const TMouseEvent &event) { } if (!cursorSet) setToolCursor(this, tool->getCursorId()); + if ( m_toolHasAssistants + && (tool->getToolHints() & TTool::HintGuidelines) + && !areAlmostEqual(m_toolPos, pos) ) + invalidateAll(); + m_toolPos = pos; + #ifdef WITH_CANON if (StopMotion::instance()->m_canon->m_pickLiveViewZoom) setToolCursor(this, ToolCursor::ZoomCursor); diff --git a/toonz/sources/toonzlib/strokegenerator.cpp b/toonz/sources/toonzlib/strokegenerator.cpp index 9a5c9c742..cac636b15 100644 --- a/toonz/sources/toonzlib/strokegenerator.cpp +++ b/toonz/sources/toonzlib/strokegenerator.cpp @@ -26,32 +26,61 @@ bool StrokeGenerator::isEmpty() const { return m_points.empty(); } //------------------------------------------------------------------- -void StrokeGenerator::add(const TThickPoint &point, double pixelSize2) { +bool StrokeGenerator::add(const TThickPoint &point, double pixelSize2) { if (m_points.empty()) { double x = point.x, y = point.y, d = point.thick + 3; m_points.push_back(point); TRectD rect(x - d, y - d, x + d, y + d); - m_modifiedRegion = rect; - m_lastPointRect = rect; - m_lastModifiedRegion = rect; + m_modifiedRegion += rect; + m_lastPointRect += rect; + m_lastModifiedRegion += rect; m_p0 = m_p1 = point; - } else { - TThickPoint lastPoint = m_points.back(); - if (tdistance2(lastPoint, point) >= 4 * pixelSize2) { - m_points.push_back(point); + return true; + } + + TThickPoint lastPoint = m_points.back(); + if (tdistance2(lastPoint, point) >= 4 * pixelSize2) { + m_points.push_back(point); + double d = std::max(point.thick, lastPoint.thick) + 3; + TRectD rect(TRectD(lastPoint, point).enlarge(d)); + m_modifiedRegion += rect; + m_lastModifiedRegion += rect; + m_lastPointRect = rect; + return true; + } + + m_points.back().thick = std::max(m_points.back().thick, point.thick); + return false; +} + +//------------------------------------------------------------------- + +void StrokeGenerator::pop() { + if (!m_points.empty()) { + TRectD rect; + TThickPoint point = m_points.back(); + m_points.pop_back(); + if (!m_points.empty()) { + const TThickPoint &lastPoint = m_points.back(); double d = std::max(point.thick, lastPoint.thick) + 3; - TRectD rect(TRectD(lastPoint, point).enlarge(d)); - m_modifiedRegion += rect; - m_lastModifiedRegion += rect; - m_lastPointRect = rect; + rect = TRectD(lastPoint, point).enlarge(d); } else { - m_points.back().thick = std::max(m_points.back().thick, point.thick); + double x = point.x, y = point.y, d = point.thick + 3; + rect = TRectD(x - d, y - d, x + d, y + d); } + m_modifiedRegion += rect; + m_lastModifiedRegion += rect; + m_lastPointRect = rect; } } //------------------------------------------------------------------- +void StrokeGenerator::setLoop(bool loop) + { m_loop = loop; } + +//------------------------------------------------------------------- + void StrokeGenerator::filterPoints() { if (m_points.size() < 10) return; @@ -130,10 +159,10 @@ void StrokeGenerator::drawFragments(int first, int last) { if (b.thick == 0) b.thick = 0.1; } // m_p0 = m_p1 = b; - v = a.thick * normalize(rotate90(b - a)); + v = a.thick * normalizeOrZero(rotate90(b - a)); m_p0 = a + v; m_p1 = a - v; - v = b.thick * normalize(rotate90(b - a)); + v = b.thick * normalizeOrZero(rotate90(b - a)); TPointD p0 = b + v; TPointD p1 = b - v; glBegin(GL_POLYGON); @@ -161,11 +190,11 @@ void StrokeGenerator::drawFragments(int first, int last) { if (c.thick == 0) c.thick = 0.1; } if (i - 1 == 0) { - v = a.thick * normalize(rotate90(b - a)); + v = a.thick * normalizeOrZero(rotate90(b - a)); m_p0 = a + v; m_p1 = a - v; } - v = b.thick * normalize(rotate90(c - a)); + v = b.thick * normalizeOrZero(rotate90(c - a)); TPointD p0 = b + v; TPointD p1 = b - v; glBegin(GL_POLYGON); @@ -185,7 +214,7 @@ void StrokeGenerator::drawFragments(int first, int last) { } if (last < 2) return; v = m_points[last].thick * - normalize(rotate90(m_points[last] - m_points[last - 1])); + normalizeOrZero(rotate90(m_points[last] - m_points[last - 1])); TPointD p0 = m_points[last] + v; TPointD p1 = m_points[last] - v; glBegin(GL_POLYGON); @@ -274,7 +303,7 @@ TPointD StrokeGenerator::getFirstPoint() { return m_points[0]; } //------------------------------------------------------------------- -TStroke *StrokeGenerator::makeStroke(double error, UINT onlyLastPoints) const { +TStroke *StrokeGenerator::makeStroke(double error, UINT onlyLastPoints, bool useLoop) const { if (onlyLastPoints == 0 || onlyLastPoints > m_points.size()) return TStroke::interpolate(m_points, error); @@ -283,7 +312,9 @@ TStroke *StrokeGenerator::makeStroke(double error, UINT onlyLastPoints) const { m_points.begin() + (m_points.size() - onlyLastPoints); copy(first, m_points.end(), lastPoints.begin()); - return TStroke::interpolate(lastPoints, error); + TStroke *stroke = TStroke::interpolate(lastPoints, error); + if (useLoop) stroke->setSelfLoop(m_loop); + return stroke; } //-------------------------------------------------------------------