diff --git a/toonz/sources/include/tools/replicator.h b/toonz/sources/include/tools/replicator.h
index c7a1b93..01399f0 100644
--- a/toonz/sources/include/tools/replicator.h
+++ b/toonz/sources/include/tools/replicator.h
@@ -41,6 +41,9 @@ public:
     bool enabledOnly,
     bool markEnabled,
     TImage *skipImage );
+  
+protected:
+  TIntProperty* createCountProperty(const TStringId &id, int def = 1, int min = 1, int max = 0);
 };
 
 
diff --git a/toonz/sources/include/tools/track.h b/toonz/sources/include/tools/track.h
index bc52bb9..2363db2 100644
--- a/toonz/sources/include/tools/track.h
+++ b/toonz/sources/include/tools/track.h
@@ -138,9 +138,9 @@ public:
   double pressureScale;
   double pressureOffset;
 
-  inline explicit TTrackTransform(
-    const TAffine &transform = TAffine(),
-    const TAffine &tiltTransform = TAffine(),
+  inline TTrackTransform(
+    const TAffine &transform,
+    const TAffine &tiltTransform,
     double pressureScale = 1,
     double pressureOffset = 0
   ):
@@ -148,7 +148,24 @@ public:
     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;
     
@@ -161,6 +178,11 @@ public:
     
     return p;
   }
+
+  inline void recalcTiltTransform()
+    { tiltTransform = makeTiltTransform(transform); }
+
+  static TAffine makeTiltTransform(const TAffine &a);
 };
 
 
diff --git a/toonz/sources/tnztools/CMakeLists.txt b/toonz/sources/tnztools/CMakeLists.txt
index e347dc8..a333996 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/replicatormirror.cpp
     editassistantstool.cpp
 )
 
diff --git a/toonz/sources/tnztools/assistants/replicatoraffine.cpp b/toonz/sources/tnztools/assistants/replicatoraffine.cpp
index 0352b5b..c9d8763 100644
--- a/toonz/sources/tnztools/assistants/replicatoraffine.cpp
+++ b/toonz/sources/tnztools/assistants/replicatoraffine.cpp
@@ -53,8 +53,8 @@ public:
     addProperty( new TBoolProperty(m_idFixAspect.str(), getFixAspect()) );
     addProperty( new TBoolProperty(m_idFixAngle.str(),  getFixAngle()) );
     addProperty( new TBoolProperty(m_idFixSkew.str(),   getFixSkew()) );
-    addProperty( new TIntProperty(m_idCount.str(), 0, multiplierSoftLimit - 1, getCount()) );
-    addProperty( new TIntProperty(m_idCountInv.str(), 0, multiplierSoftLimit - 1, getCountInv()) );
+    addProperty( createCountProperty(m_idCount, getCount(), 0) );
+    addProperty( createCountProperty(m_idCountInv, getCountInv(), 0) );
     addProperty( new TDoubleProperty(m_idPressure.str(), 0.0, 2.0, getPressure()) );
   }
 
@@ -223,20 +223,6 @@ public:
     { return getCount() + 1; }
   
   
-  static TAffine makeTiltTransform(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;
-    a.a11 *= k;
-    a.a12 *= k;
-    a.a21 *= k;
-    a.a22 *= k;
-    a.a13 = a.a23 = 0;
-    return a;
-  }
-  
-  
   void getModifiers(
     const TAffine &toTool,
     TInputModifier::List &outModifiers ) const override
@@ -259,7 +245,7 @@ public:
       TTrackTransform tt;
       for(int j = 0; j < t[i].count; ++j) {
         tt.transform = t[i].aff * tt.transform;
-        tt.tiltTransform = makeTiltTransform(tt.transform);
+        tt.tiltTransform = TTrackTransform::makeTiltTransform(tt.transform);
         tt.pressureScale *= t[i].pressure;
         modifier->transforms.push_back(tt);
       }
diff --git a/toonz/sources/tnztools/assistants/replicatormirror.cpp b/toonz/sources/tnztools/assistants/replicatormirror.cpp
new file mode 100644
index 0000000..138f41c
--- /dev/null
+++ b/toonz/sources/tnztools/assistants/replicatormirror.cpp
@@ -0,0 +1,154 @@
+
+
+// TnzTools includes
+#include <tools/replicator.h>
+#include <tools/modifiers/modifierclone.h>
+#include <tools/assistants/guidelineline.h>
+
+
+// TnzCore includes
+#include <tgl.h>
+
+
+//*****************************************************************************************
+//    TReplicatorMirror implementation
+//*****************************************************************************************
+
+class TReplicatorMirror final : public TReplicator {
+  Q_DECLARE_TR_FUNCTIONS(TReplicatorAffine)
+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(50, 0)) )
+  {
+    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;
+      }
+    }
+    return b;
+  }
+  
+  
+  TAffine getAffine(const TAffine &toTool = TAffine()) const {
+    TPointD c = toTool*m_a.position;
+    TPointD x = toTool*fixB() - c;
+    if (!(norm2(x) > TConsts::epsilon*TConsts::epsilon))
+      x = TPointD(1, 0);
+    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 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<TReplicatorMirror> replicatorMirror("replicatorMirror");
diff --git a/toonz/sources/tnztools/replicator.cpp b/toonz/sources/tnztools/replicator.cpp
index febbf9e..411966d 100644
--- a/toonz/sources/tnztools/replicator.cpp
+++ b/toonz/sources/tnztools/replicator.cpp
@@ -40,6 +40,19 @@ TReplicator::getModifiers(const TAffine&, TInputModifier::List&) 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 - 1;
+  assert(min < max && def < max);
+  TIntProperty *property = new TIntProperty(id.str(), min, max, def);
+  property->setSpinner();
+  return property;
+}
+
+//---------------------------------------------------------------------------------------------------
+
 int
 TReplicator::scanReplicators(
   TTool *tool,
diff --git a/toonz/sources/tnztools/track.cpp b/toonz/sources/tnztools/track.cpp
index 2637216..2d0df30 100644
--- a/toonz/sources/tnztools/track.cpp
+++ b/toonz/sources/tnztools/track.cpp
@@ -11,6 +11,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
 //*****************************************************************************************