diff --git a/toonz/sources/include/tools/assistant.h b/toonz/sources/include/tools/assistant.h
index a3ca619..52b7247 100644
--- a/toonz/sources/include/tools/assistant.h
+++ b/toonz/sources/include/tools/assistant.h
@@ -99,7 +99,8 @@ public:
     Circle,
     CircleFill,
     CircleCross,
-    CircleGrid
+    CircleDots,
+    CircleDoubleDots,
   };
 
   const TStringId name;
@@ -245,7 +246,7 @@ public:
   TPropertyGroup& getProperties() const
     { return m_properties; }
   void propertyChanged(const TStringId &name)
-    { LockEvents lock(*this); onPropertyChanged(name); }
+    { onPropertyChanged(name); }
 
   const TAssistantPoint& getBasePoint() const;
 
@@ -299,10 +300,18 @@ protected:
   //! put value from property to variant
   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;
+  double getDrawingAlpha(bool enabled = true) const;
+  double getDrawingGridAlpha() const;
+
+  void drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize, double alpha) const;
+  void drawDot(const TPointD &p, double alpha) const;
   void drawPoint(const TAssistantPoint &point, double pixelSize) const;
 
+  inline void drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize) const
+    { drawSegment(p0, p1, pixelSize, getDrawingAlpha()); }
+  inline void drawDot(const TPointD &p) const
+    { drawDot(p, getDrawingAlpha()); }
+
   void addProperty(TProperty *p);
   void setTranslation(const TStringId &name, const QString &localName) const;
 
diff --git a/toonz/sources/tnztools/assistant.cpp b/toonz/sources/tnztools/assistant.cpp
index ddd523a..84d2725 100644
--- a/toonz/sources/tnztools/assistant.cpp
+++ b/toonz/sources/tnztools/assistant.cpp
@@ -380,12 +380,22 @@ TAssistant::onPropertyChanged(const TStringId &name) {
 
 //---------------------------------------------------------------------------------------------------
 
+double
+TAssistant::getDrawingAlpha(bool enabled) const
+  { return enabled && this->getEnabled() ? 0.5 : 0.25; }
+
+//---------------------------------------------------------------------------------------------------
+
+double
+TAssistant::getDrawingGridAlpha() const
+  { return 0.2; }
+
+//---------------------------------------------------------------------------------------------------
+
 void
-TAssistant::drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize, 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);
+TAssistant::drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize, double alpha) const {
+  double colorBlack[4] = { 0.0, 0.0, 0.0, alpha };
+  double colorWhite[4] = { 1.0, 1.0, 1.0, alpha };
 
   glPushAttrib(GL_ALL_ATTRIB_BITS);
   tglEnableBlending();
@@ -406,17 +416,15 @@ 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);
+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 };
 
   glPushAttrib(GL_ALL_ATTRIB_BITS);
   tglEnableBlending();
 
   glColor4dv(colorWhite);
-  tglEnablePointSmooth(3.0);
+  tglEnablePointSmooth(4.0);
   glBegin(GL_POINTS);
   glVertex2d(p.x, p.y);
   glEnd();
@@ -439,9 +447,10 @@ TAssistant::drawPoint(const TAssistantPoint &point, double pixelSize) const {
   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 alpha = 0.5;
+  double colorBlack[4] = { 0.0, 0.0, 0.0, alpha };
+  double colorGray[4]  = { 0.5, 0.5, 0.5, alpha };
+  double colorWhite[4] = { 1.0, 1.0, 1.0, alpha };
   double width = 0.5;
 
   if (point.selected) {
@@ -461,6 +470,8 @@ TAssistant::drawPoint(const TAssistantPoint &point, double pixelSize) const {
 
   TPointD crossDx(pixelSize*crossSize, 0.0);
   TPointD crossDy(0.0, pixelSize*crossSize);
+  TPointD gridDx(pixelSize*radius, 0.0);
+  TPointD gridDy(0.0, pixelSize*radius);
 
   // back line
   tglEnableLineSmooth(true, 2.0*std::max(1.0, width));
@@ -468,7 +479,7 @@ TAssistant::drawPoint(const TAssistantPoint &point, double pixelSize) const {
   if (point.type == TAssistantPoint::CircleCross) {
     tglDrawSegment(point.position - crossDx, point.position + crossDx);
     tglDrawSegment(point.position - crossDy, point.position + crossDy);
-  }
+  } else
   tglDrawCircle(point.position, radius*pixelSize);
 
   // front line
@@ -480,6 +491,27 @@ TAssistant::drawPoint(const TAssistantPoint &point, double pixelSize) const {
   }
   tglDrawCircle(point.position, radius*pixelSize);
 
+  // dots
+  switch(point.type) {
+  case TAssistantPoint::CircleDoubleDots:
+    drawDot(point.position - gridDx*0.5, alpha);
+    drawDot(point.position + gridDx*0.5, alpha);
+    drawDot(point.position - gridDy*0.5, alpha);
+    drawDot(point.position + gridDy*0.5, alpha);
+    //no break
+  case TAssistantPoint::CircleDots:
+    drawDot(point.position - gridDx, alpha);
+    drawDot(point.position + gridDx, alpha);
+    drawDot(point.position - gridDy, alpha);
+    drawDot(point.position + gridDy, alpha);
+    //no break
+  case TAssistantPoint::Circle:
+    drawDot(point.position, alpha);
+    break;
+  default:
+    break;
+  }
+
   glPopAttrib();
 }
 
diff --git a/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp b/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp
index 949c0af..54047bd 100644
--- a/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp
+++ b/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp
@@ -29,6 +29,9 @@ protected:
   TAssistantPoint &m_grid0;
   TAssistantPoint &m_grid1;
 
+  int m_gridRays; //!< for simple grid only, not perspective
+  double m_gridRadiusPart;
+
 public:
   TAssistantVanishingPoint(TMetaObject &object):
     TAssistant(object),
@@ -40,8 +43,10 @@ public:
     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) )
+    m_grid0 ( addPoint("grid0",  TAssistantPoint::CircleDoubleDots, TPointD(-12.5,-25.0)) ),
+    m_grid1 ( addPoint("grid1",  TAssistantPoint::CircleDots,       TPointD( 12.5,-25.0)) ),
+    m_gridRays(),
+    m_gridRadiusPart()
   {
     addProperty( new TBoolProperty(m_idPassThrough.str(), getPassThrough()) );
     addProperty( new TBoolProperty(m_idGrid.str(), getGrid()) );
@@ -51,13 +56,12 @@ public:
   static QString getLocalName()
     { return tr("Vanishing Point"); }
 
-  void updateTranslation() const {
+  void updateTranslation() const override {
     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
@@ -65,9 +69,9 @@ public:
   inline bool getPerspective() const
     { return data()[m_idPerspective].getBool(); }
 
-  void onDataChanged(const TVariant &value) {
+  void onDataChanged(const TVariant &value) override {
     TAssistant::onDataChanged(value);
-    m_grid0.visible = m_grid1.visible = ((const TVariant&)data())[m_idGrid].getBool();
+    m_grid0.visible = m_grid1.visible = getGrid();
   }
 
 private:
@@ -103,17 +107,58 @@ private:
   void fixSidePoint(TAssistantPoint &p0, TAssistantPoint &p1)
     { fixSidePoint(p0, p1, p0.position); }
 
+  void fixGrid1(const TPointD &previousCenter, const TPointD &previousGrid0) {
+    TPointD dx = previousCenter - previousGrid0;
+    double l = norm2(dx);
+    if (l <= TConsts::epsilon*TConsts::epsilon) return;
+    dx = dx*(1.0/sqrt(l));
+    TPointD dy(-dx.y, dx.x);
+
+    TPointD d = m_grid1.position - previousGrid0;
+    double x = (dx*d);
+    double y = (dy*d);
+
+    dx = m_center.position - m_grid0.position;
+    l = norm2(dx);
+    if (l <= TConsts::epsilon*TConsts::epsilon) return;
+    dx = dx*(1.0/sqrt(l));
+    dy = TPointD(-dx.y, dx.x);
+
+    m_grid1.position = m_grid0.position + dx*x + dy*y;
+  }
+
+  void recalcGrid() {
+    const double minStep = 5.0;
+
+    TPointD d0 = m_grid0.position - m_center.position;
+    TPointD d1 = m_grid1.position - m_center.position;
+    double l = norm(d0);
+    if (l <= TConsts::epsilon) return;
+    if (norm2(d1) <= TConsts::epsilon*TConsts::epsilon) return;
+    double a0 = atan(d0);
+    double a1 = atan(d1);
+    double da = fabs(a1 - a0);
+    if (da > M_PI) da = M_PI - da;
+    if (da < TConsts::epsilon) da = TConsts::epsilon;
+    double count = M_2PI/da;
+    m_gridRadiusPart = minStep/da/l;
+    m_gridRays = count < 1e6 && m_gridRadiusPart < 1.0
+               ? (int)round(count) : 0;
+  }
+
 public:
   void onFixPoints() override {
     fixSidePoint(m_a0, m_a1);
     fixSidePoint(m_b0, m_b1);
     fixCenter();
+    recalcGrid();
   }
 
   void onMovePoint(TAssistantPoint &point, const TPointD &position) override {
     if (&point == &getBasePoint())
       { move(position); return; }
 
+    TPointD previousCenter = m_center.position;
     TPointD previous = point.position;
     point.position = position;
     if (&point == &m_a0) {
@@ -134,6 +179,15 @@ public:
       fixSidePoint(m_b0, m_b1);
       fixSidePoint(m_a0, m_a1);
     }
+
+    if (&point == &m_grid0) {
+      fixGrid1(previousCenter, previous);
+    } else
+    if (&point != &m_grid1) {
+      fixGrid1(previousCenter, m_grid0.position);
+    }
+
+    recalcGrid();
   }
 
   void getGuidelines(
@@ -158,23 +212,88 @@ public:
     }
   }
 
+  void drawSimpleGrid() const {
+    if (m_gridRays <= 0) return;
+
+    double alpha = getDrawingGridAlpha();
+    TAffine4 modelview, projection;
+    glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a);
+    glGetDoublev(GL_PROJECTION_MATRIX, projection.a);
+    TAffine matrix = (projection*modelview).get2d();
+    TAffine matrixInv = matrix.inv();
+    double pixelSize = sqrt(tglGetPixelSize2());
+
+    const TPointD &p = m_center.position;
+    const TRectD oneBox(-1.0, -1.0, 1.0, 1.0);
+    TPointD dp = m_grid0.position - p;
+    double step = M_2PI/(double)m_gridRays;
+
+    // calculate range
+    int raysCount = m_gridRays;
+    if (!(matrixInv*oneBox).contains(p)) {
+      TPointD corners[4] = {
+        TPointD(oneBox.x0, oneBox.y0),
+        TPointD(oneBox.x0, oneBox.y1),
+        TPointD(oneBox.x1, oneBox.y0),
+        TPointD(oneBox.x1, oneBox.y1) };
+      double angles[4];
+      double a0 = 0.0, a1 = 0.0, da = 0.0;
+      for(int i = 0; i < 4; ++i) {
+        angles[i] = atan(matrixInv*corners[i] - p) + M_2PI;
+        for(int j = 0; j < i; ++j) {
+          double d = fabs(angles[i] - angles[j]);
+          if (d > M_PI) d = M_PI - d;
+          if (d > da) da = d, a0 = angles[i], a1 = angles[j];
+        }
+      }
+      if (a1 < a0) std::swap(a1, a0);
+      if (a1 - a0 > M_PI) { std::swap(a1, a0); a1 += M_2PI; }
+      double a = atan(dp) + M_2PI;
+      a0 = ceil ((a0 - a)/step)*step + a;
+      a1 = floor((a1 - a)/step)*step + a;
+
+      double s = sin(a0 - a);
+      double c = cos(a0 - a);
+      dp = TPointD(c*dp.x - s*dp.y, s*dp.x + c*dp.y);
+      raysCount = (int)round((a1 - a0)/step);
+    }
+
+    // draw rays
+    double s = sin(step);
+    double c = cos(step);
+    for(int i = 0; i < raysCount; ++i) {
+      TPointD p0 = matrix*(p + dp*m_gridRadiusPart);
+      TPointD p1 = matrix*(p + dp);
+      if (TGuidelineLineBase::truncateInfiniteRay(oneBox, p0, p1))
+        drawSegment(matrixInv*p0, matrixInv*p1, pixelSize, alpha);
+      dp = TPointD(c*dp.x - s*dp.y, s*dp.x + c*dp.y);
+    }
+  }
+
+  void drawPerspectiveGrid() const {
+    // TODO:
+  }
+
   void draw(TToolViewer *viewer, bool enabled) const override {
     double pixelSize = sqrt(tglGetPixelSize2());
     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);
+    double alpha = getDrawingAlpha(enabled);
+    drawSegment(p-dx-dy, p+dx+dy, pixelSize, alpha);
+    drawSegment(p-dx+dy, p+dx-dy, pixelSize, alpha);
+    if (getGrid()) {
+      if (getPerspective())
+        drawPerspectiveGrid();
+      else
+        drawSimpleGrid();
+    }
   }
 
   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);
   }
 };
diff --git a/toonz/sources/tnztools/assistants/guidelineline.cpp b/toonz/sources/tnztools/assistants/guidelineline.cpp
index ff30738..faa5e16 100644
--- a/toonz/sources/tnztools/assistants/guidelineline.cpp
+++ b/toonz/sources/tnztools/assistants/guidelineline.cpp
@@ -20,37 +20,54 @@ TGuidelineLineBase::calcDirection(const TPointD &p0, const TPointD &p1) {
   return k > TConsts::epsilon*TConsts::epsilon ? d*(1.0/sqrt(k)) : TPointD();
 }
 
-void
+
+static bool
+truncateX(double &x0, double &y0, double &x1, double &y1, double minx, double maxx) {
+  double dx = x1 - x0;
+  if (fabs(dx) < TConsts::epsilon) return false;
+  double k = (y1 - y0)/dx;
+  if (dx > 0.0) {
+    y0 += k*(minx - x0); x0 = minx;
+    y1 += k*(maxx - x1); x1 = maxx;
+  } else {
+    y0 += k*(maxx - x0); x0 = maxx;
+    y1 += k*(minx - x1); x1 = minx;
+  }
+  return true;
+}
+
+bool
 TGuidelineLineBase::truncateInfiniteLine(const TRectD &bounds, TPointD &p0, TPointD &p1) {
+  if (bounds.isEmpty()) return false;
   TPointD d = p0 - p1;
   TDimensionD size = bounds.getSize();
   if (fabs(d.x)*bounds.y0 > bounds.x0*fabs(d.y)) {
     // horizontal
-    if (fabs(d.x) < TConsts::epsilon) return;
-    double k = d.y/d.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));
-    }
+    if (!truncateX(p0.x, p0.y, p1.x, p1.y, bounds.x0, bounds.x1)) return false;
+    if (p0.y <= bounds.y0 && p1.y <= bounds.y0) return false;
+    if (p0.y >= bounds.y1 && p1.y >= bounds.y1) return false;
   } else {
     // vertical
-    if (fabs(d.y) < TConsts::epsilon) return;
-    double k = d.x/d.y;
-    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);
-    }
+    if (!truncateX(p0.y, p0.x, p1.y, p1.x, bounds.y0, bounds.y1)) return false;
+    if (p0.x <= bounds.x0 && p1.x <= bounds.x0) return false;
+    if (p0.x >= bounds.x1 && p1.x >= bounds.x1) return false;
+  }
+  return true;
+}
+
+bool
+TGuidelineLineBase::truncateInfiniteRay(const TRectD &bounds, TPointD &p0, TPointD &p1) {
+  if (bounds.isEmpty()) return false;
+  TRectD b(bounds);
+  if (b.contains(p0)) {
+    (p0.x < p1.x ? b.x0 : b.x1) = p0.x;
+    (p0.y < p1.y ? b.y0 : b.y1) = p0.y;
   }
+  return truncateInfiniteLine(b, p0, p1);
 }
 
 void
-TGuidelineLineBase::drawInliniteLine(const TPointD &p0, const TPointD &p1, bool ray, bool active, bool enabled) const {
+TGuidelineLineBase::drawInfiniteLine(const TPointD &p0, const TPointD &p1, bool ray, bool active, bool enabled) const {
   TAffine4 modelview, projection;
   glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a);
   glGetDoublev(GL_PROJECTION_MATRIX, projection.a);
@@ -58,11 +75,13 @@ TGuidelineLineBase::drawInliniteLine(const TPointD &p0, const TPointD &p1, bool 
   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);
+  if ( ray
+     ? !truncateInfiniteRay (TRectD(-1.0, -1.0, 1.0, 1.0), pp0, pp1)
+     : !truncateInfiniteLine(TRectD(-1.0, -1.0, 1.0, 1.0), pp0, pp1) ) return;
 
   double pixelSize = sqrt(tglGetPixelSize2());
   TAffine matrixInv = matrix.inv();
-  drawSegment((ray ? p0 : matrixInv*pp0), matrixInv*pp1, pixelSize, active, enabled);
+  drawSegment(matrixInv*pp0, matrixInv*pp1, pixelSize, active, enabled);
 }
 
 
@@ -104,7 +123,7 @@ TGuidelineInfiniteLine::transformPoint(const TTrackPoint &point) const {
 
 void
 TGuidelineInfiniteLine::draw(bool active, bool enabled) const
-  { drawInliniteLine(p0, p1, false, active, enabled); }
+  { drawInfiniteLine(p0, p1, false, active, enabled); }
 
 
 //*****************************************************************************************
@@ -124,5 +143,5 @@ TGuidelineRay::transformPoint(const TTrackPoint &point) const {
 
 void
 TGuidelineRay::draw(bool active, bool enabled) const
-  { drawInliniteLine(p0, p1, true, active, enabled); }
+  { drawInfiniteLine(p0, p1, true, active, enabled); }
 
diff --git a/toonz/sources/tnztools/assistants/guidelineline.h b/toonz/sources/tnztools/assistants/guidelineline.h
index cb2d76d..27262ca 100644
--- a/toonz/sources/tnztools/assistants/guidelineline.h
+++ b/toonz/sources/tnztools/assistants/guidelineline.h
@@ -32,9 +32,10 @@ public:
   const TPointD p1;
 
   TGuidelineLineBase(bool enabled, double magnetism, const TPointD &p0, const TPointD &p1);
-  void drawInliniteLine(const TPointD &p0, const TPointD &p1, bool ray, bool active, bool enabled) const;
+  void drawInfiniteLine(const TPointD &p0, const TPointD &p1, bool ray, bool active, bool enabled) const;
   static TPointD calcDirection(const TPointD &p0, const TPointD &p1);
-  static void truncateInfiniteLine(const TRectD &bounds, TPointD &p0, TPointD &p1);
+  static bool truncateInfiniteLine(const TRectD &bounds, TPointD &p0, TPointD &p1);
+  static bool truncateInfiniteRay(const TRectD &bounds, TPointD &p0, TPointD &p1);
 };