diff --git a/toonz/sources/include/tools/assistant.h b/toonz/sources/include/tools/assistant.h index e915737..f79377a 100644 --- a/toonz/sources/include/tools/assistant.h +++ b/toonz/sources/include/tools/assistant.h @@ -326,6 +326,10 @@ protected: 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; diff --git a/toonz/sources/include/tools/modifiers/modifierjitter.h b/toonz/sources/include/tools/modifiers/modifierjitter.h new file mode 100644 index 0000000..2f048e4 --- /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/tnztools/CMakeLists.txt b/toonz/sources/tnztools/CMakeLists.txt index 368aa99..1518eee 100644 --- a/toonz/sources/tnztools/CMakeLists.txt +++ b/toonz/sources/tnztools/CMakeLists.txt @@ -52,6 +52,7 @@ set(HEADERS ../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 @@ -131,6 +132,7 @@ set(SOURCES replicator.cpp modifiers/modifierassistants.cpp modifiers/modifierclone.cpp + modifiers/modifierjitter.cpp modifiers/modifierline.cpp modifiers/modifiersegmentation.cpp modifiers/modifiersimplify.cpp @@ -144,6 +146,7 @@ set(SOURCES 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 b7c37f1..a342f5e 100644 --- a/toonz/sources/tnztools/assistant.cpp +++ b/toonz/sources/tnztools/assistant.cpp @@ -202,6 +202,17 @@ TAssistantBase::getBasePoint() const //--------------------------------------------------------------------------------------------------- +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 TAssistantBase::addProperty(TProperty *p) { m_properties.add(p); } diff --git a/toonz/sources/tnztools/assistants/replicatorjitter.cpp b/toonz/sources/tnztools/assistants/replicatorjitter.cpp new file mode 100644 index 0000000..befb329 --- /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 index 0fca92d..0c608c8 100644 --- a/toonz/sources/tnztools/assistants/replicatormirror.cpp +++ b/toonz/sources/tnztools/assistants/replicatormirror.cpp @@ -30,7 +30,7 @@ public: m_idDiscreteAngle("discreteAngle"), m_idPressure("pressure"), m_a( addPoint("a", TAssistantPoint::CircleCross) ), - m_b( addPoint("b", TAssistantPoint::Circle, TPointD(50, 0)) ) + 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()) ); diff --git a/toonz/sources/tnztools/modifiers/modifierjitter.cpp b/toonz/sources/tnztools/modifiers/modifierjitter.cpp new file mode 100644 index 0000000..292bb82 --- /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 index 1699928..6e63cb6 100644 --- a/toonz/sources/tnztools/replicator.cpp +++ b/toonz/sources/tnztools/replicator.cpp @@ -51,10 +51,7 @@ 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; - assert(min < max && def < max); - TIntProperty *property = new TIntProperty(id.str(), min, max, def); - property->setSpinner(); - return property; + return createSpinProperty(id, def, min, max); } //---------------------------------------------------------------------------------------------------