diff --git a/toonz/sources/common/tcore/tmathutil.cpp b/toonz/sources/common/tcore/tmathutil.cpp
index 4ce50a9..1d31cab 100644
--- a/toonz/sources/common/tcore/tmathutil.cpp
+++ b/toonz/sources/common/tcore/tmathutil.cpp
@@ -738,6 +738,114 @@ int rootFinding(const std::vector<double> &in_poly, std::vector<double> &sol) {
 
 //-----------------------------------------------------------------------------
 
+int solveEquation2(std::complex<double> *roots, double a, double b, double c) {
+  if (isAlmostZero(a)) {
+    if (isAlmostZero(b)) return 0;
+    roots[0] = -c/b;
+    return 1;
+  }
+
+  double D = b*b - a*c*4;
+  double k = 0.5/a;
+  std::complex<double> d = D < 0
+                         ? std::complex<double>(0, sqrt(-D))
+                         : std::complex<double>(sqrt(D));
+  roots[0] = (-b - d)*k;
+  roots[1] = (-b + d)*k;
+  return 2;
+}
+
+//-----------------------------------------------------------------------------
+
+int solveEquation3(std::complex<double> *roots, double a, double b, double c, double d) {
+  if (isAlmostZero(a))
+    return solveEquation2(roots, b, c, d);
+
+  // x = y - b/(3*a)
+  // y*y*y + p*y + q = 0
+  double p = (3*a*c - b*b)/(3*a*a);
+  double q = (27*a*a*d - 9*a*b*c + 2*b*b*b)/(27*a*a*a);
+
+  double Q = p*p*p/27 + q*q/4;
+  std::complex<double> Qs = Q < 0
+                          ? std::complex<double>(0, sqrt(-Q))
+                          : std::complex<double>(sqrt(Q));
+  std::complex<double> A = pow(-q/2 + Qs, 1.0/3);
+  std::complex<double> B = pow(-q/2 - Qs, 1.0/3);
+
+  // choose complimentary B for A (A*B must be equal -p/3)
+  std::complex<double> rot(-0.5, sqrt(3.0)/2);
+  if (!isAlmostZero(A*B + p/3)) B *= rot;
+  if (!isAlmostZero(A*B + p/3)) B *= rot;
+
+  std::complex<double> Y = (A - B)*std::complex<double>(0, sqrt(3.0));
+  std::complex<double> y0 = A + B;
+  std::complex<double> y1 = (-y0 - Y)/2.0;
+  std::complex<double> y2 = (-y0 + Y)/2.0;
+
+  double dd = b/(3*a);
+  roots[0] = y0 - dd;
+  roots[1] = y1 - dd;
+  roots[2] = y2 - dd;
+  return 3;
+}
+
+//-----------------------------------------------------------------------------
+
+int solveEquation4(std::complex<double> *roots, double a, double b, double c, double d, double e) {
+  if (isAlmostZero(a))
+    return solveEquation3(roots, b, c, d, e);
+
+  // x = y - b/(4*a)
+  // y^4 + p*y^2 + q*y + r = 0
+  double dd = b/(4*a);
+  double p = (8*a*c - 3*b*b)/(8*a*a);
+  double q = (8*a*a*d - 4*a*b*c + b*b*b)/(8*a*a*a);
+  double r = (256*a*a*a*e - 64*a*a*b*d + 16*a*b*b*c - 3*b*b*b*b)/(256*a*a*a*a);
+
+  if (isAlmostZero(q)) {
+    // biquadratic equation
+    // y^4 + p*y^2 + r = 0
+    // handling this special case will give us more accurate results
+    std::complex<double> y[2];
+    solveEquation2(y, 1, p, r);
+    y[0] = sqrt(y[0]);
+    y[1] = sqrt(y[1]);
+    roots[0] = -y[0] - dd;
+    roots[1] =  y[0] - dd;
+    roots[2] = -y[1] - dd;
+    roots[3] =  y[1] - dd;
+    return 4;
+  }
+
+  // solve cubic equation
+  // z*z*z + (p/2)*z*z + ((p*p - 4*r)/16)*z - q*q/64 = 0
+  double pp = p/2;
+  double qq = (p*p - 4*r)/16;
+  double rr = -q*q/64;
+  std::complex<double> z[3];
+  solveEquation3(z, 1, pp, qq, rr);
+
+  z[0] = sqrt(z[0]);
+  z[1] = sqrt(z[1]);
+  z[2] = sqrt(z[2]);
+
+  // we need to find signs combination where following is valid:
+  // (+-z0)*(+-z1)*(+-z2) = -q/8
+  // magnitudes are always equals, we need to check signs only
+  std::complex<double> zzz = z[0]*z[1]*z[2];
+  if ((zzz.real() > 0) == (q > 0))
+    z[0] = -z[0];
+
+  roots[0] =  z[0] - z[1] - z[2] - dd;
+  roots[1] = -z[0] + z[1] - z[2] - dd;
+  roots[2] = -z[0] - z[1] + z[2] - dd;
+  roots[3] =  z[0] + z[1] + z[2] - dd;
+  return 4;
+}
+
+//-----------------------------------------------------------------------------
+
 /*
 */
 int numberOfRootsInInterval(int order, const double *polyH, double min,
diff --git a/toonz/sources/include/tgeometry.h b/toonz/sources/include/tgeometry.h
index 7654e26..f5c9a81 100644
--- a/toonz/sources/include/tgeometry.h
+++ b/toonz/sources/include/tgeometry.h
@@ -39,10 +39,10 @@ class TPointT {
 public:
   T x, y;
 
-  TPointT() : x(0), y(0){};
-  TPointT(T _x, T _y) : x(_x), y(_y){};
-  TPointT(const TPointT &point) : x(point.x), y(point.y){};
-  explicit TPointT(const TPoint4T<T> &point);
+  inline TPointT() : x(0), y(0){};
+  inline TPointT(T _x, T _y) : x(_x), y(_y){};
+  inline TPointT(const TPointT &point) : x(point.x), y(point.y){};
+  inline explicit TPointT(const TPoint4T<T> &point);
 
   inline TPointT &operator=(const TPointT &a) {
     x = a.x;
@@ -77,11 +77,11 @@ public:
     T a[4];
   };
 
-  TPoint4T():
+  inline TPoint4T():
     x(), y(), z(), w() { };
-  TPoint4T(T x, T y, T z, T w):
+  inline TPoint4T(T x, T y, T z, T w):
     x(x), y(y), z(z), w(w) { };
-  explicit TPoint4T(const TPointT<T> &p, T w = (T)1):
+  inline explicit TPoint4T(const TPointT<T> &p, T w = (T)1):
       x(p.x), y(p.y), z(), w(w) { };
 };
 
@@ -223,7 +223,7 @@ inline double tdistance2(const TPointD &p1, const TPointD &p2) {
 }
 
 inline bool operator==(const TPointD &p0, const TPointD &p1) {
-  return tdistance2(p0, p1) < TConsts::epsilon * TConsts::epsilon;
+  return tdistance2(p0, p1) <= TConsts::epsilon * TConsts::epsilon;
 }
 inline bool operator!=(const TPointD &p0, const TPointD &p1) {
   return !(p0 == p1);
@@ -1474,7 +1474,7 @@ public:
 
     inline int operator-(const Iterator &i) const {
       assert(m_flip == i.m_flip && m_begin == i.m_begin && m_end == i.m_end && m_prebegin == i.m_prebegin);
-      int ii = m_current - i.m_current;
+      int ii = (int)(m_current - i.m_current);
       return ii < 0 ? ii + size() : ii;
     }
 
diff --git a/toonz/sources/include/tmathutil.h b/toonz/sources/include/tmathutil.h
index df56563..15fec88 100644
--- a/toonz/sources/include/tmathutil.h
+++ b/toonz/sources/include/tmathutil.h
@@ -7,6 +7,7 @@
 #include "texception.h"
 
 #include <numeric>
+#include <complex>
 
 #ifndef __sgi
 #include <cmath>
@@ -128,6 +129,48 @@ DVAPI int rootFinding(const std::vector<double> &poly,
 //-----------------------------------------------------------------------------
 
 /*!
+  Find complex roots of a quadratic equation
+    a*x^2 + b*x + c = 0
+  \ret count of roots found:
+    0 if a and b are almost zero
+    1 if a is almost zero
+    2 if a is not almost zero
+    roots may have duplicates, count depends only from type of equation
+ */
+DVAPI int solveEquation2(std::complex<double> *roots, double a, double b, double c);
+
+//-----------------------------------------------------------------------------
+
+/*!
+  Find complex roots of a cubic equation:
+    a*x^3 + b*x^2 + c*x + d = 0
+  \ret count of roots found:
+    0 if a, b and c are almost zero
+    1 if a and b are almost zero
+    2 if a is almost zero
+    3 if a is not almost zero
+    roots may have duplicates, count depends only from type of equation
+ */
+DVAPI int solveEquation3(std::complex<double> *roots, double a, double b, double c, double d);
+
+//-----------------------------------------------------------------------------
+
+/*!
+  Find complex roots of a power of four equation:
+    a*x^4 + b*x^3 + c*x^2 + d*x + e = 0
+  \ret count of roots found:
+    0 if a, b, c and d are almost zero
+    1 if a, b and c are almost zero
+    2 if a and b are almost zero
+    3 if a is almost zero
+    4 if a is not almost zero
+    roots may have duplicates, count depends only from type of equation
+ */
+DVAPI int solveEquation4(std::complex<double> *roots, double a, double b, double c, double d, double e);
+
+//-----------------------------------------------------------------------------
+
+/*!
   Check if val is nearest to epsilon.
   \par val value to test
   \par eps tolerance required
@@ -140,6 +183,19 @@ inline bool isAlmostZero(double val, double eps = TConsts::epsilon) {
 //-----------------------------------------------------------------------------
 
 /*!
+  Check if complex val is nearest to epsilon.
+  \par val value to test
+  \par eps tolerance required
+  \ret true if value is nearest to zero
+ */
+inline bool isAlmostZero(std::complex<double> val, double eps = TConsts::epsilon) {
+  return val.real()*val.real() + val.imag()*val.imag() < eps*eps;
+}
+
+
+//-----------------------------------------------------------------------------
+
+/*!
   Find number of roots of a poly in interval min max.
   \par order degree of polynomious
   \par coeff of poly in crescent order
diff --git a/toonz/sources/include/tools/assistant.h b/toonz/sources/include/tools/assistant.h
index c2b2322..14931aa 100644
--- a/toonz/sources/include/tools/assistant.h
+++ b/toonz/sources/include/tools/assistant.h
@@ -66,8 +66,15 @@ public:
 
   virtual TTrackPoint transformPoint(const TTrackPoint &point) const
     { return point; }
+  
+  // this function uses in calcTrackWeight to select best guideline,
+  // does not use for drawing
+  virtual TPointD nearestPoint(const TPointD &point) const
+    { return transformPoint(TTrackPoint(point)).position; }
+  
   virtual void draw(bool active, bool enabled) const
     { }
+  
   void draw(bool active = false) const
     { draw(active, true); }
 
@@ -304,6 +311,7 @@ protected:
   double getDrawingGridAlpha() const;
 
   void drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize, double alpha) const;
+  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;
 
diff --git a/toonz/sources/include/tools/assistants/guidelineellipse.h b/toonz/sources/include/tools/assistants/guidelineellipse.h
index c8b1607..faefda3 100644
--- a/toonz/sources/include/tools/assistants/guidelineellipse.h
+++ b/toonz/sources/include/tools/assistants/guidelineellipse.h
@@ -28,17 +28,27 @@ class DVAPI TGuidelineEllipse : public TGuideline {
 public:
   const TAffine matrix;
   const TAffine matrixInv;
+  const double Rx;
+  const double Ry;
 
   TGuidelineEllipse(
     bool enabled,
     double magnetism,
-    TAffine matrix );
+    const TAffine &matrix );
 
   TGuidelineEllipse(
     bool enabled,
     double magnetism,
-    TAffine matrix,
-    TAffine matrixInv );
+    const TAffine &matrix,
+    const TAffine &matrixInv );
+
+  TGuidelineEllipse(
+    bool enabled,
+    double magnetism,
+    const TAffine &matrix,
+    const TAffine &matrixInv,
+    double Rx,
+    double Ry );
 
   //! returns false when ellipse is invisible
   static bool truncateEllipse(
@@ -49,6 +59,7 @@ public:
   static int calcSegmentsCount(const TAffine &ellipseMatrix, double pixelSize);
 
   TTrackPoint transformPoint(const TTrackPoint &point) const override;
+  TPointD nearestPoint(const TPointD &point) const override;
   void draw(bool active, bool enabled) const override;
 };
 
diff --git a/toonz/sources/include/tools/inputmanager.h b/toonz/sources/include/tools/inputmanager.h
index 7d9a58d..323dba1 100644
--- a/toonz/sources/include/tools/inputmanager.h
+++ b/toonz/sources/include/tools/inputmanager.h
@@ -47,104 +47,6 @@ typedef TSmartPointerT<TInputModifier> TInputModifierP;
 
 
 //*****************************************************************************************
-//    TInputSavePoint definition
-//*****************************************************************************************
-
-class DVAPI TInputSavePoint {
-public:
-  class DVAPI Holder {
-  private:
-    TInputSavePoint *m_savePoint;
-    bool m_lock = false;
-
-  public:
-    inline explicit Holder(TInputSavePoint *savePoint = NULL, bool lock = true):
-      m_savePoint(), m_lock()
-      { set(savePoint, lock); }
-    inline Holder(const Holder &other):
-      m_savePoint(), m_lock()
-      { *this = other; }
-    inline ~Holder()
-      { reset(); }
-
-    inline Holder& operator= (const Holder &other)
-      { set(other.m_savePoint, other.m_lock); return *this; }
-
-    inline operator bool () const
-      { return assigned(); }
-
-    inline void set(TInputSavePoint *savePoint, bool lock) {
-      if (m_savePoint != savePoint) {
-        if (m_savePoint) {
-          if (m_lock) m_savePoint->unlock();
-          m_savePoint->release();
-        }
-        m_savePoint = savePoint;
-        m_lock = lock;
-        if (m_savePoint) {
-          m_savePoint->hold();
-          if (m_lock) savePoint->lock();
-        }
-      } else
-      if (m_lock != lock) {
-        if (m_savePoint) {
-          if (lock) m_savePoint->lock();
-               else m_savePoint->unlock();
-        }
-        m_lock = lock;
-      }
-    }
-
-    inline void reset()
-      { set(NULL, false); }
-    inline void setLock(bool lock)
-      { set(m_savePoint, lock); }
-    inline void lock()
-      { setLock(true); }
-    inline void unlock()
-      { setLock(false); }
-
-    inline TInputSavePoint* savePoint() const
-      { return m_savePoint; }
-    inline bool assigned() const
-      { return savePoint(); }
-    inline bool locked() const
-      { return m_savePoint && m_lock; }
-    inline bool available() const
-      { return m_savePoint && m_savePoint->available; }
-    inline bool isFree() const
-      { return !m_savePoint || m_savePoint->isFree(); }
-  };
-
-  typedef std::vector<Holder> List;
-
-private:
-  int m_refCount;
-  int m_lockCount;
-
-  inline void hold()
-    { ++m_refCount; }
-  inline void release()
-    { if ((--m_refCount) <= 0) delete this; }
-  inline void lock()
-    { ++m_lockCount; }
-  inline void unlock()
-    { --m_lockCount; }
-
-public:
-  bool available;
-
-  inline explicit TInputSavePoint(bool available = false):
-    m_refCount(), m_lockCount(), available(available) { }
-  inline bool isFree() const
-    { return m_lockCount <= 0; }
-
-  static inline Holder create(bool available = false)
-    { return Holder(new TInputSavePoint(available)); }
-};
-
-
-//*****************************************************************************************
 //    TInputModifier definition
 //*****************************************************************************************
 
@@ -164,11 +66,9 @@ public:
 
   virtual void modifyTrack(
     const TTrack &track,
-    const TInputSavePoint::Holder &savePoint,
     TTrackList &outTracks );
   virtual void modifyTracks(
     const TTrackList &tracks,
-    const TInputSavePoint::Holder &savePoint,
     TTrackList &outTracks );
 
   virtual void modifyHover(
@@ -221,30 +121,11 @@ public:
   
   virtual void inputHoverEvent(const TInputManager &manager);
 
-  /*! paint single track-point at the top painting level */
-  virtual void inputPaintTrackPoint(const TTrackPoint &point, const TTrack &track, bool firstTrack);
-
-  /*! create new painting level and return true, or do nothing and return false
-      was:            ------O-------O------
-      become:         ------O-------O------O */
-  virtual bool inputPaintPush() { return false; }
-  /*! paint several track-points at the top painting level
-      was:            ------O-------O------
-      become:         ------O-------O------------ */
+  virtual void inputPaintTracksBegin() { }
+  virtual void inputPaintTrackPoint(const TTrackPoint &point, const TTrack &track, bool firstTrack, bool preview);
+  virtual void inputPaintTracksEnd() { }
   virtual void inputPaintTracks(const TTrackList &tracks);
-  /*! try to merge N top painting levels and return count of levels that actually merged
-      was:            ------O-------O------O------
-      become (N = 2): ------O--------------------- */
-  virtual int inputPaintApply(int count) { return 0; }
-  /*! reset top level to initial state
-      was:            ------O-------O------O------
-      become:         ------O-------O------O */
-  virtual void inputPaintCancel() { }
-  /*! cancel and pop N painting levels
-      was:            ------O-------O------O------
-      become (N = 2): ------O------- */
-  virtual void inputPaintPop(int count) { }
-  
+
   virtual void inputInvalidateRect(const TRectD &bounds) { }
   
   virtual TTool* inputGetTool() { return nullptr; };
@@ -256,26 +137,15 @@ public:
 //*****************************************************************************************
 
 class DVAPI TInputManager {
-public:
-  class TrackHandler: public TTrackHandler {
-  public:
-    std::vector<int> saves;
-    TrackHandler(TTrack &original, int keysCount = 0):
-      TTrackHandler(original), saves(keysCount, 0)
-      { }
-  };
-
 private:
   TTimerTicks m_lastTicks;
   TInputHandler *m_handler;
   TInputModifier::List m_modifiers;
   std::vector<TTrackList> m_tracks;
   std::vector<THoverList> m_hovers;
-  TInputSavePoint::List m_savePoints;
   TRectD m_prevBounds;
   TRectD m_nextBounds;
   bool m_started;
-  int m_savePointsSent;
 
   static TInputState::TouchId m_lastTouchId;
 
@@ -294,8 +164,6 @@ private:
     return m_lastTicks = ticks;
   }
   
-  void paintRollbackTo(int saveIndex, TTrackList &subTracks);
-  void paintApply(int count, TTrackList &subTracks);
   void paintTracks();
 
   int trackCompare(
diff --git a/toonz/sources/include/tools/modifiers/modifierassistants.h b/toonz/sources/include/tools/modifiers/modifierassistants.h
index 3dcd35d..0ba8059 100644
--- a/toonz/sources/include/tools/modifiers/modifierassistants.h
+++ b/toonz/sources/include/tools/modifiers/modifierassistants.h
@@ -29,7 +29,6 @@ public:
   class DVAPI Modifier: public TTrackModifier {
   public:
     bool initialized;
-    TInputSavePoint::Holder savePoint;
     TGuidelineList guidelines;
 
     Modifier(TTrackHandler &handler);
@@ -52,7 +51,6 @@ public:
 
   void modifyTrack(
     const TTrack &track,
-    const TInputSavePoint::Holder &savePoint,
     TTrackList &outTracks ) override;
 
   TRectD calcDrawBounds(const TTrackList &tracks, const THoverList &hovers) override;
diff --git a/toonz/sources/include/tools/modifiers/modifierline.h b/toonz/sources/include/tools/modifiers/modifierline.h
index a6966a2..495f711 100644
--- a/toonz/sources/include/tools/modifiers/modifierline.h
+++ b/toonz/sources/include/tools/modifiers/modifierline.h
@@ -33,14 +33,12 @@ public:
 
     bool fixAngle;
     double maxPressure;
-    TInputSavePoint::Holder savePoint;
-
+    
     TTrackPoint calcPoint(double originalIndex) override;
   };
 
   void modifyTrack(
     const TTrack &track,
-    const TInputSavePoint::Holder &savePoint,
     TTrackList &outTracks ) override;
 };
 
diff --git a/toonz/sources/include/tools/modifiers/modifiersegmentation.h b/toonz/sources/include/tools/modifiers/modifiersegmentation.h
index af4278c..48d8872 100644
--- a/toonz/sources/include/tools/modifiers/modifiersegmentation.h
+++ b/toonz/sources/include/tools/modifiers/modifiersegmentation.h
@@ -27,18 +27,21 @@
 class DVAPI TModifierSegmentation: public TInputModifier {
 private:
   TPointD m_step;
+  int m_maxLevel;
   
-  void addSegments(TTrack &track, const TTrackPoint &p0, const TTrackPoint &p1, int level = 0);
+  void addSegments(TTrack &track, const TTrackPoint &p0, const TTrackPoint &p1, int maxLevel);
 
 public:
-  TModifierSegmentation(const TPointD &step = TPointD(1.0, 1.0));
+  TModifierSegmentation(const TPointD &step = TPointD(1.0, 1.0), int level = 10);
 
   void setStep(const TPointD &step);
-  const TPointD& getStep() const { return m_step; }
+  inline const TPointD& getStep() const { return m_step; }
+
+  void setMaxLevel(int maxLevel);
+  inline int getMaxLevel() const { return m_maxLevel; }
   
   void modifyTrack(
     const TTrack &track,
-    const TInputSavePoint::Holder &savePoint,
     TTrackList &outTracks ) override;
 };
 
diff --git a/toonz/sources/include/tools/modifiers/modifiersmooth.h b/toonz/sources/include/tools/modifiers/modifiersmooth.h
new file mode 100644
index 0000000..6968d38
--- /dev/null
+++ b/toonz/sources/include/tools/modifiers/modifiersmooth.h
@@ -0,0 +1,54 @@
+#pragma once
+
+#ifndef MODIFIERSMOOTH_INCLUDED
+#define MODIFIERSMOOTH_INCLUDED
+
+// TnzTools includes
+#include <tools/inputmanager.h>
+
+
+#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
+
+
+//===================================================================
+
+//*****************************************************************************************
+//    TModifierSmooth definition
+//*****************************************************************************************
+
+class DVAPI TModifierSmooth: public TInputModifier {
+public:
+  class DVAPI Modifier: public TTrackModifier {
+  public:
+    const int radius;
+    TTrack *smoothedTrack;
+
+    Modifier(TTrackHandler &handler, int radius);
+    TTrackPoint calcPoint(double originalIndex) override;
+  };
+
+private:
+  int m_radius;
+  
+public:
+  TModifierSmooth(int radius = 10);
+
+  void setRadius(int radius);
+  int getRadius() const { return m_radius; }
+  
+  void modifyTrack(
+    const TTrack &track,
+    TTrackList &outTracks ) override;
+};
+
+
+#endif
+
diff --git a/toonz/sources/include/tools/modifiers/modifiertangents.h b/toonz/sources/include/tools/modifiers/modifiertangents.h
index 8ead779..a7e6232 100644
--- a/toonz/sources/include/tools/modifiers/modifiertangents.h
+++ b/toonz/sources/include/tools/modifiers/modifiertangents.h
@@ -31,17 +31,15 @@ public:
     explicit Modifier(TTrackHandler &handler):
       TTrackModifier(handler) { }
 
-    TInputSavePoint::Holder savePoint;
     TTrackTangentList tangents;
 
     TTrackPoint calcPoint(double originalIndex) override;
   };
 
-  TTrackTangent calcLastTangent(const TTrack &track) const;
+  TTrackTangent calcTangent(const TTrack &track, int index) const;
 
   void modifyTrack(
     const TTrack &track,
-    const TInputSavePoint::Holder &savePoint,
     TTrackList &outTracks ) override;
 };
 
diff --git a/toonz/sources/include/tools/modifiers/modifiertest.h b/toonz/sources/include/tools/modifiers/modifiertest.h
index 91911af..e410547 100644
--- a/toonz/sources/include/tools/modifiers/modifiertest.h
+++ b/toonz/sources/include/tools/modifiers/modifiertest.h
@@ -50,7 +50,6 @@ public:
   TModifierTest(int count, double radius);
 
   void modifyTrack(const TTrack &track,
-                   const TInputSavePoint::Holder &savePoint,
                    TTrackList &outTracks) override;
 };
 
diff --git a/toonz/sources/include/tools/track.h b/toonz/sources/include/tools/track.h
index eb12057..c9268d4 100644
--- a/toonz/sources/include/tools/track.h
+++ b/toonz/sources/include/tools/track.h
@@ -183,10 +183,12 @@ public:
   mutable TTrackToolHandlerP toolHandler;
   mutable int pointsRemoved;
   mutable int pointsAdded;
+  mutable int fixedPointsAdded;
 
 private:
   TTrackPointList m_points;
   const TTrackPoint m_none;
+  int m_pointsFixed;
 
 public:
 
@@ -208,7 +210,7 @@ public:
   inline TTimerTicks ticks() const
     { return keyHistory.ticks(); }
   inline bool changed() const
-    { return pointsAdded != 0 || pointsRemoved != 0; }
+    { return pointsRemoved || pointsAdded || fixedPointsAdded; }
 
   const TTrack* root() const;
   int level() const;
@@ -236,6 +238,10 @@ public:
 
   inline int size() const
     { return (int)m_points.size(); }
+  inline int fixedSize() const
+    { return m_pointsFixed; }
+  inline int previewSize() const
+    { return size() - fixedSize(); }
   inline bool empty() const
     { return m_points.empty(); }
   inline const TTrackPoint& front() const
@@ -244,6 +250,8 @@ public:
     { return point(size() - 1); }
   inline bool finished() const
     { return !m_points.empty() && back().final; }
+  inline bool fixedFinished() const
+    { return finished() && !previewSize(); }
   inline const TTrackPoint& operator[] (int index) const
     { return point(index); }
   inline const TTrackPointList& points() const
@@ -253,14 +261,21 @@ public:
     { pointsRemoved = 0; }
   inline void resetAdded() const
     { pointsAdded = 0; }
+  inline void resetFixedAdded() const
+    { fixedPointsAdded = 0; }
   inline void resetChanges() const
-    { resetRemoved(); resetAdded(); }
+    { resetRemoved(); resetAdded(); resetFixedAdded(); }
 
-  void push_back(const TTrackPoint &point);
+  void push_back(const TTrackPoint &point, bool fixed);
   void pop_back(int count = 1);
+  void fix_points(int count = 1);
 
   inline void truncate(int count)
     { pop_back(size() - count); }
+  inline void fix_to(int count)
+    { fix_points(count - fixedSize()); }
+  inline void fix_all()
+    { fix_to(size()); }
 
   inline const TTrackPoint& current() const
     { return point(size() - pointsAdded); }
@@ -381,9 +396,9 @@ public:
     return TTrackPoint(
       interpolationSpline(p0.position      , p1.position      , t0.position , t1.position , l),
       interpolationLinear(p0.pressure      , p1.pressure      , l),
-      //interpolationSpline(p0.pressure      , p1.pressure      , t0.pressure , t1.pressure , l),
+    //interpolationSpline(p0.pressure      , p1.pressure      , t0.pressure , t1.pressure , l),
       interpolationLinear(p0.tilt          , p1.tilt          , l),
-      //interpolationSpline(p0.tilt          , p1.tilt          , t0.tilt     , t1.tilt     , l),
+    //interpolationSpline(p0.tilt          , p1.tilt          , t0.tilt     , t1.tilt     , l),
       interpolationLinear(p0.originalIndex , p1.originalIndex , l),
       interpolationLinear(p0.time          , p1.time          , l),
       interpolationLinear(p0.length        , p1.length        , l),
diff --git a/toonz/sources/include/toonz/rasterstrokegenerator.h b/toonz/sources/include/toonz/rasterstrokegenerator.h
index e2c7857..1de01d3 100644
--- a/toonz/sources/include/toonz/rasterstrokegenerator.h
+++ b/toonz/sources/include/toonz/rasterstrokegenerator.h
@@ -70,7 +70,7 @@ public:
   void generateStroke(bool isPencil, bool isStraight = false) const;
 
   // Ritorna m_points
-  std::vector<TThickPoint> getPointsSequence() { return m_points; }
+  const std::vector<TThickPoint>& getPointsSequence() { return m_points; }
   void setPointsSequence(const std::vector<TThickPoint> &points) {
     m_points = points;
   }
diff --git a/toonz/sources/tnztools/CMakeLists.txt b/toonz/sources/tnztools/CMakeLists.txt
index 101eb4e..2675853 100644
--- a/toonz/sources/tnztools/CMakeLists.txt
+++ b/toonz/sources/tnztools/CMakeLists.txt
@@ -54,7 +54,7 @@ set(HEADERS
     ../include/tools/modifiers/modifiertangents.h
     ../include/tools/modifiers/modifiertest.h
     ../include/tools/modifiers/modifiersegmentation.h
-    ../include/tools/modifiers/modifierassistants.h
+    ../include/tools/modifiers/modifiersmooth.h
     ../include/tools/assistants/guidelineline.h
     ../include/tools/assistants/guidelineellipse.h
 )
@@ -130,6 +130,7 @@ set(SOURCES
     modifiers/modifiertangents.cpp
     modifiers/modifiertest.cpp
     modifiers/modifiersegmentation.cpp
+    modifiers/modifiersmooth.cpp
     assistants/guidelineline.cpp
     assistants/guidelineellipse.cpp
     assistants/assistantvanishingpoint.cpp
diff --git a/toonz/sources/tnztools/assistant.cpp b/toonz/sources/tnztools/assistant.cpp
index 5e0ec9c..826ab4e 100644
--- a/toonz/sources/tnztools/assistant.cpp
+++ b/toonz/sources/tnztools/assistant.cpp
@@ -78,14 +78,15 @@ TGuideline::calcTrackWeight(const TTrack &track, const TAffine &toScreen, bool &
       double weight = length*logNormalDistribuitionUnscaled(midStepLength, snapLenght, snapScale);
       sumWeight += weight;
 
-      TTrackPoint ntp = transformPoint(mid);
-      double deviation = tdistance(toScreen*ntp.position, p);
+      double deviation = tdistance(
+        toScreen*mid.position,
+        toScreen*nearestPoint(mid.position) );
       sumDeviation += weight*deviation;
     }
     prev = p;
 
     if (sumLength >= maxLength)
-      { outLongEnough = true; break; }
+      { outLongEnough = i < track.fixedSize(); break; }
   }
   return sumWeight > TConsts::epsilon
        ? sumDeviation/sumWeight
@@ -429,6 +430,14 @@ TAssistant::drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize, 
 //---------------------------------------------------------------------------------------------------
 
 void
+TAssistant::drawMark(const TPointD &p, const TPointD &normal, double pixelSize, double alpha) const {
+  TPointD d = normal*5*pixelSize;
+  drawSegment(p - d,p + d, pixelSize, alpha);
+}
+
+//---------------------------------------------------------------------------------------------------
+
+void
 TAssistant::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 };
diff --git a/toonz/sources/tnztools/assistants/assistantellipse.cpp b/toonz/sources/tnztools/assistants/assistantellipse.cpp
index 236c6a7..db6055e 100644
--- a/toonz/sources/tnztools/assistants/assistantellipse.cpp
+++ b/toonz/sources/tnztools/assistants/assistantellipse.cpp
@@ -249,11 +249,9 @@ private:
 
   void drawRuler(const TAffine &ellipseMatrix, double pixelSize) const {
     double minStep = 10.0*pixelSize;
-    double alpha = getDrawingGridAlpha();
+    double alpha = getDrawingAlpha();
 
-    const TAffine &em = ellipseMatrix;
-    double r = sqrt(0.5*(norm2(TPointD(em.a11, em.a21)) + norm2(TPointD(em.a12, em.a22))));
-    double actualMinStep = minStep/r;
+    TAffine em = ellipseMatrix;
     TAffine ellipseMatrixInv = ellipseMatrix.inv();
     TPointD g0 = ellipseMatrixInv*m_grid0.position;
     TPointD g1 = ellipseMatrixInv*m_grid1.position;
@@ -262,22 +260,55 @@ private:
     double ga0 = atan(g0);
     double ga1 = atan(g1);
 
+    // x and y radiuses
+    TPointD r( norm2(TPointD(em.a11, em.a21)), norm2(TPointD(em.a12, em.a22)) );
+    double avgR = 0.5*(r.x + r.y);
+    if (avgR <= TConsts::epsilon*TConsts::epsilon) return;
+    avgR = sqrt(avgR);
+    double actualMinStep = minStep/avgR;
+    r.x = sqrt(r.x);
+    r.y = sqrt(r.y);
+    
+    // remove radiuses from ellipse matrix
+    double rkx = r.x > TConsts::epsilon ? 1.0/r.x : 0.0;
+    double rky = r.y > TConsts::epsilon ? 1.0/r.y : 0.0;
+    em.a11 *= rkx; em.a21 *= rkx;
+    em.a12 *= rky; em.a22 *= rky;
+    
     if (getPerspective()) {
       // draw perspective
       if (ga0 < 0.0) { if (ga1 > 0.0) ga1 -= M_2PI; }
                 else { if (ga1 < 0.0) ga1 += M_2PI; }
       double k = 0.0, begin = 0.0, end = 0.0;
       if (!calcPerspectiveStep(actualMinStep, 0.0, M_2PI, 0.0, fabs(ga0), ga1, k, begin, end)) return;
-      for(double x = begin; fabs(x) < fabs(end); x *= k)
-        drawDot(ellipseMatrix * TPointD(cos(x), (ga0 < 0.0 ? -1.0 : 1.0)*sin(x)));
+      for(double a = begin; fabs(a) < fabs(end); a *= k) {
+        TPointD p( cos(a), (ga0 < 0.0 ? -1.0 : 1.0)*sin(a) );
+        TPointD n( p.x*r.y, p.y*r.x ); // perp to allipse
+        double nl2 = norm2(n);
+        if (nl2 > TConsts::epsilon*TConsts::epsilon) {
+          p.x *= r.x;
+          p.y *= r.y;
+          n = n*(1.0/sqrt(nl2));
+          drawMark(em*p, em.transformDirection(n), pixelSize, alpha);
+        }
+      }
     } else {
       // draw linear
       double da = ga1 - ga0;
       if (da < 0.0)         { da = -da;        std::swap(ga0, ga1); }
       if (ga1 - ga0 > M_PI) { da = M_2PI - da; std::swap(ga0, ga1); }
       if (da < actualMinStep) return;
-      for(double a = ga0 - floor(M_PI/da)*da; a < ga0 + M_PI; a += da)
-        drawDot(ellipseMatrix * TPointD(cos(a), sin(a)));
+      for(double a = ga0 - floor(M_PI/da)*da; a < ga0 + M_PI; a += da) {
+        TPointD p( cos(a), sin(a) );
+        TPointD n( p.x*r.y, p.y*r.x ); // perp to allipse
+        double nl2 = norm2(n);
+        if (nl2 > TConsts::epsilon*TConsts::epsilon) {
+          p.x *= r.x;
+          p.y *= r.y;
+          n = n*(1.0/sqrt(nl2));
+          drawMark(em*p, em.transformDirection(n), pixelSize, alpha);
+        }
+      }
     }
   }
 
diff --git a/toonz/sources/tnztools/assistants/assistantline.cpp b/toonz/sources/tnztools/assistants/assistantline.cpp
index a306f7b..d87134f 100644
--- a/toonz/sources/tnztools/assistants/assistantline.cpp
+++ b/toonz/sources/tnztools/assistants/assistantline.cpp
@@ -159,13 +159,14 @@ public:
 private:
   void drawRuler(const TPointD &a, const TPointD &b, double pixelSize, bool perspective) const {
     double minStep = 10.0*pixelSize;
-    double alpha = getDrawingGridAlpha();
+    double alpha = getDrawingAlpha();
 
     TPointD direction = b - a;
     double l2 = norm2(direction);
     if (l2 <= TConsts::epsilon*TConsts::epsilon) return;
     double dirLen = sqrt(l2);
     TPointD dirProj = direction*(1.0/l2);
+    TPointD normal = TPointD(-direction.y, direction.x)*(1.0/dirLen);
 
     double xg0 = dirProj*(m_grid0.position - a);
     double xg1 = dirProj*(m_grid1.position - a);
@@ -176,13 +177,13 @@ private:
       double k = 0.0, begin = 0.0, end = 0.0;
       if (!calcPerspectiveStep(minStep/dirLen, 0.0, 1.0, xa0, xg0, xg1, k, begin, end)) return;
       for(double x = begin; fabs(x) < fabs(end); x *= k)
-        drawDot(a + direction*(xa0 + x), alpha);
+        drawMark(a + direction*(xa0 + x), normal, pixelSize, alpha);
     } else {
       // draw linear
       double dx = fabs(xg1 - xg0);
       if (dx*dirLen < minStep) return;
       for(double x = xg0 - floor(xg0/dx)*dx; x < 1.0; x += dx)
-        drawDot(a + direction*x, alpha);
+        drawMark(a + direction*x, normal, pixelSize, alpha);
     }
   }
 
@@ -318,7 +319,14 @@ public:
       success = TGuidelineLineBase::truncateRay(oneBox, bb, aa);
     else
       success = TGuidelineLineBase::truncateInfiniteLine(oneBox, aa, bb);
-    if (!success) return;
+
+    if (!success) {
+      // line is out of screen, bud grid still can be visible
+      if (grid && getParallel())
+          drawGrid(matrix, matrixInv, pixelSize, restrictA, restrictB, perspective);
+      return;
+    }
+    
     TPointD a = matrixInv*aa;
     TPointD b = matrixInv*bb;
 
diff --git a/toonz/sources/tnztools/assistants/guidelineellipse.cpp b/toonz/sources/tnztools/assistants/guidelineellipse.cpp
index 51014c2..c18934f 100644
--- a/toonz/sources/tnztools/assistants/guidelineellipse.cpp
+++ b/toonz/sources/tnztools/assistants/guidelineellipse.cpp
@@ -3,32 +3,112 @@
 #include <tools/assistants/guidelineellipse.h>
 
 // TnzCore includes
-#include "tgl.h"
+#include <tgl.h>
+#include <tmathutil.h>
+
+
+
+// returns a point nearest to the ellipse with center in zero
+static TPointD findNearestPoint(const TPointD &p, double Rx, double Ry) {
+  Rx = fabs(Rx);
+  Ry = fabs(Ry);
+  TPointD ep;
+  
+  if (isAlmostZero(Rx)) {
+    ep.y = p.y < -Ry ? -Ry
+         : p.y >  Ry ?  Ry : p.y;
+    return ep;
+  }
+
+  if (isAlmostZero(Ry)) {
+    ep.x = p.x < -Rx ? -Rx
+         : p.x >  Rx ?  Rx : p.x;
+    return ep;
+  }
+
+  double k = 1/Rx;
+  double x0 = p.x*k;
+  double y0 = p.y*k;
+  k *= Ry;
+  k *= k; // k = (Ry/Rx)^2
+  double l = k - 1;
+
+  double a = l*l;
+  double b = 2*l*x0;
+  double c = x0*x0 + y0*y0*k - l*l;
+  double d = -b;
+  double e = -x0*x0;
+
+  double dist = INFINITY;
+  std::complex<double> roots[4];
+  int cnt = solveEquation4(roots, a, b, c, d, e);
+  for(int i = 0; i < cnt; ++i) {
+    if (!isAlmostZero(roots[i].imag()))
+      continue;
+    
+    double x = roots[i].real();
+    double y;
+    if (isAlmostZero(fabs(x) - 1)) {
+      y = 0;
+    } else
+    if (fabs(x) < 1) {
+      y = sqrt(k*(1 - x*x));
+      if (y0 < 0) y = -y;
+    } else {
+      continue;
+    }
+
+    double dd = (x0-x)*(x0-x) + (y0-y)*(y0-y);
+    if (dd < dist)
+      { ep.x = x*Rx; ep.y = y*Rx; dist = dd; }
+  }
+  return ep;
+}
+
 
 
 //*****************************************************************************************
 //    TGuidelineEllipse implementation
 //*****************************************************************************************
 
+
 TGuidelineEllipse::TGuidelineEllipse(
   bool enabled,
   double magnetism,
-  TAffine matrix
+  const TAffine &matrix,
+  const TAffine &matrixInv,
+  double Rx,
+  double Ry
 ):
   TGuideline(enabled, magnetism),
   matrix(matrix),
-  matrixInv(matrix.inv()) { }
+  matrixInv(matrixInv),
+  Rx(Rx),
+  Ry(Ry)
+  { }
 
 
 TGuidelineEllipse::TGuidelineEllipse(
   bool enabled,
   double magnetism,
-  TAffine matrix,
-  TAffine matrixInv
+  const TAffine &matrix,
+  const TAffine &matrixInv
 ):
-  TGuideline(enabled, magnetism),
-  matrix(matrix),
-  matrixInv(matrixInv) { }
+  TGuidelineEllipse(
+    enabled, magnetism, matrix, matrixInv,
+    sqrt(matrix.a11*matrix.a11 + matrix.a21*matrix.a21),
+    sqrt(matrix.a12*matrix.a12 + matrix.a22*matrix.a22) )
+  { }
+
+
+TGuidelineEllipse::TGuidelineEllipse(
+  bool enabled,
+  double magnetism,
+  const TAffine &matrix
+):
+  TGuidelineEllipse(enabled, magnetism, matrix, matrix.inv())
+  { }
+
 
 
 TTrackPoint
@@ -43,6 +123,18 @@ TGuidelineEllipse::transformPoint(const TTrackPoint &point) const
 }
 
 
+TPointD
+TGuidelineEllipse::nearestPoint(const TPointD &point) const
+{
+  TPointD p = matrixInv*point;
+  p = findNearestPoint(TPointD(p.x*Rx, p.y*Ry), Rx, Ry);
+  if (!isAlmostZero(Rx)) p.x /= Rx;
+  if (!isAlmostZero(Ry)) p.y /= Ry;
+  return matrix*p;
+}
+
+
+
 bool
 TGuidelineEllipse::truncateEllipse(
   TAngleRangeSet &ranges,
diff --git a/toonz/sources/tnztools/assistants/guidelineline.cpp b/toonz/sources/tnztools/assistants/guidelineline.cpp
index 9018ccb..cc8707a 100644
--- a/toonz/sources/tnztools/assistants/guidelineline.cpp
+++ b/toonz/sources/tnztools/assistants/guidelineline.cpp
@@ -73,12 +73,14 @@ TGuidelineLineBase::truncateInfiniteLine(const TRectD &bounds, TPointD &p0, TPoi
 bool
 TGuidelineLineBase::truncateRay(const TRectD &bounds, TPointD &p0, TPointD &p1) {
   if (bounds.isEmpty()) return false;
-  TRectD b(bounds);
 
-  if (p0.x <= p1.x) { if (b.x0 < p0.x) b.x0 = p0.x; }
-               else { if (b.x1 > p0.x) b.x1 = p0.x; }
-  if (p0.y <= p1.y) { if (b.y0 < p0.y) b.y0 = p0.y; }
-               else { if (b.y1 > p0.y) b.y1 = p0.y; }
+  // restrict bounds by p0
+  // slightly expand this restriction to include vert and hor rays
+  TRectD b(bounds);
+  if (p0.x <= p1.x) { if (b.x0 < p0.x - TConsts::epsilon) b.x0 = p0.x - TConsts::epsilon; }
+               else { if (b.x1 > p0.x + TConsts::epsilon) b.x1 = p0.x + TConsts::epsilon; }
+  if (p0.y <= p1.y) { if (b.y0 < p0.y - TConsts::epsilon) b.y0 = p0.y - TConsts::epsilon; }
+               else { if (b.y1 > p0.y + TConsts::epsilon) b.y1 = p0.y + TConsts::epsilon; }
 
   if (b.isEmpty()) return false;
   return truncateInfiniteLine(b, p0, p1);
@@ -86,7 +88,13 @@ TGuidelineLineBase::truncateRay(const TRectD &bounds, TPointD &p0, TPointD &p1) 
 
 bool
 TGuidelineLineBase::truncateLine(const TRectD &bounds, TPointD &p0, TPointD &p1) {
-  TRectD b = bounds * boundingBox(p0, p1);
+  // restrict bounds by p0 and p1
+  // slightly expand this restriction to include vert and hor lines
+  TRectD b( std::min(p0.x, p1.x) - TConsts::epsilon,
+            std::min(p0.y, p1.y) - TConsts::epsilon,
+            std::max(p0.x, p1.x) + TConsts::epsilon,
+            std::max(p0.y, p1.y) + TConsts::epsilon );
+  b *= bounds;
   if (b.isEmpty()) return false;
   return truncateInfiniteLine(b, p0, p1);
 }
diff --git a/toonz/sources/tnztools/bluredbrush.cpp b/toonz/sources/tnztools/bluredbrush.cpp
index 44f5214..61ee125 100644
--- a/toonz/sources/tnztools/bluredbrush.cpp
+++ b/toonz/sources/tnztools/bluredbrush.cpp
@@ -197,7 +197,14 @@ BluredBrush::~BluredBrush() {}
 
 //----------------------------------------------------------------------------------
 
-void BluredBrush::addPoint(const TThickPoint &p, double opacity) {
+void BluredBrush::addPoint(const TThickPoint &p, double opacity, bool keepDistance) {
+  if (keepDistance) {
+    double dist = norm2(p - m_lastPoint);
+    double d = 0.12 * m_lastPoint.thick;
+    if (dist < d*d)
+      return;
+  }
+  
   double radius      = p.thick * 0.5;
   double brushRadius = m_size * 0.5;
   double scaleFactor = radius / brushRadius;
diff --git a/toonz/sources/tnztools/bluredbrush.h b/toonz/sources/tnztools/bluredbrush.h
index 3f7a32c..650a001 100644
--- a/toonz/sources/tnztools/bluredbrush.h
+++ b/toonz/sources/tnztools/bluredbrush.h
@@ -34,7 +34,7 @@ public:
               bool doDynamicOpacity);
   ~BluredBrush();
 
-  void addPoint(const TThickPoint &p, double opacity);
+  void addPoint(const TThickPoint &p, double opacity, bool keepDistance = false);
   void addArc(const TThickPoint &pa, const TThickPoint &pb,
               const TThickPoint &pc, double opacityA, double opacityC);
   TRect getBoundFromPoints(const std::vector<TThickPoint> &points) const;
diff --git a/toonz/sources/tnztools/fullcolorbrushtool.cpp b/toonz/sources/tnztools/fullcolorbrushtool.cpp
index cdc7a4c..a2f3c28 100644
--- a/toonz/sources/tnztools/fullcolorbrushtool.cpp
+++ b/toonz/sources/tnztools/fullcolorbrushtool.cpp
@@ -306,17 +306,17 @@ bool FullColorBrushTool::askWrite(const TRect &rect) {
 
 bool FullColorBrushTool::preLeftButtonDown() {
   m_modifierAssistants->drawOnly = !FullcolorAssistants;
-  m_inputmanager.drawPreview     = false;  //! m_modifierAssistants->drawOnly;
+  m_inputmanager.drawPreview     = false; //!m_modifierAssistants->drawOnly;
 
   m_inputmanager.clearModifiers();
   m_inputmanager.addModifier(TInputModifierP(m_modifierTangents.getPointer()));
   m_inputmanager.addModifier(
       TInputModifierP(m_modifierAssistants.getPointer()));
-  m_inputmanager.addModifier(
-      TInputModifierP(m_modifierSegmentation.getPointer()));
 #ifndef NDEBUG
   m_inputmanager.addModifier(TInputModifierP(m_modifierTest.getPointer()));
 #endif
+  m_inputmanager.addModifier(
+      TInputModifierP(m_modifierSegmentation.getPointer()));
 
   touchImage();
 
@@ -505,9 +505,13 @@ void FullColorBrushTool::inputSetBusy(bool busy) {
 
 void FullColorBrushTool::inputPaintTrackPoint(const TTrackPoint &point,
                                               const TTrack &track,
-                                              bool firstTrack) {
+                                              bool firstTrack,
+                                              bool preview)
+{
   // get raster
-  if (!m_started || !getViewer()) return;
+  if (!m_started || !getViewer() || preview)
+    return;
+  
   TRasterImageP ri = (TRasterImageP)getImage(true);
   if (!ri) return;
   TRasterP ras      = ri->getRaster();
diff --git a/toonz/sources/tnztools/fullcolorbrushtool.h b/toonz/sources/tnztools/fullcolorbrushtool.h
index 239ea92..cdb8450 100644
--- a/toonz/sources/tnztools/fullcolorbrushtool.h
+++ b/toonz/sources/tnztools/fullcolorbrushtool.h
@@ -81,7 +81,7 @@ public:
                       const TInputState &state) override;
   void inputSetBusy(bool busy) override;
   void inputPaintTrackPoint(const TTrackPoint &point, const TTrack &track,
-                            bool firstTrack) override;
+                            bool firstTrack, bool preview) override;
   void inputInvalidateRect(const TRectD &bounds) override;
   TTool *inputGetTool() override { return this; };
 
diff --git a/toonz/sources/tnztools/inputmanager.cpp b/toonz/sources/tnztools/inputmanager.cpp
index bc00bb9..06b7f8d 100644
--- a/toonz/sources/tnztools/inputmanager.cpp
+++ b/toonz/sources/tnztools/inputmanager.cpp
@@ -1,5 +1,6 @@
 
 
+#include <tpixelutils.h>
 #include <tools/inputmanager.h>
 
 // TnzCore includes
@@ -29,7 +30,6 @@ TInputModifier::setManager(TInputManager *manager) {
 void
 TInputModifier::modifyTrack(
   const TTrack &track,
-  const TInputSavePoint::Holder &savePoint,
   TTrackList &outTracks )
 {
   if (!track.handler) {
@@ -51,7 +51,8 @@ TInputModifier::modifyTrack(
     TTrack &subTrack = **ti;
     subTrack.truncate(start);
     for(int i = start; i < track.size(); ++i)
-      subTrack.push_back( subTrack.modifier->calcPoint(i) );
+      subTrack.push_back( subTrack.modifier->calcPoint(i), false );
+    subTrack.fix_to(track.fixedSize());
   }
   track.resetChanges();
 }
@@ -60,11 +61,10 @@ TInputModifier::modifyTrack(
 void
 TInputModifier::modifyTracks(
     const TTrackList &tracks,
-    const TInputSavePoint::Holder &savePoint,
     TTrackList &outTracks )
 {
   for(TTrackList::const_iterator i = tracks.begin(); i != tracks.end(); ++i)
-    modifyTrack(**i, savePoint, outTracks);
+    modifyTrack(**i, outTracks);
 }
 
 
@@ -127,9 +127,9 @@ TInputModifier::draw(const TTrackList &tracks, const std::vector<TPointD> &hover
 bool
 TInputHandler::inputKeyEvent(
   bool press,
-  TInputState::Key key,
+  TInputState::Key,
   QKeyEvent *event,
-  const TInputManager &manager )
+  const TInputManager& )
 {
   return press && event && inputKeyDown(event);
 }
@@ -138,7 +138,7 @@ TInputHandler::inputKeyEvent(
 void
 TInputHandler::inputButtonEvent(
   bool press,
-  TInputState::DeviceId device,
+  TInputState::DeviceId,
   TInputState::Button button,
   const TInputManager &manager )
 {
@@ -155,8 +155,8 @@ TInputHandler::inputHoverEvent(const TInputManager &manager) {
 
 
 void
-TInputHandler::inputPaintTrackPoint(const TTrackPoint &point, const TTrack &track, bool firstTrack) {
-  if (firstTrack) {
+TInputHandler::inputPaintTrackPoint(const TTrackPoint &point, const TTrack &track, bool firstTrack, bool preview) {
+  if (firstTrack && !preview) {
     if (track.pointsAdded == track.size())
       inputLeftButtonDown(point, track);
     else
@@ -164,14 +164,25 @@ TInputHandler::inputPaintTrackPoint(const TTrackPoint &point, const TTrack &trac
       inputLeftButtonUp(point, track);
     else
       inputLeftButtonDrag(point, track);
-   }
+  }
 }
 
 
 void
 TInputHandler::inputPaintTracks(const TTrackList &tracks) {
+  // prepare tracks counters
+  for(TTrackList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
+    (*i)->pointsAdded = (*i)->fixedPointsAdded + (*i)->previewSize();
+    (*i)->resetRemoved();
+  }
+  
+  // begin paint
+  bool preview = false;
+  bool paintStarted = false;
+
   // paint track points in chronological order
   while(true) {
+    // find earlier not painted point
     TTrackP track;
     TTimerTicks minTicks = 0;
     double minTimeOffset = 0.0;
@@ -187,10 +198,27 @@ TInputHandler::inputPaintTracks(const TTrackList &tracks) {
         }
       }
     }
-    if (!track) break;
-    inputPaintTrackPoint(track->current(), *track, track == tracks.front());
+    
+    if (!track)
+      break; // all tracks are painted
+    
+    if (track->pointsAdded <= track->previewSize())
+      preview = true;
+    if (!paintStarted)
+      { inputPaintTracksBegin(); paintStarted = true; }
+    inputPaintTrackPoint(track->current(), *track, track == tracks.front(), preview);
+    
+    // update counters
     --track->pointsAdded;
+    if (!preview) {
+      assert(track->fixedPointsAdded > 0);
+      --track->fixedPointsAdded;
+    }
   }
+
+  // end paint
+  if (paintStarted)
+    inputPaintTracksEnd();
 }
 
 
@@ -205,162 +233,53 @@ TInputManager::TInputManager():
   m_tracks(1),
   m_hovers(1),
   m_started(),
-  m_savePointsSent(),
   drawPreview()
 { }
 
 
 void
-TInputManager::paintRollbackTo(int saveIndex, TTrackList &subTracks) {
-  if (saveIndex >= (int)m_savePoints.size())
-    return;
-
-  int level = saveIndex + 1;
-  if (level <= m_savePointsSent) {
-    if (m_handler) {
-      if (level < m_savePointsSent)
-        m_handler->inputPaintPop(m_savePointsSent - level);
-      m_handler->inputPaintCancel();
-    }
-    m_savePointsSent = level;
-  }
-
-  for(TTrackList::const_iterator i = subTracks.begin(); i != subTracks.end(); ++i) {
-    TTrack &track = **i;
-    if (TrackHandler *handler = dynamic_cast<TrackHandler*>(track.handler.getPointer())) {
-      handler->saves.resize(level);
-      int cnt = handler->saves[saveIndex];
-      track.resetRemoved();
-      track.pointsAdded = track.size() - cnt;
-    }
-  }
-  for(int i = level; i < (int)m_savePoints.size(); ++i)
-    m_savePoints[i].savePoint()->available = false;
-  m_savePoints.resize(level);
-}
-
-
-void
-TInputManager::paintApply(int count, TTrackList &subTracks) {
-  if (count <= 0)
-    return;
-
-  int level = (int)m_savePoints.size() - count;
-  bool resend = true;
-
-  if (level < m_savePointsSent) {
-    // apply
-    int applied = m_handler ? m_handler->inputPaintApply(m_savePointsSent - level) : false;
-    applied = std::max(0, std::min(m_savePointsSent - level, applied));
-    m_savePointsSent -= applied;
-    if (m_savePointsSent == level) resend = false;
-  }
-
-  if (level < m_savePointsSent) {
-    // rollback
-    if (m_handler) m_handler->inputPaintPop(m_savePointsSent - level);
-    m_savePointsSent = level;
+TInputManager::paintTracks() {
+  // run modifiers
+  for(int i = 0; i < (int)m_modifiers.size(); ++i) {
+    m_tracks[i+1].clear();
+    m_modifiers[i]->modifyTracks(m_tracks[i], m_tracks[i+1]);
   }
+  TTrackList &subTracks = m_tracks.back();
 
-  // remove keypoints
-  for(TTrackList::const_iterator i = subTracks.begin(); i != subTracks.end(); ++i) {
-    TTrack &track = **i;
-    if (TrackHandler *handler = dynamic_cast<TrackHandler*>(track.handler.getPointer())) {
-      if (resend) {
-        track.resetRemoved();
-        track.pointsAdded = track.size() - handler->saves[m_savePointsSent];
-      }
-      handler->saves.resize(level);
-    }
+  
+  // begin painting if need
+  bool changed = false;
+  for(TTrackList::const_iterator i = subTracks.begin(); i != subTracks.end(); ++i)
+    if ((*i)->changed())
+      { changed = true; break; }
+  if (!m_started && changed) {
+    m_started = true;
+    if (m_handler) m_handler->inputSetBusy(true);
   }
-  for(int i = level; i < (int)m_savePoints.size(); ++i)
-    m_savePoints[i].savePoint()->available = false;
-  m_savePoints.resize(level);
-}
 
-
-void
-TInputManager::paintTracks() {
+  
+  // paint tracks
+  if (changed && m_handler)
+    m_handler->inputPaintTracks(subTracks);
+  
+  
+  // end painting if all tracks are finished
   bool allFinished = true;
   for(TTrackList::const_iterator i = m_tracks.front().begin(); i != m_tracks.front().end(); ++i)
-    if (!(*i)->finished())
+    if (!(*i)->fixedFinished())
       { allFinished = false; break; }
-
-  while(true) {
-    // run modifiers
-    TInputSavePoint::Holder newSavePoint = TInputSavePoint::create(true);
-    for(int i = 0; i < (int)m_modifiers.size(); ++i) {
-      m_tracks[i+1].clear();
-      m_modifiers[i]->modifyTracks(m_tracks[i], newSavePoint, m_tracks[i+1]);
-    }
-    TTrackList &subTracks = m_tracks.back();
-
-    // is paint started?
-    if (!m_started && !subTracks.empty()) {
-      m_started = true;
-      if (m_handler) m_handler->inputSetBusy(true);
-    }
-
-    // create handlers
+  if (allFinished)
     for(TTrackList::const_iterator i = subTracks.begin(); i != subTracks.end(); ++i)
-      if (!(*i)->handler)
-        (*i)->handler = new TrackHandler(**i, (int)m_savePoints.size());
-
-    if (!m_savePoints.empty()) {
-      // rollback
-      int rollbackIndex = (int)m_savePoints.size();
-      for(TTrackList::const_iterator i = subTracks.begin(); i != subTracks.end(); ++i) {
-        TTrack &track = **i;
-        if (track.pointsRemoved > 0) {
-          int count = track.size() - track.pointsAdded;
-          if (TrackHandler *handler = dynamic_cast<TrackHandler*>(track.handler.getPointer()))
-            while(rollbackIndex > 0 && (rollbackIndex >= (int)m_savePoints.size() || handler->saves[rollbackIndex] > count))
-              --rollbackIndex;
-        }
-      }
-      paintRollbackTo(rollbackIndex, subTracks);
-
-      // apply
-      int applyCount = 0;
-      while(applyCount < (int)m_savePoints.size() && m_savePoints[(int)m_savePoints.size() - applyCount - 1].isFree())
-        ++applyCount;
-      paintApply(applyCount, subTracks);
-    }
-
-    // send to handler
-    if (m_savePointsSent == (int)m_savePoints.size() && !subTracks.empty() && m_handler)
-      m_handler->inputPaintTracks(subTracks);
-    for(TTrackList::const_iterator i = subTracks.begin(); i != subTracks.end(); ++i)
-      (*i)->resetChanges();
-
-    // is paint finished?
-    newSavePoint.unlock();
-    if (newSavePoint.isFree()) {
-      newSavePoint.savePoint()->available = false;
-      if (allFinished) {
-        paintApply((int)m_savePoints.size(), subTracks);
-        // send to tool final
-        if (!subTracks.empty()) {
-          if (m_handler) m_handler->inputPaintTracks(subTracks);
-          for(TTrackList::const_iterator i = subTracks.begin(); i != subTracks.end(); ++i)
-            (*i)->resetChanges();
-        }
-        for(std::vector<TTrackList>::iterator i = m_tracks.begin(); i != m_tracks.end(); ++i)
-          i->clear();
-        if (m_started) {
-          if (m_handler) m_handler->inputSetBusy(false);
-          m_started = false;
-        }
-      }
-      break;
+      if (!(*i)->fixedFinished())
+        { allFinished = false; break; }
+        
+  if (allFinished) {
+    if (m_started) {
+      if (m_handler) m_handler->inputSetBusy(false);
+      m_started = false;
     }
-
-    // create save point
-    if (m_handler && m_handler->inputPaintPush()) ++m_savePointsSent;
-    m_savePoints.push_back(newSavePoint);
-    for(TTrackList::const_iterator i = subTracks.begin(); i != subTracks.end(); ++i)
-      if (TrackHandler *handler = dynamic_cast<TrackHandler*>((*i)->handler.getPointer()))
-        handler->saves.push_back((*i)->size());
+    for(int i = 0; i < (int)m_tracks.size(); ++i)
+      m_tracks[i].clear();
   }
 }
 
@@ -444,14 +363,16 @@ TInputManager::addTrackPoint(
   double time,
   bool final )
 {
-  track->push_back( TTrackPoint(
-    position,
-    pressure,
-    tilt,
-    (double)track->size(),
-    time,
-    0.0, // length will calculated inside of TTrack::push_back
-    final ));
+  track->push_back(
+    TTrackPoint(
+      position,
+      pressure,
+      tilt,
+      (double)track->size(),
+      time,
+      0.0, // length will calculated inside of TTrack::push_back
+      final ),
+    true );
 }
 
 
@@ -502,15 +423,9 @@ TInputManager::finishTracks() {
 
 void
 TInputManager::reset() {
-  // forget about handler paint stack
+  // forget about handler busy state
   // assuime it was already reset by outside
   m_started = false;
-  m_savePointsSent = 0;
-
-  // reset save point
-  for(int i = 0; i < (int)m_savePoints.size(); ++i)
-    m_savePoints[i].savePoint()->available = false;
-  m_savePoints.clear();
 
   // reset tracks
   for(int i = 0; i < (int)m_tracks.size(); ++i)
@@ -660,16 +575,13 @@ TInputManager::calcDrawBounds() {
   for(int i = 0; i < (int)m_modifiers.size(); ++i)
     bounds += m_modifiers[i]->calcDrawBounds(m_tracks[i], m_hovers[i]);
 
-  if (m_savePointsSent < (int)m_savePoints.size()) {
+  if (drawPreview) {
     for(TTrackList::const_iterator ti = getOutputTracks().begin(); ti != getOutputTracks().end(); ++ti) {
       TTrack &track = **ti;
-      if (TrackHandler *handler = dynamic_cast<TrackHandler*>(track.handler.getPointer())) {
-        int start = handler->saves[m_savePointsSent] - 1;
-        if (start < 0) start = 0;
-        if (start + 1 < track.size())
-          for(int i = start + 1; i < track.size(); ++i)
-            bounds += boundingBox(track[i-1].position, track[i].position);
-      }
+      int start = std::max(0, track.fixedSize() - track.fixedPointsAdded);
+      if (start + 1 < track.size())
+        for(int i = start + 1; i < track.size(); ++i)
+          bounds += boundingBox(track[i-1].position, track[i].position);
     }
   }
 
@@ -685,53 +597,69 @@ TInputManager::draw() {
   m_prevBounds = m_nextBounds;
   m_nextBounds = TRectD();
   
-  // paint not sent sub-tracks
-  if ( debugInputManager || (drawPreview && m_savePointsSent < (int)m_savePoints.size()) ) {
+  // paint not fixed parts of tracks
+  if (drawPreview) {
     glPushAttrib(GL_ALL_ATTRIB_BITS);
     tglEnableBlending();
     tglEnableLineSmooth(true, 1.0);
     double pixelSize = sqrt(tglGetPixelSize2());
-    double colorBlack[4] = { 0.0, 0.0, 0.0, 1.0 };
-    double colorWhite[4] = { 1.0, 1.0, 1.0, 1.0 };
+    double colorBlack[4] = { 0.0, 0.0, 0.0, 0.5 };
+    double colorWhite[4] = { 1.0, 1.0, 1.0, 0.5 };
     for(TTrackList::const_iterator ti = getOutputTracks().begin(); ti != getOutputTracks().end(); ++ti) {
       TTrack &track = **ti;
-      if (TrackHandler *handler = dynamic_cast<TrackHandler*>(track.handler.getPointer())) {
-        int start = debugInputManager ? 0 : handler->saves[m_savePointsSent] - 1;
-        if (start < 0) start = 0;
-        if (start + 1 < track.size()) {
-          //int level = m_savePointsSent;
-          //colorBlack[3] = (colorWhite[3] = 0.8);
-          double radius = 2.0;
-          for(int i = start + 1; i < track.size(); ++i) {
-            //while(level < (int)handler->saves.size() && handler->saves[level] <= i)
-            //  colorBlack[3] = (colorWhite[3] *= 0.8), ++level;
-
-            const TPointD &a = track[i-1].position;
-            const TPointD &b = track[i].position;
-            TPointD d = b - a;
-
+      int start = std::max(0, track.fixedSize() - track.fixedPointsAdded);
+      const TPointD *a = &track[start].position;
+      for(int i = start + 1; i < track.size(); ++i) {
+        const TPointD *b = &track[i].position;
+        TPointD d = *b - *a;
+        double k = norm2(d);
+        if (k > TConsts::epsilon*TConsts::epsilon) {
+          k = 0.5*pixelSize/sqrt(k);
+          d = TPointD(-k*d.y, k*d.x);
+          glColor4dv(colorWhite);
+          tglDrawSegment(*a - d, *b - d);
+          glColor4dv(colorBlack);
+          tglDrawSegment(*a + d, *b + d);
+          a = b;
+        }
+      }
+    }
+    glPopAttrib();
+  }
+  
+  // paint all tracks modifications for debug
+  if (debugInputManager) {
+    double pixelSize = sqrt(tglGetPixelSize2());
+    double color[4] = { 0.0, 0.0, 0.0, 0.5 };
+    int cnt = m_tracks.size();
+    for(int li = 0; li < cnt; ++li) {
+      HSV2RGB(240 + li*120.0/(cnt-1), 1, 1, &color[0], &color[1], &color[2]);
+      glColor4dv(color);
+      for(TTrackList::const_iterator ti = m_tracks[li].begin(); ti != m_tracks[li].end(); ++ti) {
+        assert(*ti);
+        const TTrack &track = **ti;
+        int radius = 0;
+        const TPointD *a = &track[0].position;
+        for(int i = 0; i < track.size(); ++i) {
+          const TPointD *b = &track[i].position;
+          
+          if (i) {
+            TPointD d = *b - *a;
             double k = norm2(d);
-            if (k > TConsts::epsilon*TConsts::epsilon) {
-              k = 0.5*pixelSize/sqrt(k);
-              d = TPointD(-k*d.y, k*d.x);
-              glColor4dv(colorWhite);
-              tglDrawSegment(a - d, b - d);
-              glColor4dv(colorBlack);
-              tglDrawSegment(a + d, b + d);
-              radius = 2.0;
+            if (k > 4*pixelSize*pixelSize) {
+              tglDrawSegment(*a, *b);
+              radius = 0;
+              a = b;
             } else {
-              radius += 2.0;
-            }
-
-            if (debugInputManager) {
-              glColor4d(0.0, 0.0, 0.0, 0.25);
-              tglDrawCircle(b, radius*pixelSize);
+              ++radius;
             }
           }
+          
+          if (radius && li == 0)
+            tglDrawCircle(*b, radius*pixelSize*3);
         }
       }
     }
-    glPopAttrib();
   }
 
   // paint modifiers
diff --git a/toonz/sources/tnztools/modifiers/modifierassistants.cpp b/toonz/sources/tnztools/modifiers/modifierassistants.cpp
index 1a62e44..597e97d 100644
--- a/toonz/sources/tnztools/modifiers/modifierassistants.cpp
+++ b/toonz/sources/tnztools/modifiers/modifierassistants.cpp
@@ -113,64 +113,64 @@ TModifierAssistants::scanAssistants(
 void
 TModifierAssistants::modifyTrack(
   const TTrack &track,
-  const TInputSavePoint::Holder &savePoint,
   TTrackList &outTracks )
 {
   if (!track.handler) {
     track.handler = new TTrackHandler(track);
     Modifier *modifier = new Modifier(*track.handler);
-    if (!drawOnly)
-      scanAssistants(&track[0].position, 1, &modifier->guidelines, false, true);
-
     track.handler->tracks.push_back(new TTrack(modifier));
-
-    if ((int)modifier->guidelines.size() > 1) {
-      modifier->savePoint = savePoint;
-      outTracks.push_back(track.handler->tracks.front());
-      return;
-    }
   }
 
   outTracks.push_back(track.handler->tracks.front());
   TTrack &subTrack = *track.handler->tracks.front();
-  if (!track.changed()) return;
-  if (Modifier *modifier = dynamic_cast<Modifier*>(subTrack.modifier.getPointer())) {
-    // remove points
-    int start = track.size() - track.pointsAdded;
-    if (start < 0) start = 0;
-
-    if ((int)modifier->guidelines.size() > 1 && modifier->savePoint.available()) {
-      // select guideline
-      bool longEnough = false;
-      if (TInputManager *manager = getManager())
-      if (TInputHandler *handler = manager->getHandler())
-      if (TTool *tool = handler->inputGetTool())
-      if (TToolViewer *viewer = tool->getViewer()) {
-        TAffine trackToScreen = tool->getMatrix();
-        if (tool->getToolType() & TTool::LevelTool)
-          if (TObjectHandle *objHandle = TTool::getApplication()->getCurrentObject())
-            if (!objHandle->isSpline())
-              trackToScreen *= TScale(viewer->getDpiScale().x, viewer->getDpiScale().y);
-        trackToScreen *= viewer->get3dViewMatrix().get2d().inv();
-        TGuidelineP guideline = TGuideline::findBest(modifier->guidelines, track, trackToScreen, longEnough);
-        if (guideline != modifier->guidelines.front())
-          for(int i = 1; i < (int)modifier->guidelines.size(); ++i)
-            if (modifier->guidelines[i] == guideline) {
-              std::swap(modifier->guidelines[i], modifier->guidelines.front());
-              start = 0;
-              break;
-            }
-      }
-      modifier->savePoint.setLock(!longEnough);
-    } else {
-      modifier->savePoint.reset();
-    }
+  if (!track.changed())
+    return;
+  Modifier *modifier = dynamic_cast<Modifier*>(subTrack.modifier.getPointer());
+  if (!modifier)
+    return;
+    
+  // remove points
+  int start = track.size() - track.pointsAdded;
+  if (start < 0) start = 0;
+
+  if (!drawOnly && start <= 0) {
+    modifier->guidelines.clear();
+    scanAssistants(&track[0].position, 1, &modifier->guidelines, false, true);
+  }
+  
+  bool fixed = subTrack.fixedSize() || modifier->guidelines.size() <= 1;
 
-    // add points
-    subTrack.truncate(start);
-    for(int i = start; i < track.size(); ++i)
-      subTrack.push_back( modifier->calcPoint(i) );
+  // select guideline
+  if (!fixed)
+  if (TInputManager *manager = getManager())
+  if (TInputHandler *handler = manager->getHandler())
+  if (TTool *tool = handler->inputGetTool())
+  if (TToolViewer *viewer = tool->getViewer()) {
+    TAffine trackToScreen = tool->getMatrix();
+    if (tool->getToolType() & TTool::LevelTool)
+      if (TObjectHandle *objHandle = TTool::getApplication()->getCurrentObject())
+        if (!objHandle->isSpline())
+          trackToScreen *= TScale(viewer->getDpiScale().x, viewer->getDpiScale().y);
+    trackToScreen *= viewer->get3dViewMatrix().get2d();
+    TGuidelineP guideline = TGuideline::findBest(modifier->guidelines, track, trackToScreen, fixed);
+    if (guideline != modifier->guidelines.front())
+      for(int i = 1; i < (int)modifier->guidelines.size(); ++i)
+        if (modifier->guidelines[i] == guideline) {
+          std::swap(modifier->guidelines[i], modifier->guidelines.front());
+          start = 0;
+          break;
+        }
   }
+
+  // add points
+  subTrack.truncate(start);
+  for(int i = start; i < track.size(); ++i)
+    subTrack.push_back( modifier->calcPoint(i), false );
+  
+  // fix points
+  if (fixed || track.fixedFinished())
+    subTrack.fix_to(track.fixedSize());
+
   track.resetChanges();
 }
 
diff --git a/toonz/sources/tnztools/modifiers/modifierline.cpp b/toonz/sources/tnztools/modifiers/modifierline.cpp
index ed923ce..e678914 100644
--- a/toonz/sources/tnztools/modifiers/modifierline.cpp
+++ b/toonz/sources/tnztools/modifiers/modifierline.cpp
@@ -31,12 +31,10 @@ TTrackPoint TModifierLine::Modifier::calcPoint(double originalIndex) {
 }
 
 void TModifierLine::modifyTrack(const TTrack &track,
-                                const TInputSavePoint::Holder &savePoint,
                                 TTrackList &outTracks) {
   if (!track.handler) {
     track.handler       = new TTrackHandler(track);
     Modifier *modifier  = new Modifier(*track.handler);
-    modifier->savePoint = savePoint;
     track.handler->tracks.push_back(new TTrack(modifier));
   }
 
@@ -66,7 +64,6 @@ void TModifierLine::modifyTrack(const TTrack &track,
   }
   modifier->maxPressure = maxPressure;
   modifier->fixAngle    = fixAngle;
-  if (track.finished()) modifier->savePoint.reset();
 
   subTrack.truncate(0);
 
@@ -75,7 +72,7 @@ void TModifierLine::modifyTrack(const TTrack &track,
     p.originalIndex = 0;
     p.pressure      = maxPressure;
     p.tilt          = TPointD();
-    subTrack.push_back(p);
+    subTrack.push_back(p, false);
   }
 
   if (track.size() > 1) {
@@ -84,8 +81,11 @@ void TModifierLine::modifyTrack(const TTrack &track,
     p.pressure      = maxPressure;
     p.tilt          = TPointD();
     if (fixAngle) calcFixedAngle(subTrack.front(), p);
-    subTrack.push_back(p);
+    subTrack.push_back(p, false);
   }
 
+  if (track.fixedFinished())
+    subTrack.fix_all();
+
   track.resetChanges();
 }
diff --git a/toonz/sources/tnztools/modifiers/modifiersegmentation.cpp b/toonz/sources/tnztools/modifiers/modifiersegmentation.cpp
index 447c9e4..ba1232e 100644
--- a/toonz/sources/tnztools/modifiers/modifiersegmentation.cpp
+++ b/toonz/sources/tnztools/modifiers/modifiersegmentation.cpp
@@ -9,8 +9,9 @@
 //*****************************************************************************************
 
 
-TModifierSegmentation::TModifierSegmentation(const TPointD &step)
-  { setStep(step); }
+TModifierSegmentation::TModifierSegmentation(const TPointD &step, int maxLevel):
+  m_maxLevel(0)
+  { setStep(step); setMaxLevel(maxLevel); }
 
 
 void
@@ -21,30 +22,35 @@ TModifierSegmentation::setStep(const TPointD &step) {
 
 
 void
+TModifierSegmentation::setMaxLevel(int maxLevel) {
+  m_maxLevel = std::max(0, maxLevel);
+}
+
+
+void
 TModifierSegmentation::addSegments(
   TTrack &track,
   const TTrackPoint &p0,
   const TTrackPoint &p1,
   int level)
 {
-  static const int maxRecursion = 10;
   TPointD d = p1.position - p0.position;
 
-  if (level >= maxRecursion || (fabs(d.x) <= m_step.x && fabs(d.y) <= m_step.y)) {
-    track.push_back(p1);
+  if (level <= 0 || (fabs(d.x) <= m_step.x && fabs(d.y) <= m_step.y)) {
+    track.push_back(p1, false);
     return;
   }
 
+  --level;
   TTrackPoint p = track.modifier->calcPoint(0.5*(p0.originalIndex + p1.originalIndex));
-  addSegments(track, p0, p, level + 1);
-  addSegments(track, p, p1, level + 1);
+  addSegments(track, p0, p, level);
+  addSegments(track, p, p1, level);
 }
 
 
 void
 TModifierSegmentation::modifyTrack(
   const TTrack &track,
-  const TInputSavePoint::Holder &savePoint,
   TTrackList &outTracks )
 {
   if (!track.handler) {
@@ -66,16 +72,21 @@ TModifierSegmentation::modifyTrack(
   // remove points
   int start = track.size() - track.pointsAdded;
   if (start < 0) start = 0;
-  int subStart = subTrack.floorIndex(subTrack.indexByOriginalIndex(start-1)) + 1;
+  int subStart = start > 0 ? subTrack.floorIndex(subTrack.indexByOriginalIndex(start-1)) + 1 : 0;
   subTrack.truncate(subStart);
 
   // add points
   TTrackPoint p0 = subTrack.modifier->calcPoint(start - 1);
   for(int i = start; i < track.size(); ++i) {
     TTrackPoint p1 = subTrack.modifier->calcPoint(i);
-    addSegments(subTrack, p0, p1);
+    addSegments(subTrack, p0, p1, m_maxLevel);
     p0 = p1;
   }
 
+  // fix points
+  if (track.fixedSize())
+    subTrack.fix_to(
+      subTrack.floorIndex(subTrack.indexByOriginalIndex(track.fixedSize() - 1)) + 1 );
+  
   track.resetChanges();
 }
diff --git a/toonz/sources/tnztools/modifiers/modifiersmooth.cpp b/toonz/sources/tnztools/modifiers/modifiersmooth.cpp
new file mode 100644
index 0000000..449623f
--- /dev/null
+++ b/toonz/sources/tnztools/modifiers/modifiersmooth.cpp
@@ -0,0 +1,119 @@
+
+
+#include <tools/modifiers/modifiersmooth.h>
+#include <algorithm>
+
+
+//*****************************************************************************************
+//    TModifierSmooth::Modifier implementation
+//*****************************************************************************************
+
+
+TModifierSmooth::Modifier::Modifier(TTrackHandler &handler, int radius):
+  TTrackModifier(handler),
+  radius(std::max(radius, 0)),
+  smoothedTrack()
+{ }
+
+
+TTrackPoint
+TModifierSmooth::Modifier::calcPoint(double originalIndex) {
+  return smoothedTrack
+       ? smoothedTrack->interpolateLinear(originalIndex)
+       : TTrackModifier::calcPoint(originalIndex);
+}
+
+
+//*****************************************************************************************
+//    TModifierSmooth implementation
+//*****************************************************************************************
+
+
+TModifierSmooth::TModifierSmooth(int radius): m_radius()
+  { setRadius(radius); }
+
+
+void
+TModifierSmooth::setRadius(int radius)
+  { m_radius = std::max(0, radius); }
+
+
+void
+TModifierSmooth::modifyTrack(
+  const TTrack &track,
+  TTrackList &outTracks )
+{
+  if (!m_radius) {
+    TInputModifier::modifyTrack(track, outTracks);
+    return;
+  }
+  
+  if (!track.handler) {
+    track.handler = new TTrackHandler(track);
+    Modifier *modifier = new Modifier(*track.handler, m_radius);
+    modifier->smoothedTrack = new TTrack(modifier);
+    track.handler->tracks.push_back(modifier->smoothedTrack);
+  }
+
+  if (track.handler->tracks.empty())
+    return;
+
+  TTrack &subTrack = *track.handler->tracks.front();
+  outTracks.push_back(track.handler->tracks.front());
+
+  if (!track.changed())
+    return;
+  
+  Modifier *modifier = dynamic_cast<Modifier*>(subTrack.modifier.getPointer());
+  if (!modifier)
+    return;
+  int radius = modifier->radius;
+
+  // remove points
+  int start = std::max(0, track.size() - track.pointsAdded);
+  subTrack.truncate(start);
+
+  // add points
+  // extra points will be added at the beginning and at the end
+  int size = track.size() + 2*radius;
+  double k = 1.0/(2*radius + 1);
+  TTrackTangent accum;
+  for(int i = start - 2*radius; i < size; ++i) {
+    
+    const TTrackPoint &p1 = track[i];
+    accum.position += p1.position;
+    accum.pressure += p1.pressure;
+    accum.tilt     += p1.tilt;
+    if (i < start)
+      continue;
+
+    int oi = track.clampIndex(i - radius);
+    const TTrackPoint &p = track[oi];
+    subTrack.push_back(
+      TTrackPoint(
+        accum.position*k,
+        accum.pressure*k,
+        accum.tilt*k,
+        oi,
+        p.time,
+        0,
+        p.final ),
+      false );
+    
+    const TTrackPoint &p0 = track[i - 2*radius];
+    accum.position -= p0.position;
+    accum.pressure -= p0.pressure;
+    accum.tilt     -= p0.tilt;
+  }
+  
+  // fix points
+  if (track.fixedFinished()) {
+    subTrack.fix_all();
+  } else
+  if (track.fixedSize()) {
+    subTrack.fix_to(track.fixedSize());
+  }
+  
+  track.resetChanges();
+}
+
diff --git a/toonz/sources/tnztools/modifiers/modifiertangents.cpp b/toonz/sources/tnztools/modifiers/modifiertangents.cpp
index f71c56e..c7f11e0 100644
--- a/toonz/sources/tnztools/modifiers/modifiertangents.cpp
+++ b/toonz/sources/tnztools/modifiers/modifiertangents.cpp
@@ -12,23 +12,31 @@ TTrackPoint
 TModifierTangents::Modifier::calcPoint(double originalIndex) {
   double frac;
   int i0 = original.floorIndex(originalIndex, &frac);
-  int i1 = original.ceilIndex(originalIndex);
+  int i1 = i0 + 1;
 
   TTrackPoint p;
-  if (i0 >= 0) {
-    // calculate tangent length to make monotonic subdivisions,
-    // (because we don't have valid input time)
-    const TTrackPoint &p0 = original[i0];
-    const TTrackPoint &p1 = original[i1];
-    TTrackTangent t0 = i0 < (int)tangents.size() ? tangents[i0] : TTrackTangent();
-    TTrackTangent t1 = i1 < (int)tangents.size() ? tangents[i1] : TTrackTangent();
-    double l = fabs(p1.length - p0.length);
-    t0.position.x *= l;
-    t0.position.y *= l;
-    t1.position.x *= l;
-    t1.position.y *= l;
-    p = TTrack::interpolationSpline(p0, p1, t0, t1, frac);
-  }
+
+  // calculate tangent length to make monotonic subdivisions,
+  // (because we don't have valid input time)
+  const TTrackPoint &p0 = original[i0];
+  const TTrackPoint &p1 = original[i1];
+  TTrackTangent t0 = i0 >= 0 && i0 < (int)tangents.size() ? tangents[i0] : TTrackTangent();
+  TTrackTangent t1 = i1 >= 0 && i1 < (int)tangents.size() ? tangents[i1] : TTrackTangent();
+  double l = p1.length - p0.length;
+  
+  t0.position.x *= l;
+  t0.position.y *= l;
+  t0.pressure *= l;
+  t0.tilt.x *= l;
+  t0.tilt.y *= l;
+  
+  t1.position.x *= l;
+  t1.position.y *= l;
+  t1.pressure *= l;
+  t1.tilt.x *= l;
+  t1.tilt.y *= l;
+  
+  p = TTrack::interpolationSpline(p0, p1, t0, t1, frac);
   p.originalIndex = originalIndex;
   return p;
 }
@@ -40,25 +48,32 @@ TModifierTangents::Modifier::calcPoint(double originalIndex) {
 
 
 TTrackTangent
-TModifierTangents::calcLastTangent(const TTrack &track) const {
-  if (track.size() < 2) return TTrackTangent();
-  const TTrackPoint &p0 = track[track.size() - 2];
-  const TTrackPoint &p1 = track.back();
-
-  // calculate tangent (with normalized position)
-  // with valid points time normalization does not required
-  double dl = p1.length - p0.length;
+TModifierTangents::calcTangent(const TTrack &track, int index) const {
+  if (index <= 0 || index >= track.size() - 1)
+    return TTrackTangent();
+  
+  const TTrackPoint &p0 = track[index-1];
+  const TTrackPoint &p2 = track[index+1];
+
+  // calculate tangent length by time
+  // for that we need know time of actual user input
+  // instead of time when message dispatched
+  //double k = p2.time - p0.time;
+  
+  // calculate tangent based on length, util we have no valid times
+  double k = p2.length - p0.length;
+  
+  k = k > TConsts::epsilon ? 1/k : 0;
   return TTrackTangent(
-    dl > TConsts::epsilon ? (p1.position - p0.position)*(1.0/dl) : TPointD(),
-    p1.pressure - p0.pressure,
-    p1.tilt - p0.tilt );
+    (p2.position - p0.position)*k,
+    (p2.pressure - p0.pressure)*k,
+    (p2.tilt     - p0.tilt    )*k );
 }
 
 
 void
 TModifierTangents::modifyTrack(
   const TTrack &track,
-  const TInputSavePoint::Holder &savePoint,
   TTrackList &outTracks )
 {
   if (!track.handler) {
@@ -77,83 +92,30 @@ TModifierTangents::modifyTrack(
     return;
 
   outTracks.push_back(track.handler->tracks.front());
-
-  if ( !track.changed()
-    && track.size() == subTrack.size()
-    && track.size() == (int)modifier->tangents.size() )
-      return;
-
-  if (!track.changed() && subTrack.size() == track.size() - 1) {
-    // add temporary point
-    modifier->tangents.push_back(calcLastTangent(track));
-    subTrack.push_back(track.back());
-  } else {
-    // apply permanent changes
-
-    // remove points
-    int start = track.size() - track.pointsAdded;
-    if (start < 0) start = 0;
-    if (start > 1) --start;
-    subTrack.truncate(start);
-    TTrackTangent lastTangent =
-        start < (int)modifier->tangents.size() ? modifier->tangents[start]
-      : modifier->tangents.empty() ? TTrackTangent()
-      : modifier->tangents.back();
-    modifier->tangents.resize(start, lastTangent);
-
-    // add first point
-    int index = start;
-    if (index == 0) {
-      modifier->tangents.push_back(TTrackTangent());
-      subTrack.push_back(track.back());
-      ++index;
-    }
-
-    // add points with tangents
-    if (track.size() > 2) {
-      while(index < track.size() - 1) {
-        const TTrackPoint &p0 = track[index-1];
-        const TTrackPoint &p1 = track[index];
-        const TTrackPoint &p2 = track[index+1];
-
-        // calculate tangents length by time
-        // for that we need read time of user input
-        // instead of time when message dispatched
-        //double dt = p2.time - p0.time;
-        //double k = dt > TConsts::epsilon ? (p1.time - p0.time)/dt : 0.0;
-        //TTrackTangent tangent(
-        //  (p2.position - p0.position)*k,
-        //  (p2.pressure - p0.pressure)*k,
-        //  (p2.tilt - p0.tilt)*k );
-        
-        // calculate tangent (with normalized position)
-        TPointD d = p2.position - p0.position;
-        double k = norm2(d);
-        k = k > TConsts::epsilon*TConsts::epsilon ? 1.0/sqrt(k) : 0.0;
-        TTrackTangent tangent(
-          d*k,
-          (p2.pressure - p0.pressure)*0.5,
-          (p2.tilt - p0.tilt)*0.5 );
-        
-        modifier->tangents.push_back(tangent);
-        subTrack.push_back(p1);
-        ++index;
-      }
-    }
-
-    track.resetChanges();
-
-    // release previous key point
-    modifier->savePoint.reset();
-
-    if (track.finished()) {
-      // finish
-      modifier->tangents.push_back(calcLastTangent(track));
-      subTrack.push_back(track.back());
-    } else {
-      // save key point
-      modifier->savePoint = savePoint;
-    }
+  if (!track.changed())
+    return;
+  
+  // update subTrack
+  int start = track.size() - track.pointsAdded;
+  if (start > 1) --start;
+  if (start < 0) start = 0;
+  subTrack.truncate(start);
+  for(int i = start; i < track.size(); ++i)
+    subTrack.push_back(track[i], false);
+  
+  // update tangents
+  modifier->tangents.resize(start);
+  for(int i = start; i < track.size(); ++i)
+    modifier->tangents.push_back(calcTangent(track, i));
+  
+  // fix points
+  if (track.fixedFinished()) {
+    subTrack.fix_all();
+  } else
+  if (track.fixedSize()) {
+    subTrack.fix_to(std::max(track.fixedSize() - 1, 1));
   }
+  
+  track.resetChanges();
 }
 
diff --git a/toonz/sources/tnztools/modifiers/modifiertest.cpp b/toonz/sources/tnztools/modifiers/modifiertest.cpp
index 2033a2b..7801f3b 100644
--- a/toonz/sources/tnztools/modifiers/modifiertest.cpp
+++ b/toonz/sources/tnztools/modifiers/modifiertest.cpp
@@ -52,7 +52,6 @@ TModifierTest::TModifierTest(int count, double radius)
     : count(count), radius(radius) {}
 
 void TModifierTest::modifyTrack(const TTrack &track,
-                                const TInputSavePoint::Holder &savePoint,
                                 TTrackList &outTracks) {
   const double segmentSize = 2.0 * M_PI / 10.0;
 
@@ -68,7 +67,7 @@ void TModifierTest::modifyTrack(const TTrack &track,
 
   Handler *handler = dynamic_cast<Handler *>(track.handler.getPointer());
   if (!handler) {
-    TInputModifier::modifyTrack(track, savePoint, outTracks);
+    TInputModifier::modifyTrack(track, outTracks);
     return;
   }
 
@@ -124,14 +123,19 @@ void TModifierTest::modifyTrack(const TTrack &track,
           double end  = 1.0 - 0.5 * step;
           for (double frac = step; frac < end; frac += step)
             subTrack.push_back(
-                subTrack.modifier->calcPoint((double)i - 1.0 + frac));
+                subTrack.modifier->calcPoint((double)i - 1.0 + frac), false);
         }
       }
-      subTrack.push_back(subTrack.modifier->calcPoint(i));
+      subTrack.push_back(subTrack.modifier->calcPoint(i), false);
     }
+    
+    // fix points
+    if (track.fixedSize())
+      subTrack.fix_to(
+        subTrack.floorIndex(subTrack.indexByOriginalIndex(track.fixedSize() - 1)) + 1 );
   }
 
   track.resetChanges();
 }
 
-#endif
\ No newline at end of file
+#endif
diff --git a/toonz/sources/tnztools/toonzrasterbrushtool.cpp b/toonz/sources/tnztools/toonzrasterbrushtool.cpp
index f199b1a..539368a 100644
--- a/toonz/sources/tnztools/toonzrasterbrushtool.cpp
+++ b/toonz/sources/tnztools/toonzrasterbrushtool.cpp
@@ -6,7 +6,6 @@
 #include "tools/toolhandle.h"
 #include "tools/toolutils.h"
 #include "tools/tooloptions.h"
-#include "bluredbrush.h"
 
 // TnzQt includes
 #include "toonzqt/dvdialog.h"
@@ -21,7 +20,6 @@
 #include "toonz/txsheet.h"
 #include "toonz/tstageobject.h"
 #include "toonz/tstageobjectspline.h"
-#include "toonz/rasterstrokegenerator.h"
 #include "toonz/ttileset.h"
 #include "toonz/txshsimplelevel.h"
 #include "toonz/toonzimageutils.h"
@@ -57,6 +55,7 @@ TEnv::DoubleVar RasterBrushHardness("RasterBrushHardness", 100);
 TEnv::DoubleVar RasterBrushModifierSize("RasterBrushModifierSize", 0);
 TEnv::StringVar RasterBrushPreset("RasterBrushPreset", "<custom>");
 TEnv::IntVar BrushLockAlpha("InknpaintBrushLockAlpha", 0);
+TEnv::IntVar RasterBrushAssistants("RasterBrushAssistants", 0);
 
 //-------------------------------------------------------------------
 #define CUSTOM_WSTR L"<custom>"
@@ -401,19 +400,6 @@ namespace {
 
 //-------------------------------------------------------------------
 
-void addStrokeToImage(TTool::Application *application, const TVectorImageP &vi,
-                      TStroke *stroke, bool breakAngles, bool frameCreated,
-                      bool levelCreated, TXshSimpleLevel *sLevel = NULL,
-                      TFrameId id = TFrameId::NO_FRAME) {
-  QMutexLocker lock(vi->getMutex());
-  addStroke(application, vi.getPointer(), stroke, breakAngles, frameCreated,
-            levelCreated, sLevel, id);
-  // la notifica viene gia fatta da addStroke!
-  // getApplication()->getCurrentTool()->getTool()->notifyImageChanged();
-}
-
-//---------------------------------------------------------------------------------------------------------
-
 enum DrawOrder { OverAll = 0, UnderAll, PaletteOrder };
 
 void getAboveStyleIdSet(int styleId, TPaletteP palette,
@@ -450,27 +436,27 @@ public:
       , m_points(points)
       , m_styleId(styleId)
       , m_selective(selective)
+      , m_isPaletteOrder(isPaletteOrder)
       , m_isPencil(isPencil)
       , m_isStraight(isStraight)
-      , m_isPaletteOrder(isPaletteOrder)
       , m_modifierLockAlpha(lockAlpha) {}
 
   void redo() const override {
     insertLevelAndFrameIfNeeded();
     TToonzImageP image = getImage();
     TRasterCM32P ras   = image->getRaster();
-    RasterStrokeGenerator m_rasterTrack(
+    RasterStrokeGenerator rasterTrack(
         ras, BRUSH, NONE, m_styleId, m_points[0], m_selective, 0,
         m_modifierLockAlpha, !m_isPencil, m_isPaletteOrder);
     if (m_isPaletteOrder) {
       QSet<int> aboveStyleIds;
       getAboveStyleIdSet(m_styleId, image->getPalette(), aboveStyleIds);
-      m_rasterTrack.setAboveStyleIds(aboveStyleIds);
+      rasterTrack.setAboveStyleIds(aboveStyleIds);
     }
-    m_rasterTrack.setPointsSequence(m_points);
-    m_rasterTrack.generateStroke(m_isPencil, m_isStraight);
+    rasterTrack.setPointsSequence(m_points);
+    rasterTrack.generateStroke(m_isPencil, m_isStraight);
     image->setSavebox(image->getSavebox() +
-                      m_rasterTrack.getBBox(m_rasterTrack.getPointsSequence()));
+                      rasterTrack.getBBox(rasterTrack.getPointsSequence()));
     ToolUtils::updateSaveBox();
     TTool::getApplication()->getCurrentXsheet()->notifyXsheetChanged();
     notifyImageChanged();
@@ -506,8 +492,8 @@ public:
       , m_styleId(styleId)
       , m_drawOrder(drawOrder)
       , m_maxThick(maxThick)
-      , m_isStraight(isStraight)
       , m_hardness(hardness)
+      , m_isStraight(isStraight)
       , m_modifierLockAlpha(lockAlpha) {}
 
   void redo() const override {
@@ -632,15 +618,6 @@ double computeThickness(double pressure, const TDoublePairProperty &property) {
   return (thick0 + (thick1 - thick0) * t) * 0.5;
 }
 
-//---------------------------------------------------------------------------------------------------------
-
-int computeThickness(double pressure, const TIntPairProperty &property) {
-  double t   = pressure * pressure * pressure;
-  int thick0 = property.getValue().first;
-  int thick1 = property.getValue().second;
-  return tround(thick0 + (thick1 - thick0) * t);
-}
-
 }  // namespace
 
 //--------------------------------------------------------------------------------------------------
@@ -878,18 +855,15 @@ ToonzRasterBrushTool::ToonzRasterBrushTool(std::string name, int targetType)
     , m_pencil("Pencil", false)
     , m_pressure("Pressure", true)
     , m_modifierSize("ModifierSize", -3, 3, 0, true)
-    , m_rasterTrack(0)
-    , m_styleId(0)
-    , m_bluredBrush(0)
-    , m_active(false)
+    , m_modifierLockAlpha("Lock Alpha", false)
+    , m_assistants("Assistants", true)
+    , m_targetType(targetType)
     , m_enabled(false)
     , m_isPrompting(false)
     , m_firstTime(true)
     , m_presetsLoaded(false)
-    , m_targetType(targetType)
-    , m_workingFrameId(TFrameId())
     , m_notifier(0)
-    , m_modifierLockAlpha("Lock Alpha", false) {
+{
   bind(targetType);
 
   m_rasThickness.setNonLinearSlider();
@@ -901,6 +875,7 @@ ToonzRasterBrushTool::ToonzRasterBrushTool(std::string name, int targetType)
   m_prop[0].bind(m_modifierSize);
   m_prop[0].bind(m_modifierLockAlpha);
   m_prop[0].bind(m_pencil);
+  m_prop[0].bind(m_assistants);
   m_pencil.setId("PencilMode");
 
   m_drawOrder.addValue(L"Over All");
@@ -915,6 +890,21 @@ ToonzRasterBrushTool::ToonzRasterBrushTool(std::string name, int targetType)
   m_preset.addValue(CUSTOM_WSTR);
   m_pressure.setId("PressureSensitivity");
   m_modifierLockAlpha.setId("LockAlpha");
+
+  m_inputmanager.setHandler(this);
+  m_modifierLine               = new TModifierLine();
+  m_modifierTangents           = new TModifierTangents();
+  m_modifierAssistants         = new TModifierAssistants();
+  m_modifierSegmentation       = new TModifierSegmentation();
+  m_modifierSmoothSegmentation = new TModifierSegmentation(TPointD(1, 1), 3);
+  for(int i = 0; i < 3; ++i)
+    m_modifierSmooth[i]        = new TModifierSmooth();
+#ifndef NDEBUG
+  m_modifierTest = new TModifierTest(5, 40);
+#endif
+
+  m_inputmanager.addModifier(
+      TInputModifierP(m_modifierAssistants.getPointer()));
 }
 
 //-------------------------------------------------------------------------------------------------------
@@ -1051,6 +1041,8 @@ void ToonzRasterBrushTool::drawEmptyCircle(TPointD pos, int thick,
   if (!isPencil)
     tglDrawCircle(pos, (thick + 1) * 0.5);
   else {
+    pos.x = floor(pos.x) + 0.5;
+    pos.y = floor(pos.y) + 0.5;
     int x = 0, y = tround((thick * 0.5) - 0.5);
     int d           = 3 - 2 * (int)(thick * 0.5);
     bool horizontal = true, isDecimal = thick % 2 != 0;
@@ -1109,70 +1101,7 @@ void ToonzRasterBrushTool::updateTranslation() {
   m_pencil.setQStringName(tr("Pencil"));
   m_pressure.setQStringName(tr("Pressure"));
   m_modifierLockAlpha.setQStringName(tr("Lock Alpha"));
-}
-
-//---------------------------------------------------------------------------------------------------
-
-void ToonzRasterBrushTool::updateWorkAndBackupRasters(const TRect &rect) {
-  TToonzImageP ti = TImageP(getImage(false, 1));
-  if (!ti) return;
-
-  TRasterCM32P ras = ti->getRaster();
-
-  if (m_isMyPaintStyleSelected) {
-    const int denominator = 8;
-    TRect enlargedRect    = rect + m_lastRect;
-    int dx                = (enlargedRect.getLx() - 1) / denominator + 1;
-    int dy                = (enlargedRect.getLy() - 1) / denominator + 1;
-
-    if (m_lastRect.isEmpty()) {
-      enlargedRect.x0 -= dx;
-      enlargedRect.y0 -= dy;
-      enlargedRect.x1 += dx;
-      enlargedRect.y1 += dy;
-
-      TRect _rect = enlargedRect * ras->getBounds();
-      if (_rect.isEmpty()) return;
-
-      m_workRas->extract(_rect)->copy(ras->extract(_rect));
-      m_backupRas->extract(_rect)->copy(ras->extract(_rect));
-    } else {
-      if (enlargedRect.x0 < m_lastRect.x0) enlargedRect.x0 -= dx;
-      if (enlargedRect.y0 < m_lastRect.y0) enlargedRect.y0 -= dy;
-      if (enlargedRect.x1 > m_lastRect.x1) enlargedRect.x1 += dx;
-      if (enlargedRect.y1 > m_lastRect.y1) enlargedRect.y1 += dy;
-
-      TRect _rect = enlargedRect * ras->getBounds();
-      if (_rect.isEmpty()) return;
-
-      TRect _lastRect    = m_lastRect * ras->getBounds();
-      QList<TRect> rects = ToolUtils::splitRect(_rect, _lastRect);
-      for (int i = 0; i < rects.size(); i++) {
-        m_workRas->extract(rects[i])->copy(ras->extract(rects[i]));
-        m_backupRas->extract(rects[i])->copy(ras->extract(rects[i]));
-      }
-    }
-
-    m_lastRect = enlargedRect;
-    return;
-  }
-
-  TRect _rect     = rect * ras->getBounds();
-  TRect _lastRect = m_lastRect * ras->getBounds();
-
-  if (_rect.isEmpty()) return;
-
-  if (m_lastRect.isEmpty()) {
-    m_workRas->extract(_rect)->clear();
-    m_backupRas->extract(_rect)->copy(ras->extract(_rect));
-    return;
-  }
-
-  QList<TRect> rects = ToolUtils::splitRect(_rect, _lastRect);
-  for (int i = 0; i < rects.size(); i++) {
-    m_workRas->extract(rects[i])->clear();
-    m_backupRas->extract(rects[i])->copy(ras->extract(rects[i]));
-  }
+  m_assistants.setQStringName(tr("Assistants"));
 }
 
 //---------------------------------------------------------------------------------------------------
@@ -1205,18 +1134,8 @@ void ToonzRasterBrushTool::onActivate() {
 //--------------------------------------------------------------------------------------------------
 
 void ToonzRasterBrushTool::onDeactivate() {
-  /*---
-   * ドラッグ中にツールが切り替わった場合に備え、onDeactivateにもMouseReleaseと同じ処理を行う
-   * ---*/
-  if (m_tileSaver) {
-    bool isValid = m_enabled && m_active;
-    m_enabled    = false;
-    m_active     = false;
-    if (isValid) {
-      finishRasterBrush(m_mousePos,
-                        1); /*-- 最後のストロークの筆圧は1とする --*/
-    }
-  }
+  m_inputmanager.finishTracks();
+  m_enabled   = false;
   m_workRas   = TRaster32P();
   m_backupRas = TRasterCM32P();
 }
@@ -1229,16 +1148,34 @@ bool ToonzRasterBrushTool::askRead(const TRect &rect) { return askWrite(rect); }
 
 bool ToonzRasterBrushTool::askWrite(const TRect &rect) {
   if (rect.isEmpty()) return true;
-  m_strokeRect += rect;
-  m_strokeSegmentRect += rect;
+  m_painting.myPaint.strokeSegmentRect += rect;
   updateWorkAndBackupRasters(rect);
-  m_tileSaver->save(rect);
+  m_painting.tileSaver->save(rect);
   return true;
 }
 
 //--------------------------------------------------------------------------------------------------
 
 bool ToonzRasterBrushTool::preLeftButtonDown() {
+  int smoothRadius = (int)round(m_smooth.getValue());
+  m_modifierAssistants->drawOnly = !RasterBrushAssistants;
+  m_inputmanager.drawPreview     = false; //!m_modifierAssistants->drawOnly;
+
+  m_inputmanager.clearModifiers();
+  m_inputmanager.addModifier(TInputModifierP(m_modifierTangents.getPointer()));
+  if (smoothRadius > 0) {
+    m_inputmanager.addModifier(TInputModifierP(m_modifierSmoothSegmentation.getPointer()));
+    for(int i = 0; i < 3; ++i) {
+      m_modifierSmooth[i]->setRadius(smoothRadius);
+      m_inputmanager.addModifier(TInputModifierP(m_modifierSmooth[i].getPointer()));
+    }
+  }
+  m_inputmanager.addModifier(TInputModifierP(m_modifierAssistants.getPointer()));
+#ifndef NDEBUG
+  m_inputmanager.addModifier(TInputModifierP(m_modifierTest.getPointer()));
+#endif
+  m_inputmanager.addModifier(TInputModifierP(m_modifierSegmentation.getPointer()));
+
   touchImage();
   if (m_isFrameCreated) {
     setWorkAndBackupImages();
@@ -1250,654 +1187,339 @@ bool ToonzRasterBrushTool::preLeftButtonDown() {
   return true;
 }
 
-//--------------------------------------------------------------------------------------------------
+//---------------------------------------------------------------------------------------------------
 
-void ToonzRasterBrushTool::leftButtonDown(const TPointD &pos,
+void ToonzRasterBrushTool::handleMouseEvent(MouseEventType type,
+                                          const TPointD &pos,
                                           const TMouseEvent &e) {
-  TTool::Application *app = TTool::getApplication();
-  if (!app) return;
-
-  int col   = app->getCurrentColumn()->getColumnIndex();
-  m_enabled = col >= 0 || app->getCurrentFrame()->isEditingLevel();
-  // todo: gestire autoenable
-  if (!m_enabled) return;
-
-  TPointD centeredPos = getCenteredCursorPos(pos);
-
-  m_currentColor = TPixel32::Black;
-  m_active       = !!getImage(true);
-  if (!m_active) {
-    m_active = !!touchImage();
-  }
-  if (!m_active) return;
-
-  if (m_active) {
-    // nel caso che il colore corrente sia un cleanup/studiopalette color
-    // oppure il colore di un colorfield
-    m_styleId       = app->getCurrentLevelStyleIndex();
-    TColorStyle *cs = app->getCurrentLevelStyle();
-    if (cs) {
-      TRasterStyleFx *rfx = cs ? cs->getRasterStyleFx() : 0;
-      m_active = cs != 0 && (cs->isStrokeStyle() || (rfx && rfx->isInkStyle()));
-      m_currentColor   = cs->getAverageColor();
-      m_currentColor.m = 255;
-    } else {
-      m_styleId      = 1;
-      m_currentColor = TPixel32::Black;
-    }
-  }
-
-  // Modifier to do straight line
-  if (e.isShiftPressed()) {
-    m_isStraight = true;
-    m_firstPoint = pos;
-    m_lastPoint  = pos;
+  TTimerTicks t = TToolTimer::ticks();
+  bool alt      = e.getModifiersMask() & TMouseEvent::ALT_KEY;
+  bool shift    = e.getModifiersMask() & TMouseEvent::SHIFT_KEY;
+  bool control  = e.getModifiersMask() & TMouseEvent::CTRL_KEY;
+
+  if (shift && type == ME_DOWN && e.button() == Qt::LeftButton && !m_painting.active) {
+    m_modifierAssistants->drawOnly = true;
+    m_inputmanager.clearModifiers();
+    m_inputmanager.addModifier(TInputModifierP(m_modifierLine.getPointer()));
+    m_inputmanager.addModifier(TInputModifierP(m_modifierAssistants.getPointer()));
+    m_inputmanager.addModifier(TInputModifierP(m_modifierSegmentation.getPointer()));
+    m_inputmanager.drawPreview = true;
   }
 
-  double pressure;
-  if (m_isMyPaintStyleSelected)  // mypaint brush case
-    pressure = m_pressure.getValue() && e.isTablet() ? e.m_pressure : 0.5;
-  else
-    pressure = m_pressure.getValue() ? e.m_pressure : 1.0;
-
-  // assert(0<=m_styleId && m_styleId<2);
-  TImageP img = getImage(true);
-  TToonzImageP ri(img);
-  TRasterCM32P ras = ri->getRaster();
-  if (ras) {
-    TPointD rasCenter = ras->getCenterD();
-    m_tileSet         = new TTileSetCM32(ras->getSize());
-    m_tileSaver       = new TTileSaverCM32(ras, m_tileSet);
-    double maxThick   = m_rasThickness.getValue().second;
-    double thickness  = (m_pressure.getValue())
-                            ? computeThickness(pressure, m_rasThickness) * 2
-                            : maxThick;
-
-    /*--- ストロークの最初にMaxサイズの円が描かれてしまう不具合を防止する
-     * ---*/
-    if (m_pressure.getValue() && e.m_pressure == 1.0 && !m_isStraight)
-      thickness = m_rasThickness.getValue().first;
-
-    TPointD halfThick(maxThick * 0.5, maxThick * 0.5);
-    TRectD invalidateRect(centeredPos - halfThick, centeredPos + halfThick);
-    TPointD dpi;
-    ri->getDpi(dpi.x, dpi.y);
-    TRectD previousTipRect(m_brushPos - halfThick, m_brushPos + halfThick);
-    if (dpi.x > Stage::inch || dpi.y > Stage::inch)
-      previousTipRect *= dpi.x / Stage::inch;
-    invalidateRect += previousTipRect;
-
-    // if the drawOrder mode = "Palette Order",
-    // get styleId list which is above the current style in the palette
-    DrawOrder drawOrder = (DrawOrder)m_drawOrder.getIndex();
-    QSet<int> aboveStyleIds;
-    if (drawOrder == PaletteOrder) {
-      getAboveStyleIdSet(m_styleId, ri->getPalette(), aboveStyleIds);
-    }
-
-    // mypaint brush case
-    if (m_isMyPaintStyleSelected) {
-      TPointD point(centeredPos + rasCenter);
-
-      updateCurrentStyle();
-      if (!(m_workRas && m_backupRas)) setWorkAndBackupImages();
-      m_workRas->lock();
-      mypaint::Brush mypaintBrush;
-      TMyPaintBrushStyle *mypaintStyle =
-          dynamic_cast<TMyPaintBrushStyle *>(app->getCurrentLevelStyle());
-      {  // applyToonzBrushSettings
-        mypaintBrush.fromBrush(mypaintStyle->getBrush());
-        double modifierSize = m_modifierSize.getValue() * log(2.0);
-        float baseSize =
-            mypaintBrush.getBaseValue(MYPAINT_BRUSH_SETTING_RADIUS_LOGARITHMIC);
-        mypaintBrush.setBaseValue(MYPAINT_BRUSH_SETTING_RADIUS_LOGARITHMIC,
-                                  baseSize + modifierSize);
-      }
-      m_toonz_brush =
-          new MyPaintToonzBrush(m_workRas, *this, mypaintBrush, true);
-      m_strokeRect.empty();
-      m_strokeSegmentRect.empty();
-      m_toonz_brush->beginStroke();
-      m_toonz_brush->strokeTo(point, pressure, TPointD(), restartBrushTimer());
-      TRect updateRect = m_strokeSegmentRect * ras->getBounds();
-      if (!updateRect.isEmpty()) {
-        // ras->extract(updateRect)->copy(m_workRas->extract(updateRect));
-        m_toonz_brush->updateDrawing(ri->getRaster(), m_backupRas, m_strokeRect,
-                                     m_styleId, m_modifierLockAlpha.getValue());
-      }
-      m_lastRect = m_strokeRect;
-
-      TPointD thickOffset(m_maxCursorThick * 0.5, m_maxCursorThick * 0.5);
-      invalidateRect = convert(m_strokeSegmentRect) - rasCenter;
-      invalidateRect +=
-          TRectD(centeredPos - thickOffset, centeredPos + thickOffset);
-      invalidateRect +=
-          TRectD(m_brushPos - thickOffset, m_brushPos + thickOffset);
-    } else if (m_hardness.getValue() == 100 || m_pencil.getValue()) {
-      /*-- Pencilモードでなく、Hardness=100 の場合のブラシサイズを1段階下げる
-       * --*/
-      if (!m_pencil.getValue() && !m_isStraight) thickness -= 1.0;
-
-      TThickPoint thickPoint(centeredPos + convert(ras->getCenter()),
-                             thickness);
-      m_rasterTrack = new RasterStrokeGenerator(
-          ras, BRUSH, NONE, m_styleId, thickPoint, drawOrder != OverAll, 0,
-          m_modifierLockAlpha.getValue(), !m_pencil.getValue(),
-          drawOrder == PaletteOrder);
-
-      if (drawOrder == PaletteOrder)
-        m_rasterTrack->setAboveStyleIds(aboveStyleIds);
-
-      m_tileSaver->save(m_rasterTrack->getLastRect());
-      if (!m_isStraight)
-        m_rasterTrack->generateLastPieceOfStroke(m_pencil.getValue());
-
-      std::vector<TThickPoint> pts;
-      if (m_smooth.getValue() == 0) {
-        pts.push_back(thickPoint);
-      } else {
-        m_smoothStroke.beginStroke(m_smooth.getValue());
-        m_smoothStroke.addPoint(thickPoint);
-        m_smoothStroke.getSmoothPoints(pts);
-      }
-    } else {
-      m_points.clear();
-      TThickPoint point(centeredPos + rasCenter, thickness);
-      m_points.push_back(point);
-      m_bluredBrush = new BluredBrush(m_workRas, maxThick, m_brushPad, false);
-
-      if (drawOrder == PaletteOrder)
-        m_bluredBrush->setAboveStyleIds(aboveStyleIds);
-
-      m_strokeRect = m_bluredBrush->getBoundFromPoints(m_points);
-      updateWorkAndBackupRasters(m_strokeRect);
-      m_tileSaver->save(m_strokeRect);
-      m_bluredBrush->addPoint(point, 1);
-      if (!m_isStraight)
-        m_bluredBrush->updateDrawing(ri->getRaster(), m_backupRas, m_strokeRect,
-                                     m_styleId, drawOrder,
-                                     m_modifierLockAlpha.getValue());
-      m_lastRect = m_strokeRect;
-
-      std::vector<TThickPoint> pts;
-      if (m_smooth.getValue() == 0) {
-        pts.push_back(point);
-      } else {
-        m_smoothStroke.beginStroke(m_smooth.getValue());
-        m_smoothStroke.addPoint(point);
-        m_smoothStroke.getSmoothPoints(pts);
-      }
-    }
-    /*-- 作業中のFidを登録 --*/
-    m_workingFrameId = getFrameId();
+  if (alt != m_inputmanager.state.isKeyPressed(TKey::alt))
+    m_inputmanager.keyEvent(alt, TKey::alt, t, nullptr);
+  if (shift != m_inputmanager.state.isKeyPressed(TKey::shift))
+    m_inputmanager.keyEvent(shift, TKey::shift, t, nullptr);
+  if (control != m_inputmanager.state.isKeyPressed(TKey::control))
+    m_inputmanager.keyEvent(control, TKey::control, t, nullptr);
 
-    invalidate(invalidateRect.enlarge(2));
+  if (type == ME_MOVE) {
+    THoverList hovers(1, pos);
+    m_inputmanager.hoverEvent(hovers);
+  } else {
+    m_inputmanager.trackEvent(e.isTablet(), 0, pos,
+                              e.isTablet() ? &e.m_pressure : nullptr, nullptr,
+                              type == ME_UP, t);
+    m_inputmanager.processTracks();
   }
-  // updating m_brushPos is needed to refresh viewer properly
-  m_mousePos = pos;
-  m_brushPos = getCenteredCursorPos(pos);
 }
 
-//-------------------------------------------------------------------------------------------------------------
+//---------------------------------------------------------------------------------------------------
 
+void ToonzRasterBrushTool::leftButtonDown(const TPointD &pos,
+                                        const TMouseEvent &e) {
+  handleMouseEvent(ME_DOWN, pos, e);
+}
 void ToonzRasterBrushTool::leftButtonDrag(const TPointD &pos,
-                                          const TMouseEvent &e) {
-  TRectD invalidateRect;
-  m_lastPoint = pos;
-
-  if (!m_enabled || !m_active) {
-    m_mousePos = pos;
-    m_brushPos = getCenteredCursorPos(pos);
-    return;
-  }
+                                        const TMouseEvent &e) {
+  handleMouseEvent(ME_DRAG, pos, e);
+}
+void ToonzRasterBrushTool::leftButtonUp(const TPointD &pos,
+                                      const TMouseEvent &e) {
+  handleMouseEvent(ME_UP, pos, e);
+}
+void ToonzRasterBrushTool::mouseMove(const TPointD &pos, const TMouseEvent &e) {
+  handleMouseEvent(ME_MOVE, pos, e);
+}
 
-  TPointD centeredPos = getCenteredCursorPos(m_lastPoint);
+//--------------------------------------------------------------------------------------------------
 
-  double pressure;
-  if (m_isMyPaintStyleSelected)  // mypaint brush case
-    pressure = m_pressure.getValue() && e.isTablet() ? e.m_pressure : 0.5;
-  else
-    pressure = m_pressure.getValue() ? e.m_pressure : 1.0;
-
-  TToonzImageP ti     = TImageP(getImage(true));
-  TPointD rasCenter   = ti->getRaster()->getCenterD();
-  double maxThickness = m_rasThickness.getValue().second;
-  double thickness    = (m_pressure.getValue())
-                            ? computeThickness(pressure, m_rasThickness) * 2
-                            : maxThickness;
-
-  if (m_maxPressure < pressure) m_maxPressure = pressure;
-
-  if (m_isStraight) {
-    invalidateRect = TRectD(m_firstPoint, m_lastPoint).enlarge(2);
-    if (e.isCtrlPressed()) {
-      double distance = (m_brushPos.x - m_maxCursorThick + 1) * 0.5;
-      TRectD brushRect =
-          TRectD(TPointD(m_brushPos.x - distance, m_brushPos.y - distance),
-                 TPointD(m_brushPos.x + distance, m_brushPos.y + distance));
-      invalidateRect += (brushRect);
-      double denominator = m_lastPoint.x - m_firstPoint.x;
-      if (denominator == 0) denominator == 0.001;
-      double slope = ((m_lastPoint.y - m_firstPoint.y) / denominator);
-      double angle = std::atan(slope) * (180 / 3.14159);
-      if (abs(angle) > 67.5)
-        m_lastPoint.x = m_firstPoint.x;
-      else if (abs(angle) < 22.5)
-        m_lastPoint.y = m_firstPoint.y;
-      else {
-        double xDistance = m_lastPoint.x - m_firstPoint.x;
-        double yDistance = m_lastPoint.y - m_firstPoint.y;
-        if (abs(xDistance) > abs(yDistance)) {
-          if (abs(yDistance) == yDistance)
-            m_lastPoint.y = m_firstPoint.y + abs(xDistance);
-          else
-            m_lastPoint.y = m_firstPoint.y - abs(xDistance);
-        } else {
-          if (abs(xDistance) == xDistance)
-            m_lastPoint.x = m_firstPoint.x + abs(yDistance);
-          else
-            m_lastPoint.x = m_firstPoint.x - abs(yDistance);
-        }
-      }
-    }
-    m_mousePos = pos;
-    m_brushPos = getCenteredCursorPos(pos);
-    invalidate(invalidateRect);
+void ToonzRasterBrushTool::inputSetBusy(bool busy) {
+  if (m_painting.active == busy)
     return;
-  }
+  
+  if (busy) {
+    // begin painting
+    
+    TTool::Application *app = TTool::getApplication();
+    if (!app) return;
 
-  if (m_isMyPaintStyleSelected) {
-    TRasterP ras = ti->getRaster();
-    TPointD point(centeredPos + rasCenter);
-
-    m_strokeSegmentRect.empty();
-    m_toonz_brush->strokeTo(point, pressure, TPointD(), restartBrushTimer());
-    TRect updateRect = m_strokeSegmentRect * ras->getBounds();
-    if (!updateRect.isEmpty()) {
-      // ras->extract(updateRect)->copy(m_workRaster->extract(updateRect));
-      m_toonz_brush->updateDrawing(ras, m_backupRas, m_strokeSegmentRect,
-                                   m_styleId, m_modifierLockAlpha.getValue());
-    }
-    m_lastRect = m_strokeRect;
-
-    TPointD thickOffset(m_maxCursorThick * 0.5, m_maxCursorThick * 0.5);
-    invalidateRect = convert(m_strokeSegmentRect) - rasCenter;
-    invalidateRect +=
-        TRectD(centeredPos - thickOffset, centeredPos + thickOffset);
-    invalidateRect +=
-        TRectD(m_brushPos - thickOffset, m_brushPos + thickOffset);
-  } else if (m_rasterTrack &&
-             (m_hardness.getValue() == 100 || m_pencil.getValue())) {
-    /*-- Pencilモードでなく、Hardness=100 の場合のブラシサイズを1段階下げる
-     * --*/
-    if (!m_pencil.getValue()) thickness -= 1.0;
-
-    TThickPoint thickPoint(centeredPos + rasCenter, thickness);
-    std::vector<TThickPoint> pts;
-    if (m_smooth.getValue() == 0) {
-      pts.push_back(thickPoint);
+    int col   = app->getCurrentColumn()->getColumnIndex();
+    m_enabled = col >= 0 || app->getCurrentFrame()->isEditingLevel();
+    // todo: gestire autoenable
+    if (!m_enabled) return;
+
+    m_painting.active = !!getImage(true);
+    if (!m_painting.active)
+      m_painting.active = !!touchImage();
+    if (!m_painting.active)
+      return;
+    
+    // nel caso che il colore corrente sia un cleanup/studiopalette color
+    // oppure il colore di un colorfield
+    updateCurrentStyle();
+    if (TColorStyle *cs = app->getCurrentLevelStyle()) {
+      m_painting.styleId = app->getCurrentLevelStyleIndex();
+      TRasterStyleFx *rfx = cs->getRasterStyleFx();
+      if (!cs->isStrokeStyle() && (!rfx || !rfx->isInkStyle()))
+          { m_painting.active = false; return; }
     } else {
-      m_smoothStroke.addPoint(thickPoint);
-      m_smoothStroke.getSmoothPoints(pts);
+      m_painting.styleId = 1;
     }
-    for (size_t i = 0; i < pts.size(); ++i) {
-      const TThickPoint &thickPoint = pts[i];
-      m_rasterTrack->add(thickPoint);
-      m_tileSaver->save(m_rasterTrack->getLastRect());
-      m_rasterTrack->generateLastPieceOfStroke(m_pencil.getValue());
-      std::vector<TThickPoint> brushPoints = m_rasterTrack->getPointsSequence();
-      int m                                = (int)brushPoints.size();
-      std::vector<TThickPoint> points;
-      if (m == 3) {
-        points.push_back(brushPoints[0]);
-        points.push_back(brushPoints[1]);
-      } else {
-        points.push_back(brushPoints[m - 4]);
-        points.push_back(brushPoints[m - 3]);
-        points.push_back(brushPoints[m - 2]);
-      }
-      invalidateRect += ToolUtils::getBounds(points, maxThickness) - rasCenter;
+    
+    m_painting.frameId = getFrameId();
+
+    TImageP img = getImage(true);
+    TToonzImageP ri(img);
+    TRasterCM32P ras = ri->getRaster();
+    if (!ras)
+      { m_painting.active = false; return; }
+      
+    m_painting.tileSet   = new TTileSetCM32(ras->getSize());
+    m_painting.tileSaver = new TTileSaverCM32(ras, m_painting.tileSet);
+    m_painting.affectedRect.empty();
+    setWorkAndBackupImages();
+    
+    if (m_isMyPaintStyleSelected) {
+      // init myPaint drawing
+      
+      m_painting.myPaint.isActive = true;
+      m_painting.myPaint.strokeSegmentRect.empty();
+      
+      m_workRas->lock();
+      
+      // prepare base brush
+      if ( TMyPaintBrushStyle *mypaintStyle = dynamic_cast<TMyPaintBrushStyle *>(app->getCurrentLevelStyle()) )
+        m_painting.myPaint.baseBrush.fromBrush( mypaintStyle->getBrush() ); else
+          m_painting.myPaint.baseBrush.fromDefaults();
+      double modifierSize = m_modifierSize.getValue() * log(2.0);
+      float baseSize = m_painting.myPaint.baseBrush.getBaseValue( MYPAINT_BRUSH_SETTING_RADIUS_LOGARITHMIC );
+      m_painting.myPaint.baseBrush.setBaseValue( MYPAINT_BRUSH_SETTING_RADIUS_LOGARITHMIC, baseSize + modifierSize );
+    } else
+    if (m_hardness.getValue() == 100 || m_pencil.getValue()) {
+      // init pencil drawing
+      
+      m_painting.pencil.isActive = true;
+      m_painting.pencil.realPencil = m_pencil.getValue();
+    } else {
+      // init blured brush drawing (regular drawing)
+      
+      m_painting.blured.isActive = true;
     }
+    
   } else {
-    // antialiased brush
-    assert(m_workRas.getPointer() && m_backupRas.getPointer());
-    TThickPoint thickPoint(centeredPos + rasCenter, thickness);
-    std::vector<TThickPoint> pts;
-    if (m_smooth.getValue() == 0) {
-      pts.push_back(thickPoint);
-    } else {
-      m_smoothStroke.addPoint(thickPoint);
-      m_smoothStroke.getSmoothPoints(pts);
+    // finish painting
+    
+    if (m_painting.myPaint.isActive) {
+      // finish myPaint drawing
+      m_workRas->unlock();
     }
-    for (size_t i = 0; i < pts.size(); ++i) {
-      TThickPoint old = m_points.back();
-
-      const TThickPoint &point = pts[i];
-      TThickPoint mid((old + point) * 0.5, (point.thick + old.thick) * 0.5);
-      m_points.push_back(mid);
-      m_points.push_back(point);
-
-      TRect bbox;
-      int m = (int)m_points.size();
-      std::vector<TThickPoint> points;
-      if (m == 3) {
-        // ho appena cominciato. devo disegnare un segmento
-        TThickPoint pa = m_points.front();
-        points.push_back(pa);
-        points.push_back(mid);
-        bbox = m_bluredBrush->getBoundFromPoints(points);
-        updateWorkAndBackupRasters(bbox + m_lastRect);
-        m_tileSaver->save(bbox);
-        m_bluredBrush->addArc(pa, (mid + pa) * 0.5, mid, 1, 1);
-        m_lastRect += bbox;
-      } else {
-        points.push_back(m_points[m - 4]);
-        points.push_back(old);
-        points.push_back(mid);
-        bbox = m_bluredBrush->getBoundFromPoints(points);
-        updateWorkAndBackupRasters(bbox + m_lastRect);
-        m_tileSaver->save(bbox);
-        m_bluredBrush->addArc(m_points[m - 4], old, mid, 1, 1);
-        m_lastRect += bbox;
-      }
-      invalidateRect += ToolUtils::getBounds(points, maxThickness) - rasCenter;
 
-      m_bluredBrush->updateDrawing(ti->getRaster(), m_backupRas, bbox,
-                                   m_styleId, m_drawOrder.getIndex(),
-                                   m_modifierLockAlpha.getValue());
-      m_strokeRect += bbox;
+    delete m_painting.tileSaver;
+    m_painting.tileSaver = nullptr;
+
+    // add undo record
+    TFrameId frameId = m_painting.frameId.isEmptyFrame() ? getCurrentFid() : m_painting.frameId;
+    if (m_painting.tileSet->getTileCount() > 0) {
+      TTool::Application *app   = TTool::getApplication();
+      TXshLevel *level          = app->getCurrentLevel()->getLevel();
+      TXshSimpleLevelP simLevel = level->getSimpleLevel();
+      TRasterCM32P ras          = TToonzImageP( getImage(true) )->getRaster();
+      TRasterCM32P subras       = ras->extract(m_painting.affectedRect)->clone();
+      TUndoManager::manager()->add(new MyPaintBrushUndo(
+          m_painting.tileSet, simLevel.getPointer(), frameId, m_isFrameCreated,
+          m_isLevelCreated, subras, m_painting.affectedRect.getP00()));
+      // MyPaintBrushUndo will delete tileSet by it self
+    } else {
+      // delete tileSet here because MyPaintBrushUndo will not do it
+      delete m_painting.tileSet;
     }
-  }
+    m_painting.tileSet = nullptr;
 
-  // clear & draw brush tip when drawing smooth stroke
-  if (m_smooth.getValue() != 0) {
-    TPointD halfThick(m_maxThick * 0.5, m_maxThick * 0.5);
-    invalidateRect += TRectD(m_brushPos - halfThick, m_brushPos + halfThick);
-    invalidateRect += TRectD(centeredPos - halfThick, centeredPos + halfThick);
-  }
+    /*-- 作業中のフレームをリセット --*/
+    m_painting.frameId = TFrameId();
 
-  m_mousePos = pos;
-  m_brushPos = getCenteredCursorPos(pos);
 
-  invalidate(invalidateRect.enlarge(2));
-}
-
-//---------------------------------------------------------------------------------------------------------------
-
-void ToonzRasterBrushTool::leftButtonUp(const TPointD &pos,
-                                        const TMouseEvent &e) {
-  bool isValid = m_enabled && m_active;
-  m_enabled    = false;
-  m_active     = false;
-  if (!isValid) {
-    return;
+    m_painting.myPaint.isActive = false;
+    m_painting.pencil.isActive = false;
+    m_painting.blured.isActive = false;
+    m_painting.active = false;
+    
+    /*-- FIdを指定して、描画中にフレームが動いても、
+      描画開始時のFidのサムネイルが更新されるようにする。--*/
+    notifyImageChanged(frameId);
+    ToolUtils::updateSaveBox();
   }
-  TPointD centeredPos;
-  if (m_isStraight)
-    centeredPos = getCenteredCursorPos(m_lastPoint);
-  else
-    centeredPos = getCenteredCursorPos(pos);
-
-  double pressure;
-  if (m_isMyPaintStyleSelected)  // mypaint brush case
-    pressure = m_pressure.getValue() && e.isTablet() ? e.m_pressure : 0.5;
-  else
-    pressure = m_pressure.getValue() ? e.m_pressure : 1.0;
-
-  if (m_isStraight && m_maxPressure > 0.0) pressure = m_maxPressure;
-
-  finishRasterBrush(centeredPos, pressure);
-  int tc = ToonzCheck::instance()->getChecks();
-  if (tc & ToonzCheck::eGap || tc & ToonzCheck::eAutoclose || m_isStraight)
-    invalidate();
-
-  m_isStraight  = false;
-  m_maxPressure = -1.0;
 }
 
-//---------------------------------------------------------------------------------------------------------------
-/*!
- * ドラッグ中にツールが切り替わった場合に備え、onDeactivate時とMouseRelease時にと同じ終了処理を行う
- */
-void ToonzRasterBrushTool::finishRasterBrush(const TPointD &pos,
-                                             double pressureVal) {
-  TToonzImageP ti = TImageP(getImage(true));
-
-  if (!ti) return;
-
-  TPointD rasCenter         = ti->getRaster()->getCenterD();
-  TTool::Application *app   = TTool::getApplication();
-  TXshLevel *level          = app->getCurrentLevel()->getLevel();
-  TXshSimpleLevelP simLevel = level->getSimpleLevel();
-
-  /*--
-   * 描画中にカレントフレームが変わっても、描画開始時のFidに対してUndoを記録する
-   * --*/
-  TFrameId frameId =
-      m_workingFrameId.isEmptyFrame() ? getCurrentFid() : m_workingFrameId;
-  if (m_isMyPaintStyleSelected) {
-    TRasterCM32P ras = ti->getRaster();
-    TPointD point(pos + rasCenter);
-    double pressure = m_pressure.getValue() ? pressureVal : 0.5;
-
-    m_strokeSegmentRect.empty();
-    m_toonz_brush->strokeTo(point, pressure, TPointD(), restartBrushTimer());
-    m_toonz_brush->endStroke();
-    TRect updateRect = m_strokeSegmentRect * ras->getBounds();
-    if (!updateRect.isEmpty()) {
-      // ras->extract(updateRect)->copy(m_workRaster->extract(updateRect));
-      m_toonz_brush->updateDrawing(ras, m_backupRas, m_strokeSegmentRect,
-                                   m_styleId, m_modifierLockAlpha.getValue());
-    }
-    TPointD thickOffset(m_maxCursorThick * 0.5,
-                        m_maxCursorThick * 0.5);  // TODO
-    TRectD invalidateRect = convert(m_strokeSegmentRect) - rasCenter;
-    invalidateRect += TRectD(pos - thickOffset, pos + thickOffset);
-    invalidate(invalidateRect.enlarge(2.0));
-
-    if (m_toonz_brush) {
-      delete m_toonz_brush;
-      m_toonz_brush = 0;
-    }
-
-    m_lastRect.empty();
-    m_workRas->unlock();
-
-    if (m_tileSet->getTileCount() > 0) {
-      TRasterCM32P subras = ras->extract(m_strokeRect)->clone();
-      TUndoManager::manager()->add(new MyPaintBrushUndo(
-          m_tileSet, simLevel.getPointer(), frameId, m_isFrameCreated,
-          m_isLevelCreated, subras, m_strokeRect.getP00()));
-    }
-
-  } else if (m_rasterTrack &&
-             (m_hardness.getValue() == 100 || m_pencil.getValue())) {
-    double thickness = m_pressure.getValue()
-                           ? computeThickness(pressureVal, m_rasThickness) * 2
-                           : m_rasThickness.getValue().second;
-
-    if (!m_isStraight) {
-      // ストロークの最初にMaxサイズの円が描かれてしまう不具合を防止する
-      if (m_pressure.getValue() && pressureVal == 1.0)
-        thickness = m_rasThickness.getValue().first;
+//--------------------------------------------------------------------------------------------------
 
-      // Pencilモードでなく、Hardness=100 の場合のブラシサイズを1段階下げる
-      if (!m_pencil.getValue()) thickness -= 1.0;
-    }
+void ToonzRasterBrushTool::inputPaintTrackPoint(const TTrackPoint &point, const TTrack &track, bool firstTrack, bool preview) {
+  if (!m_painting.active || preview)
+    return;
+  
+  TImageP img = getImage(true);
+  TToonzImageP ri(img);
+  TRasterCM32P ras = ri->getRaster();
+  if (!ras)
+    return;
+  TPointD rasCenter = ras->getCenterD();
+  TPointD fixedPosition = getCenteredCursorPos(point.position);
+  
 
-    TRectD invalidateRect;
-    TThickPoint thickPoint(pos + rasCenter, thickness);
-    std::vector<TThickPoint> pts;
-    if (m_smooth.getValue() == 0 || m_isStraight) {
-      pts.push_back(thickPoint);
-    } else {
-      m_smoothStroke.addPoint(thickPoint);
-      m_smoothStroke.endStroke();
-      m_smoothStroke.getSmoothPoints(pts);
+  TRectD invalidateRect;
+  bool firstPoint = track.size() == track.pointsAdded;
+  bool lastPoint  = track.pointsAdded == 1 && track.finished();
+  
+  // first point must be without handler, following points must be with handler
+  // other behaviour is possible bug and must be ignored
+  assert(firstPoint == !track.toolHandler);
+  if (firstPoint != !track.toolHandler)
+    return;
+  
+  if (m_painting.myPaint.isActive) {
+    // mypaint case
+    
+    // init brush
+    MyPaintStroke *handler;
+    if (firstPoint) {
+      handler = new MyPaintStroke(m_workRas, *this, m_painting.myPaint.baseBrush, false);
+      handler->brush.beginStroke();
+      track.toolHandler = handler;
     }
-    for (size_t i = 0; i < pts.size(); ++i) {
-      const TThickPoint &thickPoint = pts[i];
-      m_rasterTrack->add(thickPoint);
-      m_tileSaver->save(m_rasterTrack->getLastRect(m_isStraight));
-      m_rasterTrack->generateLastPieceOfStroke(m_pencil.getValue(), true,
-                                               m_isStraight);
-
-      std::vector<TThickPoint> brushPoints = m_rasterTrack->getPointsSequence();
-      int m                                = (int)brushPoints.size();
-      std::vector<TThickPoint> points;
-      if (m_isStraight) {
-        points.push_back(brushPoints[0]);
-        points.push_back(brushPoints[2]);
-      } else if (m == 3) {
-        points.push_back(brushPoints[0]);
-        points.push_back(brushPoints[1]);
-      } else {
-        points.push_back(brushPoints[m - 4]);
-        points.push_back(brushPoints[m - 3]);
-        points.push_back(brushPoints[m - 2]);
+    handler = dynamic_cast<MyPaintStroke*>(track.toolHandler.getPointer());
+    if (!handler) return;
+    
+    // paint stroke
+    m_painting.myPaint.strokeSegmentRect.empty();
+    handler->brush.strokeTo( fixedPosition + rasCenter, point.pressure,
+                             point.tilt, point.time - track.previous().time );
+    if (lastPoint)
+      handler->brush.endStroke();
+    
+    // update affected area
+    TRect updateRect = m_painting.myPaint.strokeSegmentRect * ras->getBounds();
+    m_painting.affectedRect += updateRect;
+    if (!updateRect.isEmpty())
+      handler->brush.updateDrawing( ras, m_backupRas, m_painting.myPaint.strokeSegmentRect,
+                                    m_painting.styleId, m_modifierLockAlpha.getValue() );
+    
+    // determine invalidate rect
+    invalidateRect += convert(m_painting.myPaint.strokeSegmentRect) - rasCenter;
+  } else
+  if (m_painting.pencil.isActive) {
+    // pencil case
+    
+    // Pencilモードでなく、Hardness=100 の場合のブラシサイズを1段階下げる
+    double thickness = computeThickness(point.pressure, m_rasThickness)*2;
+    //if (!m_painting.pencil.realPencil && !m_modifierLine->getManager())
+    //  thickness -= 1.0;
+    TThickPoint thickPoint(fixedPosition + rasCenter, thickness);
+
+    // init brush
+    PencilStroke *handler;
+    if (firstPoint) {
+      DrawOrder drawOrder = (DrawOrder)m_drawOrder.getIndex();
+      handler = new PencilStroke( ras, BRUSH, NONE, m_painting.styleId, thickPoint, drawOrder != OverAll, 0,
+                                  m_modifierLockAlpha.getValue(), !m_painting.pencil.realPencil,
+                                  drawOrder == PaletteOrder );
+      
+      // if the drawOrder mode = "Palette Order",
+      // get styleId list which is above the current style in the palette
+      if (drawOrder == PaletteOrder) {
+        QSet<int> aboveStyleIds;
+        getAboveStyleIdSet(m_painting.styleId, ri->getPalette(), aboveStyleIds);
+        handler->brush.setAboveStyleIds(aboveStyleIds);
       }
-      double maxThickness = m_rasThickness.getValue().second;
-      invalidateRect += ToolUtils::getBounds(points, maxThickness) - rasCenter;
-    }
-    invalidate(invalidateRect.enlarge(2));
-
-    if (m_tileSet->getTileCount() > 0) {
-      TUndoManager::manager()->add(new RasterBrushUndo(
-          m_tileSet, m_rasterTrack->getPointsSequence(),
-          m_rasterTrack->getStyleId(), m_rasterTrack->isSelective(),
-          simLevel.getPointer(), frameId, m_pencil.getValue(), m_isFrameCreated,
-          m_isLevelCreated, m_rasterTrack->isPaletteOrder(),
-          m_rasterTrack->isAlphaLocked(), m_isStraight));
+      track.toolHandler = handler;
     }
-    delete m_rasterTrack;
-    m_rasterTrack = 0;
-  } else {
-    double maxThickness = m_rasThickness.getValue().second;
-    double thickness    = (m_pressure.getValue())
-                              ? computeThickness(pressureVal, m_rasThickness) * 2
-                              : maxThickness;
-    TPointD rasCenter   = ti->getRaster()->getCenterD();
-    TRectD invalidateRect;
-    TThickPoint thickPoint(pos + rasCenter, thickness);
-    std::vector<TThickPoint> pts;
-    if (m_smooth.getValue() == 0 || m_isStraight) {
-      pts.push_back(thickPoint);
-    } else {
-      m_smoothStroke.addPoint(thickPoint);
-      m_smoothStroke.endStroke();
-      m_smoothStroke.getSmoothPoints(pts);
-    }
-    // we need to skip the for-loop here if pts.size() == 0 or else
-    // (pts.size() - 1) becomes ULLONG_MAX since size_t is unsigned
-    if (pts.size() > 0) {
-      // this doesn't get run for a straight line
-      for (size_t i = 0; i < pts.size() - 1; ++i) {
-        TThickPoint old = m_points.back();
-
-        const TThickPoint &point = pts[i];
-        TThickPoint mid((old + point) * 0.5, (point.thick + old.thick) * 0.5);
-        m_points.push_back(mid);
-        m_points.push_back(point);
-
-        TRect bbox;
-        int m = (int)m_points.size();
-        std::vector<TThickPoint> points;
-        if (m == 3) {
-          // ho appena cominciato. devo disegnare un segmento
-          TThickPoint pa = m_points.front();
-          points.push_back(pa);
-          points.push_back(mid);
-          bbox = m_bluredBrush->getBoundFromPoints(points);
-          updateWorkAndBackupRasters(bbox + m_lastRect);
-          m_tileSaver->save(bbox);
-          m_bluredBrush->addArc(pa, (mid + pa) * 0.5, mid, 1, 1);
-          m_lastRect += bbox;
-        } else {
-          points.push_back(m_points[m - 4]);
-          points.push_back(old);
-          points.push_back(mid);
-          bbox = m_bluredBrush->getBoundFromPoints(points);
-          updateWorkAndBackupRasters(bbox + m_lastRect);
-          m_tileSaver->save(bbox);
-          m_bluredBrush->addArc(m_points[m - 4], old, mid, 1, 1);
-          m_lastRect += bbox;
-        }
-
-        invalidateRect +=
-            ToolUtils::getBounds(points, maxThickness) - rasCenter;
-
-        m_bluredBrush->updateDrawing(ti->getRaster(), m_backupRas, bbox,
-                                     m_styleId, m_drawOrder.getIndex(),
-                                     m_modifierLockAlpha.getValue());
-        m_strokeRect += bbox;
-      }
-      if (!m_isStraight && m_points.size() > 1) {
-        TThickPoint point = pts.back();
-        m_points.push_back(point);
-      }
-      int m = m_points.size();
-      std::vector<TThickPoint> points;
-      if (!m_isStraight && m_points.size() > 1) {
-        points.push_back(m_points[m - 3]);
-        points.push_back(m_points[m - 2]);
-        points.push_back(m_points[m - 1]);
-      } else {
-        const TThickPoint point = m_points[0];
-        TThickPoint mid((thickPoint + point) * 0.5,
-                        (point.thick + thickPoint.thick) * 0.5);
-        m_points.push_back(mid);
-        m_points.push_back(thickPoint);
-        points.push_back(m_points[0]);
-        points.push_back(m_points[1]);
-        points.push_back(m_points[2]);
+    handler = dynamic_cast<PencilStroke*>(track.toolHandler.getPointer());
+    if (!handler) return;
+
+    // paint stroke
+    if (!firstPoint)
+      handler->brush.add(thickPoint);
+    
+    // update affected area
+    TRect strokeRect = handler->brush.getLastRect() * ras->getBounds();
+    m_painting.tileSaver->save( strokeRect );
+    m_painting.affectedRect += strokeRect;
+    handler->brush.generateLastPieceOfStroke( m_painting.pencil.realPencil );
+    
+    // determine invalidate rect
+    invalidateRect += convert(strokeRect) - rasCenter;
+  } else
+  if (m_painting.blured.isActive) {
+    // blured brush case (aka regular brush)
+
+    double maxThick = m_rasThickness.getValue().second;
+    DrawOrder drawOrder = (DrawOrder)m_drawOrder.getIndex();
+    
+    // init brush
+    BluredStroke *handler;
+    if (firstPoint) {
+      handler = new BluredStroke(m_workRas, maxThick, m_brushPad, false);
+      // if the drawOrder mode = "Palette Order",
+      // get styleId list which is above the current style in the palette
+      if (drawOrder == PaletteOrder) {
+        QSet<int> aboveStyleIds;
+        getAboveStyleIdSet(m_painting.styleId, ri->getPalette(), aboveStyleIds);
+        handler->brush.setAboveStyleIds(aboveStyleIds);
       }
-      TRect bbox = m_bluredBrush->getBoundFromPoints(points);
-      updateWorkAndBackupRasters(bbox);
-      m_tileSaver->save(bbox);
-      m_bluredBrush->addArc(points[0], points[1], points[2], 1, 1);
-      m_bluredBrush->updateDrawing(ti->getRaster(), m_backupRas, bbox,
-                                   m_styleId, m_drawOrder.getIndex(),
-                                   m_modifierLockAlpha.getValue());
-
-      invalidateRect += ToolUtils::getBounds(points, maxThickness) - rasCenter;
-
-      m_lastRect += bbox;
-      m_strokeRect += bbox;
-    }
-    if (!invalidateRect.isEmpty()) invalidate(invalidateRect.enlarge(2));
-    m_lastRect.empty();
-
-    delete m_bluredBrush;
-    m_bluredBrush = 0;
-
-    if (m_tileSet->getTileCount() > 0) {
-      TUndoManager::manager()->add(new RasterBluredBrushUndo(
-          m_tileSet, m_points, m_styleId, (DrawOrder)m_drawOrder.getIndex(),
-          m_modifierLockAlpha.getValue(), simLevel.getPointer(), frameId,
-          m_rasThickness.getValue().second, m_hardness.getValue() * 0.01,
-          m_isFrameCreated, m_isLevelCreated, m_isStraight));
+      track.toolHandler = handler;
     }
+    handler = dynamic_cast<BluredStroke*>(track.toolHandler.getPointer());
+    if (!handler) return;
+
+    // paint stroke
+    double radius = computeThickness(point.pressure, m_rasThickness);
+    TThickPoint thickPoint(fixedPosition + rasCenter, radius*2);
+    TRect strokeRect( tfloor(thickPoint.x - maxThick - 0.999),
+                      tfloor(thickPoint.y - maxThick - 0.999),
+                      tceil(thickPoint.x + maxThick + 0.999),
+                      tceil(thickPoint.y + maxThick + 0.999) );
+    strokeRect *= ras->getBounds();
+    m_painting.affectedRect += strokeRect;
+    updateWorkAndBackupRasters(m_painting.affectedRect);
+    m_painting.tileSaver->save(strokeRect);
+    handler->brush.addPoint(thickPoint, 1, !lastPoint);
+    handler->brush.updateDrawing( ras, m_backupRas, strokeRect,
+                                  m_painting.styleId, drawOrder,
+                                  m_modifierLockAlpha.getValue() );
+    
+    invalidateRect += convert(strokeRect) - rasCenter;
   }
-  delete m_tileSaver;
-
-  m_tileSaver = 0;
-
-  /*-- FIdを指定して、描画中にフレームが動いても、
-    描画開始時のFidのサムネイルが更新されるようにする。--*/
-  notifyImageChanged(frameId);
 
-  m_strokeRect.empty();
-
-  ToolUtils::updateSaveBox();
-
-  /*-- 作業中のフレームをリセット --*/
-  m_workingFrameId = TFrameId();
+  // invalidate rect
+  if (firstTrack) {
+    // for the first track we will paint cursor circle
+    // here we will invalidate rects for it
+    double thickness = m_isMyPaintStyleSelected ? m_maxCursorThick : m_maxThick*0.5;
+    TPointD thickOffset(thickness + 1, thickness + 1);
+    invalidateRect += TRectD(m_brushPos    - thickOffset, m_brushPos    + thickOffset);
+    invalidateRect += TRectD(fixedPosition - thickOffset, fixedPosition + thickOffset);
+    m_mousePos = point.position;
+    m_brushPos = fixedPosition;
+  }
+ 
+  if (!invalidateRect.isEmpty())
+    invalidate(invalidateRect.enlarge(2));
 }
+
 //---------------------------------------------------------------------------------------------------------------
-// 明日はここをMyPaintのときにカーソルを消せるように修正する!!!!!!
-void ToonzRasterBrushTool::mouseMove(const TPointD &pos, const TMouseEvent &e) {
-  qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
 
+// 明日はここをMyPaintのときにカーソルを消せるように修正する!!!!!!
+void ToonzRasterBrushTool::inputMouseMove(const TPointD &position, const TInputState &state) {
   struct Locals {
     ToonzRasterBrushTool *m_this;
 
@@ -1909,18 +1531,7 @@ void ToonzRasterBrushTool::mouseMove(const TPointD &pos, const TMouseEvent &e) {
       TTool::getApplication()->getCurrentTool()->notifyToolChanged();
     }
 
-    void addMinMax(TDoublePairProperty &prop, double add) {
-      if (add == 0.0) return;
-      const TDoublePairProperty::Range &range = prop.getRange();
-
-      TDoublePairProperty::Value value = prop.getValue();
-      value.first  = tcrop(value.first + add, range.first, range.second);
-      value.second = tcrop(value.second + add, range.first, range.second);
-
-      setValue(prop, value);
-    }
-
-    void addMinMaxSeparate(TDoublePairProperty &prop, double min, double max) {
+    void addMinMax(TDoublePairProperty &prop, double min, double max) {
       if (min == 0.0 && max == 0.0) return;
       const TDoublePairProperty::Range &range = prop.getRange();
 
@@ -1933,40 +1544,34 @@ void ToonzRasterBrushTool::mouseMove(const TPointD &pos, const TMouseEvent &e) {
 
       setValue(prop, value);
     }
-
   } locals = {this};
 
-  // if (e.isAltPressed() && !e.isCtrlPressed()) {
-  // const TPointD &diff = pos - m_mousePos;
-  // double add = (fabs(diff.x) > fabs(diff.y)) ? diff.x : diff.y;
-
-  // locals.addMinMax(
-  //  TToonzImageP(getImage(false, 1)) ? m_rasThickness : m_thickness, add);
-  //} else
-
   double thickness =
       (m_isMyPaintStyleSelected) ? (double)(m_maxCursorThick + 1) : m_maxThick;
   TPointD halfThick(thickness * 0.5, thickness * 0.5);
   TRectD invalidateRect(m_brushPos - halfThick, m_brushPos + halfThick);
 
-  if (e.isCtrlPressed() && e.isAltPressed() && !e.isShiftPressed() &&
-      Preferences::instance()->useCtrlAltToResizeBrushEnabled()) {
+  if ( Preferences::instance()->useCtrlAltToResizeBrushEnabled()
+    && state.isKeyPressed(TKey::control)
+    && state.isKeyPressed(TKey::alt)
+    && !state.isKeyPressed(TKey::shift) )
+  {
     // Resize the brush if CTRL+ALT is pressed and the preference is enabled.
-    const TPointD &diff = pos - m_mousePos;
+    const TPointD &diff = position - m_mousePos;
     double max          = diff.x / 2;
     double min          = diff.y / 2;
 
-    locals.addMinMaxSeparate(m_rasThickness, min, max);
+    locals.addMinMax(m_rasThickness, min, max);
 
     double radius = m_rasThickness.getValue().second * 0.5;
     invalidateRect += TRectD(m_brushPos - TPointD(radius, radius),
                              m_brushPos + TPointD(radius, radius));
 
   } else {
-    m_mousePos = pos;
-    m_brushPos = getCenteredCursorPos(pos);
+    m_mousePos = position;
+    m_brushPos = getCenteredCursorPos(position);
 
-    invalidateRect += TRectD(pos - halfThick, pos + halfThick);
+    invalidateRect += TRectD(position - halfThick, position + halfThick);
   }
 
   invalidate(invalidateRect.enlarge(2));
@@ -1980,10 +1585,7 @@ void ToonzRasterBrushTool::mouseMove(const TPointD &pos, const TMouseEvent &e) {
 //-------------------------------------------------------------------------------------------------------------
 
 void ToonzRasterBrushTool::draw() {
-  if (m_isStraight) {
-    tglDrawSegment(m_firstPoint, m_lastPoint);
-    invalidate(TRectD(m_firstPoint, m_lastPoint).enlarge(2));
-  }
+  m_inputmanager.draw();
 
   /*--ショートカットでのツール切り替え時に赤点が描かれるのを防止する--*/
   if (m_minThick == 0 && m_maxThick == 0 &&
@@ -2032,25 +1634,9 @@ void ToonzRasterBrushTool::draw() {
 //--------------------------------------------------------------------------------------------------------------
 
 void ToonzRasterBrushTool::onEnter() {
-  TImageP img = getImage(false);
-
   m_minThick = m_rasThickness.getValue().first;
   m_maxThick = m_rasThickness.getValue().second;
   updateCurrentStyle();
-
-  Application *app = getApplication();
-
-  m_styleId       = app->getCurrentLevelStyleIndex();
-  TColorStyle *cs = app->getCurrentLevelStyle();
-  if (cs) {
-    TRasterStyleFx *rfx = cs->getRasterStyleFx();
-    m_active            = cs->isStrokeStyle() || (rfx && rfx->isInkStyle());
-    m_currentColor      = cs->getAverageColor();
-    m_currentColor.m    = 255;
-  } else {
-    m_currentColor = TPixel32::Black;
-  }
-  m_active = img;
 }
 
 //----------------------------------------------------------------------------------------------------------
@@ -2074,7 +1660,6 @@ TPropertyGroup *ToonzRasterBrushTool::getProperties(int idx) {
 
 void ToonzRasterBrushTool::onImageChanged() {
   if (!isEnabled()) return;
-
   setWorkAndBackupImages();
 }
 
@@ -2086,22 +1671,57 @@ void ToonzRasterBrushTool::setWorkAndBackupImages() {
   TRasterP ras   = ti->getRaster();
   TDimension dim = ras->getSize();
 
-  double hardness = m_hardness.getValue() * 0.01;
-  if (!m_isMyPaintStyleSelected && hardness == 1.0 &&
-      ras->getPixelSize() == 4) {
-    m_workRas   = TRaster32P();
-    m_backupRas = TRasterCM32P();
+  if (!m_workRas || m_workRas->getLx() < dim.lx || m_workRas->getLy() < dim.ly)
+    m_workRas = TRaster32P(dim);
+  if (!m_backupRas || m_backupRas->getLx() < dim.lx || m_backupRas->getLy() < dim.ly)
+    m_backupRas = TRasterCM32P(dim);
+
+  m_workBackupRect.empty();
+}
+
+//---------------------------------------------------------------------------------------------------
+
+void ToonzRasterBrushTool::updateWorkAndBackupRasters(const TRect &rect) {
+  TToonzImageP ti = TImageP(getImage(false, 1));
+  if (!ti) return;
+  TRasterCM32P ras = ti->getRaster();
+
+  // work and backup rect will additionaly enlarged to 1/8 of it size
+  // in each affected direction to predict future possible enlargements in this direction
+  const int denominator = 8;
+  TRect enlargedRect    = rect + m_workBackupRect;
+  int dx                = (enlargedRect.getLx() - 1) / denominator + 1;
+  int dy                = (enlargedRect.getLy() - 1) / denominator + 1;
+
+  if (m_workBackupRect.isEmpty()) {
+    enlargedRect.x0 -= dx;
+    enlargedRect.y0 -= dy;
+    enlargedRect.x1 += dx;
+    enlargedRect.y1 += dy;
+
+    enlargedRect *= ras->getBounds();
+    if (enlargedRect.isEmpty()) return;
+
+    m_workRas->extract(enlargedRect)->copy(ras->extract(enlargedRect));
+    m_backupRas->extract(enlargedRect)->copy(ras->extract(enlargedRect));
   } else {
-    if (!m_workRas || m_workRas->getLx() > dim.lx ||
-        m_workRas->getLy() > dim.ly)
-      m_workRas = TRaster32P(dim);
-    if (!m_backupRas || m_backupRas->getLx() > dim.lx ||
-        m_backupRas->getLy() > dim.ly)
-      m_backupRas = TRasterCM32P(dim);
-
-    m_strokeRect.empty();
-    m_lastRect.empty();
+    if (enlargedRect.x0 < m_workBackupRect.x0) enlargedRect.x0 -= dx;
+    if (enlargedRect.y0 < m_workBackupRect.y0) enlargedRect.y0 -= dy;
+    if (enlargedRect.x1 > m_workBackupRect.x1) enlargedRect.x1 += dx;
+    if (enlargedRect.y1 > m_workBackupRect.y1) enlargedRect.y1 += dy;
+
+    enlargedRect *= ras->getBounds();
+    if (enlargedRect.isEmpty()) return;
+
+    TRect lastRect     = m_workBackupRect * ras->getBounds();
+    QList<TRect> rects = ToolUtils::splitRect(enlargedRect, lastRect);
+    for (int i = 0; i < rects.size(); i++) {
+      m_workRas->extract(rects[i])->copy(ras->extract(rects[i]));
+      m_backupRas->extract(rects[i])->copy(ras->extract(rects[i]));
+    }
   }
+
+  m_workBackupRect = enlargedRect;
 }
 
 //------------------------------------------------------------------
@@ -2131,6 +1751,7 @@ bool ToonzRasterBrushTool::onPropertyChanged(std::string propertyName) {
   RasterBrushHardness      = m_hardness.getValue();
   RasterBrushModifierSize  = m_modifierSize.getValue();
   BrushLockAlpha           = m_modifierLockAlpha.getValue();
+  RasterBrushAssistants    = m_assistants.getValue();
 
   // Recalculate/reset based on changed settings
   if (propertyName == m_rasThickness.getName()) {
@@ -2138,8 +1759,6 @@ bool ToonzRasterBrushTool::onPropertyChanged(std::string propertyName) {
     m_maxThick = m_rasThickness.getValue().second;
   }
 
-  if (propertyName == m_hardness.getName()) setWorkAndBackupImages();
-
   if (propertyName == m_hardness.getName() ||
       propertyName == m_rasThickness.getName()) {
     m_brushPad = getBrushPad(m_rasThickness.getValue().second,
@@ -2202,13 +1821,12 @@ void ToonzRasterBrushTool::loadPreset() {
     m_pressure.setValue(preset.m_pressure);
     m_modifierSize.setValue(preset.m_modifierSize);
     m_modifierLockAlpha.setValue(preset.m_modifierLockAlpha);
+    m_assistants.setValue(preset.m_assistants);
 
     // Recalculate based on updated presets
     m_minThick = m_rasThickness.getValue().first;
     m_maxThick = m_rasThickness.getValue().second;
 
-    setWorkAndBackupImages();
-
     m_brushPad = ToolUtils::getBrushPad(preset.m_max, preset.m_hardness * 0.01);
   } catch (...) {
   }
@@ -2230,6 +1848,7 @@ void ToonzRasterBrushTool::addPreset(QString name) {
   preset.m_pressure          = m_pressure.getValue();
   preset.m_modifierSize      = m_modifierSize.getValue();
   preset.m_modifierLockAlpha = m_modifierLockAlpha.getValue();
+  preset.m_assistants        = m_assistants.getValue();
 
   // Pass the preset to the manager
   m_presetsManager.addPreset(preset);
@@ -2268,13 +1887,12 @@ void ToonzRasterBrushTool::loadLastBrush() {
   m_smooth.setValue(BrushSmooth);
   m_modifierSize.setValue(RasterBrushModifierSize);
   m_modifierLockAlpha.setValue(BrushLockAlpha ? 1 : 0);
+  m_assistants.setValue(RasterBrushAssistants ? 1 : 0);
 
   // Recalculate based on prior values
   m_minThick = m_rasThickness.getValue().first;
   m_maxThick = m_rasThickness.getValue().second;
 
-  setWorkAndBackupImages();
-
   m_brushPad = getBrushPad(m_rasThickness.getValue().second,
                            m_hardness.getValue() * 0.01);
 }
@@ -2289,20 +1907,13 @@ bool ToonzRasterBrushTool::isPencilModeActive() {
 //------------------------------------------------------------------
 
 void ToonzRasterBrushTool::onColorStyleChanged() {
-  // in case the style switched while drawing
-  if (m_tileSaver) {
-    bool isValid = m_enabled && m_active;
-    m_enabled    = false;
-    if (isValid) {
-      finishRasterBrush(m_mousePos, 1);
-    }
-  }
+  m_inputmanager.finishTracks();
+  m_enabled = false;
 
   TTool::Application *app = getApplication();
   TMyPaintBrushStyle *mpbs =
       dynamic_cast<TMyPaintBrushStyle *>(app->getCurrentLevelStyle());
   m_isMyPaintStyleSelected = (mpbs) ? true : false;
-  setWorkAndBackupImages();
   getApplication()->getCurrentTool()->notifyToolChanged();
 }
 
@@ -2374,8 +1985,8 @@ BrushData::BrushData()
     , m_opacityMax(0.0)
     , m_pencil(false)
     , m_pressure(false)
-    , m_modifierSize(0.0)
     , m_drawOrder(0)
+    , m_modifierSize(0.0)
     , m_modifierOpacity(0.0)
     , m_modifierEraser(0.0)
     , m_modifierLockAlpha(0.0)
diff --git a/toonz/sources/tnztools/toonzrasterbrushtool.h b/toonz/sources/tnztools/toonzrasterbrushtool.h
index 3ac8fae..681230c 100644
--- a/toonz/sources/tnztools/toonzrasterbrushtool.h
+++ b/toonz/sources/tnztools/toonzrasterbrushtool.h
@@ -3,15 +3,28 @@
 #ifndef TOONZRASTERBRUSHTOOL_H
 #define TOONZRASTERBRUSHTOOL_H
 
-#include "tgeometry.h"
-#include "tproperty.h"
-#include "trasterimage.h"
-#include "ttoonzimage.h"
-#include "tstroke.h"
-#include "toonz/strokegenerator.h"
-
-#include "tools/tool.h"
-#include "tools/cursors.h"
+#include <tgeometry.h>
+#include <tproperty.h>
+#include <trasterimage.h>
+#include <ttoonzimage.h>
+#include <tstroke.h>
+#include <toonz/strokegenerator.h>
+#include <toonz/rasterstrokegenerator.h>
+
+#include <tools/tool.h>
+#include <tools/cursors.h>
+
+#include <tools/inputmanager.h>
+#include <tools/modifiers/modifierline.h>
+#include <tools/modifiers/modifiertangents.h>
+#include <tools/modifiers/modifierassistants.h>
+#include <tools/modifiers/modifiersegmentation.h>
+#include <tools/modifiers/modifiersmooth.h>
+#ifndef NDEBUG
+#include <tools/modifiers/modifiertest.h>
+#endif
+
+#include "bluredbrush.h"
 #include "mypainttoonzbrush.h"
 
 #include <QCoreApplication>
@@ -118,7 +131,9 @@ private:
 //   Toonz Raster Brush Tool declaration
 //************************************************************************
 
-class ToonzRasterBrushTool final : public TTool, public RasterController {
+class ToonzRasterBrushTool final : public TTool,
+                                   public RasterController,
+                                   public TInputHandler {
   Q_DECLARE_TR_FUNCTIONS(ToonzRasterBrushTool)
 
   void updateCurrentStyle();
@@ -142,6 +157,14 @@ public:
   void leftButtonUp(const TPointD &pos, const TMouseEvent &e) override;
   void mouseMove(const TPointD &pos, const TMouseEvent &e) override;
 
+  void inputMouseMove(const TPointD &position,
+                      const TInputState &state) override;
+  void inputSetBusy(bool busy) override;
+  void inputPaintTrackPoint(const TTrackPoint &point, const TTrack &track,
+                            bool firstTrack, bool preview) override;
+  void inputInvalidateRect(const TRectD &bounds) override { invalidate(bounds); }
+  TTool *inputGetTool() override { return this; };
+
   void draw() override;
 
   void onEnter() override;
@@ -162,7 +185,6 @@ public:
 
   void loadLastBrush();
 
-  void finishRasterBrush(const TPointD &pos, double pressureVal);
   // return true if the pencil mode is active in the Brush / PaintBrush / Eraser
   // Tools.
   bool isPencilModeActive() override;
@@ -172,7 +194,92 @@ public:
   bool askWrite(const TRect &rect) override;
   bool isMyPaintStyleSelected() { return m_isMyPaintStyleSelected; }
 
+private:
+  enum MouseEventType { ME_DOWN, ME_DRAG, ME_UP, ME_MOVE };
+  void handleMouseEvent(MouseEventType type, const TPointD &pos,
+                        const TMouseEvent &e);
+
 protected:
+  TInputManager m_inputmanager;
+  TSmartPointerT<TModifierLine> m_modifierLine;
+  TSmartPointerT<TModifierTangents> m_modifierTangents;
+  TSmartPointerT<TModifierAssistants> m_modifierAssistants;
+  TSmartPointerT<TModifierSegmentation> m_modifierSegmentation;
+  TSmartPointerT<TModifierSegmentation> m_modifierSmoothSegmentation;
+  TSmartPointerT<TModifierSmooth> m_modifierSmooth[3];
+#ifndef NDEBUG
+  TSmartPointerT<TModifierTest> m_modifierTest;
+#endif
+
+  class MyPaintStroke: public TTrackToolHandler {
+  public:
+    MyPaintToonzBrush brush;
+    
+    inline MyPaintStroke(
+      const TRaster32P &ras,
+      RasterController &controller,
+      const mypaint::Brush &brush,
+      bool interpolation = false
+    ): 
+      brush(ras, controller, brush, interpolation)
+    { }
+  };
+  
+  class PencilStroke: public TTrackToolHandler {
+  public:
+    RasterStrokeGenerator brush;
+    
+    inline PencilStroke( const TRasterCM32P &raster, Tasks task,
+                         ColorType colorType, int styleId, const TThickPoint &p,
+                         bool selective, int selectedStyle, bool lockAlpha,
+                         bool keepAntialias, bool isPaletteOrder = false
+    ):
+      brush(raster, task, colorType, styleId, p, selective, selectedStyle, lockAlpha, keepAntialias, isPaletteOrder)
+    { }
+  };
+
+  class BluredStroke: public TTrackToolHandler {
+  public:
+    BluredBrush brush;
+    
+    inline BluredStroke( const TRaster32P &ras, int size,
+                         const QRadialGradient &gradient, bool doDynamicOpacity
+    ):
+      brush(ras, size, gradient, doDynamicOpacity)
+    { }
+  };
+  
+  struct Painting {
+    // initial painting input
+    bool active = false;
+    int styleId = 0;
+    bool smooth = false;
+    // 作業中のFrameIdをクリック時に保存し、マウスリリース時(Undoの登録時)に別のフレームに 移動していたときの不具合を修正する。
+    TFrameId frameId;
+    
+    // common variables
+    TTileSetCM32 *tileSet = nullptr;
+    TTileSaverCM32 *tileSaver = nullptr;
+    TRect affectedRect;
+    
+    SmoothStroke smoothStroke;
+    
+    struct Pencil {
+      bool isActive = false;
+      bool realPencil = false;
+    } pencil;
+    
+    struct Blured {
+      bool isActive = false;
+    } blured;
+    
+    struct MyPaint {
+      bool isActive = false;
+      mypaint::Brush baseBrush;
+      TRect strokeSegmentRect;
+    } myPaint;
+  } m_painting;
+  
   TPropertyGroup m_prop[2];
 
   TDoublePairProperty m_rasThickness;
@@ -184,12 +291,8 @@ protected:
   TBoolProperty m_pressure;
   TDoubleProperty m_modifierSize;
   TBoolProperty m_modifierLockAlpha;
+  TBoolProperty m_assistants;
 
-  RasterStrokeGenerator *m_rasterTrack;
-  TTileSetCM32 *m_tileSet;
-  TTileSaverCM32 *m_tileSaver;
-  TPixel32 m_currentColor;
-  int m_styleId;
   double m_minThick, m_maxThick;
 
   int m_targetType;
@@ -197,44 +300,27 @@ protected:
       m_mousePos,  //!< Current mouse position, in world coordinates.
       m_brushPos;  //!< World position the brush will be painted at.
 
-  BluredBrush *m_bluredBrush;
   QRadialGradient m_brushPad;
 
   TRasterCM32P m_backupRas;
   TRaster32P m_workRas;
-
-  std::vector<TThickPoint> m_points;
-  TRect m_strokeRect, m_lastRect,
-      m_strokeSegmentRect;  // used with mypaint brush
-
-  SmoothStroke m_smoothStroke;
+  TRect m_workBackupRect;
 
   BrushPresetManager
       m_presetsManager;  //!< Manager for presets of this tool instance
 
-  bool m_active, m_enabled,
+  bool m_enabled,
       m_isPrompting,  //!< Whether the tool is prompting for spline
                       //! substitution.
       m_firstTime, m_presetsLoaded;
 
-  /*---
-作業中のFrameIdをクリック時に保存し、マウスリリース時(Undoの登録時)に別のフレームに
-移動していたときの不具合を修正する。---*/
-  TFrameId m_workingFrameId;
-
   ToonzRasterBrushToolNotifier *m_notifier;
   bool m_isMyPaintStyleSelected    = false;
-  MyPaintToonzBrush *m_toonz_brush = 0;
   QElapsedTimer m_brushTimer;
   int m_minCursorThick, m_maxCursorThick;
 
   bool m_propertyUpdating = false;
 
-  bool m_isStraight = false;
-  TPointD m_firstPoint;
-  TPointD m_lastPoint;
-  double m_maxPressure = -1.0;
-
 protected:
   static void drawLine(const TPointD &point, const TPointD &centre,
                        bool horizontal, bool isDecimal);
diff --git a/toonz/sources/tnztools/track.cpp b/toonz/sources/tnztools/track.cpp
index 8d048d1..5af347a 100644
--- a/toonz/sources/tnztools/track.cpp
+++ b/toonz/sources/tnztools/track.cpp
@@ -42,7 +42,9 @@ TTrack::TTrack(
   hasPressure(hasPressure),
   hasTilt(hasTilt),
   pointsRemoved(),
-  pointsAdded()
+  pointsAdded(),
+  fixedPointsAdded(),
+  m_pointsFixed()
   { }
 
 TTrack::TTrack(const TTrackModifierP &modifier):
@@ -55,7 +57,9 @@ TTrack::TTrack(const TTrackModifierP &modifier):
   hasTilt(modifier->original.hasTilt),
   modifier(modifier),
   pointsRemoved(),
-  pointsAdded()
+  pointsAdded(),
+  fixedPointsAdded(),
+  m_pointsFixed()
   { }
 
 const TTrack*
@@ -82,7 +86,7 @@ TTrack::floorIndex(double index, double *outFrac) const {
 }
 
 void
-TTrack::push_back(const TTrackPoint &point) {
+TTrack::push_back(const TTrackPoint &point, bool fixed) {
   m_points.push_back(point);
   if (size() > 1) {
     const TTrackPoint &prev = *(m_points.rbegin() + 1);
@@ -100,12 +104,14 @@ TTrack::push_back(const TTrackPoint &point) {
     p.length = prev.length + sqrt(d.x*d.x + d.y*d.y);
   }
   ++pointsAdded;
+  if (fixed) fix_all();
 }
 
 void
 TTrack::pop_back(int count) {
   if (count > size()) count = size();
   if (count <= 0) return;
+  assert(size() - count >= m_pointsFixed);
   m_points.resize(size() - count);
   if (pointsAdded > count)
     { pointsAdded -= count; return; }
@@ -114,6 +120,15 @@ TTrack::pop_back(int count) {
   pointsRemoved += count;
 }
 
+void
+TTrack::fix_points(int count) {
+  count = std::min(count, previewSize());
+  assert(count >= 0);
+  if (count <= 0) return;
+  m_pointsFixed += count;
+  fixedPointsAdded += count;
+}
+
 
 TTrackPoint
 TTrack::calcPoint(double index) const {