diff --git a/src/epoint.py b/src/epoint.py
--- a/src/epoint.py
+++ b/src/epoint.py
@@ -6,6 +6,15 @@ class EPoint:
   def __init__(self,x,y):
     self.x=x
     self.y=y
+    
+  def fromTuple(tup): return EPoint(tup[0],tup[1])
+  
+  def fromProjective(point):
+    if point.item(0)==0: return None
+    return EPoint(point.item(1)/point.item(0),point.item(2)/point.item(0))
+    
+  def toProjective(self):
+    return (1,self.x,self.y)
   
   def dist(self,a):
     return math.sqrt((self.x-a.x)**2+(self.y-a.y)**2)
@@ -13,8 +22,48 @@ class EPoint:
   def __add__(self,a):
     return EPoint(self.x+a.x,self.y+a.y)
     
+  def __sub__(self,a):
+    return EPoint(self.x-a.x,self.y-a.y)
+    
+  def __mul__(self,k):
+    return EPoint(self.x*k,self.y*k)
+    
+  def __rmul__(self,k):
+    return self*k
+    
   def __truediv__(self,k):
     return EPoint(self.x/k,self.y/k)
     
-  def __str__(self): return "({0},{1})".format(self.x,self.y)
-  def __repr__(self): return "EPoint({0},{1})".format(self.x,self.y)
+  def __floordiv__(self,k):
+    return EPoint(self.x//k,self.y//k)
+    
+  def __iadd__(self,a):
+    self.x+=a.x
+    self.y+=a.y
+    return self
+    
+  def __isub__(self,a):
+    self.x-=a.x
+    self.y-=a.y
+    return self
+  
+  def __imul__(self,k):
+    self.x*=k
+    self.y*=k
+    return self
+    
+  def __itruediv__(self,k):
+    self.x/=k
+    self.y/=k
+    return self
+    
+  def __ifloordiv__(self,k):
+    self.x//=k
+    self.y//=k
+    return self
+    
+  def __neg__(self):
+    return EPoint(-self.x,-self.y)
+    
+  def __str__(self): return "({0},{1})".format(round(self.x,3),round(self.y,3))
+  def __repr__(self): return "EPoint({0},{1})".format(round(self.x,3),round(self.y,3))
diff --git a/src/grid.py b/src/grid.py
--- a/src/grid.py
+++ b/src/grid.py
@@ -1,74 +1,74 @@
 import numpy
+from epoint import *
+
 
-## Multiplicates the vector as to set the first nonzero coordinate to 1.
-def canonize(v):
-  if v.item(0)!=0: factor=v.item(0)
-  elif v.item(1)!=0: factor=v.item(1)
-  elif v.item(2)!=0: factor=v.item(2)
-  else: factor=1
-  return v/factor
-  return [x/factor for x in v]
+## Projective transformation of a point with a matrix A.
+#  
+#  Takes a point as a horizontal vector and multiplies it transposed with A from left.
+#  
+#  @return transformed point as a numpy.array
+def transformPoint(point,A):
+  return (A*numpy.matrix(point).transpose()).getA1()
 
-def transformPoint(point,A):
-  # print('#68',numpy.asarray(([1]+point)*A))
-  x=canonize((A*numpy.matrix([1]+point).transpose()).getA1())
-  return x[1:]
 
 class Grid:
-  
+  ## Creates a Grid from the provided Corners object.
+  #  
+  #  Finds the vanishing points of the board lines (corner points define perspectively transformed parallel lines). The vanishing points define the image horizon.
+  #  
+  #  The horizon can be used to construct a matrix for affine rectification of the image (restoring parallel lines parallelism). We transform the corner points by this matrix,
+  #  interpolate them to get proper intersections' coordinates and then transform these back to get their placement at the original image.
+  #  
+  #  The result is stored in grid.intersections, a boardSize*boardSize list with [row][column] coordinates.
+  #  
+  #  @param corners a properly initialized Corners object. !! Needs a check for the proper initialization.
   def __init__(self,corners):
-    # ab
-    # cd
-    a,b,c,d=corners
+    # ad
+    # bc
+    a,b,c,d=[c.toProjective() for c in corners.corners]
     
     p1=numpy.cross(a,b)
     p2=numpy.cross(c,d)
     vanish1=numpy.cross(p1,p2)
-    
-    print('#16',p1,p2,vanish1)
-    
-    p3=numpy.cross(a,c)
-    p4=numpy.cross(b,d)
-    vanish2=numpy.cross(p3,p4)
+    # !! 32 bit int can overflow. keeping it reasonably small. might want to use a cleaner solution
+    vanish1=EPoint.fromProjective(vanish1).toProjective()
     
-    print('#32',p3,p4,vanish2)
-    
-    horizon=canonize(numpy.cross(vanish1,vanish2))
+    p3=numpy.cross(a,d)
+    p4=numpy.cross(b,c)
+    vanish2=numpy.cross(p3,p4)
+    vanish2=EPoint.fromProjective(vanish2).toProjective()
     
-    # horizon.x+=10
-    # horizon[1]+=10
-    
-    print('#48',horizon)
+    horizon=numpy.cross(vanish1,vanish2)
+
+    horizon=EPoint.fromProjective(horizon).toProjective()
     
     rectiMatrix=numpy.matrix([horizon,[0,1,0],[0,0,1]])
     rectiMatrixInv=numpy.linalg.inv(rectiMatrix)
     
-    print('#64',rectiMatrixInv)
-    print('#72',transformPoint([0,0],rectiMatrixInv))
     
-    self.intersections=[[[c,r] for c in range(3)] for r in range(3)]
-    self.intersections=[[transformPoint(point,rectiMatrixInv) for point in line] for line in self.intersections]
-    
-    
-    # b1=canonize(numpy.cross(horizon,p1))
-    # b2=canonize(numpy.cross(horizon,p2))
-    # b3=canonize(numpy.cross(horizon,p3))
-    # b4=canonize(numpy.cross(horizon,p4))
-    
-    # print('#64',b1,b2,b3,b4)
+    affineCorners=[EPoint.fromProjective(transformPoint(x,rectiMatrix)) for x in (a,b,c,d)]
+    x=[affineCorners[0]-affineCorners[3],affineCorners[1]-affineCorners[2],affineCorners[0]-affineCorners[1],affineCorners[3]-affineCorners[2]]
     
-    # self.intersections=[]
-    # boardSize=4
-    # for r in range(boardSize):
-      # self.intersections.append([None]*boardSize)
-      # rowLine=numpy.cross(((b1*r+b2*(boardSize-1-r)) / (boardSize-1)), vanish1)
-      # print('#80',rowLine)
-      # for c in range(boardSize):
-        # colLine=numpy.cross(((b3*c+b4*(boardSize-1-c)) / (boardSize-1)), vanish2)
-        # print('#88',colLine)
-        # self.intersections[r][c]=numpy.cross(rowLine,colLine)
-        
-# x=Grid([Vector3(1,0,10),Vector3(1,10,10),Vector3(1,0,0),Vector3(1,10,0)])
-x=Grid([[1,0,10],[1,7,7],[1,0,0],[1,10,0]])
-for line in x.intersections:
-  print('#96',line)
+    self.intersections=[]
+    boardSize=19
+    for r in range(boardSize):
+      self.intersections.append([None]*boardSize)
+      rowStart=(affineCorners[0]*(boardSize-1-r)+affineCorners[1]*r) / (boardSize-1)
+      rowEnd=(affineCorners[3]*(boardSize-1-r)+affineCorners[2]*r) / (boardSize-1)
+      
+      for c in range(boardSize):
+        affineIntersection=(rowStart*(boardSize-1-c)+rowEnd*c) / (boardSize-1)
+        self.intersections[r][c]=EPoint.fromProjective(transformPoint(affineIntersection.toProjective(),rectiMatrixInv))
+
+
+
+# from corners import Corners
+# corn=Corners()
+
+# corn.add(106,86)
+# corn.add(57,321)
+# corn.add(416,320)
+# corn.add(365,86)
+# corn.add(365,88)
+
+# x=Grid(corn)
diff --git a/src/gui.py b/src/gui.py
--- a/src/gui.py
+++ b/src/gui.py
@@ -20,7 +20,8 @@ class Application(tk.Frame):
     # a captured frame with overlay graphics
     self.imgView=tk.Canvas(self)
     self.imgView.configure(width=480,height=360)
-    imgOrig=PIL.Image.open("../images/1.jpg")
+    imgOrig=PIL.Image.open("../images/7.jpg")
+    
     self.img=ImageTk.PhotoImage(imgOrig.resize((int(self.imgView['width']),int(self.imgView['height'])),resample=PIL.Image.BILINEAR))
     
     self.imgView.bind('<1>',lambda e: self.addCorner(e.x,e.y))
@@ -46,6 +47,16 @@ class Application(tk.Frame):
     for corner in self.corners.corners:
       self.markPoint(corner.x,corner.y)
     
+    if self.boardGrid!=None:
+      for r in range(19):
+        a=self.boardGrid.intersections[r][0]
+        b=self.boardGrid.intersections[r][-1]
+        self.imgView.create_line(a.x,a.y,b.x,b.y,fill='#00ff00')
+      for c in range(19):
+        a=self.boardGrid.intersections[0][c]
+        b=self.boardGrid.intersections[-1][c]
+        self.imgView.create_line(a.x,a.y,b.x,b.y,fill='#00ff00')
+    
     self.imgView.grid()
     
   ## Marks a point at the image with a green cross. Used for corners.