diff --git a/toonz/sources/include/tmetaimage.h b/toonz/sources/include/tmetaimage.h index 5726d1a..9d360c7 100644 --- a/toonz/sources/include/tmetaimage.h +++ b/toonz/sources/include/tmetaimage.h @@ -181,8 +181,10 @@ protected: 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/tnztools/CMakeLists.txt b/toonz/sources/tnztools/CMakeLists.txt index a333996..db7abdf 100644 --- a/toonz/sources/tnztools/CMakeLists.txt +++ b/toonz/sources/tnztools/CMakeLists.txt @@ -143,6 +143,7 @@ set(SOURCES assistants/assistantline.cpp assistants/assistantellipse.cpp assistants/replicatoraffine.cpp + assistants/replicatorgrid.cpp assistants/replicatormirror.cpp editassistantstool.cpp ) diff --git a/toonz/sources/tnztools/assistants/replicatorgrid.cpp b/toonz/sources/tnztools/assistants/replicatorgrid.cpp new file mode 100644 index 0000000..00e1cef --- /dev/null +++ b/toonz/sources/tnztools/assistants/replicatorgrid.cpp @@ -0,0 +1,267 @@ + + +// TnzTools includes +#include +#include + + +// TnzCore includes +#include + + +//***************************************************************************************** +// TReplicatorGrid implementation +//***************************************************************************************** + +class TReplicatorGrid final : public TReplicator { + Q_DECLARE_TR_FUNCTIONS(TReplicatorAffine) +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(4); + setCountB(4); + 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 da = a - c; + double la = norm2(da); + if (la > TConsts::epsilon*TConsts::epsilon) { + TPointD db = b - c; + TPointD pa(-da.y, da.x); + double l = pa*db/la; + b = pa*l + c; + } + } + } + + + void onMovePoint(TAssistantPoint &point, const TPointD &position) override { + TPointD pc = m_center.position; + point.position = position; + if (&point == &m_center) { + TPointD d = m_center.position - pc; + m_a.position += d; + m_b.position += d; + } else { + onFixPoints(); + } + } + + + void onDataFieldChanged(const TStringId &name, const TVariant &value) override { + TReplicator::onDataFieldChanged(name, value); + if ( name == m_idFixAngle + || name == m_idFixSkew ) + fixPoints(); + } + + + static bool makeMirror(TAffine &mirror, const TPointD &c, const TPointD &x) { + if (!(norm2(x) > TConsts::epsilon*TConsts::epsilon)) + return false; + 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 ); + mirror = t1*t0.inv(); + return true; + } + + +public: + int getMultipler() const override { + return (getCountA() + getCountAInv()) + * (getCountB() + getCountBInv()) + * (getMirrorA() ? 2 : 1) + * (getMirrorB() ? 2 : 1); + } + + + 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; + + TAffine ma, mb; + + bool mirrorA = getMirrorA() && makeMirror(ma, c, da); + bool mirrorB = getMirrorB() && makeMirror(ma, c, db); + + 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 ) )); + } + outModifiers.push_back(modifier); + } + + + void draw(TToolViewer*, bool enabled) const override { + double alpha = getDrawingAlpha(enabled); + 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, alpha); + drawSegment(o, o + db*0.2, pixelSize, alpha); + } + if (mirrorA) drawSegment(o, o - da*0.2, pixelSize, alpha); + if (mirrorB) drawSegment(o, o - db*0.2, pixelSize, alpha); + } + } +}; + + +//***************************************************************************************** +// Registration +//***************************************************************************************** + +static TAssistantTypeT replicatorGrid("replicatorGrid"); diff --git a/toonz/sources/tnztools/replicator.cpp b/toonz/sources/tnztools/replicator.cpp index 411966d..59d1a4c 100644 --- a/toonz/sources/tnztools/replicator.cpp +++ b/toonz/sources/tnztools/replicator.cpp @@ -44,7 +44,7 @@ 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 - 1; + if (max <= 0) max = multiplierSoftLimit; assert(min < max && def < max); TIntProperty *property = new TIntProperty(id.str(), min, max, def); property->setSpinner();