diff --git a/toonz/sources/include/tools/assistant.h b/toonz/sources/include/tools/assistant.h
index 448f53f..a3ca619 100644
--- a/toonz/sources/include/tools/assistant.h
+++ b/toonz/sources/include/tools/assistant.h
@@ -47,6 +47,7 @@ class TGuideline;
 typedef TSmartPointerT<TGuideline> TGuidelineP;
 typedef std::vector<TGuidelineP> TGuidelineList;
 typedef std::map<TStringId, TAssistantPoint> TAssistantPointMap;
+typedef std::vector<const TAssistantPoint*> TAssistantPointOrder;
 
 //===================================================================
 
@@ -102,6 +103,8 @@ public:
   };
 
   const TStringId name;
+  const TPointD defPosition;
+
   Type type;
   TPointD position;
   double radius;
@@ -109,7 +112,7 @@ public:
 
   mutable bool selected;
 
-  explicit TAssistantPoint(const TStringId &name);
+  explicit TAssistantPoint(const TStringId &name, const TPointD &defPosition = TPointD());
 };
 
 
@@ -193,6 +196,7 @@ protected:
   const TStringId m_idMagnetism;
 
   TAssistantPointMap m_points;
+  TAssistantPointOrder m_pointsOrder;
   TAssistantPoint* m_basePoint;
 
   mutable TPropertyGroup m_properties;
@@ -205,6 +209,8 @@ public:
 
   inline const TAssistantPointMap& points() const
     { return m_points; }
+  inline const TAssistantPointOrder& pointsOrder() const
+    { return m_pointsOrder; }
 
   inline const TAssistantPoint* findPoint(const TStringId &name) const {
     TAssistantPointMap::const_iterator i = points().find(name);
@@ -212,6 +218,7 @@ public:
   }
 
   void fixPoints();
+  void move(const TPointD &position);
   void movePoint(const TStringId &name, const TPointD &position);
   void setPointSelection(const TStringId &name, bool selected) const;
   void setAllPointsSelection(bool selected) const;
@@ -246,35 +253,37 @@ protected:
   TAssistantPoint& addPoint(
     const TStringId &name,
     TAssistantPoint::Type type,
-    const TPointD &position,
+    const TPointD &defPosition,
     bool visible,
     double radius );
 
   TAssistantPoint& addPoint(
     const TStringId &name,
     TAssistantPoint::Type type = TAssistantPoint::Circle,
-    const TPointD &position    = TPointD(),
+    const TPointD &defPosition = TPointD(),
     bool visible               = true );
 
   inline TAssistantPoint& addPoint(
     const std::string &name,
     TAssistantPoint::Type type,
-    const TPointD &position,
+    const TPointD &defPosition,
     bool visible,
     double radius )
-      { return addPoint(TStringId(name), type, position, visible, radius); }
+      { return addPoint(TStringId(name), type, defPosition, visible, radius); }
 
   inline TAssistantPoint& addPoint(
     const std::string &name,
     TAssistantPoint::Type type = TAssistantPoint::Circle,
-    const TPointD &position    = TPointD(),
+    const TPointD &defPosition = TPointD(),
     bool visible               = true )
-      { return addPoint(TStringId(name), type, position, visible); }
+      { return addPoint(TStringId(name), type, defPosition, visible); }
 
   //! usually called when meta-object created
   void onSetDefaults() override;
   //! called when part of variant data changed
   void onDataChanged(const TVariant &value) override;
+  //! called when field of root struct of variant data changed
+  virtual void onDataFieldChanged(const TStringId &name, const TVariant &value);
   //! load object data from variant
   virtual void onAllDataChanged();
   //! fix positions of all points (as like as all points moved)
@@ -291,6 +300,7 @@ protected:
   virtual void onPropertyChanged(const TStringId &name);
 
   void drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize, bool enabled = true) const;
+  void drawDot(const TPointD &p, bool enabled = true) const;
   void drawPoint(const TAssistantPoint &point, double pixelSize) const;
 
   void addProperty(TProperty *p);
diff --git a/toonz/sources/tnztools/assistant.cpp b/toonz/sources/tnztools/assistant.cpp
index a7a4451..ddd523a 100644
--- a/toonz/sources/tnztools/assistant.cpp
+++ b/toonz/sources/tnztools/assistant.cpp
@@ -106,8 +106,9 @@ TGuideline::findBest(const TGuidelineList &guidelines, const TTrack &track, cons
 //    TAssistantPoint implementation
 //************************************************************************
 
-TAssistantPoint::TAssistantPoint(const TStringId &name):
+TAssistantPoint::TAssistantPoint(const TStringId &name, const TPointD &defPosition):
   name(name),
+  defPosition(defPosition),
   type(Circle),
   position(position),
   radius(10.0),
@@ -147,15 +148,15 @@ TAssistantPoint&
 TAssistant::addPoint(
   const TStringId &name,
   TAssistantPoint::Type type,
-  const TPointD &position,
+  const TPointD &defPosition,
   bool visible,
   double radius )
 {
   assert(!m_points.count(name));
   TAssistantPoint &p = m_points.insert(
-    TAssistantPointMap::value_type(name, TAssistantPoint(name)) ).first->second;
+    TAssistantPointMap::value_type(name, TAssistantPoint(name, defPosition)) ).first->second;
+  m_pointsOrder.push_back(&p);
   p.type     = type;
-  p.position = position;
   p.radius   = radius;
   p.visible  = visible;
   if (!m_basePoint) m_basePoint = &p;
@@ -168,9 +169,9 @@ TAssistantPoint&
 TAssistant::addPoint(
   const TStringId &name,
   TAssistantPoint::Type type,
-  const TPointD &position,
+  const TPointD &defPosition,
   bool visible )
-    { return addPoint(name, type, position, visible, 10.0); }
+    { return addPoint(name, type, defPosition, visible, 10.0); }
 
 //---------------------------------------------------------------------------------------------------
 
@@ -204,6 +205,10 @@ void
 TAssistant::onSetDefaults() {
   setEnabled(true);
   setMagnetism(1.0);
+  for(TAssistantPointMap::iterator i = m_points.begin(); i != m_points.end(); ++i)
+    i->second.position = i->second.defPosition;
+  fixPoints();
+  fixData();
 }
 
 //---------------------------------------------------------------------------------------------------
@@ -215,6 +220,16 @@ TAssistant::fixPoints()
 //---------------------------------------------------------------------------------------------------
 
 void
+TAssistant::move(const TPointD &position) {
+  TPointD d = position - getBasePoint().position;
+  for(TAssistantPointMap::iterator i = m_points.begin(); i != m_points.end(); ++i)
+    i->second.position += d;
+  fixPoints();
+}
+
+//---------------------------------------------------------------------------------------------------
+
+void
 TAssistant::movePoint(const TStringId &name, const TPointD &position) {
   TAssistantPointMap::iterator i = m_points.find(name);
   if (i != m_points.end())
@@ -255,13 +270,20 @@ TAssistant::onDataChanged(const TVariant &value) {
     movePoint(entry.field(), position);
   } else
   if (data().getChildPathEntry(value, entry) && entry.isField()) {
-    updateProperty(entry.field(), data()[entry.field()]);
+    onDataFieldChanged(entry.field(), data()[entry.field()]);
   }
 }
 
 //---------------------------------------------------------------------------------------------------
 
 void
+TAssistant::onDataFieldChanged(const TStringId &name, const TVariant &value) {
+  updateProperty(name, value);
+}
+
+//---------------------------------------------------------------------------------------------------
+
+void
 TAssistant::onAllDataChanged() {
   const TVariant& pointsData = data()[m_idPoints];
   for(TAssistantPointMap::iterator i = m_points.begin(); i != m_points.end(); ++i) {
@@ -384,6 +406,33 @@ TAssistant::drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize, 
 //---------------------------------------------------------------------------------------------------
 
 void
+TAssistant::drawDot(const TPointD &p, bool enabled) const {
+  double colorBlack[4] = { 0.0, 0.0, 0.0, 0.5 };
+  double colorWhite[4] = { 1.0, 1.0, 1.0, 0.5 };
+  if (!enabled || !this->getEnabled())
+    colorBlack[3] = (colorWhite[3] *= 0.5);
+
+  glPushAttrib(GL_ALL_ATTRIB_BITS);
+  tglEnableBlending();
+
+  glColor4dv(colorWhite);
+  tglEnablePointSmooth(3.0);
+  glBegin(GL_POINTS);
+  glVertex2d(p.x, p.y);
+  glEnd();
+
+  glColor4dv(colorBlack);
+  tglEnablePointSmooth(2.0);
+  glBegin(GL_POINTS);
+  glVertex2d(p.x, p.y);
+  glEnd();
+
+  glPopAttrib();
+}
+
+//---------------------------------------------------------------------------------------------------
+
+void
 TAssistant::drawPoint(const TAssistantPoint &point, double pixelSize) const {
   if (!point.visible) return;
 
diff --git a/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp b/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp
index 0e7887b..949c0af 100644
--- a/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp
+++ b/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp
@@ -15,41 +15,171 @@
 
 class DVAPI TAssistantVanishingPoint final : public TAssistant {
   Q_DECLARE_TR_FUNCTIONS(TAssistantVanishingPoint)
+public:
+  const TStringId m_idPassThrough;
+  const TStringId m_idGrid;
+  const TStringId m_idPerspective;
+
 protected:
-  TAssistantPoint &m_pointCenter;
+  TAssistantPoint &m_center;
+  TAssistantPoint &m_a0;
+  TAssistantPoint &m_a1;
+  TAssistantPoint &m_b0;
+  TAssistantPoint &m_b1;
+  TAssistantPoint &m_grid0;
+  TAssistantPoint &m_grid1;
 
 public:
   TAssistantVanishingPoint(TMetaObject &object):
     TAssistant(object),
-    m_pointCenter( addPoint("center", TAssistantPoint::CircleCross) )
-  { }
+    m_idPassThrough("passThrough"),
+    m_idGrid("grid"),
+    m_idPerspective("perspective"),
+    m_center( addPoint("center", TAssistantPoint::CircleCross) ),
+    m_a0    ( addPoint("A0",     TAssistantPoint::Circle, TPointD(-50.0, 0.0)) ),
+    m_a1    ( addPoint("A1",     TAssistantPoint::Circle, TPointD(-75.0, 0.0)) ),
+    m_b0    ( addPoint("B0",     TAssistantPoint::Circle, TPointD( 50.0, 0.0)) ),
+    m_b1    ( addPoint("B1",     TAssistantPoint::Circle, TPointD( 75.0, 0.0)) ),
+    m_grid0 ( addPoint("grid0",  TAssistantPoint::CircleGrid) ),
+    m_grid1 ( addPoint("grid1",  TAssistantPoint::CircleGrid) )
+  {
+    addProperty( new TBoolProperty(m_idPassThrough.str(), getPassThrough()) );
+    addProperty( new TBoolProperty(m_idGrid.str(), getGrid()) );
+    addProperty( new TBoolProperty(m_idPerspective.str(), getPerspective()) );
+  }
 
   static QString getLocalName()
     { return tr("Vanishing Point"); }
 
+  void updateTranslation() const {
+    setTranslation(m_idPassThrough, tr("Pass Through"));
+    setTranslation(m_idGrid, tr("Grid"));
+    setTranslation(m_idPerspective, tr("Perspective"));
+  }
+
+
+  inline bool getPassThrough() const
+    { return data()[m_idPassThrough].getBool(); }
+  inline bool getGrid() const
+    { return data()[m_idGrid].getBool(); }
+  inline bool getPerspective() const
+    { return data()[m_idPerspective].getBool(); }
+
+  void onDataChanged(const TVariant &value) {
+    TAssistant::onDataChanged(value);
+    m_grid0.visible = m_grid1.visible = ((const TVariant&)data())[m_idGrid].getBool();
+  }
+
+private:
+  void fixCenter() {
+    if ( !(m_a0.position == m_a1.position)
+      && !(m_b0.position == m_b1.position) )
+    {
+      const TPointD &a = m_a0.position;
+      const TPointD &b = m_b0.position;
+      const TPointD da = m_a1.position - a;
+      const TPointD db = m_b1.position - b;
+      const TPointD ab = b - a;
+      double k = db.x*da.y - db.y*da.x;
+      if (fabs(k) > TConsts::epsilon) {
+        double lb = (da.x*ab.y - da.y*ab.x)/k;
+        m_center.position.x = lb*db.x + b.x;
+        m_center.position.y = lb*db.y + b.y;
+      }
+    }
+  }
+
+  void fixSidePoint(TAssistantPoint &p0, TAssistantPoint &p1, TPointD previousP0) {
+    if (p0.position != m_center.position && p0.position != p1.position) {
+      TPointD dp0 = p0.position - m_center.position;
+      TPointD dp1 = p1.position - previousP0;
+      double l0 = norm(dp0);
+      double l1 = norm(dp1);
+      if (l0 > TConsts::epsilon && l0 + l1 > TConsts::epsilon)
+        p1.position = m_center.position + dp0*((l0 + l1)/l0);
+    }
+  }
+
+  void fixSidePoint(TAssistantPoint &p0, TAssistantPoint &p1)
+    { fixSidePoint(p0, p1, p0.position); }
+
+public:
+  void onFixPoints() override {
+    fixSidePoint(m_a0, m_a1);
+    fixSidePoint(m_b0, m_b1);
+    fixCenter();
+  }
+
+  void onMovePoint(TAssistantPoint &point, const TPointD &position) override {
+    if (&point == &getBasePoint())
+      { move(position); return; }
+
+    TPointD previous = point.position;
+    point.position = position;
+    if (&point == &m_a0) {
+      fixSidePoint(m_a0, m_a1, previous);
+      fixSidePoint(m_b0, m_b1);
+    } else
+    if (&point == &m_b0) {
+      fixSidePoint(m_a0, m_a1);
+      fixSidePoint(m_b0, m_b1, previous);
+    } else
+    if (&point == &m_a1) {
+      fixCenter();
+      fixSidePoint(m_a0, m_a1);
+      fixSidePoint(m_b0, m_b1);
+    } else
+    if (&point == &m_b1) {
+      fixCenter();
+      fixSidePoint(m_b0, m_b1);
+      fixSidePoint(m_a0, m_a1);
+    }
+  }
+
   void getGuidelines(
     const TPointD &position,
     const TAffine &toTool,
     TGuidelineList &outGuidelines ) const override
   {
-    outGuidelines.push_back(TGuidelineP(
-      new TGuidelineInfiniteLine(
-        getEnabled(),
-        getMagnetism(),
-        toTool * m_pointCenter.position,
-        position )));
+    if (getPassThrough()) {
+      outGuidelines.push_back(TGuidelineP(
+        new TGuidelineInfiniteLine(
+          getEnabled(),
+          getMagnetism(),
+          toTool * m_center.position,
+          position )));
+    } else {
+      outGuidelines.push_back(TGuidelineP(
+        new TGuidelineRay(
+          getEnabled(),
+          getMagnetism(),
+          toTool * m_center.position,
+          position )));
+    }
   }
 
   void draw(TToolViewer *viewer, bool enabled) const override {
     double pixelSize = sqrt(tglGetPixelSize2());
-    const TPointD &p = m_pointCenter.position;
+    const TPointD &p = m_center.position;
     TPointD dx(20.0*pixelSize, 0.0);
     TPointD dy(0.0, 10.0*pixelSize);
     drawSegment(p-dx-dy, p+dx+dy, pixelSize, enabled);
     drawSegment(p-dx+dy, p+dx-dy, pixelSize, enabled);
   }
+
+  void drawEdit(TToolViewer *viewer) const override {
+    double pixelSize = sqrt(tglGetPixelSize2());
+    drawSegment(m_center.position, m_a1.position, pixelSize);
+    drawSegment(m_center.position, m_b1.position, pixelSize);
+    drawDot(m_a0.position);
+    drawDot(m_a1.position);
+    drawDot(m_b0.position);
+    drawDot(m_b1.position);
+    TAssistant::drawEdit(viewer);
+  }
 };
 
+
 //*****************************************************************************************
 //    Registration
 //*****************************************************************************************
diff --git a/toonz/sources/tnztools/assistants/guidelineline.cpp b/toonz/sources/tnztools/assistants/guidelineline.cpp
index 9ae3bf2..ff30738 100644
--- a/toonz/sources/tnztools/assistants/guidelineline.cpp
+++ b/toonz/sources/tnztools/assistants/guidelineline.cpp
@@ -28,14 +28,24 @@ TGuidelineLineBase::truncateInfiniteLine(const TRectD &bounds, TPointD &p0, TPoi
     // horizontal
     if (fabs(d.x) < TConsts::epsilon) return;
     double k = d.y/d.x;
-    p1 = TPointD(bounds.x1, p0.y + k*(bounds.x1 - p0.x));
-    p0 = TPointD(bounds.x0, p0.y + k*(bounds.x0 - p0.x));
+    if (d.x > 0.0) {
+      p1 = TPointD(bounds.x0, p0.y + k*(bounds.x0 - p0.x));
+      p0 = TPointD(bounds.x1, p0.y + k*(bounds.x1 - p0.x));
+    } else {
+      p1 = TPointD(bounds.x1, p0.y + k*(bounds.x1 - p0.x));
+      p0 = TPointD(bounds.x0, p0.y + k*(bounds.x0 - p0.x));
+    }
   } else {
     // vertical
     if (fabs(d.y) < TConsts::epsilon) return;
     double k = d.x/d.y;
-    p1 = TPointD(p0.x + k*(bounds.y1 - p0.y), bounds.y1);
-    p0 = TPointD(p0.x + k*(bounds.y0 - p0.y), bounds.y0);
+    if (d.y > 0.0) {
+      p1 = TPointD(p0.x + k*(bounds.y0 - p0.y), bounds.y0);
+      p0 = TPointD(p0.x + k*(bounds.y1 - p0.y), bounds.y1);
+    } else {
+      p1 = TPointD(p0.x + k*(bounds.y1 - p0.y), bounds.y1);
+      p0 = TPointD(p0.x + k*(bounds.y0 - p0.y), bounds.y0);
+    }
   }
 }
 
diff --git a/toonz/sources/tnztools/editassistantstool.cpp b/toonz/sources/tnztools/editassistantstool.cpp
index f694613..44b1e67 100644
--- a/toonz/sources/tnztools/editassistantstool.cpp
+++ b/toonz/sources/tnztools/editassistantstool.cpp
@@ -357,14 +357,20 @@ protected:
         if (!assistant) continue;
 
         assistant->deselectAll();
-        const TAssistantPointMap &points = assistant->points();
-        for(TAssistantPointMap::const_iterator j = points.begin(); j != points.end() && m_currentAssistantIndex < 0; ++j) {
-          const TAssistantPoint &p = j->second;
+
+        // last points is less significant and don't affecting the first points
+        // so iterate points in reverse order to avoid unsolvable points overlapping
+        const TAssistantPointOrder &points = assistant->pointsOrder();
+        for( TAssistantPointOrder::const_reverse_iterator j = points.rbegin();
+             j != points.rend() && m_currentAssistantIndex < 0;
+             ++j )
+        {
+          const TAssistantPoint &p = **j;
           TPointD offset = p.position - position;
           if (p.visible && norm2(offset) <= p.radius*p.radius*pixelSize*pixelSize) {
             m_currentAssistant.set(*i);
             m_currentAssistantIndex = i - (*m_reader)->begin();
-            m_currentPointName = j->first;
+            m_currentPointName = p.name;
             m_currentPointOffset = offset;
             assistant->selectAll();
           }
@@ -379,7 +385,7 @@ protected:
   bool apply() {
     bool success = false;
     if (m_currentAssistantChanged || m_currentAssistantCreated) {
-      if (Closer closer = write(ModePoint)) {
+      if (Closer closer = write(ModeAssistant)) {
         m_writeAssistant->fixData();
         TUndoManager::manager()->add(new EditAssistantsUndo(
           getApplication()->getCurrentLevel()->getLevel()->getSimpleLevel(),
@@ -419,12 +425,13 @@ public:
     m_dragging = true;
     if (m_newAssisnantType) {
       // create assistant
-      resetCurrentPoint();
+      resetCurrentPoint(false);
       if (Closer closer = write(ModeImage)) {
         TMetaObjectP object(new TMetaObject(m_newAssisnantType));
         if (TAssistant *assistant = object->getHandler<TAssistant>()) {
           assistant->setDefaults();
-          assistant->movePoint(assistant->getBasePoint().name, position);
+          assistant->move(position);
+          assistant->selectAll();
           m_currentAssistantCreated = true;
           m_currentAssistantChanged = true;
           m_currentAssistantIndex = (int)(*m_writer)->size();
@@ -435,6 +442,7 @@ public:
           (*m_writer)->push_back(object);
         }
       }
+      updateOptionsBox();
       m_newAssisnantType.reset();
     } else {
       findCurrentPoint(position);
@@ -445,19 +453,29 @@ public:
   }
 
   void leftButtonDrag(const TPointD &position, const TMouseEvent&) override {
-    if (Closer closer = write(ModePoint, true))
-      m_writeAssistant->movePoint(
-        m_currentPointName,
-        position + m_currentPointOffset);
+    if (m_currentAssistantCreated) {
+      if (Closer closer = write(ModeAssistant, true))
+        m_writeAssistant->move( position + m_currentPointOffset );
+    } else {
+      if (Closer closer = write(ModePoint, true))
+        m_writeAssistant->movePoint(
+          m_currentPointName,
+          position + m_currentPointOffset);
+    }
     m_currentPosition = position;
     getViewer()->GLInvalidateAll();
   }
 
   void leftButtonUp(const TPointD &position, const TMouseEvent&) override {
-    if (Closer closer = write(ModePoint, true))
-      m_writeAssistant->movePoint(
-        m_currentPointName,
-        position + m_currentPointOffset);
+    if (m_currentAssistantCreated) {
+      if (Closer closer = write(ModeAssistant, true))
+        m_writeAssistant->move( position + m_currentPointOffset );
+    } else {
+      if (Closer closer = write(ModePoint, true))
+        m_writeAssistant->movePoint(
+          m_currentPointName,
+          position + m_currentPointOffset);
+    }
 
     apply();
     m_assistantType.setIndex(0);