diff --git a/toonz/sources/common/tmetaimage/tmetaimage.cpp b/toonz/sources/common/tmetaimage/tmetaimage.cpp
index 8a0c5b4..f7af5d5 100644
--- a/toonz/sources/common/tmetaimage/tmetaimage.cpp
+++ b/toonz/sources/common/tmetaimage/tmetaimage.cpp
@@ -30,7 +30,7 @@ TMetaObject::setType(const TStringId &name) {
     if (m_handler) delete m_handler;
     m_type = name;
     Registry::const_iterator i = registry().find(m_type);
-    m_handler = i == registry().end() ? NULL : i->second();
+    m_handler = i == registry().end() ? NULL : i->second(*this);
     onVariantChanged(m_data);
   }
 }
diff --git a/toonz/sources/common/tvariant.cpp b/toonz/sources/common/tvariant.cpp
index 2716709..9d26d86 100644
--- a/toonz/sources/common/tvariant.cpp
+++ b/toonz/sources/common/tvariant.cpp
@@ -208,3 +208,15 @@ TVariant::findCommonParent(const TVariant &other) const {
 
   return NULL;
 }
+
+//---------------------------------------------------------
+
+size_t
+TVariant::getMemSize() const {
+  size_t s = sizeof(*this);
+  for(TVariantList::const_iterator i = m_list.begin(); i != m_list.end(); ++i)
+    s += i->getMemSize();
+  for(TVariantMap::const_iterator i = m_map.begin(); i != m_map.end(); ++i)
+    s += sizeof(*i) - sizeof(*this) + i->second.getMemSize();
+  return s;
+}
diff --git a/toonz/sources/include/tmetaimage.h b/toonz/sources/include/tmetaimage.h
index 75a876e..f172e48 100644
--- a/toonz/sources/include/tmetaimage.h
+++ b/toonz/sources/include/tmetaimage.h
@@ -36,14 +36,15 @@ typedef std::vector<TMetaObjectR> TMetaObjectRefList;
 
 class DVAPI TMetaObject: public TSmartObject, public TVariantOwner {
 public:
-  typedef TMetaObjectHandler* (*Fabric)();
+  typedef TMetaObjectHandler* (*Fabric)(TMetaObject&);
   typedef std::map<TStringId, Fabric> Registry;
 
   template<typename T>
   class Registrator {
   public:
     typedef T Type;
-    static TMetaObjectHandler* fabric() { return new Type(); }
+    static TMetaObjectHandler* fabric(TMetaObject &obj)
+      { return new Type(obj); }
     Registrator(const std::string &typeName)
       { registerType(typeName, fabric); }
     Registrator(const TStringId &typeName)
@@ -76,7 +77,7 @@ public:
 
   template<typename T>
   const T* getHandler() const
-    { return dynamic_cast<T*>(m_handler); }
+    { return dynamic_cast<const T*>(m_handler); }
   template<typename T>
   T* getHandler()
     { return dynamic_cast<T*>(m_handler); }
@@ -97,7 +98,7 @@ public:
 class DVAPI TMetaObjectHandler {
 private:
   TMetaObject &m_object;
-  TAtomicVar m_fixindData;
+  TAtomicVar m_fixingData;
 
 public:
   TMetaObjectHandler(TMetaObject &object):
@@ -119,12 +120,12 @@ protected:
 
 public:
   void dataChanged(const TVariant &value)
-    { if (m_fixindData != 0) onFixData(); }
+    { if (m_fixingData == 0) onDataChanged(value); }
 
   void fixData() {
-    ++m_fixindData;
-    if (m_fixindData == 1) onFixData();
-    --m_fixindData;
+    ++m_fixingData;
+    if (m_fixingData == 1) onFixData();
+    --m_fixingData;
   }
 };
 
diff --git a/toonz/sources/include/tools/assistant.h b/toonz/sources/include/tools/assistant.h
index 34c50e9..2578a4b 100644
--- a/toonz/sources/include/tools/assistant.h
+++ b/toonz/sources/include/tools/assistant.h
@@ -77,10 +77,11 @@ public:
 
   Type type;
   TPointD position;
-  bool selected;
+  mutable bool selected;
+  double radius;
 
-  inline explicit TAssistantPoint(Type type = Circle, const TPointD &position = TPointD()):
-    type(Circle), position(position), selected() { }
+  explicit TAssistantPoint(Type type = Circle, const TPointD &position = TPointD());
+  TAssistantPoint(Type type, const TPointD &position, double radius);
 };
 
 //*****************************************************************************************
@@ -105,17 +106,17 @@ public:
   inline const int pointsCount() const
     { return (int)m_points.size(); }
 
-  void fixPoints(int index, const TPointD &position);
+  void fixPoints();
   void movePoint(int index, const TPointD &position);
-  void setPointSelection(int index, bool selected);
+  void setPointSelection(int index, bool selected) const;
 
-  inline void selectPoint(int index)
+  inline void selectPoint(int index) const
     { setPointSelection(index, true); }
-  inline void deselectPoint(int index)
+  inline void deselectPoint(int index) const
     { setPointSelection(index, false); }
-  inline void selectAll()
+  inline void selectAll() const
     { for(int i = 0; i < pointsCount(); ++i) setPointSelection(i, false); }
-  inline void deselectAll()
+  inline void deselectAll() const
     { for(int i = 0; i < pointsCount(); ++i) setPointSelection(i, false); }
 
 protected:
@@ -123,7 +124,7 @@ protected:
   void onDataChanged(const TVariant &value) override;
   //! load object data from variant
   virtual void onAllDataChanged();
-  //! fix positions of all points
+  //! fix positions of all points (as like as all points moved)
   virtual void onFixPoints();
   //! try to move point
   virtual void onMovePoint(int index, const TPointD &position);
diff --git a/toonz/sources/include/tools/tool.h b/toonz/sources/include/tools/tool.h
index 0701889..4e79da4 100644
--- a/toonz/sources/include/tools/tool.h
+++ b/toonz/sources/include/tools/tool.h
@@ -283,13 +283,15 @@ public:
     MeshImage   = 0x8,   //!< Will work on mesh images
     Splines     = 0x10,  //!< Will work on motion paths
 
-    LevelColumns = 0x20,  //!< Will work on level columns
-    MeshColumns  = 0x40,  //!< Will work on mesh columns
+    LevelColumns= 0x20,  //!< Will work on level columns
+    MeshColumns = 0x40,  //!< Will work on mesh columns
 
     EmptyTarget = 0x80,  //!< Will work on empty cells/columns
 
+    MetaImage   = 0x100, //!< Will work on mets images
+
     CommonImages = VectorImage | ToonzImage | RasterImage,
-    AllImages    = CommonImages | MeshImage,
+    AllImages    = CommonImages | MeshImage | MetaImage,
     Vectors      = VectorImage | Splines,
 
     CommonLevels = CommonImages | LevelColumns,
diff --git a/toonz/sources/include/tools/toolcommandids.h b/toonz/sources/include/tools/toolcommandids.h
index 8d2524f..33b78dc 100644
--- a/toonz/sources/include/tools/toolcommandids.h
+++ b/toonz/sources/include/tools/toolcommandids.h
@@ -30,5 +30,6 @@
 #define T_Hand "T_Hand"
 #define T_Ruler "T_Ruler"
 #define T_Finger "T_Finger"
+#define T_EditAssistants "T_EditAssistants"
 
 #endif  // TOOL_COMMAND_IDS_H
diff --git a/toonz/sources/include/tvariant.h b/toonz/sources/include/tvariant.h
index 224e083..b5f6fe4 100644
--- a/toonz/sources/include/tvariant.h
+++ b/toonz/sources/include/tvariant.h
@@ -359,6 +359,9 @@ public:
 
   const TVariant* findCommonParent(const TVariant &other) const;
 
+  // memory
+  size_t getMemSize() const;
+
   // serialization
   // TODO:
 };
diff --git a/toonz/sources/tnztools/CMakeLists.txt b/toonz/sources/tnztools/CMakeLists.txt
index 247f89c..9e67138 100644
--- a/toonz/sources/tnztools/CMakeLists.txt
+++ b/toonz/sources/tnztools/CMakeLists.txt
@@ -127,6 +127,7 @@ set(SOURCES
     modifiers/modifierassistants.cpp
     assistants/guidelineline.cpp
     assistants/assistantvanishingpoint.cpp
+    editassistantstool.cpp
 )
 
 set(RESOURCES tnztools.qrc)
diff --git a/toonz/sources/tnztools/assistant.cpp b/toonz/sources/tnztools/assistant.cpp
index e2e5ad7..8e33f1d 100644
--- a/toonz/sources/tnztools/assistant.cpp
+++ b/toonz/sources/tnztools/assistant.cpp
@@ -22,7 +22,7 @@ TGuideline::drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize, 
   TPointD d = p1 - p0;
   double k = norm2(d);
   if (k > TConsts::epsilon*TConsts::epsilon) {
-    double k = 0.5*pixelSize/sqrt(k);
+    k = 0.5*pixelSize/sqrt(k);
     d = TPointD(-k*d.y, k*d.x);
     glColor4dv(colorWhite);
     tglDrawSegment(p0 - d, p1 - d);
@@ -91,6 +91,32 @@ TGuideline::findBest(const TGuidelineList &guidelines, const TTrack &track, cons
 
 
 //************************************************************************
+//    TAssistantPoint implementation
+//************************************************************************
+
+TAssistantPoint::TAssistantPoint(
+  Type type,
+  const TPointD &position
+):
+  type(type),
+  position(position),
+  radius(10.0),
+  selected() { }
+
+//---------------------------------------------------------------------------------------------------
+
+TAssistantPoint::TAssistantPoint(
+  Type type,
+  const TPointD &position,
+  double radius
+):
+  type(type),
+  position(position),
+  radius(radius),
+  selected() { }
+
+
+//************************************************************************
 //    TAssistant implementation
 //************************************************************************
 
@@ -112,7 +138,7 @@ TAssistant::blank() {
 //---------------------------------------------------------------------------------------------------
 
 void
-TAssistant::fixPoints(int index, const TPointD &position)
+TAssistant::fixPoints()
   { onFixPoints(); }
 
 //---------------------------------------------------------------------------------------------------
@@ -124,7 +150,7 @@ TAssistant::movePoint(int index, const TPointD &position)
 //---------------------------------------------------------------------------------------------------
 
 void
-TAssistant::setPointSelection(int index, bool selected) {
+TAssistant::setPointSelection(int index, bool selected)  const {
   if (index >= 0 && index < pointsCount())
     m_points[index].selected = selected;
 }
@@ -159,11 +185,11 @@ TAssistant::onAllDataChanged() {
       pointData[m_idX].getDouble(),
       pointData[m_idY].getDouble() );
   }
+  fixPoints();
 }
 
 //---------------------------------------------------------------------------------------------------
 
-//! fix positions of all points
 void
 TAssistant::onFixPoints()
   { }
@@ -213,45 +239,49 @@ TAssistant::drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize) 
 
 void
 TAssistant::drawPoint(const TAssistantPoint &point, double pixelSize) const {
-  double radius = 10.0;
+  double radius = point.radius;
   double crossSize = 1.2*radius;
 
   double colorBlack[4] = { 0.0, 0.0, 0.0, 0.5 };
   double colorGray[4]  = { 0.5, 0.5, 0.5, 0.5 };
   double colorWhite[4] = { 1.0, 1.0, 1.0, 0.5 };
+  double width = 0.5;
 
   if (point.selected) {
     colorBlack[2] = 1.0;
     colorGray[2] = 1.0;
+    width = 2.0;
   }
 
   glPushAttrib(GL_ALL_ATTRIB_BITS);
 
+  // fill
   tglEnableBlending();
-  tglEnableLineSmooth(true, 0.5);
-
   if (point.type == TAssistantPoint::CircleFill) {
     glColor4dv(colorGray);
     tglDrawDisk(point.position, radius*pixelSize);
   }
 
-  if (point.type == TAssistantPoint::CircleCross) {
-    TPointD dp(0.5*pixelSize, 0.5*pixelSize);
-    TPointD dx(pixelSize*crossSize, 0.0);
-    TPointD dy(0.0, pixelSize*crossSize);
+  TPointD crossDx(pixelSize*crossSize, 0.0);
+  TPointD crossDy(0.0, pixelSize*crossSize);
 
-    glColor4dv(colorWhite);
-    tglDrawSegment(point.position - dx + dp, point.position + dx + dp);
-    tglDrawSegment(point.position - dy + dp, point.position + dy + dp);
-    glColor4dv(colorBlack);
-    tglDrawSegment(point.position - dx - dp, point.position + dx - dp);
-    tglDrawSegment(point.position - dy - dp, point.position + dy - dp);
+  // back line
+  tglEnableLineSmooth(true, 2.0*std::max(1.0, width));
+  glColor4dv(colorWhite);
+  if (point.type == TAssistantPoint::CircleCross) {
+    tglDrawSegment(point.position - crossDx, point.position + crossDx);
+    tglDrawSegment(point.position - crossDy, point.position + crossDy);
   }
+  tglDrawCircle(point.position, radius*pixelSize);
 
-  glColor4dv(colorWhite);
-  tglDrawCircle(point.position, (radius + 0.5)*pixelSize);
+  // front line
+  glLineWidth(width);
   glColor4dv(colorBlack);
-  tglDrawCircle(point.position, (radius - 0.5)*pixelSize);
+  if (point.type == TAssistantPoint::CircleCross) {
+    tglDrawSegment(point.position - crossDx, point.position + crossDx);
+    tglDrawSegment(point.position - crossDy, point.position + crossDy);
+  }
+  tglDrawCircle(point.position, radius*pixelSize);
 
   glPopAttrib();
 }
diff --git a/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp b/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp
index f500427..73489c0 100644
--- a/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp
+++ b/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp
@@ -7,6 +7,14 @@
 #include "tgl.h"
 
 
+
+//*****************************************************************************************
+//    Registration
+//*****************************************************************************************
+
+static TMetaObject::Registrator<TAssistantVanishingPoint> assistantVanishingPoint("assistantVanishingPoint");
+
+
 //*****************************************************************************************
 //    TAssistantVanishingPoint implementation
 //*****************************************************************************************
diff --git a/toonz/sources/tnztools/assistants/guidelineline.cpp b/toonz/sources/tnztools/assistants/guidelineline.cpp
index 3ca0eea..55287df 100644
--- a/toonz/sources/tnztools/assistants/guidelineline.cpp
+++ b/toonz/sources/tnztools/assistants/guidelineline.cpp
@@ -41,16 +41,17 @@ TGuidelineLineBase::truncateInfiniteLine(const TRectD &bounds, TPointD &p0, TPoi
 
 void
 TGuidelineLineBase::drawInliniteLine(const TPointD &p0, const TPointD &p1, bool ray, bool active) const {
-  TAffine4 modelview;
+  TAffine4 modelview, projection;
   glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a);
+  glGetDoublev(GL_PROJECTION_MATRIX, projection.a);
 
-  TAffine matrix = modelview.get2d();
+  TAffine matrix = (projection*modelview).get2d();
   TPointD pp0 = matrix*p0;
   TPointD pp1 = matrix*p1;
   truncateInfiniteLine(TRectD(-1.0, -1.0, 1.0, 1.0), pp0, pp1);
 
+  double pixelSize = sqrt(tglGetPixelSize2());
   TAffine matrixInv = matrix.inv();
-  double pixelSize = norm(TPointD(matrixInv.a11 + matrixInv.a12, matrixInv.a21 + matrixInv.a22));
   drawSegment((ray ? p0 : matrixInv*pp0), matrixInv*pp1, pixelSize, active);
 }
 
diff --git a/toonz/sources/tnztools/assistants/guidelineline.h b/toonz/sources/tnztools/assistants/guidelineline.h
index 470f953..f283940 100644
--- a/toonz/sources/tnztools/assistants/guidelineline.h
+++ b/toonz/sources/tnztools/assistants/guidelineline.h
@@ -44,8 +44,6 @@ public:
 
 class DVAPI TGuidelineLine : public TGuidelineLineBase {
 public:
-  const TPointD p0;
-  const TPointD p1;
   const TPointD dir;
   const double dist;
 
@@ -61,8 +59,6 @@ public:
 
 class DVAPI TGuidelineInfiniteLine : public TGuidelineLineBase {
 public:
-  const TPointD p0;
-  const TPointD p1;
   const TPointD dir;
 
   TGuidelineInfiniteLine(const TPointD &p0, const TPointD &p1);
@@ -77,8 +73,6 @@ public:
 
 class DVAPI TGuidelineRay : public TGuidelineLineBase {
 public:
-  const TPointD p0;
-  const TPointD p1;
   const TPointD dir;
 
   TGuidelineRay(const TPointD &p0, const TPointD &p1);
diff --git a/toonz/sources/tnztools/editassistantstool.cpp b/toonz/sources/tnztools/editassistantstool.cpp
new file mode 100644
index 0000000..4cef33a
--- /dev/null
+++ b/toonz/sources/tnztools/editassistantstool.cpp
@@ -0,0 +1,308 @@
+
+// TnzTools includes
+#include <tools/tool.h>
+#include <tools/toolutils.h>
+#include <tools/toolhandle.h>
+#include <tools/cursors.h>
+#include <tools/assistant.h>
+
+// TnzLib includes
+#include <toonz/tapplication.h>
+#include <toonz/txshlevelhandle.h>
+
+// TnzCore includes
+#include <tgl.h>
+#include <tproperty.h>
+#include <tmetaimage.h>
+
+// For Qt translation support
+#include <QCoreApplication>
+
+#include <map>
+
+
+//-------------------------------------------------------------------
+
+//=============================================================================
+// Edit Assistants Undo
+//-----------------------------------------------------------------------------
+
+class EditAssistantsUndo final : public ToolUtils::TToolUndo {
+private:
+  bool m_isAssistantCreated;
+  TMetaObjectP m_metaObject;
+  TVariant m_oldData;
+  TVariant m_newData;
+  size_t m_size;
+
+public:
+  EditAssistantsUndo(
+    TXshSimpleLevel *level,
+    const TFrameId &frameId,
+    bool isAssistantCreated,
+    TMetaObjectR metaObject,
+    TVariant oldData
+  ):
+    ToolUtils::TToolUndo(level, frameId),
+    m_isAssistantCreated(isAssistantCreated),
+    m_metaObject(metaObject.getPointer()),
+    m_oldData(oldData),
+    m_newData(m_metaObject->data()),
+    m_size(m_oldData.getMemSize() + m_newData.getMemSize())
+  { }
+
+  int getSize() const override
+    { return m_size; }
+  QString getToolName() override
+    { return QString("Edit Assistants Tool"); }
+
+  void undo() const override {
+    if (TMetaImage *metaImage = dynamic_cast<TMetaImage*>(m_level->getFrame(m_frameId, true).getPointer())) {
+      TMetaImage::Writer writer(*metaImage);
+      if (m_isAssistantCreated) {
+        for(TMetaObjectRefList::iterator i = writer->begin(); i != writer->end(); ++i)
+          if (*i == m_metaObject) { writer->erase(i); break; }
+      } else {
+        m_metaObject->data() = m_oldData;
+        if (TMetaObjectHandler *handler = m_metaObject->getHandler<TMetaObjectHandler>())
+          handler->fixData();
+      }
+      notifyImageChanged();
+    }
+  }
+
+  void redo() const override {
+    if (TMetaImage *metaImage = dynamic_cast<TMetaImage*>(m_level->getFrame(m_frameId, true).getPointer())) {
+      TMetaImage::Writer writer(*metaImage);
+      m_metaObject->data() = m_newData;
+      if (TMetaObjectHandler *handler = m_metaObject->getHandler<TMetaObjectHandler>())
+        handler->fixData();
+      if (m_isAssistantCreated)
+        writer->push_back(TMetaObjectR(m_metaObject.getPointer()));
+      notifyImageChanged();
+    }
+  }
+};
+
+
+//=============================================================================
+// Edit Assistants Tool
+//-----------------------------------------------------------------------------
+
+class EditAssistantsTool final : public TTool {
+  Q_DECLARE_TR_FUNCTIONS(EditAssistantsTool)
+public:
+  typedef std::map<std::wstring, TStringId> TypeMap;
+
+protected:
+  TPropertyGroup m_prop;
+  TEnumProperty m_assistantType;
+  TypeMap m_localnameToType;
+  TStringId m_newAssisnantType;
+
+  bool           m_dragging;
+  bool           m_currentAssistantCreated;
+  int            m_currentAssistantIndex;
+  int            m_currentPointIndex;
+  TPointD        m_currentPointOffset;
+  TVariant       m_currentAssistantBackup;
+  TPointD        m_currentPosition;
+  TGuidelineList m_currentGuidelines;
+
+public:
+  EditAssistantsTool():
+    TTool("T_EditAssistants"),
+    m_assistantType("AssistantType"),
+    m_dragging(),
+    m_currentAssistantCreated(),
+    m_currentAssistantIndex(-1),
+    m_currentPointIndex(-1)
+  {
+    bind(MetaImage);
+    m_prop.add(&m_assistantType);
+    updateTranslation();
+  }
+  
+  ~EditAssistantsTool()
+    { m_prop.clear(); }
+
+  ToolType getToolType() const override
+    { return TTool::LevelWriteTool; }
+  int getCursorId() const override
+    { return ToolCursor::StrokeSelectCursor; }
+  TPropertyGroup* getProperties(int targetType) override
+    { return &m_prop; }
+  void onImageChanged() override
+    { getViewer()->GLInvalidateAll(); }
+
+  void addAssistantType(const std::string &name, const std::string &typeName) {
+    const std::wstring localName = tr(name.c_str()).toStdWString();
+    if (m_localnameToType.count(localName)) return;
+    m_localnameToType[localName] = TStringId(typeName);
+    m_assistantType.addValue(localName);
+  }
+
+  void updateTranslation() override {
+    m_assistantType.setQStringName(tr("Assistant Type"));
+    m_assistantType.deleteAllValues();
+    m_localnameToType.clear();
+    addAssistantType("--", "");
+    addAssistantType("Vanishing Point", "assistantVanishingPoint");
+    m_assistantType.setIndex(0);
+  }
+
+  bool onPropertyChanged(std::string propertyName) override {
+    TypeMap::const_iterator i = m_localnameToType.find(m_assistantType.getValue());
+    m_newAssisnantType = i == m_localnameToType.end() ? TStringId() : i->second;
+    return true;
+  }
+
+  void resetCurrentPoint() {
+    m_currentAssistantCreated = false;
+    m_currentAssistantIndex = -1;
+    m_currentPointIndex = -1;
+    m_currentPointOffset = TPointD();
+    m_currentAssistantBackup.reset();
+  }
+
+  const TAssistant* findCurrentPoint(const TPointD &position) {
+    resetCurrentPoint();
+    TMetaImage *mi = dynamic_cast<TMetaImage*>(getImage(false));
+    if (!mi) return NULL;
+
+    double pixelSize2 = tglGetPixelSize2();
+    TMetaImage::Reader reader(*mi);
+    const TAssistant *currentAssisntant = NULL;
+    for(TMetaObjectRefList::const_iterator i = reader->begin(); i != reader->end(); ++i) {
+      if (!*i) continue;
+      const TAssistant *assistant = (*i)->getHandler<TAssistant>();
+      if (!assistant) continue;
+      assistant->deselectAll();
+      for(int j = 0; j < assistant->pointsCount() && m_currentAssistantIndex < 0; ++j) {
+        const TAssistantPoint &p = assistant->points()[j];
+        TPointD offset = p.position - position;
+        if (norm2(offset) <= p.radius*p.radius*pixelSize2) {
+          m_currentAssistantIndex = i - reader->begin();
+          m_currentPointIndex = j;
+          m_currentPointOffset = offset;
+          currentAssisntant = assistant;
+          assistant->selectPoint(j);
+          break;
+        }
+      }
+    }
+    return currentAssisntant;
+  }
+
+  void mouseMove(const TPointD &position, const TMouseEvent&) override {
+    if (m_dragging) return;
+    findCurrentPoint(position);
+    m_currentPosition = position;
+    getViewer()->GLInvalidateAll();
+  }
+
+  void leftButtonDown(const TPointD &position, const TMouseEvent&) override {
+    m_dragging = true;
+    if (m_newAssisnantType) {
+      // create assistant
+      resetCurrentPoint();
+      if (TMetaImage *mi = dynamic_cast<TMetaImage*>(getImage(true))) {
+        TMetaImage::Writer writer(*mi);
+        TMetaObjectR obj(new TMetaObject(m_newAssisnantType));
+        if (TAssistant *assistant = obj->getHandler<TAssistant>()) {
+          if (assistant->pointsCount()) {
+            assistant->movePoint(0, position);
+            m_currentAssistantCreated = true;
+            m_currentAssistantIndex = (int)writer->size();
+            m_currentPointIndex = 0;
+            m_currentPointOffset = TPointD();
+          }
+          writer->push_back(obj);
+        }
+      }
+      m_newAssisnantType.reset();
+    } else
+    if (const TAssistant *assistant = findCurrentPoint(position)) {
+      m_currentAssistantBackup = assistant->data();
+    }
+    m_currentPosition = position;
+    getViewer()->GLInvalidateAll();
+  }
+
+  void leftButtonDrag(const TPointD &position, const TMouseEvent&) override {
+    if (m_currentAssistantIndex >= 0)
+    if (m_currentPointIndex >= 0)
+    if (TMetaImage *mi = dynamic_cast<TMetaImage*>(getImage(true)))
+    {
+      TMetaImage::Writer writer(*mi);
+      if (m_currentAssistantIndex < (int)writer->size())
+      if (TMetaObjectR obj = (*writer)[m_currentAssistantIndex])
+      if (TAssistant *assistant = obj->getHandler<TAssistant>())
+      if (m_currentPointIndex < assistant->pointsCount())
+      {
+        assistant->movePoint(
+          m_currentPointIndex,
+          position + m_currentPointOffset);
+      }
+    }
+    m_currentPosition = position;
+    getViewer()->GLInvalidateAll();
+  }
+
+  void leftButtonUp(const TPointD &position, const TMouseEvent&) override {
+    if (m_currentAssistantIndex >= 0)
+    if (m_currentPointIndex >= 0)
+    if (TMetaImage *mi = dynamic_cast<TMetaImage*>(getImage(true)))
+    {
+      TMetaImage::Writer writer(*mi);
+      if (m_currentAssistantIndex < (int)writer->size())
+      if (TMetaObjectR obj = (*writer)[m_currentAssistantIndex])
+      if (TAssistant *assistant = obj->getHandler<TAssistant>())
+      if (m_currentPointIndex < assistant->pointsCount())
+      {
+        assistant->movePoint(
+          m_currentPointIndex,
+          position + m_currentPointOffset);
+        assistant->fixData();
+        TUndoManager::manager()->add(new EditAssistantsUndo(
+          getApplication()->getCurrentLevel()->getLevel()->getSimpleLevel(),
+          getCurrentFid(),
+          m_currentAssistantCreated,
+          obj,
+          m_currentAssistantBackup ));
+      }
+    }
+    m_assistantType.setIndex(0);
+    getApplication()->getCurrentTool()->notifyToolChanged();
+    m_currentPosition = position;
+    getViewer()->GLInvalidateAll();
+    m_dragging = false;
+  }
+
+  void draw() override {
+    m_currentGuidelines.clear();
+
+    // draw assistants
+    TMetaImage *mi = dynamic_cast<TMetaImage*>(getImage(false));
+    if (!mi) return;
+    TMetaImage::Reader reader(*mi);
+    for(TMetaObjectRefList::const_iterator i = reader->begin(); i != reader->end(); ++i)
+      if (*i)
+      if (const TAssistant *assistant = (*i)->getHandler<TAssistant>())
+      {
+        assistant->drawEdit(getViewer());
+        assistant->getGuidelines(
+          m_currentPosition + m_currentPointOffset,
+          TAffine(),
+          m_currentGuidelines );
+      }
+
+    // draw guidelines
+    for(TGuidelineList::const_iterator i = m_currentGuidelines.begin(); i != m_currentGuidelines.end(); ++i)
+      (*i)->draw();
+  }
+};
+
+//-------------------------------------------------------------------
+
+EditAssistantsTool editAssistantsTool;
diff --git a/toonz/sources/tnztools/tool.cpp b/toonz/sources/tnztools/tool.cpp
index b97e9bd..36f0e23 100644
--- a/toonz/sources/tnztools/tool.cpp
+++ b/toonz/sources/tnztools/tool.cpp
@@ -209,6 +209,8 @@ void TTool::bind(int targetType) {
         std::make_pair(std::make_pair(name, RasterImage), &theDummyTool));
     toolTable->insert(
         std::make_pair(std::make_pair(name, MeshImage), &theDummyTool));
+    toolTable->insert(
+        std::make_pair(std::make_pair(name, MetaImage), &theDummyTool));
 
     ToolSelector *toolSelector = new ToolSelector(name);
     CommandManager::instance()->setHandler(
@@ -224,6 +226,8 @@ void TTool::bind(int targetType) {
     (*toolTable)[std::make_pair(name, RasterImage)] = this;
   if (targetType & MeshImage)
     (*toolTable)[std::make_pair(name, MeshImage)] = this;
+  if (targetType & MetaImage)
+    (*toolTable)[std::make_pair(name, MetaImage)] = this;
 }
 
 //-----------------------------------------------------------------------------
@@ -1026,6 +1030,11 @@ QString TTool::updateEnabled(int rowIndex, int columnIndex) {
         return (
             enable(false),
             QObject::tr("The current tool cannot be used on a Mesh Level."));
+
+      if ((levelType == META_XSHLEVEL) && !(targetType & MetaImage))
+        return (
+            enable(false),
+            QObject::tr("The current tool cannot be used on a Assistants (Meta) Level."));
     }
 
     // Check against impossibly traceable movements on the column
diff --git a/toonz/sources/tnztools/toolhandle.cpp b/toonz/sources/tnztools/toolhandle.cpp
index bd5d307..be4ff6b 100644
--- a/toonz/sources/tnztools/toolhandle.cpp
+++ b/toonz/sources/tnztools/toolhandle.cpp
@@ -126,13 +126,16 @@ void ToolHandle::onImageChanged(TImage::Type imageType) {
   case TImage::TOONZ_RASTER:
     targetType = TTool::ToonzImage;
     break;
+  case TImage::MESH:
+    targetType = TTool::MeshImage;
+    break;
+  case TImage::META:
+    targetType = TTool::MetaImage;
+    break;
   case TImage::VECTOR:
   default:
     targetType = TTool::VectorImage;
     break;
-  case TImage::MESH:
-    targetType = TTool::MeshImage;
-    break;
   }
 
   if (targetType != m_toolTargetType) {
diff --git a/toonz/sources/toonz/mainwindow.cpp b/toonz/sources/toonz/mainwindow.cpp
index 8345aa2..0959e26 100644
--- a/toonz/sources/toonz/mainwindow.cpp
+++ b/toonz/sources/toonz/mainwindow.cpp
@@ -2472,6 +2472,7 @@ void MainWindow::defineActions() {
   createToolAction(T_Plastic, "plastic", QT_TR_NOOP("Plastic Tool"), "X");
   createToolAction(T_Ruler, "ruler", QT_TR_NOOP("Ruler Tool"), "");
   createToolAction(T_Finger, "finger", QT_TR_NOOP("Finger Tool"), "");
+  createToolAction(T_EditAssistants, "geometric", QT_TR_NOOP("Edit Assistants"), "");
 
   /*-- Animate tool + mode switching shortcuts --*/
   createAction(MI_EditNextMode, QT_TR_NOOP("Animate Tool - Next Mode"), "",
diff --git a/toonz/sources/toonz/tapp.cpp b/toonz/sources/toonz/tapp.cpp
index 512b12c..a3cb66b 100644
--- a/toonz/sources/toonz/tapp.cpp
+++ b/toonz/sources/toonz/tapp.cpp
@@ -325,11 +325,13 @@ int TApp::getCurrentImageType() {
       return TImage::TOONZ_RASTER;
     case OVL_XSHLEVEL:
       return TImage::RASTER;
+    case META_XSHLEVEL:
+      return TImage::META;
+    case MESH_XSHLEVEL:
+      return TImage::MESH;
     case PLI_XSHLEVEL:
     default:
       return TImage::VECTOR;
-    case MESH_XSHLEVEL:
-      return TImage::MESH;
     }
   }
 
diff --git a/toonz/sources/toonz/toolbar.cpp b/toonz/sources/toonz/toolbar.cpp
index 5dd543a..39b51e8 100755
--- a/toonz/sources/toonz/toolbar.cpp
+++ b/toonz/sources/toonz/toolbar.cpp
@@ -44,6 +44,7 @@ struct {
                     {T_Tape, false, 0},        {T_Finger, false, 0},
                     {"Separator_3", false, 0}, {T_StylePicker, false, 0},
                     {T_RGBPicker, false, 0},   {T_Ruler, false, 0},
+                    {T_EditAssistants, false, 0},
                     {"Separator_4", false, 0}, {T_ControlPointEditor, false, 0},
                     {T_Pinch, true, 0},        {T_Pump, true, 0},
                     {T_Magnet, true, 0},       {T_Bender, true, 0},
diff --git a/toonz/sources/toonzqt/icongenerator.cpp b/toonz/sources/toonzqt/icongenerator.cpp
index c3f8264..0314ce2 100644
--- a/toonz/sources/toonzqt/icongenerator.cpp
+++ b/toonz/sources/toonzqt/icongenerator.cpp
@@ -1592,7 +1592,7 @@ void IconGenerator::invalidate(TXshLevel *xl, const TFrameId &fid,
                                             m_settings));
       break;
     default:
-      assert(false);
+      addTask(id, new NoImageIconRenderer(id, getIconSize()));
       break;
     }
 
@@ -1630,7 +1630,7 @@ void IconGenerator::invalidate(TXshLevel *xl, const TFrameId &fid,
                                             m_settings));
       break;
     default:
-      assert(false);
+      addTask(id, new NoImageIconRenderer(id, TDimension(80, 60)));
       break;
     }