From da847a9e2adbc0ef538ba36b65f84010a16c4d3b Mon Sep 17 00:00:00 2001 From: Ivan Mahonin Date: Sun, 27 Aug 2023 08:12:26 +0700 Subject: [PATCH] Brush stroke replication feature --- toonz/sources/include/tmetaimage.h | 6 +- toonz/sources/include/tools/assistant.h | 63 +++- toonz/sources/include/tools/inputmanager.h | 4 + .../include/tools/modifiers/modifierclone.h | 55 +++ .../include/tools/modifiers/modifierjitter.h | 61 ++++ toonz/sources/include/tools/replicator.h | 58 +++ toonz/sources/include/tools/tool.h | 15 +- toonz/sources/include/tools/track.h | 61 ++++ toonz/sources/include/toonzqt/intfield.h | 17 +- toonz/sources/include/tproperty.h | 8 +- toonz/sources/tnztools/CMakeLists.txt | 11 + toonz/sources/tnztools/assistant.cpp | 240 +++++++++--- .../tnztools/assistants/assistantellipse.cpp | 2 +- .../tnztools/assistants/assistantline.cpp | 1 + .../assistants/assistantvanishingpoint.cpp | 1 + .../tnztools/assistants/replicatoraffine.cpp | 341 ++++++++++++++++++ .../tnztools/assistants/replicatorgrid.cpp | 335 +++++++++++++++++ .../tnztools/assistants/replicatorjitter.cpp | 149 ++++++++ .../tnztools/assistants/replicatormirror.cpp | 161 +++++++++ .../tnztools/assistants/replicatorstar.cpp | 230 ++++++++++++ toonz/sources/tnztools/editassistantstool.cpp | 225 ++++++++++-- toonz/sources/tnztools/fullcolorbrushtool.cpp | 27 +- toonz/sources/tnztools/fullcolorbrushtool.h | 6 +- .../tnztools/modifiers/modifierclone.cpp | 75 ++++ .../tnztools/modifiers/modifierjitter.cpp | 172 +++++++++ toonz/sources/tnztools/replicator.cpp | 187 ++++++++++ toonz/sources/tnztools/tool.cpp | 2 +- .../sources/tnztools/tooloptionscontrols.cpp | 14 +- .../sources/tnztools/toonzrasterbrushtool.cpp | 29 +- toonz/sources/tnztools/toonzrasterbrushtool.h | 6 +- .../sources/tnztools/toonzvectorbrushtool.cpp | 30 +- toonz/sources/tnztools/toonzvectorbrushtool.h | 8 +- toonz/sources/tnztools/track.cpp | 14 + toonz/sources/toonz/sceneviewer.cpp | 45 ++- toonz/sources/toonz/sceneviewer.h | 1 + toonz/sources/toonz/sceneviewerevents.cpp | 8 +- toonz/sources/toonzqt/intfield.cpp | 61 +++- 37 files changed, 2599 insertions(+), 130 deletions(-) create mode 100644 toonz/sources/include/tools/modifiers/modifierclone.h create mode 100644 toonz/sources/include/tools/modifiers/modifierjitter.h create mode 100644 toonz/sources/include/tools/replicator.h create mode 100644 toonz/sources/tnztools/assistants/replicatoraffine.cpp create mode 100644 toonz/sources/tnztools/assistants/replicatorgrid.cpp create mode 100644 toonz/sources/tnztools/assistants/replicatorjitter.cpp create mode 100644 toonz/sources/tnztools/assistants/replicatormirror.cpp create mode 100644 toonz/sources/tnztools/assistants/replicatorstar.cpp create mode 100644 toonz/sources/tnztools/modifiers/modifierclone.cpp create mode 100644 toonz/sources/tnztools/modifiers/modifierjitter.cpp create mode 100644 toonz/sources/tnztools/replicator.cpp diff --git a/toonz/sources/include/tmetaimage.h b/toonz/sources/include/tmetaimage.h index 5726d1a2b1..9d360c7ac3 100644 --- a/toonz/sources/include/tmetaimage.h +++ b/toonz/sources/include/tmetaimage.h @@ -181,8 +181,10 @@ class DVAPI TMetaObjectHandler { virtual void onFixData() { } public: - void setDefaults() - { onSetDefaults(); } + void setDefaults() { + { LockEvents lock(*this); onSetDefaults(); } + data().touch(); + } void dataChanged(const TVariant &value) { if (m_locks == 0) onDataChanged(value); } void fixData() diff --git a/toonz/sources/include/tools/assistant.h b/toonz/sources/include/tools/assistant.h index 5be247d426..f79377a57e 100644 --- a/toonz/sources/include/tools/assistant.h +++ b/toonz/sources/include/tools/assistant.h @@ -42,6 +42,7 @@ class TPropertyGroup; class TTool; class TToolViewer; class TAssistant; +class TAssistantBase; class TAssistantPoint; class TGuideline; @@ -134,7 +135,7 @@ class DVAPI TAssistantType: public TMetaObjectType { TAssistantType(const TStringId &name): TMetaObjectType(name) { } TMetaObjectHandler* createHandler(TMetaObject &obj) const override; - virtual TAssistant* createAssistant(TMetaObject &obj) const + virtual TAssistantBase* createAssistant(TMetaObject &obj) const { return 0; } }; @@ -182,7 +183,7 @@ class TAssistantTypeT: public TAssistantType { if (!alias5.empty()) registerAlias(TStringId(alias5)); } - TAssistant* createAssistant(TMetaObject &obj) const override + TAssistantBase* createAssistant(TMetaObject &obj) const override { return new Type(obj); } QString getLocalName() const override { QString localName = Type::getLocalName(); @@ -192,11 +193,19 @@ class TAssistantTypeT: public TAssistantType { //***************************************************************************************** -// TAssistant definition +// TAssistantBase definition //***************************************************************************************** -class DVAPI TAssistant : public TMetaObjectHandler { - Q_DECLARE_TR_FUNCTIONS(TAssistant) +class DVAPI TAssistantBase : public TMetaObjectHandler { + Q_DECLARE_TR_FUNCTIONS(TAssistantBase) +public: + enum { + DRAW_ERROR = 1, + }; + + static unsigned int drawFlags; + static const double lineWidthScale; + protected: const TStringId m_idEnabled; const TStringId m_idPoints; @@ -211,7 +220,7 @@ class DVAPI TAssistant : public TMetaObjectHandler { mutable TPropertyGroup m_properties; public: - TAssistant(TMetaObject &object); + TAssistantBase(TMetaObject &object); static QString getLocalName() { return QString(); } @@ -237,11 +246,6 @@ class DVAPI TAssistant : public TMetaObjectHandler { void setEnabled(bool x) { if (getEnabled() != x) data()[m_idEnabled].setBool(x); } - double getMagnetism() const - { return data()[m_idMagnetism].getDouble(); } - void setMagnetism(double x) - { if (getMagnetism() != x) data()[m_idMagnetism].setDouble(x); } - inline void selectPoint(const TStringId &name) const { setPointSelection(name, true); } inline void deselectPoint(const TStringId &name) const @@ -249,7 +253,7 @@ class DVAPI TAssistant : public TMetaObjectHandler { inline void selectAll() const { setAllPointsSelection(true); } inline void deselectAll() const - { setAllPointsSelection(false); } + { setAllPointsSelection(false); } TPropertyGroup& getProperties() const { return m_properties; } @@ -300,7 +304,7 @@ class DVAPI TAssistant : public TMetaObjectHandler { //! try to move point virtual void onMovePoint(TAssistantPoint &point, const TPointD &position); //! save object data to variant - virtual void onFixData(); + void onFixData() override; //! load all properties from variant virtual void updateProperties(); //! load single property from variant @@ -315,21 +319,52 @@ class DVAPI TAssistant : public TMetaObjectHandler { void drawMark(const TPointD &p, const TPointD &normal, double pixelSize, double alpha) const; void drawDot(const TPointD &p, double alpha) const; void drawPoint(const TAssistantPoint &point, double pixelSize) const; + void drawIndex(const TPointD &p, int index, bool selected, double pixelSize) const; inline void drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize) const { drawSegment(p0, p1, pixelSize, getDrawingAlpha()); } inline void drawDot(const TPointD &p) const { drawDot(p, getDrawingAlpha()); } + TIntProperty* createSpinProperty(const TStringId &id, int def, int min, int max, bool hasMax = true); + inline TIntProperty* createSpinProperty(const TStringId &id, int def, int min) + { return createSpinProperty(id, def, min, 0, false); } + void addProperty(TProperty *p); void setTranslation(const TStringId &name, const QString &localName) const; public: virtual void updateTranslation() const; - virtual void getGuidelines(const TPointD &position, const TAffine &toTool, TGuidelineList &outGuidelines) const; virtual void draw(TToolViewer *viewer, bool enabled) const; void draw(TToolViewer *viewer) const { draw(viewer, true); } virtual void drawEdit(TToolViewer *viewer) const; + virtual void drawEdit(TToolViewer *viewer, int index) const; +}; + + +//***************************************************************************************** +// TAssistant definition +//***************************************************************************************** + +class DVAPI TAssistant : public TAssistantBase { + Q_DECLARE_TR_FUNCTIONS(TAssistant) +protected: + const TStringId m_idMagnetism; + + void onSetDefaults() override; + void onFixData() override; + +public: + TAssistant(TMetaObject &object); + + double getMagnetism() const + { return data()[m_idMagnetism].getDouble(); } + void setMagnetism(double x) + { if (getMagnetism() != x) data()[m_idMagnetism].setDouble(x); } + +public: + void updateTranslation() const override; + virtual void getGuidelines(const TPointD &position, const TAffine &toTool, TGuidelineList &outGuidelines) const; static bool calcPerspectiveStep( double minStep, diff --git a/toonz/sources/include/tools/inputmanager.h b/toonz/sources/include/tools/inputmanager.h index 3dfcc51328..011b05aa04 100644 --- a/toonz/sources/include/tools/inputmanager.h +++ b/toonz/sources/include/tools/inputmanager.h @@ -222,6 +222,10 @@ class DVAPI TInputManager { void insertModifier(int index, const TInputModifierP &modifier); void addModifier(const TInputModifierP &modifier) { insertModifier(getModifiersCount(), modifier); } + void addModifiers(const TInputModifier::List &modifiers) { + for(TInputModifier::List::const_iterator i = modifiers.begin(); i != modifiers.end(); ++i) + addModifier(*i); + } void removeModifier(int index); void removeModifier(const TInputModifierP &modifier) { removeModifier(findModifier(modifier)); } diff --git a/toonz/sources/include/tools/modifiers/modifierclone.h b/toonz/sources/include/tools/modifiers/modifierclone.h new file mode 100644 index 0000000000..214e70f40f --- /dev/null +++ b/toonz/sources/include/tools/modifiers/modifierclone.h @@ -0,0 +1,55 @@ +#pragma once + +#ifndef MODIFIERCLONE_INCLUDED +#define MODIFIERCLONE_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 + +//=================================================================== + +//***************************************************************************************** +// TModifierClone definition +//***************************************************************************************** + +class DVAPI TModifierClone : public TInputModifier { +public: + class DVAPI Handler : public TMultiTrackHandler { + public: + TTrackP original; + inline explicit Handler(const TTrackP &original = TTrackP()): + original(original) { } + }; + + class DVAPI Interpolator : public TTrackInterpolator { + public: + const TTrackTransform transform; + inline Interpolator(TTrack &track, const TTrackTransform &transform): + TTrackInterpolator(track), transform(transform) { } + TTrackPoint interpolateFromOriginal(double originalIndex); + TTrackPoint interpolate(double index) override; + }; + + +public: + bool keepOriginals; + TTrackTransformList transforms; + + TModifierClone(bool keepOriginals = true); + + void modifyTrack( + const TTrack &track, + TTrackList &outTracks ) override; +}; + +#endif diff --git a/toonz/sources/include/tools/modifiers/modifierjitter.h b/toonz/sources/include/tools/modifiers/modifierjitter.h new file mode 100644 index 0000000000..2f048e4637 --- /dev/null +++ b/toonz/sources/include/tools/modifiers/modifierjitter.h @@ -0,0 +1,61 @@ +#pragma once + +#ifndef MODIFIERJITTER_INCLUDED +#define MODIFIERJITTER_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 + +//=================================================================== + +//***************************************************************************************** +// TModifierJitter definition +//***************************************************************************************** + +class DVAPI TModifierJitter : public TInputModifier { +public: + typedef TSubTrackHandler Handler; + + class DVAPI Interpolator : public TTrackInterpolator { + public: + const unsigned int seedX; + const unsigned int seedY; + const double frequency; + const double amplitude; + Interpolator(TTrack &track, double period, double amplitude); + TTrackPoint interpolateFromOriginal(double originalIndex); + TTrackPoint interpolate(double index) override; + }; + +public: + double period; + double amplitude; + int skipFirst; + + TModifierJitter( + double period = 30, + double amplitude = 10, + int skipFirst = 0 ); + + void modifyTrack( + const TTrack &track, + TTrackList &outTracks ) override; + + void modifyTracks( + const TTrackList &tracks, + TTrackList &outTracks ) override; + + static double func(unsigned int seed, double x); +}; + +#endif diff --git a/toonz/sources/include/tools/replicator.h b/toonz/sources/include/tools/replicator.h new file mode 100644 index 0000000000..05568021cf --- /dev/null +++ b/toonz/sources/include/tools/replicator.h @@ -0,0 +1,58 @@ +#pragma once + +#ifndef REPLICATOR_INCLUDED +#define REPLICATOR_INCLUDED + +// TnzTools includes +#include +#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 + + +//***************************************************************************************** +// TReplicator definition +//***************************************************************************************** + +class DVAPI TReplicator : public TAssistantBase { + Q_DECLARE_TR_FUNCTIONS(TReplicator) +public: + typedef std::vector PointList; + + static const int multiplierSoftLimit; + static const int multiplierLimit; + + TReplicator(TMetaObject &object); + + virtual int getMultipler() const; + virtual void getPoints(const TAffine &toTool, PointList &points) const; + virtual void getModifiers(const TAffine &toTool, TInputModifier::List &outModifiers) const; + + static void transformPoints(const TAffine &aff, PointList &points, int count); + static void drawReplicatorPoints(const TPointD *points, int count); + + //! return summary multiplier, or 0 is no replicators found + static int scanReplicators( + TTool *tool, + PointList *inOutPoints, + TInputModifier::List *outModifiers, + bool draw, + bool enabledOnly, + bool markEnabled, + bool drawPoints, + TImage *skipImage ); + +protected: + TIntProperty* createCountProperty(const TStringId &id, int def = 1, int min = 1, int max = 0); +}; + + +#endif diff --git a/toonz/sources/include/tools/tool.h b/toonz/sources/include/tools/tool.h index 2be17c8bb4..b0b50be059 100644 --- a/toonz/sources/include/tools/tool.h +++ b/toonz/sources/include/tools/tool.h @@ -301,14 +301,19 @@ class DVAPI TTool { }; 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 + { HintNone = 0, + HintAssistants = 1 << 0, //!< Draw asistants when tool active + HintAssistantsGuidelines = 1 << 1, //!< Draw asistant guidelines + HintAssistantsEnabled = 1 << 2, //!< Mark active assistants + HintReplicators = 1 << 3, //!< Draw replicators + HintReplicatorsPoints = 1 << 4, //!< Draw replicated points + HintReplicatorsEnabled = 1 << 5, //!< Mark active replicators HintAssistantsAll = HintAssistants - | HintGuidelines + | HintAssistantsGuidelines | HintAssistantsEnabled, + HintReplicatorsAll = HintReplicators + | HintReplicatorsEnabled, }; public: diff --git a/toonz/sources/include/tools/track.h b/toonz/sources/include/tools/track.h index a8177b33d3..2363db2085 100644 --- a/toonz/sources/include/tools/track.h +++ b/toonz/sources/include/tools/track.h @@ -36,6 +36,7 @@ class TTrack; class TTrackPoint; class TTrackTangent; +class TTrackTransform; class TTrackHandler; class TSubTrackHandler; class TMultiTrackHandler; @@ -49,6 +50,7 @@ typedef TSmartPointerT TTrackInterpolatorP; typedef std::vector TTrackPointList; typedef std::vector TTrackTangentList; +typedef std::vector TTrackTransformList; typedef std::vector TTrackList; //=================================================================== @@ -125,6 +127,65 @@ class DVAPI TTrackTangent { }; +//***************************************************************************************** +// TTrackHandler definition +//***************************************************************************************** + +class DVAPI TTrackTransform { +public: + TAffine transform; + TAffine tiltTransform; + double pressureScale; + double pressureOffset; + + inline TTrackTransform( + const TAffine &transform, + const TAffine &tiltTransform, + double pressureScale = 1, + double pressureOffset = 0 + ): + transform(transform), + tiltTransform(tiltTransform), + pressureScale(pressureScale), + pressureOffset(pressureOffset) { } + + inline explicit TTrackTransform( + const TAffine &transform, + double pressureScale = 1, + double pressureOffset = 0 + ): + transform(transform), + tiltTransform(makeTiltTransform(transform)), + pressureScale(pressureScale), + pressureOffset(pressureOffset) { } + + inline explicit TTrackTransform( + double pressureScale = 1, + double pressureOffset = 0 + ): + pressureScale(pressureScale), + pressureOffset(pressureOffset) { } + + inline TTrackPoint apply(TTrackPoint p) const { + p.position = transform * p.position; + + TPointD t = tiltTransform * p.tilt; + p.tilt.x = t.x > -1 ? (t.x < 1 ? t.x : 1) : -1; + p.tilt.y = t.y > -1 ? (t.y < 1 ? t.y : 1) : -1; + + double pr = p.pressure*pressureScale + pressureOffset; + p.pressure = pr > 0 ? (pr < 1 ? pr : 1) : 0; + + return p; + } + + inline void recalcTiltTransform() + { tiltTransform = makeTiltTransform(transform); } + + static TAffine makeTiltTransform(const TAffine &a); +}; + + //***************************************************************************************** // TTrackHandler definition //***************************************************************************************** diff --git a/toonz/sources/include/toonzqt/intfield.h b/toonz/sources/include/toonzqt/intfield.h index 61d14b2876..f18125f709 100644 --- a/toonz/sources/include/toonzqt/intfield.h +++ b/toonz/sources/include/toonzqt/intfield.h @@ -20,6 +20,7 @@ // forward declaration class QSlider; +class QPushButton; class QIntValidator; //============================================================================= @@ -166,12 +167,17 @@ class DVAPI IntField : public QWidget { RollerField *m_roller; IntLineEdit *m_lineEdit; QSlider *m_slider; + QPushButton *m_inc; + QPushButton *m_dec; + bool m_isMaxRangeLimited; bool m_isLinearSlider; public: - IntField(QWidget *parent = 0, bool isMaxRangeLimited = true, - bool isRollerHide = true); + IntField(QWidget *parent = 0, + bool isMaxRangeLimited = true, + bool isRollerHide = true, + bool isSpinnerHide = true ); ~IntField() {} /*! Set to \b minValue and \b maxValue slider and text field range. @@ -204,6 +210,10 @@ class DVAPI IntField : public QWidget { void enableRoller(bool enable); bool rollerIsEnabled(); + /*! If \b enable is false set spinner disable and hide it. */ + void enableSpinner(bool enable); + bool spinnerIsEnabled(); + void setLineEditBackgroundColor(QColor color); protected: @@ -220,6 +230,9 @@ protected slots: void onSliderChanged(int value); void onSliderReleased() { emit valueChanged(false); } + void onIncClicked(); + void onDecClicked(); + /*! Set slider and roller value to current value in text field. \n This protected slot is called when text editing is finished. \n If slider value is different from text field value emit signal diff --git a/toonz/sources/include/tproperty.h b/toonz/sources/include/tproperty.h index f7c2ea5bdd..b9c52e0929 100644 --- a/toonz/sources/include/tproperty.h +++ b/toonz/sources/include/tproperty.h @@ -122,7 +122,8 @@ class TRangeProperty final : public TProperty { , m_range(minValue, maxValue) , m_value(minValue) , m_isMaxRangeLimited(isMaxRangeLimited) - , m_isLinearSlider(true) { + , m_isLinearSlider(true) + , m_isSpinner(false) { setValue(value); } @@ -151,11 +152,16 @@ class TRangeProperty final : public TProperty { void setNonLinearSlider() { m_isLinearSlider = false; } bool isLinearSlider() { return m_isLinearSlider; } + //! has meaning for int properties only + void setSpinner() { m_isSpinner = true; } + bool isSpinner() { return m_isSpinner; } + private: Range m_range; T m_value; bool m_isMaxRangeLimited; bool m_isLinearSlider; + bool m_isSpinner; }; //--------------------------------------------------------- diff --git a/toonz/sources/tnztools/CMakeLists.txt b/toonz/sources/tnztools/CMakeLists.txt index 9ca30f7cc8..1518eeeda5 100644 --- a/toonz/sources/tnztools/CMakeLists.txt +++ b/toonz/sources/tnztools/CMakeLists.txt @@ -49,7 +49,10 @@ set(HEADERS ../include/tools/track.h ../include/tools/inputmanager.h ../include/tools/assistant.h + ../include/tools/replicator.h ../include/tools/modifiers/modifierassistants.h + ../include/tools/modifiers/modifierclone.h + ../include/tools/modifiers/modifierjitter.h ../include/tools/modifiers/modifierline.h ../include/tools/modifiers/modifiersegmentation.h ../include/tools/modifiers/modifiersimplify.h @@ -126,7 +129,10 @@ set(SOURCES track.cpp inputmanager.cpp assistant.cpp + replicator.cpp modifiers/modifierassistants.cpp + modifiers/modifierclone.cpp + modifiers/modifierjitter.cpp modifiers/modifierline.cpp modifiers/modifiersegmentation.cpp modifiers/modifiersimplify.cpp @@ -138,6 +144,11 @@ set(SOURCES assistants/assistantvanishingpoint.cpp assistants/assistantline.cpp assistants/assistantellipse.cpp + assistants/replicatoraffine.cpp + assistants/replicatorgrid.cpp + assistants/replicatorjitter.cpp + assistants/replicatormirror.cpp + assistants/replicatorstar.cpp editassistantstool.cpp ) diff --git a/toonz/sources/tnztools/assistant.cpp b/toonz/sources/tnztools/assistant.cpp index d43d4c865f..a342f5e94a 100644 --- a/toonz/sources/tnztools/assistant.cpp +++ b/toonz/sources/tnztools/assistant.cpp @@ -20,11 +20,15 @@ #ifdef MACOSX -const double line_width_scale = 1.5; +const double TAssistantBase::lineWidthScale = 1.5; #else -const double line_width_scale = 1.0; +const double TAssistantBase::lineWidthScale = 1.0; #endif + +unsigned int TAssistantBase::drawFlags = 0; + + //************************************************************************ // TGuideline implementation //************************************************************************ @@ -47,11 +51,11 @@ TGuideline::drawSegment( glPushAttrib(GL_ALL_ATTRIB_BITS); tglEnableBlending(); - tglEnableLineSmooth(true, 1.0 * line_width_scale); + tglEnableLineSmooth(true, 1.0 * TAssistant::lineWidthScale); TPointD d = p1 - p0; double k = norm2(d); if (k > TConsts::epsilon*TConsts::epsilon) { - k = 0.5*pixelSize*line_width_scale/sqrt(k); + k = 0.5*pixelSize*TAssistant::lineWidthScale/sqrt(k); d = TPointD(-k*d.y, k*d.x); glColor4dv(colorWhite); tglDrawSegment(p0 - d, p1 - d); @@ -145,26 +149,24 @@ TAssistantType::createHandler(TMetaObject &obj) const //************************************************************************ -// TAssistant implementation +// TAssistantBase implementation //************************************************************************ -TAssistant::TAssistant(TMetaObject &object): +TAssistantBase::TAssistantBase(TMetaObject &object): TMetaObjectHandler(object), m_idEnabled("enabled"), m_idPoints("points"), m_idX("x"), m_idY("y"), - m_idMagnetism("magnetism"), m_basePoint() { addProperty( new TBoolProperty(m_idEnabled.str(), getEnabled()) ); - addProperty( new TDoubleProperty(m_idMagnetism.str(), 0.0, 1.0, getMagnetism()) ); } //--------------------------------------------------------------------------------------------------- TAssistantPoint& -TAssistant::addPoint( +TAssistantBase::addPoint( const TStringId &name, TAssistantPoint::Type type, const TPointD &defPosition, @@ -185,7 +187,7 @@ TAssistant::addPoint( //--------------------------------------------------------------------------------------------------- TAssistantPoint& -TAssistant::addPoint( +TAssistantBase::addPoint( const TStringId &name, TAssistantPoint::Type type, const TPointD &defPosition, @@ -195,35 +197,43 @@ TAssistant::addPoint( //--------------------------------------------------------------------------------------------------- const TAssistantPoint& -TAssistant::getBasePoint() const +TAssistantBase::getBasePoint() const { assert(m_basePoint); return *m_basePoint; } //--------------------------------------------------------------------------------------------------- +TIntProperty* +TAssistantBase::createSpinProperty(const TStringId &id, int def, int min, int max, bool hasMax) { + if (!hasMax && max < def) max = def; + assert(min <= def && def <= max); + TIntProperty *property = new TIntProperty(id.str(), min, max, def, hasMax); + property->setSpinner(); + return property; +} + +//--------------------------------------------------------------------------------------------------- + void -TAssistant::addProperty(TProperty *p) +TAssistantBase::addProperty(TProperty *p) { m_properties.add(p); } //--------------------------------------------------------------------------------------------------- void -TAssistant::setTranslation(const TStringId &name, const QString &localName) const +TAssistantBase::setTranslation(const TStringId &name, const QString &localName) const { m_properties.getProperty(name)->setQStringName( localName ); } //--------------------------------------------------------------------------------------------------- void -TAssistant::updateTranslation() const { - setTranslation(m_idEnabled, tr("Enabled")); - setTranslation(m_idMagnetism, tr("Magnetism")); -} +TAssistantBase::updateTranslation() const + { setTranslation(m_idEnabled, tr("Enabled")); } //--------------------------------------------------------------------------------------------------- void -TAssistant::onSetDefaults() { +TAssistantBase::onSetDefaults() { setEnabled(true); - setMagnetism(1.0); for(TAssistantPointMap::iterator i = m_points.begin(); i != m_points.end(); ++i) i->second.position = i->second.defPosition; fixPoints(); @@ -233,13 +243,13 @@ TAssistant::onSetDefaults() { //--------------------------------------------------------------------------------------------------- void -TAssistant::fixPoints() +TAssistantBase::fixPoints() { onFixPoints(); } //--------------------------------------------------------------------------------------------------- bool -TAssistant::move(const TPointD &position) { +TAssistantBase::move(const TPointD &position) { TPointD d = position - getBasePoint().position; if (d != TPointD()) { for(TAssistantPointMap::iterator i = m_points.begin(); i != m_points.end(); ++i) @@ -253,7 +263,7 @@ TAssistant::move(const TPointD &position) { //--------------------------------------------------------------------------------------------------- bool -TAssistant::movePoint(const TStringId &name, const TPointD &position) { +TAssistantBase::movePoint(const TStringId &name, const TPointD &position) { TAssistantPointMap::iterator i = m_points.find(name); if (i != m_points.end() && i->second.position != position) { onMovePoint(i->second, position); @@ -265,7 +275,7 @@ TAssistant::movePoint(const TStringId &name, const TPointD &position) { //--------------------------------------------------------------------------------------------------- void -TAssistant::setPointSelection(const TStringId &name, bool selected) const { +TAssistantBase::setPointSelection(const TStringId &name, bool selected) const { if (const TAssistantPoint *p = findPoint(name)) p->selected = selected; } @@ -273,7 +283,7 @@ TAssistant::setPointSelection(const TStringId &name, bool selected) const { //--------------------------------------------------------------------------------------------------- void -TAssistant::setAllPointsSelection(bool selected) const { +TAssistantBase::setAllPointsSelection(bool selected) const { for(TAssistantPointMap::const_iterator i = points().begin(); i != points().end(); ++i) i->second.selected = selected; } @@ -281,7 +291,7 @@ TAssistant::setAllPointsSelection(bool selected) const { //--------------------------------------------------------------------------------------------------- void -TAssistant::onDataChanged(const TVariant &value) { +TAssistantBase::onDataChanged(const TVariant &value) { const TVariant& pointsData = data()[m_idPoints]; TVariantPathEntry entry; @@ -303,14 +313,13 @@ TAssistant::onDataChanged(const TVariant &value) { //--------------------------------------------------------------------------------------------------- void -TAssistant::onDataFieldChanged(const TStringId &name, const TVariant &value) { - updateProperty(name, value); -} +TAssistantBase::onDataFieldChanged(const TStringId &name, const TVariant &value) + { updateProperty(name, value); } //--------------------------------------------------------------------------------------------------- void -TAssistant::onAllDataChanged() { +TAssistantBase::onAllDataChanged() { const TVariant& pointsData = data()[m_idPoints]; for(TAssistantPointMap::iterator i = m_points.begin(); i != m_points.end(); ++i) { const TVariant& pointData = pointsData[i->first]; @@ -325,32 +334,31 @@ TAssistant::onAllDataChanged() { //--------------------------------------------------------------------------------------------------- void -TAssistant::onFixPoints() +TAssistantBase::onFixPoints() { } //--------------------------------------------------------------------------------------------------- void -TAssistant::onMovePoint(TAssistantPoint &point, const TPointD &position) +TAssistantBase::onMovePoint(TAssistantPoint &point, const TPointD &position) { point.position = position; } //--------------------------------------------------------------------------------------------------- void -TAssistant::onFixData() { +TAssistantBase::onFixData() { TVariant& pointsData = data()[m_idPoints]; for(TAssistantPointMap::const_iterator i = points().begin(); i != points().end(); ++i) { TVariant& pointData = pointsData[i->first]; pointData[m_idX].setDouble( i->second.position.x ); pointData[m_idY].setDouble( i->second.position.y ); } - setMagnetism( std::max(0.0, std::min(1.0, getMagnetism())) ); } //--------------------------------------------------------------------------------------------------- void -TAssistant::updateProperties() { +TAssistantBase::updateProperties() { const TVariantMap &map = data().getMap(); for(TVariantMap::const_iterator i = map.begin(); i != map.end(); ++i) if (i->first != m_idPoints) @@ -360,7 +368,7 @@ TAssistant::updateProperties() { //--------------------------------------------------------------------------------------------------- void -TAssistant::updateProperty(const TStringId &name, const TVariant &value) { +TAssistantBase::updateProperty(const TStringId &name, const TVariant &value) { TProperty *property = m_properties.getProperty(name); if (!property) return; @@ -371,6 +379,9 @@ TAssistant::updateProperty(const TStringId &name, const TVariant &value) { if (TDoubleProperty *doubleProperty = dynamic_cast(property)) { doubleProperty->setValue( value.getDouble() ); } else + if (TIntProperty *intProperty = dynamic_cast(property)) { + intProperty->setValue( (int)value.getDouble() ); + } else if (TStringProperty *stringProperty = dynamic_cast(property)) { stringProperty->setValue( to_wstring(value.getString()) ); } else @@ -382,7 +393,7 @@ TAssistant::updateProperty(const TStringId &name, const TVariant &value) { //--------------------------------------------------------------------------------------------------- void -TAssistant::onPropertyChanged(const TStringId &name) { +TAssistantBase::onPropertyChanged(const TStringId &name) { TProperty *property = m_properties.getProperty(name); if (!property) return; @@ -396,6 +407,9 @@ TAssistant::onPropertyChanged(const TStringId &name) { if (TDoubleProperty *doubleProperty = dynamic_cast(property)) { data()[name].setDouble( doubleProperty->getValue() ); } else + if (TIntProperty *intProperty = dynamic_cast(property)) { + data()[name].setDouble( (double)intProperty->getValue() ); + } else if (TStringProperty *stringProperty = dynamic_cast(property)) { data()[name].setString( to_string(stringProperty->getValue()) ); } else @@ -407,29 +421,31 @@ TAssistant::onPropertyChanged(const TStringId &name) { //--------------------------------------------------------------------------------------------------- double -TAssistant::getDrawingAlpha(bool enabled) const +TAssistantBase::getDrawingAlpha(bool enabled) const { return enabled && this->getEnabled() ? 0.5 : 0.25; } //--------------------------------------------------------------------------------------------------- double -TAssistant::getDrawingGridAlpha() const +TAssistantBase::getDrawingGridAlpha() const { return 0.2; } //--------------------------------------------------------------------------------------------------- void -TAssistant::drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize, double alpha) const { +TAssistantBase::drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize, double alpha) const { double colorBlack[4] = { 0.0, 0.0, 0.0, alpha }; double colorWhite[4] = { 1.0, 1.0, 1.0, alpha }; + + if (drawFlags & DRAW_ERROR) colorBlack[0] = 1; glPushAttrib(GL_ALL_ATTRIB_BITS); tglEnableBlending(); - tglEnableLineSmooth(true, 1.0 * line_width_scale); + tglEnableLineSmooth(true, 1.0 * lineWidthScale); TPointD d = p1 - p0; double k = norm2(d); if (k > TConsts::epsilon*TConsts::epsilon) { - k = 0.5*pixelSize*line_width_scale/sqrt(k); + k = 0.5*pixelSize*lineWidthScale/sqrt(k); d = TPointD(-k*d.y, k*d.x); glColor4dv(colorWhite); tglDrawSegment(p0 - d, p1 - d); @@ -442,7 +458,7 @@ TAssistant::drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize, //--------------------------------------------------------------------------------------------------- void -TAssistant::drawMark(const TPointD &p, const TPointD &normal, double pixelSize, double alpha) const { +TAssistantBase::drawMark(const TPointD &p, const TPointD &normal, double pixelSize, double alpha) const { TPointD d = normal*5*pixelSize; drawSegment(p - d,p + d, pixelSize, alpha); } @@ -450,7 +466,7 @@ TAssistant::drawMark(const TPointD &p, const TPointD &normal, double pixelSize, //--------------------------------------------------------------------------------------------------- void -TAssistant::drawDot(const TPointD &p, double alpha) const { +TAssistantBase::drawDot(const TPointD &p, double alpha) const { double colorBlack[4] = { 0.0, 0.0, 0.0, alpha }; double colorWhite[4] = { 1.0, 1.0, 1.0, alpha }; @@ -475,7 +491,7 @@ TAssistant::drawDot(const TPointD &p, double alpha) const { //--------------------------------------------------------------------------------------------------- void -TAssistant::drawPoint(const TAssistantPoint &point, double pixelSize) const { +TAssistantBase::drawPoint(const TAssistantPoint &point, double pixelSize) const { if (!point.visible) return; double radius = point.radius; @@ -508,7 +524,7 @@ TAssistant::drawPoint(const TAssistantPoint &point, double pixelSize) const { TPointD gridDy(0.0, pixelSize*radius); // back line - tglEnableLineSmooth(true, 2.0*width*line_width_scale); + tglEnableLineSmooth(true, 2.0*width*lineWidthScale); glColor4dv(colorWhite); if (point.type == TAssistantPoint::CircleCross) { tglDrawSegment(point.position - crossDx, point.position + crossDx); @@ -517,7 +533,7 @@ TAssistant::drawPoint(const TAssistantPoint &point, double pixelSize) const { tglDrawCircle(point.position, radius*pixelSize); // front line - glLineWidth(width * line_width_scale); + glLineWidth(width * lineWidthScale); glColor4dv(colorBlack); if (point.type == TAssistantPoint::CircleCross) { tglDrawSegment(point.position - crossDx, point.position + crossDx); @@ -552,19 +568,91 @@ TAssistant::drawPoint(const TAssistantPoint &point, double pixelSize) const { //--------------------------------------------------------------------------------------------------- void -TAssistant::getGuidelines(const TPointD &position, const TAffine &toTool, TGuidelineList &outGuidelines) const - { } +TAssistantBase::drawIndex(const TPointD &p, int index, bool selected, double pixelSize) const { + static const int segments[7][4] = { + { 0, 2, 1, 2 }, // A + { 1, 1, 1, 2 }, // B + A + + { 1, 0, 1, 1 }, // C F B + { 0, 0, 1, 0 }, // D + G + + { 0, 0, 0, 1 }, // E E C + { 0, 1, 0, 2 }, // F + D + + { 0, 1, 1, 1 } }; // G + + static const int glyphs[][7] = { + // A B C D E F G + {1,1,1,1,1,1,0}, // 0 + {0,1,1,0,0,0,0}, // 1 + {1,1,0,1,1,0,1}, // 2 + {1,1,1,1,0,0,1}, // 3 + {0,1,1,0,0,1,1}, // 4 + {1,0,1,1,0,1,1}, // 5 + {1,0,1,1,1,1,1}, // 6 + {1,1,1,0,0,1,0}, // 7 + {1,1,1,1,1,1,1}, // 8 + {1,1,1,1,0,1,1} }; // 9 + + if (index < 0) index = 0; + + int len = 0; + int digits[16] = {}; + for(int i = index; i; i /= 10) + digits[len++] = i%10; + if (!len) len = 1; + + double w = 5, h = 5, d = 0.5, dx = w+2; + double alpha = 0.5; + double colorBlack[4] = { 0.0, 0.0, 0.0, alpha }; + double colorWhite[4] = { 1.0, 1.0, 1.0, alpha }; + if (selected) colorBlack[2] = 1.0; + + glPushAttrib(GL_ALL_ATTRIB_BITS); + tglEnableBlending(); + tglEnableLineSmooth(true, 1.0 * lineWidthScale); + double k = 0.5*pixelSize*lineWidthScale; + + double y = p.y; + for(int i = 0; i < len; ++i) { + double x = p.x + dx*(len-i-1); + const int *g = glyphs[digits[i]]; + for(int i = 0; i < 7; ++i) { + if (!g[i]) continue; + const int *s = segments[i]; + if (s[0] == s[2]) { + // vertical + glColor4dv(colorWhite); + tglDrawSegment( + TPointD(x + s[0]*w + k, y + s[1]*h + d), + TPointD(x + s[2]*w + k, y + s[3]*h - d) ); + glColor4dv(colorBlack); + tglDrawSegment( + TPointD(x + s[0]*w - k, y + s[1]*h + d), + TPointD(x + s[2]*w - k, y + s[3]*h - d) ); + } else { + // horisontal + glColor4dv(colorWhite); + tglDrawSegment( + TPointD(x + s[0]*w + d, y + s[1]*h + k), + TPointD(x + s[2]*w - d, y + s[3]*h + k) ); + glColor4dv(colorBlack); + tglDrawSegment( + TPointD(x + s[0]*w + d, y + s[1]*h - k), + TPointD(x + s[2]*w - d, y + s[3]*h - k) ); + } + } + } + glPopAttrib(); +} //--------------------------------------------------------------------------------------------------- void -TAssistant::draw(TToolViewer *viewer, bool enabled) const +TAssistantBase::draw(TToolViewer*, bool) const { } //--------------------------------------------------------------------------------------------------- void -TAssistant::drawEdit(TToolViewer *viewer) const { +TAssistantBase::drawEdit(TToolViewer *viewer) const { // paint all points draw(viewer); double pixelSize = sqrt(tglGetPixelSize2()); @@ -574,6 +662,56 @@ TAssistant::drawEdit(TToolViewer *viewer) const { //--------------------------------------------------------------------------------------------------- +void +TAssistantBase::drawEdit(TToolViewer *viewer, int index) const { + drawEdit(viewer); + drawIndex( + getBasePoint().position + TPointD(8, 8), + index, getBasePoint().selected, sqrt(tglGetPixelSize2()) ); +} + + +//************************************************************************ +// TAssistant implementation +//************************************************************************ + +TAssistant::TAssistant(TMetaObject &object): + TAssistantBase(object), + m_idMagnetism("magnetism") + { addProperty( new TDoubleProperty(m_idMagnetism.str(), 0.0, 1.0, getMagnetism()) ); } + +//--------------------------------------------------------------------------------------------------- + +void +TAssistant::updateTranslation() const { + TAssistantBase::updateTranslation(); + setTranslation(m_idMagnetism, tr("Magnetism")); +} + +//--------------------------------------------------------------------------------------------------- + +void +TAssistant::onSetDefaults() { + setMagnetism(1.0); + TAssistantBase::onSetDefaults(); +} + +//--------------------------------------------------------------------------------------------------- + +void +TAssistant::onFixData() { + TAssistantBase::onFixData(); + setMagnetism( std::max(0.0, std::min(1.0, getMagnetism())) ); +} + +//--------------------------------------------------------------------------------------------------- + +void +TAssistant::getGuidelines(const TPointD&, const TAffine&, TGuidelineList&) const + { } + +//--------------------------------------------------------------------------------------------------- + bool TAssistant::calcPerspectiveStep( double minStep, @@ -669,11 +807,11 @@ TAssistant::scanAssistants( if (!enabledOnly || assistant->getEnabled()) { found = true; + if (!doSomething) return 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(); diff --git a/toonz/sources/tnztools/assistants/assistantellipse.cpp b/toonz/sources/tnztools/assistants/assistantellipse.cpp index db6055ee3e..29e2060417 100644 --- a/toonz/sources/tnztools/assistants/assistantellipse.cpp +++ b/toonz/sources/tnztools/assistants/assistantellipse.cpp @@ -57,6 +57,7 @@ class TAssistantEllipse final : public TAssistant { { return tr("Ellipse"); } void updateTranslation() const override { + TAssistant::updateTranslation(); setTranslation(m_idRestricktA, tr("Restrict A")); setTranslation(m_idRestricktB, tr("Restrict B")); setTranslation(m_idRepeat, tr("Repeat")); @@ -448,7 +449,6 @@ class TAssistantEllipse final : public TAssistant { bool grid = getGrid(); bool ruler = getRestrictA() && getRestrictB(); bool concentric = !getRestrictA() && !getRestrictB(); - bool perspective = getPerspective(); drawSegment( ellipseMatrix*TPointD(-crossSize, 0.0), ellipseMatrix*TPointD( crossSize, 0.0), pixelSize, alpha); diff --git a/toonz/sources/tnztools/assistants/assistantline.cpp b/toonz/sources/tnztools/assistants/assistantline.cpp index d87134f12a..f0a1fd6cd4 100644 --- a/toonz/sources/tnztools/assistants/assistantline.cpp +++ b/toonz/sources/tnztools/assistants/assistantline.cpp @@ -52,6 +52,7 @@ class TAssistantLine final : public TAssistant { { return tr("Line"); } void updateTranslation() const override { + TAssistant::updateTranslation(); setTranslation(m_idRestricktA, tr("Restrict A")); setTranslation(m_idRestricktB, tr("Restrict B")); setTranslation(m_idParallel, tr("Parallel")); diff --git a/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp b/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp index b660e6019b..61b0792a83 100644 --- a/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp +++ b/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp @@ -51,6 +51,7 @@ class TAssistantVanishingPoint final : public TAssistant { { return tr("Vanishing Point"); } void updateTranslation() const override { + TAssistant::updateTranslation(); setTranslation(m_idPassThrough, tr("Pass Through")); setTranslation(m_idGrid, tr("Grid")); setTranslation(m_idPerspective, tr("Perspective")); diff --git a/toonz/sources/tnztools/assistants/replicatoraffine.cpp b/toonz/sources/tnztools/assistants/replicatoraffine.cpp new file mode 100644 index 0000000000..f637c9072a --- /dev/null +++ b/toonz/sources/tnztools/assistants/replicatoraffine.cpp @@ -0,0 +1,341 @@ + + +// TnzTools includes +#include +#include + + +// TnzCore includes +#include + + +//***************************************************************************************** +// TReplicatorAffine implementation +//***************************************************************************************** + +class TReplicatorAffine final : public TReplicator { + Q_DECLARE_TR_FUNCTIONS(TReplicatorAffine) +public: + const TStringId m_idFixScale; + const TStringId m_idFixAspect; + const TStringId m_idFixAngle; + const TStringId m_idFixSkew; + const TStringId m_idCount; + const TStringId m_idCountInv; + const TStringId m_idPressure; + +protected: + TAssistantPoint &m_center0; + TAssistantPoint &m_a0; + TAssistantPoint &m_b0; + TAssistantPoint &m_center1; + TAssistantPoint &m_a1; + TAssistantPoint &m_b1; + +public: + TReplicatorAffine(TMetaObject &object): + TReplicator(object), + m_idFixScale("fixScale"), + m_idFixAspect("fixAspect"), + m_idFixAngle("fixAngle"), + m_idFixSkew("fixSkew"), + m_idCount("count"), + m_idCountInv("countInv"), + m_idPressure("pressure"), + m_center0( addPoint("center0", TAssistantPoint::CircleCross) ), + m_a0 ( addPoint("a0", TAssistantPoint::CircleFill, TPointD(40, 0)) ), + m_b0 ( addPoint("b0", TAssistantPoint::Circle, TPointD( 0, 40)) ), + m_center1( addPoint("center1", TAssistantPoint::Circle, TPointD(50, 0)) ), + m_a1 ( addPoint("a1", TAssistantPoint::CircleFill, TPointD(90, 0)) ), + m_b1 ( addPoint("b1", TAssistantPoint::Circle, TPointD(50, 40)) ) + { + addProperty( new TBoolProperty(m_idFixScale.str(), getFixScale()) ); + addProperty( new TBoolProperty(m_idFixAspect.str(), getFixAspect()) ); + addProperty( new TBoolProperty(m_idFixAngle.str(), getFixAngle()) ); + addProperty( new TBoolProperty(m_idFixSkew.str(), getFixSkew()) ); + addProperty( createCountProperty(m_idCount, getCount(), 0) ); + addProperty( createCountProperty(m_idCountInv, getCountInv(), 0) ); + addProperty( new TDoubleProperty(m_idPressure.str(), 0.0, 2.0, getPressure()) ); + } + + + static QString getLocalName() + { return tr("Replicator Affine"); } + + + void updateTranslation() const override { + TReplicator::updateTranslation(); + setTranslation(m_idFixScale, tr("Fix Scale")); + setTranslation(m_idFixAspect, tr("Fix Aspect")); + setTranslation(m_idFixAngle, tr("Fix Angle")); + setTranslation(m_idFixSkew, tr("Fix Skew")); + setTranslation(m_idCount, tr("Count")); + setTranslation(m_idCountInv, tr("Inv. Count")); + setTranslation(m_idPressure, tr("Pressure")); + } + + + inline bool getFixScale() const + { return data()[m_idFixScale].getBool(); } + inline bool getFixAspect() const + { return data()[m_idFixAspect].getBool(); } + inline bool getFixAngle() const + { return data()[m_idFixAngle].getBool(); } + inline bool getFixSkew() const + { return data()[m_idFixSkew].getBool(); } + inline int getCount() const + { return (int)data()[m_idCount].getDouble(); } + inline int getCountInv() const + { return (int)data()[m_idCountInv].getDouble(); } + inline double getPressure() const + { return data()[m_idPressure].getDouble(); } + +protected: + inline void setCount(int x) + { if (getCount() != (double)x) data()[m_idCount].setDouble((double)x); } + inline void setCountInv(int x) + { if (getCountInv() != (double)x) data()[m_idCountInv].setDouble((double)x); } + inline void setPressure(double x) + { if (getPressure() != x) data()[m_idPressure].setDouble(x); } + + + void onSetDefaults() override { + setCount(1); + setPressure(1); + TReplicator::onSetDefaults(); + } + + + void onFixData() override { + TReplicator::onFixData(); + setCount( std::max(0, std::min(multiplierSoftLimit - 1, getCount())) ); + setCountInv( std::max(0, std::min(multiplierSoftLimit - 1, getCountInv())) ); + setPressure( std::max(0.0, std::min(2.0, getPressure())) ); + } + + + TAffine getAffine(const TAffine &toTool = TAffine()) const { + TPointD c, x, y; + c = toTool*m_center0.position; + x = toTool*m_a0.position - c; + y = toTool*m_b0.position - c; + TAffine t0( x.x, y.x, c.x, + x.y, y.y, c.y ); + c = toTool*m_center1.position; + x = toTool*m_a1.position - c; + y = toTool*m_b1.position - c; + TAffine t1( x.x, y.x, c.x, + x.y, y.y, c.y ); + return t1*t0.inv(); + } + + + bool isMirror() const { + TPointD da0 = m_a0.position - m_center0.position; + TPointD db0 = m_b0.position - m_center0.position; + TPointD pa0(-da0.y, da0.x); + + TPointD da1 = m_a1.position - m_center1.position; + TPointD db1 = m_b1.position - m_center1.position; + TPointD pa1(-da1.y, da0.x); + + return (pa0*db0 < 0) != (pa1*db1 < 0); + } + + + void doFixPoints(int mirror) { + TPointD &c0 = m_center0.position; + TPointD &a0 = m_a0.position; + TPointD &b0 = m_b0.position; + + TPointD &c1 = m_center1.position; + TPointD &a1 = m_a1.position; + TPointD &b1 = m_b1.position; + + if (getFixScale()) { + double la0 = tdistance(a0, c0); + double lb0 = tdistance(b0, c0); + double la1 = tdistance(a1, c1); + double lb1 = tdistance(b1, c1); + a1 = la1 > TConsts::epsilon + ? (a1 - c1)*(la0/la1) + c1 + : a0 - c0 + c1; + b1 = lb1 > TConsts::epsilon + ? (b1 - c1)*(lb0/lb1) + c1 + : b0 - c0 + c1; + } else + if (getFixAspect()) { + double la0 = tdistance(a0, c0); + double lb0 = tdistance(b0, c0); + double la1 = tdistance(a1, c1); + double lb1 = tdistance(b1, c1); + if (la0 > TConsts::epsilon) + b1 = lb1 > TConsts::epsilon + ? (b1 - c1)*(lb0/lb1*la1/la0) + c1 + : (b0 - c0)*(la1/la0) + c1; + } + + if (getFixAngle()) { + double la0 = tdistance(a0, c0); + double lb0 = tdistance(b0, c0); + double la1 = tdistance(a1, c1); + double lb1 = tdistance(b1, c1); + if (la0 > TConsts::epsilon) + a1 = (a0 - c0)*(la1/la0) + c1; + if (lb0 > TConsts::epsilon) + b1 = (b0 - c0)*(lb1/lb0) + c1; + } else + if (getFixSkew()) { + TPointD da0 = a0 - c0; + TPointD pa0(-da0.y, da0.x); + double x = (b0 - c0)*da0; + double y = (b0 - c0)*pa0; + + TPointD da1 = a1 - c1; + TPointD db1 = b1 - c1; + TPointD pa1(-da1.y, da1.x); + if (mirror) y *= mirror; else + if ((pa1*db1 < 0) != (y < 0)) y = -y; + + TPointD p = da1*x + pa1*y; + double l = norm2(p); + if (l > TConsts::epsilon*TConsts::epsilon) + b1 = p*sqrt(norm2(db1)/l) + c1; + } + } + + + void onFixPoints() override { + doFixPoints(0); + } + + + void onMovePoint(TAssistantPoint &point, const TPointD &position) override { + TPointD pc0 = m_center0.position; + TPointD pc1 = m_center1.position; + int mirror = &point == &m_b1 ? 0 : (isMirror() ? -1 : 1); + point.position = position; + if (&point == &m_center0) { + TPointD d = m_center0.position - pc0; + m_a0.position += d; + m_b0.position += d; + m_a1.position += d; + m_b1.position += d; + m_center1.position += d; + } else + if (&point == &m_center1) { + TPointD d = m_center1.position - pc1; + m_a1.position += d; + m_b1.position += d; + } + doFixPoints(mirror); + } + + + void onDataFieldChanged(const TStringId &name, const TVariant &value) override { + TReplicator::onDataFieldChanged(name, value); + if ( name == m_idFixScale + || name == m_idFixAspect + || name == m_idFixAngle + || name == m_idFixSkew ) + fixPoints(); + } + +public: + int getMultipler() const override + { return getCount() + 1; } + + + void getPoints(const TAffine &toTool, PointList &points) const override { + points.reserve(points.size() * getMultipler()); + int pointsCount = (int)points.size(); + + TAffine aff = getAffine(toTool); + struct { + int count; + TAffine aff; + } t[] = { + { getCount(), aff }, + { getCountInv(), aff.inv() } }; + + for(int i = 0; i < 2; ++i) { + TAffine a; + for(int j = 0; j < t[i].count; ++j) { + a = t[i].aff * a; + transformPoints(a, points, pointsCount); + } + } + } + + + void getModifiers( + const TAffine &toTool, + TInputModifier::List &outModifiers ) const override + { + TAffine aff = getAffine(toTool); + double pressure = getPressure(); + double pressureInv = fabs(pressure) > TConsts::epsilon ? 1/pressure : 0; + + struct { + int count; + TAffine aff; + double pressure; + } t[] = { + { getCount(), aff, pressure }, + { getCountInv(), aff.inv(), pressureInv }, + }; + + TModifierClone *modifier = new TModifierClone(); + for(int i = 0; i < 2; ++i) { + TTrackTransform tt; + for(int j = 0; j < t[i].count; ++j) { + tt.transform = t[i].aff * tt.transform; + tt.tiltTransform = TTrackTransform::makeTiltTransform(tt.transform); + tt.pressureScale *= t[i].pressure; + modifier->transforms.push_back(tt); + } + } + + outModifiers.push_back(modifier); + } + + + void draw(TToolViewer*, bool enabled) const override { + double alpha = getDrawingAlpha(enabled); + double gridAlpha = getDrawingGridAlpha(); + double pixelSize = sqrt(tglGetPixelSize2()); + + TPointD c = m_center0.position; + TPointD a = m_a0.position; + TPointD b = m_b0.position; + TAffine aff = getAffine(); + + // draw base + drawSegment(c, a, pixelSize, alpha); + drawSegment(c, b, pixelSize, alpha); + + // draw clones + TAffine t; + for(int i = getCount(); i > 0; --i) { + t = aff * t; + drawSegment(t*c, t*a, pixelSize, gridAlpha); + drawSegment(t*c, t*b, pixelSize, gridAlpha); + } + + // draw inverted clones + t = TAffine(); + aff = aff.inv(); + for(int i = getCountInv(); i > 0; --i) { + t = aff * t; + drawSegment(t*c, t*a, pixelSize, gridAlpha); + drawSegment(t*c, t*b, pixelSize, gridAlpha); + } + } +}; + + +//***************************************************************************************** +// Registration +//***************************************************************************************** + +static TAssistantTypeT replicatorAffine("replicatorAffine"); diff --git a/toonz/sources/tnztools/assistants/replicatorgrid.cpp b/toonz/sources/tnztools/assistants/replicatorgrid.cpp new file mode 100644 index 0000000000..b276024f69 --- /dev/null +++ b/toonz/sources/tnztools/assistants/replicatorgrid.cpp @@ -0,0 +1,335 @@ + + +// TnzTools includes +#include +#include + + +// TnzCore includes +#include + + +//***************************************************************************************** +// TReplicatorGrid implementation +//***************************************************************************************** + +class TReplicatorGrid final : public TReplicator { + Q_DECLARE_TR_FUNCTIONS(TReplicatorGrid) +public: + const TStringId m_idFixAngle; + const TStringId m_idFixSkew; + const TStringId m_idMirrorA; + const TStringId m_idMirrorB; + const TStringId m_idCountA; + const TStringId m_idCountAInv; + const TStringId m_idCountB; + const TStringId m_idCountBInv; + +protected: + TAssistantPoint &m_center; + TAssistantPoint &m_a; + TAssistantPoint &m_b; + +public: + TReplicatorGrid(TMetaObject &object): + TReplicator(object), + m_idFixAngle("fixAngle"), + m_idFixSkew("fixSkew"), + m_idMirrorA("mirrorA"), + m_idMirrorB("mirrorB"), + m_idCountA("countA"), + m_idCountAInv("countAInv"), + m_idCountB("countB"), + m_idCountBInv("countBInv"), + m_center( addPoint("center", TAssistantPoint::CircleCross) ), + m_a ( addPoint("a", TAssistantPoint::CircleFill, TPointD(80, 0)) ), + m_b ( addPoint("b", TAssistantPoint::Circle, TPointD( 0, -80)) ) + { + addProperty( new TBoolProperty(m_idFixAngle.str(), getFixAngle()) ); + addProperty( new TBoolProperty(m_idFixSkew.str(), getFixSkew()) ); + addProperty( new TBoolProperty(m_idMirrorA.str(), getMirrorA()) ); + addProperty( new TBoolProperty(m_idMirrorB.str(), getMirrorB()) ); + addProperty( createCountProperty(m_idCountA, getCountA(), 1) ); + addProperty( createCountProperty(m_idCountAInv, getCountAInv(), 0) ); + addProperty( createCountProperty(m_idCountB, getCountB(), 1) ); + addProperty( createCountProperty(m_idCountBInv, getCountBInv(), 0) ); + } + + + static QString getLocalName() + { return tr("Replicator Grid"); } + + + void updateTranslation() const override { + TReplicator::updateTranslation(); + setTranslation(m_idFixAngle, tr("Fix Angle")); + setTranslation(m_idFixSkew, tr("Fix Skew")); + setTranslation(m_idMirrorA, tr("Mirror A")); + setTranslation(m_idMirrorB, tr("Mirror B")); + setTranslation(m_idCountA, tr("Count A")); + setTranslation(m_idCountAInv, tr("Inv. Count A")); + setTranslation(m_idCountB, tr("Count B")); + setTranslation(m_idCountBInv, tr("Inv. Count B")); + } + + + inline bool getFixAngle() const + { return data()[m_idFixAngle].getBool(); } + inline bool getFixSkew() const + { return data()[m_idFixSkew].getBool(); } + inline bool getMirrorA() const + { return data()[m_idMirrorA].getBool(); } + inline bool getMirrorB() const + { return data()[m_idMirrorB].getBool(); } + inline int getCountA() const + { return (int)data()[m_idCountA].getDouble(); } + inline int getCountAInv() const + { return (int)data()[m_idCountAInv].getDouble(); } + inline int getCountB() const + { return (int)data()[m_idCountB].getDouble(); } + inline int getCountBInv() const + { return (int)data()[m_idCountBInv].getDouble(); } + +protected: + inline void setCountA(int x) + { if (getCountA() != (double)x) data()[m_idCountA].setDouble((double)x); } + inline void setCountAInv(int x) + { if (getCountAInv() != (double)x) data()[m_idCountAInv].setDouble((double)x); } + inline void setCountB(int x) + { if (getCountB() != (double)x) data()[m_idCountB].setDouble((double)x); } + inline void setCountBInv(int x) + { if (getCountBInv() != (double)x) data()[m_idCountBInv].setDouble((double)x); } + + + void onSetDefaults() override { + setCountA(3); + setCountAInv(2); + setCountB(2); + setCountBInv(1); + TReplicator::onSetDefaults(); + } + + + void onFixData() override { + TReplicator::onFixData(); + setCountA( std::max(1, std::min(multiplierSoftLimit, getCountA())) ); + setCountAInv( std::max(0, std::min(multiplierSoftLimit, getCountAInv())) ); + setCountB( std::max(1, std::min(multiplierSoftLimit, getCountB())) ); + setCountBInv( std::max(0, std::min(multiplierSoftLimit, getCountBInv())) ); + } + + + void onFixPoints() override { + TPointD &c = m_center.position; + TPointD &a = m_a.position; + TPointD &b = m_b.position; + + if (getFixAngle()) { + double la = tdistance(a, c); + double lb = tdistance(b, c); + a = TPointD(a.x < c.x ? -la : la, 0) + c; + b = TPointD(0, b.y < c.y ? -lb : lb) + c; + } else + if (getFixSkew()) { + TPointD pa(c.y - a.y, a.x - c.x); + double l = norm2(pa); + if (l > TConsts::epsilon*TConsts::epsilon) { + TPointD db = b - c; + double k = sqrt(norm2(db)/l); + if (db*pa < 0) k = -k; + b = pa*k + c; + } + } + } + + + void onMovePoint(TAssistantPoint &point, const TPointD &position) override { + TPointD pc = m_center.position; + TPointD pa = m_a.position; + point.position = position; + if (&point == &m_center) { + TPointD d = m_center.position - pc; + m_a.position += d; + m_b.position += d; + } else { + fixPoints(); + } + } + + + void onDataFieldChanged(const TStringId &name, const TVariant &value) override { + TReplicator::onDataFieldChanged(name, value); + if ( name == m_idFixAngle + || name == m_idFixSkew ) + fixPoints(); + } + + +public: + int getMultipler() const override { + return (getCountA() + getCountAInv()) + * (getCountB() + getCountBInv()) + * (getMirrorA() ? 2 : 1) + * (getMirrorB() ? 2 : 1); + } + + + void getPoints(const TAffine &toTool, PointList &points) const override { + points.reserve(points.size() * getMultipler()); + int pointsCount = (int)points.size(); + + TPointD c = toTool*m_center.position; + TPointD da = toTool*m_a.position - c; + TPointD db = toTool*m_b.position - c; + + bool mirrorA = getMirrorA(); + bool mirrorB = getMirrorB(); + TAffine t, ma, mb, mc; + if (mirrorA || mirrorB) { + if (fabs(da*db) > TConsts::epsilon) { + t = TAffine( da.x, db.x, c.x, + da.y, db.y, c.y ).inv(); + ma = TAffine( -da.x, db.x, c.x, + -da.y, db.y, c.y )*t; + mb = TAffine( da.x, -db.x, c.x, + da.y, -db.y, c.y )*t; + mc = TAffine( -da.x, -db.x, c.x, + -da.y, -db.y, c.y )*t; + } else { + mirrorA = mirrorB = false; + } + } + + int a1 = getCountA(); + int b1 = getCountB(); + int a0 = -getCountAInv(); + int b0 = -getCountBInv(); + + for(int ib = b0; ib < b1; ++ib) + for(int ia = a0; ia < a1; ++ia) { + TPointD o = da*ia + db*ib; + if (ia || ib) + transformPoints( + TAffine( 1, 0, o.x, + 0, 1, o.y ), + points, pointsCount ); + if (mirrorA) + transformPoints( + TAffine( ma.a11, ma.a12, ma.a13 + o.x, + ma.a21, ma.a22, ma.a23 + o.y ), + points, pointsCount ); + if (mirrorB) + transformPoints( + TAffine( mb.a11, mb.a12, mb.a13 + o.x, + mb.a21, mb.a22, mb.a23 + o.y ), + points, pointsCount ); + if (mirrorA && mirrorB) + transformPoints( + TAffine( mc.a11, mc.a12, mc.a13 + o.x, + mc.a21, mc.a22, mc.a23 + o.y ), + points, pointsCount ); + } + } + + + void getModifiers( + const TAffine &toTool, + TInputModifier::List &outModifiers ) const override + { + TPointD c = toTool*m_center.position; + TPointD da = toTool*m_a.position - c; + TPointD db = toTool*m_b.position - c; + + bool mirrorA = getMirrorA(); + bool mirrorB = getMirrorB(); + TAffine t, ma, mb, mc; + if (mirrorA || mirrorB) { + if (fabs(da*db) > TConsts::epsilon) { + t = TAffine( da.x, db.x, c.x, + da.y, db.y, c.y ).inv(); + ma = TAffine( -da.x, db.x, c.x, + -da.y, db.y, c.y )*t; + mb = TAffine( da.x, -db.x, c.x, + da.y, -db.y, c.y )*t; + mc = TAffine( -da.x, -db.x, c.x, + -da.y, -db.y, c.y )*t; + } else { + mirrorA = mirrorB = false; + } + } + + int a1 = getCountA(); + int b1 = getCountB(); + int a0 = -getCountAInv(); + int b0 = -getCountBInv(); + + TModifierClone *modifier = new TModifierClone(); + for(int ib = b0; ib < b1; ++ib) + for(int ia = a0; ia < a1; ++ia) { + TPointD o = da*ia + db*ib; + if (ia || ib) + modifier->transforms.push_back(TTrackTransform( + TAffine( 1, 0, o.x, + 0, 1, o.y ) )); + if (mirrorA) + modifier->transforms.push_back(TTrackTransform( + TAffine( ma.a11, ma.a12, ma.a13 + o.x, + ma.a21, ma.a22, ma.a23 + o.y ) )); + if (mirrorB) + modifier->transforms.push_back(TTrackTransform( + TAffine( mb.a11, mb.a12, mb.a13 + o.x, + mb.a21, mb.a22, mb.a23 + o.y ) )); + if (mirrorA && mirrorB) + modifier->transforms.push_back(TTrackTransform( + TAffine( mc.a11, mc.a12, mc.a13 + o.x, + mc.a21, mc.a22, mc.a23 + o.y ) )); + } + outModifiers.push_back(modifier); + } + + + void draw(TToolViewer*, bool enabled) const override { + double alpha = getDrawingAlpha(enabled); + double gridAlpha = getDrawingGridAlpha(); + double pixelSize = sqrt(tglGetPixelSize2()); + + TPointD c = m_center.position; + TPointD a = m_a.position; + TPointD b = m_b.position; + TPointD da = a - c; + TPointD db = b - c; + + int a1 = getCountA(); + int b1 = getCountB(); + int a0 = -getCountAInv(); + int b0 = -getCountBInv(); + + bool mirrorA = getMirrorA(); + bool mirrorB = getMirrorB(); + + // draw base + drawSegment(c, a, pixelSize, alpha); + drawSegment(c, b, pixelSize, alpha); + + // draw clones + for(int ib = b0; ib < b1; ++ib) + for(int ia = a0; ia < a1; ++ia) { + TPointD o = c + da*ia + db*ib; + if (ia || ib) { + drawSegment(o, o + da*0.2, pixelSize, gridAlpha); + drawSegment(o, o + db*0.2, pixelSize, gridAlpha); + } + if (mirrorA && (ib || ia != 1)) + drawSegment(o - da*0.2, o, pixelSize, gridAlpha); + if (mirrorB && (ia || ib != 1)) + drawSegment(o - db*0.2, o, pixelSize, gridAlpha); + } + } +}; + + +//***************************************************************************************** +// Registration +//***************************************************************************************** + +static TAssistantTypeT replicatorGrid("replicatorGrid"); diff --git a/toonz/sources/tnztools/assistants/replicatorjitter.cpp b/toonz/sources/tnztools/assistants/replicatorjitter.cpp new file mode 100644 index 0000000000..befb3299e1 --- /dev/null +++ b/toonz/sources/tnztools/assistants/replicatorjitter.cpp @@ -0,0 +1,149 @@ + + +// TnzTools includes +#include +#include + + +// TnzCore includes +#include + + +//***************************************************************************************** +// TReplicatorJitter implementation +//***************************************************************************************** + +class TReplicatorJitter final : public TReplicator { + Q_DECLARE_TR_FUNCTIONS(TReplicatorJitter) +public: + const TStringId m_idSkipFirst; + const TStringId m_idPeriod; + const TStringId m_idAmplitude; + +protected: + TAssistantPoint &m_center; + +public: + TReplicatorJitter(TMetaObject &object): + TReplicator(object), + m_idSkipFirst("skipFirst"), + m_idPeriod("period"), + m_idAmplitude("m_idAmplitude"), + m_center( addPoint("center", TAssistantPoint::CircleCross) ) + { + addProperty( createSpinProperty(m_idSkipFirst, getSkipFirst(), 0) ); + + TDoubleProperty *p; + + p = new TDoubleProperty(m_idPeriod.str(), 0.0, 1000, getPeriod()); + p->setNonLinearSlider(); + addProperty(p); + + p = new TDoubleProperty(m_idAmplitude.str(), 0.0, 1000, getAmplitude()); + p->setNonLinearSlider(); + addProperty(p); + } + + + static QString getLocalName() + { return tr("Jitter"); } + + + void updateTranslation() const override { + TReplicator::updateTranslation(); + setTranslation(m_idSkipFirst, tr("Skip First Tracks")); + setTranslation(m_idPeriod, tr("Period")); + setTranslation(m_idAmplitude, tr("Amplitude")); + } + + + inline int getSkipFirst() const + { return (int)data()[m_idSkipFirst].getDouble(); } + inline double getPeriod() const + { return data()[m_idPeriod].getDouble(); } + inline double getAmplitude() const + { return data()[m_idAmplitude].getDouble(); } + +protected: + inline void setSkipFirst(int x) + { if (getSkipFirst() != (double)x) data()[m_idSkipFirst].setDouble((double)x); } + inline void setPeriod(double x) + { if (getPeriod() != x) data()[m_idPeriod].setDouble(x); } + inline void setAmplitude(double x) + { if (getAmplitude() != x) data()[m_idAmplitude].setDouble(x); } + + + void onSetDefaults() override { + setPeriod(30); + setAmplitude(10); + TReplicator::onSetDefaults(); + } + + + void onFixData() override { + TReplicator::onFixData(); + setPeriod( std::max(0.0, std::min(1000.0, getPeriod())) ); + setAmplitude( std::max(0.0, std::min(1000.0, getAmplitude())) ); + } + + + double getScale(const TAffine &a) const { + return sqrt( a.a11*a.a11 + a.a12*a.a12 + + a.a21*a.a21 + a.a22*a.a22 )/2; + } + +public: + + void getPoints(const TAffine &toTool, PointList &points) const override { + int skipFirst = getSkipFirst(); + if (skipFirst < 0) skipFirst = 0; + if (skipFirst >= (int)points.size()) return; + + double scale = getScale(toTool); + double period = getPeriod()*scale; + double amplitude = getAmplitude()*scale; + if (!(period > TConsts::epsilon && amplitude > TConsts::epsilon)) { + int seedX = 0; + int seedY = 7722441; + for(PointList::iterator i = points.begin() + skipFirst; i != points.end(); ++i) { + i->x += TModifierJitter::func(seedX, 0)*amplitude; + i->y += TModifierJitter::func(seedY, 0)*amplitude; + ++seedX, ++seedY; + } + } + } + + + void getModifiers( + const TAffine &toTool, + TInputModifier::List &outModifiers ) const override + { + double scale = getScale(toTool); + outModifiers.push_back(new TModifierJitter( + getPeriod()*scale, + getAmplitude()*scale, + getSkipFirst() )); + } + + + void draw(TToolViewer*, bool enabled) const override { + double alpha = getDrawingAlpha(enabled); + double pixelSize = sqrt(tglGetPixelSize2()); + + TPointD c = m_center.position; + double h = getPeriod()/2; + double q = h/2; + double a = getAmplitude(); + + drawSegment(TPointD(c.x-h, c.y ), TPointD(c.x-q, c.y+a), pixelSize, alpha); + drawSegment(TPointD(c.x-q, c.y+a), TPointD(c.x+q, c.y-a), pixelSize, alpha); + drawSegment(TPointD(c.x+q, c.y-a), TPointD(c.x+h, c.y ), pixelSize, alpha); + } +}; + + +//***************************************************************************************** +// Registration +//***************************************************************************************** + +static TAssistantTypeT replicatorJitter("replicatorJitter"); diff --git a/toonz/sources/tnztools/assistants/replicatormirror.cpp b/toonz/sources/tnztools/assistants/replicatormirror.cpp new file mode 100644 index 0000000000..0c608c8e2d --- /dev/null +++ b/toonz/sources/tnztools/assistants/replicatormirror.cpp @@ -0,0 +1,161 @@ + + +// TnzTools includes +#include +#include +#include + + +// TnzCore includes +#include + + +//***************************************************************************************** +// TReplicatorMirror implementation +//***************************************************************************************** + +class TReplicatorMirror final : public TReplicator { + Q_DECLARE_TR_FUNCTIONS(TReplicatorMirror) +public: + const TStringId m_idDiscreteAngle; + const TStringId m_idPressure; + +protected: + TAssistantPoint &m_a; + TAssistantPoint &m_b; + +public: + TReplicatorMirror(TMetaObject &object): + TReplicator(object), + m_idDiscreteAngle("discreteAngle"), + m_idPressure("pressure"), + m_a( addPoint("a", TAssistantPoint::CircleCross) ), + m_b( addPoint("b", TAssistantPoint::Circle, TPointD(0, -50)) ) + { + addProperty( new TBoolProperty(m_idDiscreteAngle.str(), getDiscreteAngle()) ); + addProperty( new TDoubleProperty(m_idPressure.str(), 0.0, 2.0, getPressure()) ); + } + + + static QString getLocalName() + { return tr("Replicator Mirror"); } + + + void updateTranslation() const override { + TReplicator::updateTranslation(); + setTranslation(m_idDiscreteAngle, tr("Discrete Angle")); + setTranslation(m_idPressure, tr("Pressure")); + } + + + inline bool getDiscreteAngle() const + { return data()[m_idDiscreteAngle].getBool(); } + inline double getPressure() const + { return data()[m_idPressure].getDouble(); } + +protected: + inline void setPressure(double x) + { if (getPressure() != x) data()[m_idPressure].setDouble(x); } + + + void onSetDefaults() override { + setPressure(1); + TReplicator::onSetDefaults(); + } + + + void onFixData() override { + TReplicator::onFixData(); + setPressure( std::max(0.0, std::min(2.0, getPressure())) ); + } + + + TPointD fixB() const { + TPointD b = m_b.position; + + if (getDiscreteAngle()) { + TPointD d = b - m_a.position; + double l = norm2(d); + if (l > TConsts::epsilon*TConsts::epsilon) { + l = sqrt(l); + double angle = atan2(d.y, d.x); + angle = round(angle*4/M_PI)*M_PI/4; + b.x = cos(angle)*l + m_a.position.x; + b.y = sin(angle)*l + m_a.position.y; + } + } + + if (areAlmostEqual(b, m_a.position)) + b = m_a.position + TPointD(1, 0); + + return b; + } + + + TAffine getAffine(const TAffine &toTool = TAffine()) const { + TPointD c = toTool*m_a.position; + TPointD x = toTool*fixB() - c; + TPointD y(-x.y, x.x); + + TAffine t0( x.x, y.x, c.x, + x.y, y.y, c.y ); + TAffine t1( x.x, -y.x, c.x, + x.y, -y.y, c.y ); + return t1*t0.inv(); + } + + + void onMovePoint(TAssistantPoint &point, const TPointD &position) override { + if (&point == &m_a) + m_b.position += position - m_a.position; + point.position = position; + } + + +public: + int getMultipler() const override + { return 2; } + + + void getPoints(const TAffine &toTool, PointList &points) const override + { transformPoints(getAffine(toTool), points, (int)points.size()); } + + + void getModifiers( + const TAffine &toTool, + TInputModifier::List &outModifiers ) const override + { + TModifierClone *modifier = new TModifierClone(); + modifier->transforms.push_back(TTrackTransform( + getAffine(toTool), getPressure() )); + outModifiers.push_back(modifier); + } + + + void draw(TToolViewer*, bool enabled) const override { + TPointD p0 = m_a.position; + TPointD p1 = fixB(); + + TAffine4 modelview, projection; + glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a); + glGetDoublev(GL_PROJECTION_MATRIX, projection.a); + + TAffine matrix = (projection*modelview).get2d(); + TPointD pp0 = matrix*p0; + TPointD pp1 = matrix*p1; + const TRectD oneBox(-1.0, -1.0, 1.0, 1.0); + if (!TGuidelineLine::truncateInfiniteLine(oneBox, pp0, pp1)) return; + + double alpha = getDrawingAlpha(enabled); + double pixelSize = sqrt(tglGetPixelSize2()); + TAffine matrixInv = matrix.inv(); + drawSegment(matrixInv*pp0, matrixInv*pp1, pixelSize, alpha); + } +}; + + +//***************************************************************************************** +// Registration +//***************************************************************************************** + +static TAssistantTypeT replicatorMirror("replicatorMirror"); diff --git a/toonz/sources/tnztools/assistants/replicatorstar.cpp b/toonz/sources/tnztools/assistants/replicatorstar.cpp new file mode 100644 index 0000000000..f9821d7e61 --- /dev/null +++ b/toonz/sources/tnztools/assistants/replicatorstar.cpp @@ -0,0 +1,230 @@ + + +// TnzTools includes +#include +#include +#include + + +// TnzCore includes +#include + + +//***************************************************************************************** +// TReplicatorStar implementation +//***************************************************************************************** + +class TReplicatorStar final : public TReplicator { + Q_DECLARE_TR_FUNCTIONS(TReplicatorStar) +public: + const TStringId m_idDiscreteAngle; + const TStringId m_idMirror; + const TStringId m_idCount; + +protected: + TAssistantPoint &m_center; + TAssistantPoint &m_a; + +public: + TReplicatorStar(TMetaObject &object): + TReplicator(object), + m_idDiscreteAngle("discreteAngle"), + m_idMirror("mirror"), + m_idCount("count"), + m_center( addPoint("center", TAssistantPoint::CircleCross) ), + m_a ( addPoint("a", TAssistantPoint::Circle, TPointD(80, 0)) ) + { + addProperty( new TBoolProperty(m_idDiscreteAngle.str(), getDiscreteAngle()) ); + addProperty( new TBoolProperty(m_idMirror.str(), getMirror()) ); + addProperty( createCountProperty(m_idCount, getCount(), 2) ); + } + + + static QString getLocalName() + { return tr("Replicator Star"); } + + + void updateTranslation() const override { + TReplicator::updateTranslation(); + setTranslation(m_idDiscreteAngle, tr("Discrete Angle")); + setTranslation(m_idMirror, tr("Mirror")); + setTranslation(m_idCount, tr("Count")); + } + + + inline bool getDiscreteAngle() const + { return data()[m_idDiscreteAngle].getBool(); } + inline bool getMirror() const + { return data()[m_idMirror].getBool(); } + inline int getCount() const + { return (int)data()[m_idCount].getDouble(); } + +protected: + inline void setCount(int x) + { if (getCount() != (double)x) data()[m_idCount].setDouble((double)x); } + + + void onSetDefaults() override { + setCount(6); + TReplicator::onSetDefaults(); + } + + + void onFixData() override { + TReplicator::onFixData(); + setCount( std::max(1, std::min(multiplierSoftLimit - 1, getCount())) ); + } + + + TPointD fixA() const { + TPointD a = m_a.position; + + if (getDiscreteAngle()) { + TPointD d = a - m_center.position; + double l = norm2(d); + if (l > TConsts::epsilon*TConsts::epsilon) { + l = sqrt(l); + int count = getCount(); + if (count > 0) { + double angle = atan2(d.y, d.x); + angle = round(angle*2*count/M_PI)*M_PI/(2*count); + a.x = cos(angle)*l + m_center.position.x; + a.y = sin(angle)*l + m_center.position.y; + } + } + } + + if (areAlmostEqual(a, m_center.position)) + a = m_center.position + TPointD(1, 0); + + return a; + } + + + void onMovePoint(TAssistantPoint &point, const TPointD &position) override { + if (&point == &m_center) + m_a.position += position - m_center.position; + point.position = position; + } + + +public: + int getMultipler() const override + { return getCount() + 1; } + + + void getPoints(const TAffine &toTool, PointList &points) const override { + points.reserve(points.size() * getMultipler()); + int pointsCount = (int)points.size(); + + int count = getCount(); + bool mirror = getMirror(); + + TPointD c = toTool*m_center.position; + TPointD x = toTool*fixA() - c; + TPointD y(-x.y, x.x); + + TAffine t1( x.x, y.x, c.x, + x.y, y.y, c.y ); + TAffine t2( x.x, -y.x, c.x, + x.y, -y.y, c.y ); + + TAffine t0 = t1.inv(); + TRotation r(360.0/getCount()); + + for(int i = 0; i < count; ++i) { + if (i) + transformPoints(t1*t0, points, pointsCount); + if (mirror) { + transformPoints(t2*t0, points, pointsCount); + t2 *= r; + } + t1 *= r; + } + } + + + void getModifiers( + const TAffine &toTool, + TInputModifier::List &outModifiers ) const override + { + int count = getCount(); + bool mirror = getMirror(); + + TPointD c = toTool*m_center.position; + TPointD x = toTool*fixA() - c; + TPointD y(-x.y, x.x); + + TAffine t1( x.x, y.x, c.x, + x.y, y.y, c.y ); + TAffine t2( x.x, -y.x, c.x, + x.y, -y.y, c.y ); + + TAffine t0 = t1.inv(); + TRotation r(360.0/getCount()); + + TModifierClone *modifier = new TModifierClone(); + for(int i = 0; i < count; ++i) { + if (i) + modifier->transforms.push_back(TTrackTransform(t1*t0)); + if (mirror) { + modifier->transforms.push_back(TTrackTransform(t2*t0)); + t2 *= r; + } + t1 *= r; + } + + outModifiers.push_back(modifier); + } + + + void draw(TToolViewer*, bool enabled) const override { + double alpha = getDrawingAlpha(enabled); + double gridAlpha = getDrawingGridAlpha(); + double pixelSize = sqrt(tglGetPixelSize2()); + + int count = getCount(); + bool mirror = getMirror(); + + TPointD c = m_center.position; + TPointD a = fixA(); + TPointD d = normalize(a - c); + + double spacing = 10*pixelSize; + double l = spacing*count/M_2PI; + + TPointD p0 = c + d*l; + TPointD p1 = p0 + d; + TRotation r(360.0/count); + + TAffine4 modelview, projection; + glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a); + glGetDoublev(GL_PROJECTION_MATRIX, projection.a); + + TAffine matrix = (projection*modelview).get2d(); + TAffine matrixInv = matrix.inv(); + const TRectD oneBox(-1.0, -1.0, 1.0, 1.0); + + for(int i = 0; i < count; ++i) { + TPointD pp0 = matrix*p0; + TPointD pp1 = matrix*p1; + if (TGuidelineLine::truncateRay(oneBox, pp0, pp1)) + drawSegment(matrixInv*pp0, matrixInv*pp1, pixelSize, i ? gridAlpha : alpha); + p0 = r*(p0 - c) + c; + p1 = r*(p1 - c) + c; + } + + TPointD p = TPointD(-d.y, d.x); + drawSegment( (mirror ? a+p*15 : a), a-p*15, pixelSize, alpha ); + + drawSegment(c - d*10, c + d*10, pixelSize, alpha); + drawSegment(c - p*10, c + p*10, pixelSize, alpha); + } +}; + + +//***************************************************************************************** +// Registration +//***************************************************************************************** + +static TAssistantTypeT replicatorStar("replicatorStar"); diff --git a/toonz/sources/tnztools/editassistantstool.cpp b/toonz/sources/tnztools/editassistantstool.cpp index ed217b90c9..e50a907691 100644 --- a/toonz/sources/tnztools/editassistantstool.cpp +++ b/toonz/sources/tnztools/editassistantstool.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include // TnzLib includes @@ -31,6 +32,64 @@ //------------------------------------------------------------------- +//============================================================================= +// Edit Assistants Swap Undo +//----------------------------------------------------------------------------- + +class EditAssistantsReorderUndo final : public ToolUtils::TToolUndo { +private: + int m_oldPos; + int m_newPos; + +public: + EditAssistantsReorderUndo( + TXshSimpleLevel *level, + const TFrameId &frameId, + int m_oldPos, + int m_newPos + ): + ToolUtils::TToolUndo(level, frameId), + m_oldPos(m_oldPos), + m_newPos(m_newPos) + { } + + QString getToolName() override + { return QString("Edit Assistants Tool"); } + + int getSize() const override + { return sizeof(*this); } + + void process(int oldPos, int newPos) const { + if (oldPos == newPos || oldPos < 0 || newPos < 0) + return; + TMetaImage *metaImage = dynamic_cast(m_level->getFrame(m_frameId, true).getPointer()); + if (!metaImage) + return; + + TMetaImage::Writer writer(*metaImage); + TMetaObjectList &list = *writer; + if (oldPos >= list.size() || newPos >= writer->size()) + return; + + TMetaObjectP obj = list[oldPos]; + list.erase(list.begin() + oldPos); + list.insert(list.begin() + newPos, obj); + } + + void undo() const override { + process(m_newPos, m_oldPos); + TTool::getApplication()->getCurrentXsheet()->notifyXsheetChanged(); + notifyImageChanged(); + } + + void redo() const override { + process(m_oldPos, m_newPos); + TTool::getApplication()->getCurrentXsheet()->notifyXsheetChanged(); + notifyImageChanged(); + } +}; + + //============================================================================= // Edit Assistants Undo //----------------------------------------------------------------------------- @@ -64,7 +123,7 @@ class EditAssistantsUndo final : public ToolUtils::TToolUndo { m_metaObject(metaObject), m_oldData(oldData), m_newData(m_metaObject->data()), - m_size(m_oldData.getMemSize() + m_newData.getMemSize()) + m_size(sizeof(*this) + m_oldData.getMemSize() + m_newData.getMemSize()) { } int getSize() const override @@ -144,6 +203,7 @@ class EditAssistantsTool final : public TTool { TPropertyGroup m_allProperties; TPropertyGroup m_toolProperties; TEnumProperty m_assistantType; + TIntProperty m_replicatorIndex; TStringId m_newAssisnantType; bool m_dragging; @@ -158,16 +218,18 @@ class EditAssistantsTool final : public TTool { TPointD m_currentPointOffset; TPointD m_currentPosition; TGuidelineList m_currentGuidelines; + + TReplicator::PointList m_replicatorPoints; + + TMetaImage::Reader *m_reader; + TMetaImage *m_readImage; + TMetaObjectPC m_readObject; + const TAssistantBase *m_readAssistant; - TMetaImage::Reader *m_reader; - TMetaImage *m_readImage; - TMetaObjectPC m_readObject; - const TAssistant *m_readAssistant; - - TMetaImage::Writer *m_writer; - TMetaImage *m_writeImage; - TMetaObjectP m_writeObject; - TAssistant *m_writeAssistant; + TMetaImage::Writer *m_writer; + TMetaImage *m_writeImage; + TMetaObjectP m_writeObject; + TAssistantBase *m_writeAssistant; Selection *selection; @@ -175,6 +237,7 @@ class EditAssistantsTool final : public TTool { EditAssistantsTool(): TTool("T_EditAssistants"), m_assistantType("AssistantType"), + m_replicatorIndex("ReplicatorIndex", 1, 10, 1, false), m_dragging(), m_dragAllPoints(), m_currentAssistantCreated(), @@ -190,6 +253,7 @@ class EditAssistantsTool final : public TTool { selection = new Selection(*this); bind(MetaImage | EmptyTarget); m_toolProperties.bind(m_assistantType); + m_replicatorIndex.setSpinner(); updateTranslation(); } @@ -201,7 +265,7 @@ class EditAssistantsTool final : public TTool { ToolType getToolType() const override { return TTool::LevelWriteTool; } unsigned int getToolHints() const override - { return TTool::getToolHints() & ~HintAssistantsAll; } + { return TTool::getToolHints() & ~(HintAssistantsAll | HintReplicatorsAll); } int getCursorId() const override { return ToolCursor::StrokeSelectCursor; } void onImageChanged() override { @@ -233,6 +297,10 @@ class EditAssistantsTool final : public TTool { m_allProperties.bind( *m_toolProperties.getProperty(i) ); if (Closer closer = read(ModeAssistant)) { m_readAssistant->updateTranslation(); + if (int i = readReplicatorIndex(*m_reader)) { + m_replicatorIndex.setValue(i); + m_allProperties.bind( m_replicatorIndex ); + } TPropertyGroup &assistantProperties = m_readAssistant->getProperties(); for(int i = 0; i < assistantProperties.getPropertyCount(); ++i) m_allProperties.bind( *assistantProperties.getProperty(i) ); @@ -250,15 +318,18 @@ class EditAssistantsTool final : public TTool { void updateTranslation() override { m_assistantType.setQStringName( tr("Assistant Type") ); + m_replicatorIndex.setQStringName( tr("Order") ); updateAssistantTypes(); if (Closer closer = read(ModeAssistant)) m_readAssistant->updateTranslation(); } bool onPropertyChanged(std::string name, bool addToUndo) override { - if (TProperty *property = m_toolProperties.getProperty(name)) { - if (name == m_assistantType.getName()) - m_newAssisnantType = TStringId::find( to_string(m_assistantType.getValue()) ); + if (m_replicatorIndex.getName() == name) { + writeReplicatorIndex(m_replicatorIndex.getValue()); + } else + if (name == m_assistantType.getName()) { + m_newAssisnantType = TStringId::find( to_string(m_assistantType.getValue()) ); } else { if (Closer closer = write(ModeAssistant, true)) m_writeAssistant->propertyChanged(TStringId::find(name)); @@ -302,7 +373,7 @@ class EditAssistantsTool final : public TTool { && (**m_reader)[m_currentAssistantIndex] == m_currentAssistant ) { m_readObject = (**m_reader)[m_currentAssistantIndex]; - m_readAssistant = m_readObject->getHandler(); + m_readAssistant = m_readObject->getHandler(); if (mode == ModeAssistant) return true; if (m_readAssistant->findPoint(m_currentPointName)) { @@ -338,7 +409,7 @@ class EditAssistantsTool final : public TTool { && (**m_writer)[m_currentAssistantIndex] == m_currentAssistant ) { m_writeObject = (**m_writer)[m_currentAssistantIndex]; - m_writeAssistant = m_writeObject->getHandler(); + m_writeAssistant = m_writeObject->getHandler(); if ( (mode == ModeAssistant) || (mode == ModePoint && m_writeAssistant->findPoint(m_currentPointName)) ) { @@ -394,7 +465,7 @@ class EditAssistantsTool final : public TTool { if (Closer closer = read(ModeImage)) for(TMetaObjectListCW::iterator i = (*m_reader)->begin(); i != (*m_reader)->end(); ++i) if (*i) - if (const TAssistant *assistant = (*i)->getHandler()) + if (const TAssistantBase *assistant = (*i)->getHandler()) assistant->deselectAll(); if (updateOptionsBox) this->updateOptionsBox(); @@ -407,7 +478,7 @@ class EditAssistantsTool final : public TTool { for(TMetaObjectListCW::iterator i = (*m_reader)->begin(); i != (*m_reader)->end(); ++i) { if (!*i) continue; - const TAssistant *assistant = (*i)->getHandler(); + const TAssistantBase *assistant = (*i)->getHandler(); if (!assistant) continue; assistant->deselectAll(); @@ -469,6 +540,76 @@ class EditAssistantsTool final : public TTool { return success; } + int readReplicatorIndex(TMetaImage::Reader &reader) { + int cnt = (int)reader->size(); + int index = 0; + for(int i = 0; i < cnt; ++i) { + if (const TMetaObjectPC &obj = (*reader)[i]) + if (obj->getHandler()) { + ++index; + if (m_currentAssistantIndex == i) + return index; + } + } + return 0; + } + + void writeReplicatorIndex(int index) { + apply(); + + int wantIndex = index; + int oldPos = -1; + int newPos = -1; + bool changed = false; + + if (Closer closer = write(ModeAssistant)) { + if (index < 1) + index = 1; + + int idx = 0; + int lastPos = -1; + + TMetaObjectList &list = **m_writer; + + int cnt = (int)list.size(); + for(int i = 0; i < cnt; ++i) { + if (list[i] && list[i]->getHandler()) { + ++idx; + if (m_currentAssistantIndex == i) oldPos = i; + if (idx == index) newPos = i; + lastPos = i; + } + } + + if (oldPos >= 0 && lastPos >= 0) { + assert(idx); + if (newPos < 0) + { index = idx; newPos = lastPos; } + if (oldPos != newPos) { + TMetaObjectP obj = list[oldPos]; + list.erase(list.begin() + oldPos); + list.insert(list.begin() + newPos, obj); + + TUndoManager::manager()->add(new EditAssistantsReorderUndo( + getApplication()->getCurrentLevel()->getLevel()->getSimpleLevel(), + getCurrentFid(), + oldPos, + newPos )); + + changed = true; + } + } + } + + if (changed) { + m_currentAssistantIndex = newPos; + invalidate(); + } + + if (wantIndex != index) + this->updateOptionsBox(); + } + public: void deselect() { resetCurrentPoint(); } @@ -516,7 +657,7 @@ class EditAssistantsTool final : public TTool { resetCurrentPoint(false); if (Closer closer = write(ModeImage)) { TMetaObjectP object(new TMetaObject(m_newAssisnantType)); - if (TAssistant *assistant = object->getHandler()) { + if (TAssistantBase *assistant = object->getHandler()) { assistant->setDefaults(); assistant->move(position); assistant->selectAll(); @@ -594,18 +735,28 @@ class EditAssistantsTool final : public TTool { TPointD position = m_currentPosition + m_currentPointOffset; // draw assistants + int index = 0; if (Closer closer = read(ModeImage)) for(TMetaObjectListCW::iterator i = (*m_reader)->begin(); i != (*m_reader)->end(); ++i) if (*i) - if (const TAssistant *assistant = (*i)->getHandler()) + if (const TAssistantBase *base = (*i)->getHandler()) { - assistant->drawEdit(getViewer()); - assistant->getGuidelines( - position, - TAffine(), - m_currentGuidelines ); + if (dynamic_cast(base)) { + ++index; + base->drawEdit(getViewer(), index); + } else { + base->drawEdit(getViewer()); + } + + if (const TAssistant *assistant = dynamic_cast(base)) + assistant->getGuidelines( + position, + TAffine(), + m_currentGuidelines ); } + TImage *img = getImage(false); + // draw assistans and guidelines from other layers TAssistant::scanAssistants( this, // tool @@ -615,6 +766,30 @@ class EditAssistantsTool final : public TTool { false, // enabled only false, // mark enabled true, // draw guidelines + img ); // skip image + + // draw replicators from other layers + TReplicator::scanReplicators( + this, // tool + nullptr, // in/out points + nullptr, // out modifiers + true, // draw + false, // enabled only + false, // mark enabled + false, // draw points + img ); // skip image + + // draw replicator points + m_replicatorPoints.clear(); + m_replicatorPoints.push_back(position); + TReplicator::scanReplicators( + this, // tool + &m_replicatorPoints, // in/out points + nullptr, // out modifiers + false, // draw + false, // enabled only + false, // mark enabled + true, // draw points nullptr ); // skip image } }; diff --git a/toonz/sources/tnztools/fullcolorbrushtool.cpp b/toonz/sources/tnztools/fullcolorbrushtool.cpp index d764b82777..cec1fd5e05 100644 --- a/toonz/sources/tnztools/fullcolorbrushtool.cpp +++ b/toonz/sources/tnztools/fullcolorbrushtool.cpp @@ -8,6 +8,7 @@ #include "tools/toolutils.h" #include "tools/toolhandle.h" #include "tools/tooloptions.h" +#include "tools/replicator.h" #include "mypainttoonzbrush.h" @@ -167,6 +168,18 @@ FullColorBrushTool::FullColorBrushTool(std::string name) m_pressure.setId("PressureSensitivity"); } +//------------------------------------------------------------------------------------------------------- + +unsigned int FullColorBrushTool::getToolHints() const { + unsigned int h = TTool::getToolHints() & ~HintAssistantsAll; + if (m_assistants.getValue()) { + h |= HintReplicators; + h |= HintReplicatorsPoints; + h |= HintReplicatorsEnabled; + } + return h; +} + //--------------------------------------------------------------------------------------------------- ToolOptionsBox *FullColorBrushTool::createOptionsBox() { @@ -229,6 +242,7 @@ void FullColorBrushTool::onActivate() { setWorkAndBackupImages(); onColorStyleChanged(); + updateModifiers(); } //-------------------------------------------------------------------------------------------------- @@ -304,10 +318,14 @@ bool FullColorBrushTool::askWrite(const TRect &rect) { //-------------------------------------------------------------------------------------------------- -bool FullColorBrushTool::preLeftButtonDown() { +void FullColorBrushTool::updateModifiers() { m_modifierAssistants->magnetism = m_assistants.getValue() ? 1 : 0; m_inputmanager.drawPreview = false; //!m_modifierAssistants->drawOnly; + m_modifierReplicate.clear(); + if (m_assistants.getValue()) + TReplicator::scanReplicators(this, nullptr, &m_modifierReplicate, false, true, false, false, nullptr); + m_inputmanager.clearModifiers(); m_inputmanager.addModifier(TInputModifierP(m_modifierTangents.getPointer())); m_inputmanager.addModifier( @@ -315,9 +333,15 @@ bool FullColorBrushTool::preLeftButtonDown() { #ifndef NDEBUG m_inputmanager.addModifier(TInputModifierP(m_modifierTest.getPointer())); #endif + m_inputmanager.addModifiers(m_modifierReplicate); m_inputmanager.addModifier( TInputModifierP(m_modifierSegmentation.getPointer())); +} +//-------------------------------------------------------------------------------------------------- + +bool FullColorBrushTool::preLeftButtonDown() { + updateModifiers(); touchImage(); if (m_isFrameCreated) { @@ -347,6 +371,7 @@ void FullColorBrushTool::handleMouseEvent(MouseEventType type, m_inputmanager.addModifier(TInputModifierP(m_modifierLine.getPointer())); m_inputmanager.addModifier( TInputModifierP(m_modifierAssistants.getPointer())); + m_inputmanager.addModifiers(m_modifierReplicate); m_inputmanager.addModifier( TInputModifierP(m_modifierSegmentation.getPointer())); m_inputmanager.drawPreview = true; diff --git a/toonz/sources/tnztools/fullcolorbrushtool.h b/toonz/sources/tnztools/fullcolorbrushtool.h index dd9b831de4..16ce37f1a8 100644 --- a/toonz/sources/tnztools/fullcolorbrushtool.h +++ b/toonz/sources/tnztools/fullcolorbrushtool.h @@ -61,8 +61,7 @@ class FullColorBrushTool final : public TTool, ToolType getToolType() const override { return TTool::LevelWriteTool; } - unsigned int getToolHints() const override - { return TTool::getToolHints() & ~HintAssistantsAll; } + unsigned int getToolHints() const override; ToolOptionsBox *createOptionsBox() override; @@ -115,6 +114,8 @@ class FullColorBrushTool final : public TTool, TMyPaintBrushStyle *getBrushStyle(); private: + void updateModifiers(); + enum MouseEventType { ME_DOWN, ME_DRAG, ME_UP, ME_MOVE }; void handleMouseEvent(MouseEventType type, const TPointD &pos, const TMouseEvent &e); @@ -128,6 +129,7 @@ class FullColorBrushTool final : public TTool, TSmartPointerT m_modifierTangents; TSmartPointerT m_modifierAssistants; TSmartPointerT m_modifierSegmentation; + TInputModifier::List m_modifierReplicate; TPropertyGroup m_prop; diff --git a/toonz/sources/tnztools/modifiers/modifierclone.cpp b/toonz/sources/tnztools/modifiers/modifierclone.cpp new file mode 100644 index 0000000000..05d96b80d0 --- /dev/null +++ b/toonz/sources/tnztools/modifiers/modifierclone.cpp @@ -0,0 +1,75 @@ + +#include + + +//***************************************************************************************** +// TModifierClone::Interpolator implementation +//***************************************************************************************** + +TTrackPoint TModifierClone::Interpolator::interpolateFromOriginal(double originalIndex) + { return transform.apply( track.calcPointFromOriginal(originalIndex) ); } +TTrackPoint TModifierClone::Interpolator::interpolate(double index) + { return interpolateFromOriginal(track.originalIndexByIndex(index)); } + + +//***************************************************************************************** +// TModifierClone implementation +//***************************************************************************************** + +TModifierClone::TModifierClone(bool keepOriginals): + keepOriginals(keepOriginals) { } + +void TModifierClone::modifyTrack(const TTrack &track, + TTrackList &outTracks) +{ + if (!track.handler) { + Handler *handler = new Handler(); + track.handler = handler; + if (keepOriginals) { + handler->original = new TTrack(track); + new TTrackIntrOrig(*handler->original); + } + for(TTrackTransformList::const_iterator i = transforms.begin(); i != transforms.end(); ++i) { + handler->tracks.push_back(new TTrack(track)); + TTrack &subTrack = *handler->tracks.back(); + new Interpolator(subTrack, *i); + } + } + + Handler *handler = dynamic_cast(track.handler.getPointer()); + if (!handler) + return; + + if (handler->original) + outTracks.push_back(handler->original); + outTracks.insert(outTracks.end(), handler->tracks.begin(), handler->tracks.end()); + if (!track.changed()) + return; + + int start = track.size() - track.pointsAdded; + if (start < 0) start = 0; + + // process original + if (handler->original) { + TTrack &subTrack = *handler->original; + subTrack.truncate(start); + for (int i = start; i < track.size(); ++i) + subTrack.push_back(subTrack.pointFromOriginal(i), false); + subTrack.fix_to(track.fixedSize()); + } + + // process sub-tracks + for(TTrackList::iterator i = handler->tracks.begin(); i != handler->tracks.end(); ++i) { + TTrack &subTrack = **i; + Interpolator *intr = dynamic_cast(subTrack.getInterpolator().getPointer()); + if (!intr) + continue; + subTrack.truncate(start); + for (int i = start; i < track.size(); ++i) + subTrack.push_back(intr->interpolateFromOriginal(i), false); + subTrack.fix_to(track.fixedSize()); + } + + track.resetChanges(); +} + diff --git a/toonz/sources/tnztools/modifiers/modifierjitter.cpp b/toonz/sources/tnztools/modifiers/modifierjitter.cpp new file mode 100644 index 0000000000..292bb8256e --- /dev/null +++ b/toonz/sources/tnztools/modifiers/modifierjitter.cpp @@ -0,0 +1,172 @@ + +#include + + + +//***************************************************************************************** +// static functions +//***************************************************************************************** + + +namespace { + +class Jitter { +public: + static inline double randomNext(unsigned int &seed) { + static const unsigned int max = 32767; + static const double k = 1.0/max; + seed = ((1103515245*seed + 12345) >> 16) & max; + return seed*k; + } + + static TPointD getPoint(unsigned int seed, int i, double *prevX = nullptr) { + unsigned int pseed = seed^i; + double dx = randomNext(pseed); + double dy = randomNext(pseed)*2 - 1; + if (dx < 0.5) { + double px = prevX ? *prevX : getPoint(seed, i-1).x; + px += 0.5 - i; + if (dx < px) + dx = randomNext(pseed)*(1 - px) + px; + } + return TPointD(dx + i, dy); + } + + static inline double spline(double x, TPointD *points) { + double x0 = points[1].x; + double y0 = points[1].y; + double x1 = points[2].x; + double y1 = points[2].y; + double t0 = (y1 - points[0].y)/(x1 - points[0].x)*(x1 - x0); + double t1 = (points[3].y - y0)/(points[3].x - x0)*(x1 - x0); + + double l = (x - x0)/(x1 - x0); + double ll = l*l; + double lll = ll*l; + + return y0*( 2*lll - 3*ll + 1) + + y1*(-2*lll + 3*ll ) + + t0*( lll - 2*ll + l) + + t1*( lll - ll ); + } + + static inline double func(unsigned int seed, double x) { + int i = (int)floor(x); + TPointD points[5]; + points[0] = getPoint(seed, i-2); + for(int j = 1; j < 5; ++j) + points[j] = getPoint(seed, i-2+j, &points[j-1].x); + return spline(x, &points[ x < points[2].x ? 0 : 1 ]); + } +}; + +static inline unsigned int trackSeedX(const TTrack &track) { + unsigned int seed = track.id; + Jitter::randomNext(seed); + return seed; +} + +static inline unsigned int trackSeedY(const TTrack &track) { + unsigned int seed = track.id^32143; + Jitter::randomNext(seed); + return seed; +} + +} // namespace + + + +//***************************************************************************************** +// TModifierJitter::Interpolator implementation +//***************************************************************************************** + + +TModifierJitter::Interpolator::Interpolator(TTrack &track, double period, double amplitude): + TTrackInterpolator(track), + seedX(trackSeedX(track)), + seedY(trackSeedY(track)), + frequency(fabs(period) > TConsts::epsilon ? 1/period : 0), + amplitude(fabs(period) > TConsts::epsilon ? amplitude : 0) + { } + + +TTrackPoint TModifierJitter::Interpolator::interpolateFromOriginal(double originalIndex) { + TTrackPoint p = track.calcPointFromOriginal(originalIndex); + double l = p.length*frequency; + p.position.x += Jitter::func(seedX, l)*amplitude; + p.position.y += Jitter::func(seedY, l)*amplitude; + return p; +} + + +TTrackPoint TModifierJitter::Interpolator::interpolate(double index) + { return interpolateFromOriginal(track.originalIndexByIndex(index)); } + + + +//***************************************************************************************** +// TModifierJitter implementation +//***************************************************************************************** + + +TModifierJitter::TModifierJitter(double period, double amplitude, int skipFirst): + period(period), amplitude(amplitude), skipFirst(skipFirst) { } + + +void TModifierJitter::modifyTrack(const TTrack &track, + TTrackList &outTracks) +{ + if (!track.handler && fabs(period) > TConsts::epsilon) { + Handler *handler = new Handler(); + track.handler = handler; + handler->track = new TTrack(track); + new Interpolator(*handler->track, period, amplitude); + } + + Handler *handler = dynamic_cast(track.handler.getPointer()); + if (!handler) + return TInputModifier::modifyTrack(track, outTracks); + + outTracks.push_back(handler->track); + TTrack &subTrack = *handler->track; + + if (!track.changed()) + return; + + Interpolator *intr = dynamic_cast(subTrack.getInterpolator().getPointer()); + if (!intr) + return; + + int start = track.size() - track.pointsAdded; + if (start < 0) start = 0; + + // process sub-track + subTrack.truncate(start); + for (int i = start; i < track.size(); ++i) + subTrack.push_back(intr->interpolateFromOriginal(i), false); + + // fit points + subTrack.fix_to(track.fixedSize()); + + track.resetChanges(); +} + + +void +TModifierJitter::modifyTracks( + const TTrackList &tracks, + TTrackList &outTracks ) +{ + int cnt = std::min( std::max(0, skipFirst), (int)tracks.size() ); + TTrackList::const_iterator split = tracks.begin() + cnt; + for(TTrackList::const_iterator i = tracks.begin(); i != split; ++i) + TInputModifier::modifyTrack(**i, outTracks); + for(TTrackList::const_iterator i = split; i != tracks.end(); ++i) + modifyTrack(**i, outTracks); +} + + +double TModifierJitter::func(unsigned int seed, double x) + { return Jitter::func(seed, x); } + + diff --git a/toonz/sources/tnztools/replicator.cpp b/toonz/sources/tnztools/replicator.cpp new file mode 100644 index 0000000000..6e63cb6f43 --- /dev/null +++ b/toonz/sources/tnztools/replicator.cpp @@ -0,0 +1,187 @@ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + + + +const int TReplicator::multiplierSoftLimit = 32; +const int TReplicator::multiplierLimit = 256; + + +//************************************************************************ +// TReplicator implementation +//************************************************************************ + +TReplicator::TReplicator(TMetaObject &object): + TAssistantBase(object) { } + +//--------------------------------------------------------------------------------------------------- + +int +TReplicator::getMultipler() const + { return 1; } + +//--------------------------------------------------------------------------------------------------- + +void +TReplicator::getModifiers(const TAffine&, TInputModifier::List&) const + { } + +//--------------------------------------------------------------------------------------------------- + +void +TReplicator::getPoints(const TAffine&, PointList&) const + { } + +//--------------------------------------------------------------------------------------------------- + +TIntProperty* +TReplicator::createCountProperty(const TStringId &id, int def, int min, int max) { + if (min < 0) min = 0; + if (def < min) def = min; + if (max <= 0) max = multiplierSoftLimit; + return createSpinProperty(id, def, min, max); +} + +//--------------------------------------------------------------------------------------------------- + +void +TReplicator::transformPoints(const TAffine &aff, PointList &points, int count) { + points.reserve(points.size() + count); + for(int i = 0; i < count; ++i) + points.push_back(aff*points[i]); +} + +//--------------------------------------------------------------------------------------------------- + +void +TReplicator::drawReplicatorPoints(const TPointD *points, int count) { + double colorBlack[4] = { 0.0, 0.0, 0.0, 0.3 }; + double colorWhite[4] = { 1.0, 1.0, 1.0, 0.3 }; + + glPushAttrib(GL_ALL_ATTRIB_BITS); + tglEnableBlending(); + tglEnableLineSmooth(true, 1.0 * lineWidthScale); + + double pixelSize = sqrt(tglGetPixelSize2()); + TPointD a(5*pixelSize, 0), da(0, 0.5*pixelSize); + TPointD b(0, 5*pixelSize), db(0.5*pixelSize, 0); + + for(int i = 0; i < count; ++i) { + const TPointD &p = points[i]; + glColor4dv(colorWhite); + tglDrawSegment(p - a - da, p + a - da); + tglDrawSegment(p - b - db, p + b - db); + glColor4dv(colorBlack); + tglDrawSegment(p - a + da, p + a + da); + tglDrawSegment(p - b + db, p + b + db); + } + + glPopAttrib(); +} + +//--------------------------------------------------------------------------------------------------- + +int +TReplicator::scanReplicators( + TTool *tool, + PointList *inOutPoints, + TInputModifier::List *outModifiers, + bool draw, + bool enabledOnly, + bool markEnabled, + bool drawPoints, + TImage *skipImage ) +{ + bool outOfLimit = false; + long long multiplier = 0; + int inputPoints = inOutPoints ? (int)inOutPoints->size() : 0; + + 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 TReplicator *replicator = (*i)->getHandler()) + if (!enabledOnly || replicator->getEnabled()) + { + if (!multiplier) multiplier = 1; + bool enabled = replicator->getEnabled(); + + if (enabled) { + int m = replicator->getMultipler(); + if (m <= 0) m = 1; + if (multiplier*m > multiplierLimit) { + outOfLimit = true; + } else { + multiplier *= m; + if (inOutPoints) + replicator->getPoints(imageToTrack, *inOutPoints); + if (outModifiers) + replicator->getModifiers(imageToTrack, *outModifiers); + } + } + + if (draw) { + unsigned int oldFlags = TReplicator::drawFlags; + if (enabled && outOfLimit) TReplicator::drawFlags |= TReplicator::DRAW_ERROR; + replicator->draw(viewer, enabled && markEnabled); + TReplicator::drawFlags = oldFlags; + } + } + + if (draw) glPopMatrix(); + } + } + + + if (drawPoints && inOutPoints && (int)inOutPoints->size() > inputPoints) + drawReplicatorPoints( + inOutPoints->data() + inputPoints, + (int)inOutPoints->size() - inputPoints ); + + return (int)multiplier; +} + diff --git a/toonz/sources/tnztools/tool.cpp b/toonz/sources/tnztools/tool.cpp index 0f730feef3..f7609503ef 100644 --- a/toonz/sources/tnztools/tool.cpp +++ b/toonz/sources/tnztools/tool.cpp @@ -180,7 +180,7 @@ TTool::TTool(std::string name) //------------------------------------------------------------------- unsigned int TTool::getToolHints() const - { return HintAssistants | HintGuidelines; } + { return HintAssistants | HintAssistantsGuidelines | HintReplicators; } //------------------------------------------------------------------- diff --git a/toonz/sources/tnztools/tooloptionscontrols.cpp b/toonz/sources/tnztools/tooloptionscontrols.cpp index f26ee4faca..5857d4726a 100644 --- a/toonz/sources/tnztools/tooloptionscontrols.cpp +++ b/toonz/sources/tnztools/tooloptionscontrols.cpp @@ -267,13 +267,21 @@ ToolOptionIntSlider::ToolOptionIntSlider(TTool *tool, TIntProperty *property, ToolHandle *toolHandle) : IntField(0, property->isMaxRangeLimited()) , ToolOptionControl(tool, property->getName(), toolHandle) - , m_property(property) { + , m_property(property) +{ setLinearSlider(property->isLinearSlider()); m_property->addListener(this); TIntProperty::Range range = property->getRange(); setRange(range.first, range.second); - setMaximumWidth(300); - setMinimumWidth(50); + if (property->isSpinner()) { + enableSlider(false); + enableSpinner(true); + setMinimumWidth(60); + setMaximumWidth(80); + } else { + setMinimumWidth(50); + setMaximumWidth(300); + } updateStatus(); connect(this, SIGNAL(valueChanged(bool)), SLOT(onValueChanged(bool))); // synchronize the state with the same widgets in other tool option bars diff --git a/toonz/sources/tnztools/toonzrasterbrushtool.cpp b/toonz/sources/tnztools/toonzrasterbrushtool.cpp index b4ba5d5f9c..4344e5b663 100644 --- a/toonz/sources/tnztools/toonzrasterbrushtool.cpp +++ b/toonz/sources/tnztools/toonzrasterbrushtool.cpp @@ -6,6 +6,7 @@ #include "tools/toolhandle.h" #include "tools/toolutils.h" #include "tools/tooloptions.h" +#include "tools/replicator.h" // TnzQt includes #include "toonzqt/dvdialog.h" @@ -791,6 +792,18 @@ ToonzRasterBrushTool::ToonzRasterBrushTool(std::string name, int targetType) //------------------------------------------------------------------------------------------------------- +unsigned int ToonzRasterBrushTool::getToolHints() const { + unsigned int h = TTool::getToolHints() & ~HintAssistantsAll; + if (m_assistants.getValue()) { + h |= HintReplicators; + h |= HintReplicatorsPoints; + h |= HintReplicatorsEnabled; + } + return h; +} + +//------------------------------------------------------------------------------------------------------- + ToolOptionsBox *ToonzRasterBrushTool::createOptionsBox() { TPaletteHandle *currPalette = TTool::getApplication()->getPaletteController()->getCurrentLevelPalette(); @@ -1009,6 +1022,7 @@ void ToonzRasterBrushTool::onActivate() { m_hardness.getValue() * 0.01); setWorkAndBackupImages(); + updateModifiers(); m_brushTimer.start(); // TODO:app->editImageOrSpline(); } @@ -1036,13 +1050,17 @@ bool ToonzRasterBrushTool::askWrite(const TRect &rect) { return true; } -//-------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------- -bool ToonzRasterBrushTool::preLeftButtonDown() { +void ToonzRasterBrushTool::updateModifiers() { int smoothRadius = (int)round(m_smooth.getValue()); m_modifierAssistants->magnetism = m_assistants.getValue() ? 1 : 0; m_inputmanager.drawPreview = false; //!m_modifierAssistants->drawOnly; + m_modifierReplicate.clear(); + if (m_assistants.getValue()) + TReplicator::scanReplicators(this, nullptr, &m_modifierReplicate, false, true, false, false, nullptr); + m_inputmanager.clearModifiers(); m_inputmanager.addModifier(TInputModifierP(m_modifierTangents.getPointer())); if (smoothRadius > 0) { @@ -1056,8 +1074,14 @@ bool ToonzRasterBrushTool::preLeftButtonDown() { #ifndef NDEBUG m_inputmanager.addModifier(TInputModifierP(m_modifierTest.getPointer())); #endif + m_inputmanager.addModifiers(m_modifierReplicate); m_inputmanager.addModifier(TInputModifierP(m_modifierSegmentation.getPointer())); +} +//-------------------------------------------------------------------------------------------------- + +bool ToonzRasterBrushTool::preLeftButtonDown() { + updateModifiers(); touchImage(); if (m_isFrameCreated) { setWorkAndBackupImages(); @@ -1084,6 +1108,7 @@ void ToonzRasterBrushTool::handleMouseEvent(MouseEventType type, m_inputmanager.clearModifiers(); m_inputmanager.addModifier(TInputModifierP(m_modifierLine.getPointer())); m_inputmanager.addModifier(TInputModifierP(m_modifierAssistants.getPointer())); + m_inputmanager.addModifiers(m_modifierReplicate); m_inputmanager.addModifier(TInputModifierP(m_modifierSegmentation.getPointer())); m_inputmanager.drawPreview = true; } diff --git a/toonz/sources/tnztools/toonzrasterbrushtool.h b/toonz/sources/tnztools/toonzrasterbrushtool.h index 65bc306fac..9209e92a45 100644 --- a/toonz/sources/tnztools/toonzrasterbrushtool.h +++ b/toonz/sources/tnztools/toonzrasterbrushtool.h @@ -108,8 +108,7 @@ class ToonzRasterBrushTool final : public TTool, ToolType getToolType() const override { return TTool::LevelWriteTool; } - unsigned int getToolHints() const override - { return TTool::getToolHints() & ~HintAssistantsAll; } + unsigned int getToolHints() const override; ToolOptionsBox *createOptionsBox() override; @@ -162,6 +161,8 @@ class ToonzRasterBrushTool final : public TTool, bool isMyPaintStyleSelected() { return m_isMyPaintStyleSelected; } private: + void updateModifiers(); + enum MouseEventType { ME_DOWN, ME_DRAG, ME_UP, ME_MOVE }; void handleMouseEvent(MouseEventType type, const TPointD &pos, const TMouseEvent &e); @@ -177,6 +178,7 @@ class ToonzRasterBrushTool final : public TTool, #ifndef NDEBUG TSmartPointerT m_modifierTest; #endif + TInputModifier::List m_modifierReplicate; class MyPaintStroke: public TTrackHandler { public: diff --git a/toonz/sources/tnztools/toonzvectorbrushtool.cpp b/toonz/sources/tnztools/toonzvectorbrushtool.cpp index 1e01069af6..a2e2ef3768 100644 --- a/toonz/sources/tnztools/toonzvectorbrushtool.cpp +++ b/toonz/sources/tnztools/toonzvectorbrushtool.cpp @@ -6,6 +6,7 @@ #include "tools/toolhandle.h" #include "tools/toolutils.h" #include "tools/tooloptions.h" +#include "tools/replicator.h" #include "bluredbrush.h" // TnzQt includes @@ -599,6 +600,18 @@ ToonzVectorBrushTool::ToonzVectorBrushTool(std::string name, int targetType) //------------------------------------------------------------------------------------------------------- +unsigned int ToonzVectorBrushTool::getToolHints() const { + unsigned int h = TTool::getToolHints() & ~HintAssistantsAll; + if (m_assistants.getValue()) { + h |= HintReplicators; + h |= HintReplicatorsPoints; + h |= HintReplicatorsEnabled; + } + return h; +} + +//------------------------------------------------------------------------------------------------------- + ToolOptionsBox *ToonzVectorBrushTool::createOptionsBox() { TPaletteHandle *currPalette = TTool::getApplication()->getPaletteController()->getCurrentLevelPalette(); @@ -657,6 +670,7 @@ void ToonzVectorBrushTool::onActivate() { loadLastBrush(); } resetFrameRange(); + updateModifiers(); // TODO:app->editImageOrSpline(); } @@ -1043,9 +1057,7 @@ void ToonzVectorBrushTool::inputPaintTracks(const TTrackList &tracks) { //-------------------------------------------------------------------------------------------------- -bool ToonzVectorBrushTool::preLeftButtonDown() { - if (getViewer() && getViewer()->getGuidedStrokePickerMode()) return false; - +void ToonzVectorBrushTool::updateModifiers() { m_pixelSize = getPixelSize(); int smoothRadius = (int)round(m_smooth.getValue()); m_modifierAssistants->magnetism = m_assistants.getValue() ? 1 : 0; @@ -1054,6 +1066,10 @@ bool ToonzVectorBrushTool::preLeftButtonDown() { m_modifierSimplify->step = 2*m_pixelSize; m_inputmanager.drawPreview = false; + m_modifierReplicate.clear(); + if (m_assistants.getValue()) + TReplicator::scanReplicators(this, nullptr, &m_modifierReplicate, false, true, false, false, nullptr); + m_inputmanager.clearModifiers(); m_inputmanager.addModifier(TInputModifierP(m_modifierTangents.getPointer())); if (smoothRadius > 0) { @@ -1067,9 +1083,16 @@ bool ToonzVectorBrushTool::preLeftButtonDown() { #ifndef NDEBUG m_inputmanager.addModifier(TInputModifierP(m_modifierTest.getPointer())); #endif + m_inputmanager.addModifiers(m_modifierReplicate); m_inputmanager.addModifier(TInputModifierP(m_modifierSegmentation.getPointer())); m_inputmanager.addModifier(TInputModifierP(m_modifierSimplify.getPointer())); +} +//-------------------------------------------------------------------------------------------------- + +bool ToonzVectorBrushTool::preLeftButtonDown() { + if (getViewer() && getViewer()->getGuidedStrokePickerMode()) return false; + updateModifiers(); touchImage(); if (m_isFrameCreated) { // When the xsheet frame is selected, whole viewer will be updated from @@ -1096,6 +1119,7 @@ void ToonzVectorBrushTool::handleMouseEvent(MouseEventType type, m_inputmanager.clearModifiers(); m_inputmanager.addModifier(TInputModifierP(m_modifierLine.getPointer())); m_inputmanager.addModifier(TInputModifierP(m_modifierAssistants.getPointer())); + m_inputmanager.addModifiers(m_modifierReplicate); m_inputmanager.addModifier(TInputModifierP(m_modifierSegmentation.getPointer())); m_inputmanager.addModifier(TInputModifierP(m_modifierSimplify.getPointer())); m_inputmanager.drawPreview = true; diff --git a/toonz/sources/tnztools/toonzvectorbrushtool.h b/toonz/sources/tnztools/toonzvectorbrushtool.h index 2aff0b4224..242b9bd0e3 100644 --- a/toonz/sources/tnztools/toonzvectorbrushtool.h +++ b/toonz/sources/tnztools/toonzvectorbrushtool.h @@ -99,8 +99,7 @@ class ToonzVectorBrushTool final : public TTool, ToolType getToolType() const override { return TTool::LevelWriteTool; } - unsigned int getToolHints() const override - { return TTool::getToolHints() & ~HintAssistantsAll; } + unsigned int getToolHints() const override; ToolOptionsBox *createOptionsBox() override; @@ -168,6 +167,8 @@ class ToonzVectorBrushTool final : public TTool, void snap(const TPointD &pos, bool snapEnabled, bool withSelfSnap = false); + void updateModifiers(); + enum MouseEventType { ME_DOWN, ME_DRAG, ME_UP, ME_MOVE }; void handleMouseEvent(MouseEventType type, const TPointD &pos, const TMouseEvent &e); @@ -200,13 +201,14 @@ class ToonzVectorBrushTool final : public TTool, #ifndef NDEBUG TSmartPointerT m_modifierTest; #endif + TInputModifier::List m_modifierReplicate; TrackList m_tracks; TrackList m_rangeTracks; StrokeList m_firstStrokes; TFrameId m_firstFrameId, m_veryFirstFrameId; TPixel32 m_currentColor; - int m_styleId; // bwtodo: remove + int m_styleId; double m_minThick, m_maxThick; // for snapping and framerange diff --git a/toonz/sources/tnztools/track.cpp b/toonz/sources/tnztools/track.cpp index 2637216f32..2d0df30d0c 100644 --- a/toonz/sources/tnztools/track.cpp +++ b/toonz/sources/tnztools/track.cpp @@ -10,6 +10,20 @@ TTrack::Id TTrack::m_lastId = 0; +//***************************************************************************************** +// TTrackTransform implemantation +//***************************************************************************************** + +TAffine TTrackTransform::makeTiltTransform(const TAffine &a) { + double l1 = a.a11*a.a11 + a.a21*a.a22; + double l2 = a.a11*a.a11 + a.a21*a.a22; + double l = std::max(l1, l2); + double k = l > TConsts::epsilon*TConsts::epsilon ? 1/sqrt(l) : 0; + return TAffine( a.a11*k, a.a12*k, 0, + a.a21*k, a.a22*k, 0 ); +} + + //***************************************************************************************** // TTrackIntrOrig implemantation //***************************************************************************************** diff --git a/toonz/sources/toonz/sceneviewer.cpp b/toonz/sources/toonz/sceneviewer.cpp index 7b8dab6a75..d17ff5a09d 100644 --- a/toonz/sources/toonz/sceneviewer.cpp +++ b/toonz/sources/toonz/sceneviewer.cpp @@ -24,6 +24,7 @@ #include "tools/toolcommandids.h" #include "tools/toolutils.h" #include "tools/assistant.h" +#include "tools/replicator.h" // TnzQt includes #include "toonzqt/icongenerator.h" @@ -1833,19 +1834,45 @@ void SceneViewer::drawOverlay() { glScaled(m_dpiScale.x, m_dpiScale.y, 1); m_pixelSize = sqrt(tglGetPixelSize2()) * getDevPixRatio(); + unsigned int hints = tool->getToolHints(); + // draw assistans and guidelines m_toolHasAssistants = false; - unsigned int hints = tool->getToolHints(); if (hints & TTool::HintAssistantsAll) { + bool markEnabled = hints & TTool::HintAssistantsEnabled; + bool drawGuidelines = hints & TTool::HintAssistantsGuidelines; + 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 + tool, // tool + &m_toolPos, 1, // pointer positions + nullptr, // out guidelines + true, // draw + false, // enabled only + markEnabled, // mark enabled + drawGuidelines, // draw guidelines + nullptr ); // skip image + } + + // draw replicators + m_toolReplicatedPoints.clear(); + if (hints & TTool::HintReplicatorsAll) { + bool drawPoints = hints & TTool::HintReplicatorsPoints; + bool markEnabled = hints & TTool::HintReplicatorsEnabled; + TReplicator::PointList *points = nullptr; + if (drawPoints) { + m_toolReplicatedPoints.push_back(m_toolPos); + points = &m_toolReplicatedPoints; + } + + TReplicator::scanReplicators( + tool, // tool + points, // in/out points + nullptr, // out modifiers + true, // draw + false, // enabled only + markEnabled, // mark enabled + drawPoints, // draw points + nullptr ); // skip image } // draw tool diff --git a/toonz/sources/toonz/sceneviewer.h b/toonz/sources/toonz/sceneviewer.h index dcb22d787f..a7b2f7bddd 100644 --- a/toonz/sources/toonz/sceneviewer.h +++ b/toonz/sources/toonz/sceneviewer.h @@ -73,6 +73,7 @@ class SceneViewer final : public GLWidgetForHighDpi, QPointF m_pos; TPointD m_toolPos; bool m_toolHasAssistants = false; + std::vector m_toolReplicatedPoints; 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 d0a46dde7b..2598f877c1 100644 --- a/toonz/sources/toonz/sceneviewerevents.cpp +++ b/toonz/sources/toonz/sceneviewerevents.cpp @@ -657,9 +657,15 @@ void SceneViewer::onMove(const TMouseEvent &event) { if (!cursorSet) setToolCursor(this, tool->getCursorId()); if ( m_toolHasAssistants - && (tool->getToolHints() & TTool::HintGuidelines) + && (tool->getToolHints() & TTool::HintAssistantsGuidelines) && !areAlmostEqual(m_toolPos, pos) ) invalidateAll(); + + if ( m_toolReplicatedPoints.size() > 1 + && (tool->getToolHints() & TTool::HintReplicatorsPoints) + && !areAlmostEqual(m_toolPos, pos) ) + invalidateAll(); + m_toolPos = pos; #ifdef WITH_CANON diff --git a/toonz/sources/toonzqt/intfield.cpp b/toonz/sources/toonzqt/intfield.cpp index e53c078273..e9ad0970e8 100644 --- a/toonz/sources/toonzqt/intfield.cpp +++ b/toonz/sources/toonzqt/intfield.cpp @@ -10,6 +10,7 @@ #include #include #include +#include namespace { const int NonLinearSliderPrecision = 2; @@ -263,11 +264,14 @@ void IntLineEdit::mouseReleaseEvent(QMouseEvent *e) { // IntField //----------------------------------------------------------------------------- -IntField::IntField(QWidget *parent, bool isMaxRangeLimited, bool isRollerHide) +IntField::IntField(QWidget *parent, bool isMaxRangeLimited, bool isRollerHide, + bool isSpinnerHide) : QWidget(parent) + , m_roller(0) , m_lineEdit(0) , m_slider(0) - , m_roller(0) + , m_inc(0) + , m_dec(0) , m_isMaxRangeLimited(isMaxRangeLimited) , m_isLinearSlider(true) { setObjectName("IntField"); @@ -295,6 +299,19 @@ IntField::IntField(QWidget *parent, bool isMaxRangeLimited, bool isRollerHide) layout->addWidget(field); + m_inc = new QPushButton(QString("+")); + m_dec = new QPushButton(QString("-")); + m_inc->setFixedSize(QSize(20, 20)); + m_dec->setFixedSize(QSize(20, 20)); + ret = ret + && connect(m_inc, SIGNAL(clicked()), this, SLOT(onIncClicked())) + && connect(m_dec, SIGNAL(clicked()), this, SLOT(onDecClicked())); + + if (isSpinnerHide) enableSpinner(false); + + layout->addWidget(m_inc); + layout->addWidget(m_dec); + m_slider = new QSlider(Qt::Horizontal, this); ret = ret && connect(m_slider, SIGNAL(valueChanged(int)), this, SLOT(onSliderChanged(int))); @@ -386,6 +403,24 @@ bool IntField::rollerIsEnabled() { return m_roller->isEnabled(); } //----------------------------------------------------------------------------- +void IntField::enableSpinner(bool enable) { + m_inc->setEnabled(enable); + m_dec->setEnabled(enable); + if (enable) { + m_inc->show(); + m_dec->show(); + } else { + m_inc->hide(); + m_dec->hide(); + } +} + +//----------------------------------------------------------------------------- + +bool IntField::spinnerIsEnabled() { return m_inc->isEnabled(); } + +//----------------------------------------------------------------------------- + void IntField::setLineEditBackgroundColor(QColor color) { m_lineEdit->setLineEditBackgroundColor(color); } @@ -452,6 +487,28 @@ void IntField::onSliderChanged(int sliderPos) { //----------------------------------------------------------------------------- +void IntField::onIncClicked() { + int value = m_lineEdit->getValue() + 1; + m_lineEdit->setValue(value); + m_slider->setValue(value2pos(value)); + m_roller->setValue((double)value); + m_lineEdit->setCursorPosition(0); + emit valueChanged(false); +} + +//----------------------------------------------------------------------------- + +void IntField::onDecClicked() { + int value = m_lineEdit->getValue() - 1; + m_lineEdit->setValue(value); + m_slider->setValue(value2pos(value)); + m_roller->setValue((double)value); + m_lineEdit->setCursorPosition(0); + emit valueChanged(false); +} + +//----------------------------------------------------------------------------- + void IntField::onEditingFinished() { double value = m_lineEdit->getValue(); // Controllo necessario per evitare che il segnale di cambiamento venga emesso