diff --git a/toonz/sources/include/tassistantsimage.h b/toonz/sources/include/tassistantsimage.h
index 6848556..2b1c8cc 100644
--- a/toonz/sources/include/tassistantsimage.h
+++ b/toonz/sources/include/tassistantsimage.h
@@ -34,6 +34,16 @@ public:
   std::string type;
   std::vector<TPointD> points;
   TSmartObjectP handler;
+
+  TPointD& operator[] (int index) {
+    if (index <= (int)points.size()) points.resize(index + 1);
+    return points[index];
+  }
+
+  const TPointD& operator[] (int index) const {
+    static const TPointD blank;
+    return index <= (int)points.size() ? blank : points[index];
+  }
 };
 
 //-------------------------------------------------------------------
diff --git a/toonz/sources/include/tgeometry.h b/toonz/sources/include/tgeometry.h
index bbcd801..1a16872 100644
--- a/toonz/sources/include/tgeometry.h
+++ b/toonz/sources/include/tgeometry.h
@@ -17,6 +17,14 @@
 #endif
 
 //=============================================================================
+
+inline double logNormalDistribuitionUnscaled(double x, double x0, double w)
+  { return exp(-0.5*pow(log(x/x0)/w, 2.0))/x; }
+
+inline double logNormalDistribuition(double x, double x0, double w)
+  { return logNormalDistribuitionUnscaled(x, x0, w)/(w*sqrt(2.0*M_PI)); }
+
+//=============================================================================
 /*
 * This is an example of how to use the TPointT, the TRectT and the TAffine
 * classes.
diff --git a/toonz/sources/include/tools/assistant.h b/toonz/sources/include/tools/assistant.h
new file mode 100644
index 0000000..cf09983
--- /dev/null
+++ b/toonz/sources/include/tools/assistant.h
@@ -0,0 +1,109 @@
+#pragma once
+
+#ifndef ASSISTANT_INCLUDED
+#define ASSISTANT_INCLUDED
+
+// TnzTools includes
+#include <tools/track.h>
+
+// TnzLib includes
+
+// TnzCore includes
+#include <tassistantsimage.h>
+#include <tsmartpointer.h>
+#include <tgeometry.h>
+
+// std includes
+#include <vector>
+#include <string>
+#include <map>
+
+
+#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
+
+
+//==============================================================
+
+//  Forward declarations
+
+class TToolViewer;
+class TAssistant;
+class TGuideline;
+
+typedef TSmartPointerT<TGuideline> TGuidelineP;
+typedef TSmartPointerT<TAssistant> TAssistantP;
+typedef std::vector<TGuidelineP> TGuidelineList;
+
+//===================================================================
+
+//*****************************************************************************************
+//    TGuideline definition
+//*****************************************************************************************
+
+class DVAPI TGuideline final : public TSmartObject {
+public:
+  virtual TTrackPoint transformPoint(const TTrackPoint &point) const
+    { return point; }
+
+  virtual void draw(TToolViewer *viewer, bool active) const
+    { }
+
+  void draw(TToolViewer *viewer) const
+    { draw(viewer, false); }
+
+  double calcTrackWeight(const TTrack &track, const TAffine &affine) const;
+
+  static TGuidelineP findBest(const TGuidelineList &guidelines, const TTrack &track, const TAffine &affine);
+};
+
+
+//*****************************************************************************************
+//    TAssistant definition
+//*****************************************************************************************
+
+class DVAPI TAssistant final : public TSmartObject {
+public:
+  typedef TAssistant* (*Fabric)();
+  typedef std::map<std::string, Fabric> Registry;
+
+  template<typename T>
+  class Registrator {
+  public:
+    typedef T Type;
+    static TAssistant* fabric() { return new Type(); }
+    Registrator(const std::string &name) { getRegistry()[name] = fabric; }
+  };
+
+  static Registry& getRegistry();
+  static TAssistant* create(const std::string &name);
+  static TAssistantP wrap(TAssistantDesc &desc);
+
+  virtual void assign(TAssistantDesc &desc)
+    { }
+  virtual void onMovePoint(
+    TAssistantDesc &desc,
+    int index,
+    const TPointD &position ) { desc[index] = position; }
+  virtual void getGuidelines(
+    const TAssistantDesc &desc,
+    const TPointD &position,
+    TGuidelineList &outGuidelines ) { }
+  virtual void draw(
+    const TAssistantDesc &desc,
+    TToolViewer *viewer ) { }
+  virtual void drawEdit(
+    const TAssistantDesc &desc,
+    TToolViewer *viewer,
+    int currentPointIndex ) { }
+};
+
+
+#endif
diff --git a/toonz/sources/tnztools/CMakeLists.txt b/toonz/sources/tnztools/CMakeLists.txt
index b1c8939..d06bc2d 100644
--- a/toonz/sources/tnztools/CMakeLists.txt
+++ b/toonz/sources/tnztools/CMakeLists.txt
@@ -51,6 +51,7 @@ set(HEADERS
     ../include/tools/modifiers/modifiertest.h
     ../include/tools/modifiers/modifiertangents.h
     ../include/tools/modifiers/modifiersegmentation.h
+    ../include/tools/assistant.h
 )
 
 set(SOURCES
@@ -121,6 +122,7 @@ set(SOURCES
     modifiertest.cpp
     modifiertangents.cpp
     modifiersegmentation.cpp
+    assistant.cpp
 )
 
 set(RESOURCES tnztools.qrc)
diff --git a/toonz/sources/tnztools/assistant.cpp b/toonz/sources/tnztools/assistant.cpp
new file mode 100644
index 0000000..44077b7
--- /dev/null
+++ b/toonz/sources/tnztools/assistant.cpp
@@ -0,0 +1,90 @@
+
+#include <tools/assistant.h>
+
+#include <limits>
+
+
+//************************************************************************
+//    TGuideline implementation
+//************************************************************************
+
+double
+TGuideline::calcTrackWeight(const TTrack &track, const TAffine &affine) const {
+  if (track.empty() < 1)
+    return std::numeric_limits<double>::infinity();
+
+  const double snapLenght = 20.0;
+  const double snapScale = 1.0;
+  const double maxLenght = 20.0*snapLenght*snapScale;
+
+  double sumWeight = 0.0;
+  double sumLength = 0.0;
+  double sumDeviation = 0.0;
+
+  TPointD prev = affine*track[0].position;
+  for(int i = 0; i < track.size(); ++i) {
+    const TTrackPoint &tp = track[i];
+    TPointD p = affine*tp.position;
+    double length = tdistance(p, prev);
+    sumLength += length;
+
+    double midStepLength = sumLength - 0.5*length;
+    if (midStepLength > TTrack::epsilon) {
+      double weight = length*logNormalDistribuitionUnscaled(midStepLength, snapLenght, snapScale);
+      sumWeight += weight;
+
+      TTrackPoint ntp = transformPoint(tp);
+      double deviation = tdistance(affine*ntp.position, p);
+      sumDeviation += weight*deviation;
+    }
+    prev = p;
+  }
+  if (sumWeight < TTrack::epsilon)
+    return std::numeric_limits<double>::infinity();
+  return sumDeviation/sumWeight;
+}
+
+//---------------------------------------------------------------------------------------------------
+
+TGuidelineP
+TGuideline::findBest(const TGuidelineList &guidelines, const TTrack &track, const TAffine &affine) {
+  double bestWeight = 0.0;
+  TGuidelineP best;
+  for(TGuidelineList::const_iterator i = guidelines.begin(); i != guidelines.end(); ++i) {
+    double weight = (*i)->calcTrackWeight(track, affine);
+    if (!best || weight < bestWeight)
+      { bestWeight = weight; best = *i; }
+  }
+  return best;
+}
+
+
+//************************************************************************
+//    TAssistant implementation
+//************************************************************************
+
+TAssistant::Registry&
+TAssistant::getRegistry() {
+  static Registry registry;
+  return registry;
+}
+
+//---------------------------------------------------------------------------------------------------
+
+TAssistant*
+TAssistant::create(const std::string &name) {
+  const Registry &registry = getRegistry();
+  Registry::const_iterator i = registry.find(name);
+  return i == registry.end() ? NULL : i->second();
+}
+
+//---------------------------------------------------------------------------------------------------
+
+TAssistantP
+TAssistant::wrap(TAssistantDesc &desc) {
+  if (desc.handler)
+    return dynamic_cast<TAssistant*>(desc.handler.getPointer());
+  TAssistantP assistant = create(desc.type);
+  desc.handler = assistant.getPointer();
+  return assistant;
+}