Browse Source

refactoring

olinox 8 years ago
parent
commit
17dae6af8a

+ 35 - 39
gridviewer/GridViewer.py

@@ -1,7 +1,6 @@
 '''
-Created on 26 nov. 2016
 
-@author: olinox
+    ** By Cro-Ki l@b, 2017 **
 '''
 from PyQt5.QtCore import QPointF, QMimeData
 from PyQt5.QtWidgets import QMainWindow, \
@@ -9,7 +8,7 @@ from PyQt5.QtWidgets import QMainWindow, \
 import ipdb  # until I find another way to print traceback with pyqt5
 
 from gridviewer.GridViewerCell import GridViewerCell
-from gridviewer.main import Ui_window
+from gridviewer.viewer import Ui_window
 from pypog import geometry
 
 
@@ -17,9 +16,6 @@ if __name__ == "__main__":
     import os, sys
     pypog_path = (os.path.abspath("..\\..\\"))
     sys.path.append(pypog_path)
-    
-
-
 
 class GridViewer(QMainWindow):
 
@@ -28,78 +24,78 @@ class GridViewer(QMainWindow):
         self.cells = {}
         self.selection = []
         self.createWidgets()
-        
+
     def createWidgets(self):
         self.ui = Ui_window()
-        self.ui.setupUi(self)                      
-        
+        self.ui.setupUi(self)
+
         self._scene = QGraphicsScene()
         self.ui.view.setScene(self._scene)
         self.ui.view.scale(0.5, 0.5)
-        self.ui.view.centerOn(QPointF(0,0))
+        self.ui.view.centerOn(QPointF(0, 0))
         self.ui.view.setDragMode(QGraphicsView.NoDrag)
-        
+
         self.ui.txt_coords.setPlainText("[]")
-        
+
         self.ui.btn_make.clicked.connect(self.make_grid)
         self.ui.btn_updateSelection.clicked.connect(self.update_selected_cells)
         self.ui.btn_toClipboard.clicked.connect(self.to_clipboard)
         self.ui.btn_zoom_plus.clicked.connect(self.zoom_plus)
         self.ui.btn_zoom_minus.clicked.connect(self.zoom_minus)
-        
+
         self.ui.chk_displayCoords.toggled.connect(self.update_cell_labels)
-        
+
         self.make_grid()
-        
+
     def make_grid(self):
-        
+
         self.selection = []
-        shape = geometry.HEX if self.ui.opt_hex.isChecked() else geometry.SQUARE
+        shape = geometry.FLAT_HEX if self.ui.opt_hex.isChecked() else geometry.SQUARE
         width = self.ui.spb_width.value()
         height = self.ui.spb_height.value()
-        
+
         kx = 1 if shape == geometry.SQUARE else 0.866
 
-        margin = 240 
+        margin = 240
         cell_height = 120
-        
+
         self._scene.clear()
-        
+
         self._scene.setSceneRect(0 - margin, 0 - margin, (kx * cell_height * (width + 2)) + margin, (cell_height * (height + 2)) + margin)
-        
+
         for x in range(width):
             for y in range(height):
-                
+
                 cell = GridViewerCell(self, x, y)
                 cell.generate(shape)
-                
-                self._scene.addItem(cell)   
-                             
+
+                self._scene.addItem(cell)
+
                 self.cells[(x, y)] = cell
 
 
     def add_to_selection(self, x, y):
-        self.selection.append( (x, y) )
-    
-        self.ui.txt_coords.setText( str(self.selection) )
-    
+        self.selection.append((x, y))
+
+        self.ui.txt_coords.setText(str(self.selection))
+
     def remove_from_selection(self, x, y):
-        self.selection.remove( (x, y) )
-        self.ui.txt_coords.setText( str(self.selection) )
-        
+        self.selection.remove((x, y))
+        self.ui.txt_coords.setText(str(self.selection))
+
     def update_selected_cells(self):
         try:
             new_selection = list(eval(self.ui.txt_coords.toPlainText()))
         except SyntaxError:
             QMessageBox.warning(self, "Error", "Invalid string")
-            return 
-        
+            return
+
         for x, y in tuple(self.selection):
             self.cells[(x, y)].unselect()
-            
+
         for x, y in new_selection:
             self.cells[(x, y)].select()
-            
+
     def to_clipboard(self):
         data = QMimeData()
         data.setText(self.ui.txt_coords.toPlainText())
@@ -107,7 +103,7 @@ class GridViewer(QMainWindow):
 
     def update_cell_labels(self):
         for cell in self.cells.values():
-            cell.show_label( bool(self.ui.chk_displayCoords.isChecked()) )
+            cell.show_label(bool(self.ui.chk_displayCoords.isChecked()))
 
     def zoom_plus(self):
         self.ui.view.scale(1.1, 1.1)
@@ -118,7 +114,7 @@ class GridViewer(QMainWindow):
 
 
 if __name__ == "__main__":
-    
+
     app = QApplication(sys.argv)
     gv = GridViewer()
     gv.show()

+ 25 - 26
gridviewer/GridViewerCell.py

@@ -1,7 +1,6 @@
 '''
-Created on 26 nov. 2016
 
-@author: olinox
+    ** By Cro-Ki l@b, 2017 **
 '''
 from PyQt5.QtCore import QPointF
 from PyQt5.QtGui import QPolygonF, QPen, QBrush, QColor, QFont
@@ -12,48 +11,48 @@ from pypog import geometry
 from pypog import graphic
 
 class GridViewerCell(QGraphicsPolygonItem):
-    
+
     def __init__(self, gridViewer, x, y):
         super(GridViewerCell, self).__init__()
         self.gridViewer = gridViewer
         self.x = x
         self.y = y
         self.selected = False
-        
+
     def generate(self, shape, scale=120):
-        
-        points = [QPointF(xp, yp) for xp, yp in graphic.polygon(shape, self.x, self.y, scale)]
-        qpolygon = QPolygonF( points )
-        
+
+        points = [QPointF(xp, yp) for xp, yp in graphic.g_cell(shape, self.x, self.y, scale)]
+        qpolygon = QPolygonF(points)
+
         self.setPolygon(qpolygon)
-        
+
         pen = QPen()
         pen.setWidth(3)
         self.setPen(pen)
-        
-        self.setFlag(QGraphicsItem.ItemIsFocusable)   
-        
+
+        self.setFlag(QGraphicsItem.ItemIsFocusable)
+
         self.label = QGraphicsSimpleTextItem("{}-{}".format(self.x, self.y), parent=self)
         k = 0
-        if (self.x % 2) != 0: 
+        if (self.x % 2) != 0:
             k = 0.5
-            
-        if shape == geometry.HEX:
-            self.label.setPos(QPointF(((self.x*0.866)+0.2886)*scale,  (self.y+k+0.5)*scale))
+
+        if shape == geometry.FLAT_HEX:
+            self.label.setPos(QPointF(((self.x * 0.866) + 0.2886) * scale, (self.y + k + 0.5) * scale))
         else:
-            self.label.setPos(QPointF(self.x*scale,  self.y*scale))
-        
+            self.label.setPos(QPointF(self.x * scale, self.y * scale))
+
         font = QFont()
         font.setPointSize(20)
-        self.label.setFont( font )
-        
+        self.label.setFont(font)
+
     def select(self):
-        self.setBrush( QBrush( QColor(200,0,0, 100) ) )
+        self.setBrush(QBrush(QColor(200, 0, 0, 100)))
         self.selected = True
-        self.gridViewer.add_to_selection(self.x, self.y)        
+        self.gridViewer.add_to_selection(self.x, self.y)
 
     def unselect(self):
-        self.setBrush( QBrush(  ) )
+        self.setBrush(QBrush())
         self.selected = False
         self.gridViewer.remove_from_selection(self.x, self.y)
 
@@ -63,8 +62,8 @@ class GridViewerCell(QGraphicsPolygonItem):
             self.unselect()
         else:
             self.select()
-    
+
     def show_label(self, visible):
         self.label.setVisible(visible)
-        
-    
+
+

+ 0 - 0
gridviewer/main.ui → gridviewer/qt_viewer.ui


+ 0 - 0
gridviewer/main.py → gridviewer/viewer.py


+ 0 - 159
pypog/Grid.py

@@ -1,159 +0,0 @@
-'''
-Created on 7 nov. 2016
-    Game Grid
-@author: olinox
-'''
-from pypog import geometry
-from pypog import pathfinder
-
-
-ORIGIN_HPOSITION_LEFT = 0
-ORIGIN_HPOSITION_MIDDLE = 1
-ORIGIN_HPOSITION_RIGHT = 2
-ORIGIN_VPOSITION_TOP = 10
-ORIGIN_VPOSITION_MIDDLE = 11
-ORIGIN_VPOSITION_BOTTOM = 12
-
-
-class Grid(object):
-    def __init__(self, cell_shape, width, height, roof = None):
-        self._cell_shape = None
-        self.cell_shape = cell_shape
-        
-        self._width = 0
-        self.width = width
-        self._height = 0
-        self.height = height
-        
-        self._roof = roof
-
-        self._cells = {}
-        self._build()
-        
-    def _build(self):
-        for x in range(self.width):
-            for y in range(self.height):
-                cell = Cell(self.cell_shape, x, y)
-                self._cells[(x, y)] = cell
-
-    # properties
-    @property
-    def cell_shape(self):
-        return self._cell_shape
-    
-    @cell_shape.setter
-    def cell_shape(self, cell_shape):
-        if not cell_shape in geometry.GRID_GEOMETRIES:
-            raise ValueError("'cell_shape' has to be a value from GRID_GEOMETRIES")
-        self._cell_shape = cell_shape
-        
-    @property
-    def width(self):
-        return self._width
-    
-    @width.setter
-    def width(self, width):
-        if not isinstance(width, int) or not width > 0:
-            raise ValueError("'width' has to be a strictly positive integer")
-        self._width = width
-        
-    @property
-    def height(self):
-        return self._height
-    
-    @height.setter
-    def height(self, height):
-        if not isinstance(height, int) or not height > 0:
-            raise ValueError("'width' has to be a strictly positive integer")
-        self._height = height    
-    
-    @property
-    def roof(self):
-        return self._roof
-    
-    def cell(self, x, y):
-        return self._cells[(x, y)]
-    
-    @property
-    def cells(self):
-        return self._cells
-    
-    
-    # geometric methods
-    def cases_number(self):
-        return self.height * self.width
-    
-    def in_grid(self, x, y):
-        """return True if the coordinates are in the grid"""
-        return (x > 0 and x <= self._width and y > 0 and y <= self._height)
-    
-    def line(self, x1, y1, x2, y2):
-        return geometry.line2d(self.cell_shape, x1, y1, x2, y2)
-    
-    def line3d(self, x1, y1, z1, x2, y2, z2):
-        return geometry.line3d(self.cell_shape, x1, y1, z1, x2, y2, z2)
-    
-    def zone(self, x, y, radius):
-        return geometry.zone(self.cell_shape, x, y, radius)
-    
-    def triangle(self, xa, ya, xh, yh, iAngle):
-        return geometry.triangle(self.cell_shape, xa, ya, xh, yh, iAngle)
-    
-    def triangle3d(self, xa, ya, za, xh, yh, zh, iAngle):
-        return geometry.triangle3d(self.cell_shape, xa, ya, za, xh, yh, zh, iAngle)
-
-    def rect(self, x1, y1, x2, y2):
-        return geometry.rect(x1, y1, x2, y2)
-    
-    def hollow_rect(self, x1, y1, x2, y2):
-        return geometry.hollow_rect(x1, y1, x2, y2)
-
-
-    # pathfinding methods
-    def moving_cost(self, *args):
-        return 1
-    
-    def path(self, x1, y1, x2, y2):
-        return pathfinder.path( self, (x1, y1), (x2,y2), self.moving_cost_function )
-
-
-class HexGrid(Grid):
-    def __init__(self, width, height):
-        Grid.__init__(self, geometry.HEX, width, height)
-
-class SquareGrid(Grid):
-    def __init__(self, width, height):
-        Grid.__init__(self, geometry.SQUARE, width, height)
-
-
-
-class Cell(object):
-    def __init__(self, cell_shape, x, y, z = 0):
-        self._cell_shape = cell_shape
-        self._x = x
-        self._y = y
-        self._z = z
-
-    @property
-    def x(self):
-        return self._x
-    
-    @property
-    def y(self):
-        return self._y
-    
-    @property
-    def z(self):
-        return self._z
-    
-    @property
-    def coord(self):
-        return (self._x, self._y)
-    
-    @property
-    def coord3d(self):
-        return (self._x, self._y, self._z)
-    
-    def __repr__(self):
-        return "Cell {}".format(self.coord)
-

+ 248 - 262
pypog/geometry.py

@@ -3,7 +3,7 @@
 
     2D functions return lists of (x, y) coordinates
     3D functions return lists of (x, y, z) coordinates
-    
+
     * neighbours_of function return the list of the cells around the (x, y) cell
     * zone function return the list of the cells surrounding the (x, y) cell within a 'radius' distance
     * line2d function return the list of the cells on a line between the (x1, y1) cell and the (x2, y2) cell
@@ -13,44 +13,48 @@
     * triangle function return the list of the cells in a triangle from its apex (xa, ya) to its base (xh, yh)
     * triangle3d function return the list of the cells in a cone from its apex (xa, ya, za) to its base (xh, yh, zh)
     * pivot function return a list of coordinates after a counterclockwise rotation of a given list of coordinates, around a given center
-    
-@author: olinox14
+
+
+    ** By Cro-Ki l@b, 2017 **
 '''
 from math import sqrt
 
+CELL_SHAPES = (4, 6)
 
-GRID_GEOMETRIES = (4, 6)
 SQUARE = 4
-HEX = 6
+FLAT_HEX = 61
+TOP_HEX = 62
+CELL_SHAPES = (SQUARE, FLAT_HEX, TOP_HEX)
+
 ANGLES = (1, 2, 3)
 
-## neighbours
+class UnknownCellShape(ValueError): pass
+
 
-def neighbours_of(cell_shape, x, y):
+
+# ## NEIGHBOURS
+
+def neighbours(cell_shape, x, y):
     """ returns the list of coords of the neighbours of a cell"""
     if cell_shape == SQUARE:
-        return squ_neighbours_of(x, y)
-    elif cell_shape == HEX: 
-        return hex_neighbours_of(x, y)
+        return squ_neighbours(x, y)
+    elif cell_shape == FLAT_HEX:
+        return fhex_neighbours(x, y)
     else:
-        raise ValueError("'cell_shape' has to be a value from GRID_GEOMETRIES")
+        raise ValueError("'cell_shape' has to be a value from CELL_SHAPES")
 
-def hex_neighbours_of(x, y):
+def fhex_neighbours(x, y):
     """ returns the list of coords of the neighbours of a cell on an hexagonal grid"""
-    if x%2 == 0:
-        return [(x, y-1), (x+1, y-1), (x+1, y), (x,  y+1), (x-1, y), (x-1, y-1)]
+    if x % 2 == 0:
+        return [(x, y - 1), (x + 1, y - 1), (x + 1, y), (x, y + 1), (x - 1, y), (x - 1, y - 1)]
     else:
-        return [(x, y-1), (x+1, y), (x+1, y+1), (x,  y+1), (x-1, y+1), (x-1, y)]        
+        return [(x, y - 1), (x + 1, y), (x + 1, y + 1), (x, y + 1), (x - 1, y + 1), (x - 1, y)]
 
-def squ_neighbours_of(x, y):
+def squ_neighbours(x, y):
     """ returns the list of coords of the neighbours of a cell on an square grid"""
-    return [(x-1, y-1), (x, y-1), (x+1, y-1), \
-            (x-1, y), (x+1, y)  , \
-            (x-1, y+1), (x, y+1),(x+1, y+1)]
-    
-
-
-## zones
+    return [(x - 1, y - 1), (x, y - 1), (x + 1, y - 1), \
+            (x - 1, y), (x + 1, y)  , \
+            (x - 1, y + 1), (x, y + 1), (x + 1, y + 1)]
 
 def zone(cell_shape, x0, y0, radius):
     """ returns the list of the coordinates of the cells in the zone around (x0, y0)
@@ -59,29 +63,29 @@ def zone(cell_shape, x0, y0, radius):
         raise TypeError("x0, y0, radius have to be integers")
     if not radius >= 0:
         raise ValueError("radius has to be positive")
-    buffer = frozenset( [ (x0, y0) ] )
+    buffer = frozenset([ (x0, y0) ])
 
     for _ in range(0, radius):
         current = buffer
         for x, y in current:
-            buffer |= frozenset( neighbours_of( cell_shape, x, y ) )
+            buffer |= frozenset(neighbours(cell_shape, x, y))
 
     return list(buffer)
 
 
-## line : bresenham algorithm
+# ## LINES (implementations of bresenham algorithm)
 
 def line2d(cell_shape, x1, y1, x2, y2):
     """returns a line from x1,y1 to x2,y2
     grid could be one of the GRIDTYPES values"""
     if not all(isinstance(c, int) for c in [x1, y1, x2, y2]):
         raise TypeError("x1, y1, x2, y2 have to be integers")
-    if cell_shape == HEX:
-        return _brH(x1, y1, x2, y2)
-    elif cell_shape == SQUARE: 
-        return _brC(x1, y1, x2, y2)
+    if cell_shape == FLAT_HEX:
+        return fhex_line(x1, y1, x2, y2)
+    elif cell_shape == SQUARE:
+        return squ2_line(x1, y1, x2, y2)
     else:
-        raise ValueError("'cell_shape' has to be a value from GRID_GEOMETRIES")
+        raise ValueError("'cell_shape' has to be a value from CELL_SHAPES")
 
 def line3d(cell_shape, x1, y1, z1, x2, y2, z2):
     """returns a line from x1,y1,z1 to x2,y2,z2
@@ -92,10 +96,10 @@ def line3d(cell_shape, x1, y1, z1, x2, y2, z2):
     if z1 == z2:
         return [(x, y, z1) for x, y in hoLine]
     else:
-        ligneZ = _brC(0, z1, (len(hoLine)-1), z2)
+        ligneZ = squ2_line(0, z1, (len(hoLine) - 1), z2)
         return [(hoLine[d][0], hoLine[d][1], z) for d, z in ligneZ]
 
-def _brC(x1, y1, x2, y2):
+def squ2_line(x1, y1, x2, y2):
     """Line Bresenham algorithm for square grid"""
     result = []
 
@@ -103,160 +107,149 @@ def _brC(x1, y1, x2, y2):
         return [(x1, y1)]
 
     # DIAGONAL SYMETRY
-    V = ( abs(y2 - y1) > abs(x2 - x1) )
+    V = (abs(y2 - y1) > abs(x2 - x1))
     if V: y1, x1, y2, x2 = x1, y1, x2, y2
-    
+
     # VERTICAL SYMETRY
     reversed_sym = (x1 > x2)
-    if reversed_sym:  
+    if reversed_sym:
         x2, y2, x1, y1 = x1, y1, x2, y2
-    
+
     DX = x2 - x1 ; DY = y2 - y1
     offset = 0.0
     step = 1 if DY > 0 else -1
-    alpha = ( abs( DY ) / DX )
-    
+    alpha = (abs(DY) / DX)
+
     y = y1
     for x in range(x1, x2 + 1):
         coord = (y, x) if V else (x, y)
         result.append(coord)
-        
+
         offset += alpha
         if offset > 0.5:
             y += step
             offset -= 1.0
-    
-    if reversed_sym: 
+
+    if reversed_sym:
         result.reverse()
     return result
-    
-def _brH(x1, y1, x2, y2):
+
+def fhex_line(x1, y1, x2, y2):
     """Line Bresenham algorithm for hexagonal grid"""
     if (x1, y1) == (x2, y2):
         return [(x1, y1)]
-    
+
     reversed_sym = (x1 > x2)
     if reversed_sym:
         x1, x2 = x2, x1
         y1, y2 = y2, y1
-    
-    if abs(x2 - x1) < (2*abs((y2-y1)) + abs(x2 % 2) - abs(x1 % 1)):
-        result = _brH_v(x1, y1, x2, y2)
-    else:
-        result = _brH_h(x1, y1, x2, y2)   
 
-    if reversed_sym: 
-        result.reverse()
-    return result
-         
-def _brH_h(x1, y1, x2, y2):
-    """Line Bresenham algorithm for hexagonal grid (horizontal quadrants)"""  
-    dx = x2 - x1 ; dy = y2 - y1 
-    if (x1 + x2) % 2 == 1: 
-        dy += 0.5 if x1 % 2 == 0 else -0.5
-                
-    k = dy / dx
-    pas = 1
-    
-    result = []
-    d = 0.0
-    pos = (x1, y1)
-    result.append(pos)
-    
-    while pos != (x2, y2):
-        d += k*pas
-        if d > 0:
-            x, y = pos
-            if x % 2 == 0:
-                pos = x + 1, y 
+    if abs(x2 - x1) < (2 * abs((y2 - y1)) + abs(x2 % 2) - abs(x1 % 1)):
+        # vertical quadrants
+
+        # unit is half the width: u = 0.5773
+        # half-height is then 0.8860u, or sqrt(3)/2
+        direction = 1 if y2 > y1 else -1
+
+        dx = 1.5 * (x2 - x1)
+        dy = direction * (y2 - y1)
+        if (x1 + x2) % 2 == 1:
+            if x1 % 2 == 0:
+                dy += direction * 0.5
             else:
-                pos = x + 1, y + 1 
-            result.append(pos)
-            d -= 0.5
-        else:
-            x, y = pos
-            if x % 2 == 0:
-                pos = x + 1, y - 1   
+                dy -= direction * 0.5
+
+        k = dx / (dy * sqrt(3))
+        pas = 0.5 * sqrt(3)
+
+        result = []
+        offset = 0.0
+        pos = (x1, y1)
+        result.append(pos)
+
+        while pos != (x2, y2):
+            offset += (k * pas)
+            if offset <= 0.5:
+                x, y = pos
+                pos = x, y + direction
+                result.append(pos)
+                offset += (k * pas)
             else:
-                pos = x + 1, y         
-            result.append(pos)
-            d += 0.5
-        
-        # in case of error in the algorithm, we should avoid infinite loop:
-        if pos[0] > x2:
-            result = []
-            break
-                      
-    return result
-     
-def _brH_v(x1, y1, x2, y2):
-    """Line Bresenham algorithm for hexagonal grid (vertical quadrants)"""  
-    # unit is half the width: u = 0.5773
-    # half-height is then 0.8860u, or sqrt(3)/2
-    direction = 1 if y2 > y1 else -1
- 
-    dx = 1.5 * (x2 - x1)  
-    dy = direction * (y2 - y1)    
-    if (x1 + x2) % 2 == 1: 
-        if x1 % 2 == 0:
-            dy += direction*0.5
-        else:
-            dy -= direction*0.5
- 
-    k = dx/(dy*sqrt(3)) 
-    pas = 0.5*sqrt(3)
- 
-    result = []
-    offset = 0.0
-    pos = (x1, y1)
-    result.append(pos)
-    
-    while pos != (x2, y2):
-        offset += (k*pas)
-        if offset <= 0.5:
-            x, y = pos
-            pos = x, y + direction
-            result.append(pos)
-            offset += (k*pas)
-        else:
-            x, y = pos
-            if (x %2 == 0 and direction == 1) or (x % 2 == 1 and direction == -1):
-                pos = x + 1, y
+                x, y = pos
+                if (x % 2 == 0 and direction == 1) or (x % 2 == 1 and direction == -1):
+                    pos = x + 1, y
+                else:
+                    pos = x + 1, y + direction
+                result.append(pos)
+                offset -= 1.5
+
+            # in case of error in the algorithm, we should avoid infinite loop:
+            if direction * pos[1] > direction * y2:
+                result = []
+                break
+
+    else:
+        # horizontal quadrants
+        dx = x2 - x1 ; dy = y2 - y1
+        if (x1 + x2) % 2 == 1:
+            dy += 0.5 if x1 % 2 == 0 else -0.5
+
+        k = dy / dx
+        pas = 1
+
+        result = []
+        d = 0.0
+        pos = (x1, y1)
+        result.append(pos)
+
+        while pos != (x2, y2):
+            d += k * pas
+            if d > 0:
+                x, y = pos
+                if x % 2 == 0:
+                    pos = x + 1, y
+                else:
+                    pos = x + 1, y + 1
+                result.append(pos)
+                d -= 0.5
             else:
-                pos = x + 1, y + direction            
-            result.append(pos)
-            offset -= 1.5
-        
-        # in case of error in the algorithm, we should avoid infinite loop:
-        if direction*pos[1] > direction*y2:
-            result = []
-            break
- 
-    return result 
+                x, y = pos
+                if x % 2 == 0:
+                    pos = x + 1, y - 1
+                else:
+                    pos = x + 1, y
+                result.append(pos)
+                d += 0.5
+
+            # in case of error in the algorithm, we should avoid infinite loop:
+            if pos[0] > x2:
+                result = []
+                break
+
+    if reversed_sym:
+        result.reverse()
+    return result
 
 
-## rectangles
+# ## RECTANGLES
 
-def rect(x1, y1, x2, y2):
+def rectangle(x1, y1, x2, y2):
     """return a list of cells in a rectangle between (X1, Y1), (X2, Y2)"""
     if not all(isinstance(val, int) for val in [x1, y1, x2, y2]):
         raise TypeError("x1, y1, x2, y2 should be integers")
     xa, ya, xb, yb = min([x1, x2]), min([y1, y2]), max([x1, x2]), max([y1, y2])
     return [(x, y) for x in range(xa, xb + 1) for y in range(ya, yb + 1)]
 
-def hollow_rect(x1, y1, x2, y2):
+def hollow_rectangle(x1, y1, x2, y2):
     """return a list of cells composing the sides of the rectangle between (X1, Y1), (X2, Y2)"""
     if not all(isinstance(val, int) for val in [x1, y1, x2, y2]):
         raise TypeError("x1, y1, x2, y2 should be integers")
-    return [(x, y) for x, y in rect(x1, y1, x2, y2)
+    return [(x, y) for x, y in rectangle(x1, y1, x2, y2)
             if (x == x1 or x == x2 or y == y1 or y == y2)]
-    
-    
-    
-## triangles
-    
 
 
+# ## TRIANGLES
+
 def triangle(cell_shape, xa, ya, xh, yh, iAngle):
     """Returns a list of (x, y) coordinates in a triangle
     A is the top of the triangle, H if the middle of the base
@@ -264,219 +257,219 @@ def triangle(cell_shape, xa, ya, xh, yh, iAngle):
     if not all(isinstance(c, int) for c in [xa, ya, xh, yh]):
         raise TypeError("xa, ya, xh, yh should be integers")
     if not iAngle in ANGLES:
-        raise ValueError("iAngle should be one of the ANGLES values")   
-    
+        raise ValueError("iAngle should be one of the ANGLES values")
+
     if cell_shape == SQUARE:
-        return _triangle_sq(xa, ya, xh, yh, iAngle)
-    elif cell_shape == HEX: 
-        return _triangle_hex(xa, ya, xh, yh, iAngle)
+        return squ2_triangle(xa, ya, xh, yh, iAngle)
+    elif cell_shape == FLAT_HEX:
+        return fhex2_triangle(xa, ya, xh, yh, iAngle)
     else:
-        raise ValueError("'cell_shape' has to be a value from GRID_GEOMETRIES")
+        raise ValueError("'cell_shape' has to be a value from CELL_SHAPES")
 
 def triangle3d(cell_shape, xa, ya, za, xh, yh, zh, iAngle):
     """Returns a list of (x, y, z) coordinates in a 3d-cone
     A is the top of the cone, H if the center of the base
-    
+
     WARNING: result is a dictionnary of the form {(x, y): (-z, +z)}
-    
+
     This is for performance reason, because on a 2d grid, you generrally don't need a complete list of z coordinates
     as you don't want to display them: you just want to know if an altitude is inside a range.
-    
+
     That could change in later version
     """
     # TODO: review the result form
-    
+
     if not all(isinstance(c, int) for c in [za, zh]):
         raise TypeError("xa, ya, za, xh, yh, zh should be integers")
         # a triangle2d will be built during algo, so other args will be checked later
-    
+
     if cell_shape == SQUARE:
-        return _triangle_sq_3d(xa, ya, za, xh, yh, zh, iAngle)
-    elif cell_shape == HEX: 
-        return _triangle_hex_3d(xa, ya, za, xh, yh, zh, iAngle)
+        return squ3_triangle(xa, ya, za, xh, yh, zh, iAngle)
+    elif cell_shape == FLAT_HEX:
+        return fhex3_triangle(xa, ya, za, xh, yh, zh, iAngle)
     else:
-        raise ValueError("'cell_shape' has to be a value from GRID_GEOMETRIES")    
-    
+        raise ValueError("'cell_shape' has to be a value from CELL_SHAPES")
 
-def _triangle_sq(xa, ya, xh, yh, iAngle):   
+
+def squ2_triangle(xa, ya, xh, yh, iAngle):
     """ triangle algorithm on square grid
     """
     if (xa, ya) == (xh, yh):
-        return [(xa, ya)] 
-    
+        return [(xa, ya)]
+
     result = []
-    
+
     # direction vector
     dx_dir, dy_dir = xh - xa, yh - ya
-    
+
     # normal vector
-    dx_n, dy_n = - dy_dir, dx_dir
+    dx_n, dy_n = -dy_dir, dx_dir
 
     # B and C positions
-    k = 1 / ( iAngle * sqrt(3) )
+    k = 1 / (iAngle * sqrt(3))
     xb, yb = xh + (k * dx_n), yh + (k * dy_n)
     xc, yc = xh + (-k * dx_n), yh + (-k * dy_n)
-    
+
     xb, yb = round(xb), round(yb)
     xc, yc = round(xc), round(yc)
 
     # sides:
     lines = [(xa, ya, xb, yb), (xb, yb, xc, yc), (xc, yc, xa, ya)]
-    
+
     # base (lower slope)
-    x1, y1, x2, y2 = min(lines, key=lambda x: (abs ( (x[3] - x[1]) / (x[2] - x[0]) ) if x[2] != x[0] else 10**10))
+    x1, y1, x2, y2 = min(lines, key=lambda x: (abs ((x[3] - x[1]) / (x[2] - x[0])) if x[2] != x[0] else 10 ** 10))
     base = line2d(SQUARE, x1, y1, x2, y2)
     y_base = y1
-    lines.remove( (x1, y1, x2, y2) )
-    
+    lines.remove((x1, y1, x2, y2))
+
     # 'hat' (2 other sides)
     hat = []
     y_top = None
     for x1, y1, x2, y2 in lines:
-        if y_top == None: 
+        if y_top == None:
             y_top = y2
-        hat.extend( line2d(SQUARE, x1, y1, x2, y2) )
-    
+        hat.extend(line2d(SQUARE, x1, y1, x2, y2))
+
     # sense (1 if top is under base, -1 if not)
     sense = 1 if y_top > y_base else -1
-    
+
     # rove over y values from base to hat
     for x, y in base:
         while not (x, y) in hat:
-            result.append( (x, y) )
+            result.append((x, y))
             y += sense
     result.extend(hat)
 
     return result
 
-def _triangle_hex(xa, ya, xh, yh, iAngle):   
+def fhex2_triangle(xa, ya, xh, yh, iAngle):
     """  triangle algorithm on hexagonal grid
     """
     if (xa, ya) == (xh, yh):
-        return [(xa, ya)]    
-    
+        return [(xa, ya)]
+
     result = []
-    
+
     # convert to cubic coodinates (see 'cube_coords' lib)
-    xua, yua, _ = cv_off_cube( xa, ya )
-    xuh, yuh, zuh = cv_off_cube( xh, yh )
-    
+    xua, yua, _ = cv_off_cube(xa, ya)
+    xuh, yuh, zuh = cv_off_cube(xh, yh)
+
     # direction vector
     dx_dir, dy_dir = xuh - xua, yuh - yua
-    
+
     # normal vector
-    dx_n, dy_n = - (2* dy_dir + dx_dir ), (2* dx_dir + dy_dir ) 
-    dz_n = (- dx_n - dy_n)        
+    dx_n, dy_n = -(2 * dy_dir + dx_dir), (2 * dx_dir + dy_dir)
+    dz_n = (-dx_n - dy_n)
 
     # B and C positions
-    k = 1 / ( iAngle * sqrt(3) )
+    k = 1 / (iAngle * sqrt(3))
     xub, yub, zub = xuh + (k * dx_n), yuh + (k * dy_n), zuh + (k * dz_n)
     xuc, yuc, zuc = xuh + (-k * dx_n), yuh + (-k * dy_n), zuh + (-k * dz_n)
-    
+
     xub, yub, zub = cube_round(xub, yub, zub)
     xuc, yuc, zuc = cube_round(xuc, yuc, zuc)
-    
+
     xb, yb = cv_cube_off(xub, yub, zub)
     xc, yc = cv_cube_off(xuc, yuc, zuc)
 
     # sides
     segments = [(xa, ya, xb, yb), (xb, yb, xc, yc), (xc, yc, xa, ya)]
-    
+
     # base (lower slope)
-    x1, y1, x2, y2 = min(segments, key=lambda x: (abs ( (x[3] - x[1]) / (x[2] - x[0]) ) if x[2] != x[0] else 10**10))
-    base = line2d(HEX, x1, y1, x2, y2)
+    x1, y1, x2, y2 = min(segments, key=lambda x: (abs ((x[3] - x[1]) / (x[2] - x[0])) if x[2] != x[0] else 10 ** 10))
+    base = line2d(FLAT_HEX, x1, y1, x2, y2)
     y_base = y1
-    segments.remove( (x1, y1, x2, y2) )
-    
+    segments.remove((x1, y1, x2, y2))
+
     # 'hat' (the 2 other sides)
     chapeau = []
     y_sommet = None
     for x1, y1, x2, y2 in segments:
-        if y_sommet == None: 
+        if y_sommet == None:
             y_sommet = y2
-        chapeau.extend( line2d(HEX, x1, y1, x2, y2) )
-    
+        chapeau.extend(line2d(FLAT_HEX, x1, y1, x2, y2))
+
     # sense (1 if top is under base, -1 if not)
     sens = 1 if y_sommet > y_base else -1
-    
+
     # rove over y values from base to hat
     for x, y in base:
         while not (x, y) in chapeau:
-            result.append( (x, y) )
+            result.append((x, y))
             y += sens
     result.extend(chapeau)
 
     return result
 
 
-def _triangle_sq_3d(xa, ya, za, xh, yh, zh, iAngle):
+def squ3_triangle(xa, ya, za, xh, yh, zh, iAngle):
     """ 3d triangle algorithm on square grid"""
     result = []
-    
+
     flat_triangle = triangle(SQUARE, xa, ya, xh, yh, iAngle)
-    k = 1 / ( iAngle * sqrt(3) )
+    k = 1 / (iAngle * sqrt(3))
 
-    length = max( abs(xh - xa), abs(yh - ya) )
+    length = max(abs(xh - xa), abs(yh - ya))
 
     vertical_line = line2d(SQUARE, 0, za, length, zh)
-    
+
     # build a dict with X key and value is a list of Z values
     vertical_line_dict = {d:[] for d, z in vertical_line}
     for d, z in vertical_line:
         vertical_line_dict[d].append(z)
-        
+
     # this is approximative: height is update according to the manhattan distance to center
     for x, y in flat_triangle:
-        distance = int( max( abs(x - xa), abs(y - ya) ) )
+        distance = int(max(abs(x - xa), abs(y - ya)))
         try:
             z_list = vertical_line_dict[ distance ]
         except KeyError:
             distance = length
             z_list = vertical_line_dict[ distance ]
-        dh = int( k * distance ) + 1 if distance > 0 else 0
-        result[ (x, y) ] = ( (min(z_list) - dh) , (max(z_list) + dh) ) 
+        dh = int(k * distance) + 1 if distance > 0 else 0
+        result[ (x, y) ] = ((min(z_list) - dh) , (max(z_list) + dh))
     return result
 
-def _triangle_hex_3d(xa, ya, za, xh, yh, zh, iAngle):
+def fhex3_triangle(xa, ya, za, xh, yh, zh, iAngle):
     """ 3d triangle algorithm on hexagonal grid """
-    
-    flat_triangle = triangle(HEX, xa, ya, xh, yh, iAngle)
 
-    result = {} 
-    
-    k = 1 / ( iAngle * sqrt(3) )
-    
+    flat_triangle = triangle(FLAT_HEX, xa, ya, xh, yh, iAngle)
+
+    result = {}
+
+    k = 1 / (iAngle * sqrt(3))
+
     # use cubic coordinates
     xua, yua, zua = cv_off_cube(xa, ya)
     xuh, yuh, zuh = cv_off_cube(xh, yh)
-    
-    length = max( abs(xuh - xua), abs(yuh - yua), abs(zuh - zua) )
+
+    length = max(abs(xuh - xua), abs(yuh - yua), abs(zuh - zua))
 
     vertical_line = line2d(SQUARE, 0, za, length, zh)
-    
+
     # build a dict with X key and value is a list of Z values
     vertical_line_dict = {d:[] for d, z in vertical_line}
     for d, z in vertical_line:
         vertical_line_dict[d].append(z)
-        
+
     # this is approximative: height is update according to the manhattan distance to center
     for x, y in flat_triangle:
         xu, yu, zu = cv_off_cube(x, y)
-        distance = int( max( abs(xu - xua), abs(yu - yua), abs(zu - zua) ) )
+        distance = int(max(abs(xu - xua), abs(yu - yua), abs(zu - zua)))
         try:
             z_list = vertical_line_dict[ distance ]
         except KeyError:
             distance = length
             z_list = vertical_line_dict[ distance ]
-        dh = int( k * distance ) + 1 if distance > 0 else 0
-        result[ (x, y) ] = ( (min(z_list) - dh) , (max(z_list) + dh) ) 
+        dh = int(k * distance) + 1 if distance > 0 else 0
+        result[ (x, y) ] = ((min(z_list) - dh) , (max(z_list) + dh))
     return result
 
 
-## pivot
+# ## TRANSLATIONS / ROTATIONS
 
 def pivot(cell_shape, center, coordinates, rotations):
-    """pivot 'rotations' times the coordinates (list of (x, y) tuples) 
+    """pivot 'rotations' times the coordinates (list of (x, y) tuples)
     around the center coordinates (x,y)
     Rotation is counterclockwise"""
     # check the args:
@@ -486,41 +479,40 @@ def pivot(cell_shape, center, coordinates, rotations):
         raise TypeError("'center' should be an tuple of (x, y) coordinates with x and y integers (given: {})".format(center))
     if not isinstance(x, int) or not isinstance(y, int):
         raise ValueError("'center' should be an tuple of (x, y) coordinates with x and y integers (given: {})".format(center))
-        
+
     try:
         for coord in coordinates:
             try:
                 x, y = coord
                 if not isinstance(x, int) or not isinstance(y, int):
-                    raise ValueError() 
+                    raise ValueError()
             except ValueError:
                 raise ValueError("'coordinates' should be an list of (x, y) coordinates with x and y integers (given: {})".format(coordinates))
     except TypeError:
         raise TypeError("'coordinates' should be an list of (x, y) coordinates with x and y integers (given: {})".format(coordinates))
-    
+
     if not isinstance(rotations, int):
         raise TypeError("'rotations' should be an integer (given: {})".format(rotations))
-    
+
     # call the method according to cells shape
     if cell_shape == SQUARE:
-        return _squ_pivot(center, coordinates, rotations)
-    elif cell_shape == HEX: 
-        return _hex_pivot(center, coordinates, rotations)
+        return squ_pivot(center, coordinates, rotations)
+    elif cell_shape == FLAT_HEX:
+        return fhex_pivot(center, coordinates, rotations)
     else:
-        raise ValueError("'cell_shape' has to be a value from GRID_GEOMETRIES")
+        raise ValueError("'cell_shape' has to be a value from CELL_SHAPES")
 
 
-def _hex_pivot(center, coordinates, rotations):
-    """pivot 'rotations' times the coordinates (list of (x, y) tuples) 
+def fhex_pivot(center, coordinates, rotations):
+    """pivot 'rotations' times the coordinates (list of (x, y) tuples)
     around the center coordinates (x,y)
     On hexagonal grid, rotates of 60 degrees each time"""
-    if coordinates == [center] or rotations%6 == 0:
+    if coordinates == [center] or rotations % 6 == 0:
         return coordinates
-    
     x0, y0 = center
     xu0, yu0, zu0 = cv_off_cube(x0, y0)
     result = []
-    
+
     for x, y in coordinates:
         xu, yu, zu = cv_off_cube(x, y)
         dxu, dyu, dzu = xu - xu0, yu - yu0, zu - zu0
@@ -528,69 +520,63 @@ def _hex_pivot(center, coordinates, rotations):
             dxu, dyu, dzu = -dzu, -dxu, -dyu
         xru, yru, zru = dxu + xu0, dyu + yu0, dzu + zu0
         xr, yr = cv_cube_off(xru, yru, zru)
-        result.append( (xr, yr) )
-        
+        result.append((xr, yr))
     return result
 
-def _squ_pivot(center, coordinates, rotations):
-    """pivot 'rotations' times the coordinates (list of (x, y) tuples) 
+def squ_pivot(center, coordinates, rotations):
+    """pivot 'rotations' times the coordinates (list of (x, y) tuples)
     around the center coordinates (x,y)
     On square grid, rotates of 90 degrees each time"""
-    if coordinates == [center] or rotations%4 == 0:
+    if coordinates == [center] or rotations % 4 == 0:
         return coordinates
-    
     x0, y0 = center
     result = []
-    
     for x, y in coordinates:
-        
         dx, dy = x - x0, y - y0
         for _ in range(rotations):
             dx, dy = dy, -dx
-            
         xr, yr = dx + x0, dy + y0
-        result.append( (xr, yr) )
-        
+        result.append((xr, yr))
     return result
 
-## cubic coordinates
+
+# ## CUBIC COORDINATES
 def cv_cube_off(xu, yu, zu):
     """convert cubic coordinates (xu, yu, zu) in standards coordinates (x, y) [offset]"""
-    y = int( xu + ( zu - (zu & 1) ) / 2 )
+    y = int(xu + (zu - (zu & 1)) / 2)
     x = zu
-    return (x, y)        
+    return (x, y)
 
 def cv_off_cube(x, y):
     """converts standards coordinates (x, y) [offset] in cubic coordinates (xu, yu, zu)"""
     zu = x
-    xu = int( y - ( x - (x & 1) ) / 2 )
-    yu = int( -xu -zu )
-    return (xu, yu, zu)    
+    xu = int(y - (x - (x & 1)) / 2)
+    yu = int(-xu - zu)
+    return (xu, yu, zu)
 
-# unused
+# > unused
 def cube_round(x, y, z):
     """returns the nearest cell (in cubic coords)
     x, y, z can be floating numbers, no problem."""
     rx, ry, rz = round(x), round(y), round(z)
     x_diff, y_diff, z_diff = abs(rx - x), abs(ry - y), abs(rz - z)
     if x_diff > y_diff and x_diff > z_diff:
-        rx = -ry-rz
+        rx = -ry - rz
     elif y_diff > z_diff:
-        ry = -rx-rz
+        ry = -rx - rz
     else:
-        rz = -rx-ry
+        rz = -rx - ry
     return (rx, ry, rz)
 
-# unused
+# > unused
 def hex_distance_cube(xa, ya, za, xb, yb, zb):
     """returns the manhattan distance between the two cells"""
     return max(abs(xa - xb), abs(ya - yb), abs(za - zb))
 
-# unused
+# > unused
 def distance_off(xa, ya, xb, yb):
     """ distance between A and B (offset coordinates)"""
     # 10 times quicker if no conversion...
     xua, yua, zua = cv_off_cube(xa, ya)
     xub, yub, zub = cv_off_cube(xb, yb)
     return max(abs(xua - xub), abs(yua - yub), abs(zua - zub))
-    

+ 28 - 28
pypog/graphic.py

@@ -1,35 +1,35 @@
 '''
-Created on 5 dec. 2016
+    Graphical functions
 
-@author: olinox
+    ** By Cro-Ki l@b, 2017 **
 '''
-
 from pypog import geometry
 
-
-def polygon(shape, x, y, scale = 120):
-    if shape == geometry.HEX:
-        
-        if 1 == (x % 2): 
-            y += 0.5
-            
-        return [
-                   ( ((x*0.866)+0.2886) * scale ,   y * scale), \
-                   ( ((x*0.866)+0.866) * scale  ,   y * scale), \
-                   ( ((x*0.866)+1.1547) * scale ,   (y+0.5) * scale), \
-                   ( ((x*0.866)+0.866) * scale  ,   (y+1) * scale), \
-                   ( ((x*0.866)+0.2886) * scale ,   (y+1) * scale),  \
-                   ( (x*0.866) * scale          ,   (y+0.5) * scale) 
-                ]
-
+def g_cell(shape, *args):
+    if shape == geometry.FLAT_HEX:
+        return g_flathex(*args)
     elif shape == geometry.SQUARE :
-        
-        return  [ 
-                    (x * scale,      y * scale), \
-                    ((x+1) * scale,  y * scale), \
-                    ((x+1) * scale,  (y+1) * scale), \
-                    (x * scale,       (y+1) * scale) 
-                ]   
-        
+        return g_square(*args)
     else:
-        raise ValueError("'shape' has to be a value from GEOMETRIES")
+        raise geometry.UnknownCellShape()
+
+def g_flathex(x, y, scale=120):
+    if x % 2 != 0:
+        y += 0.5
+    return [
+               (((x * 0.866) + 0.2886) * scale , y * scale), \
+               (((x * 0.866) + 0.866) * scale  , y * scale), \
+               (((x * 0.866) + 1.1547) * scale , (y + 0.5) * scale), \
+               (((x * 0.866) + 0.866) * scale  , (y + 1) * scale), \
+               (((x * 0.866) + 0.2886) * scale , (y + 1) * scale), \
+               ((x * 0.866) * scale          , (y + 0.5) * scale)
+            ]
+
+def g_square(x, y, scale=120):
+    return  [
+                (x * scale, y * scale), \
+                ((x + 1) * scale, y * scale), \
+                ((x + 1) * scale, (y + 1) * scale), \
+                (x * scale, (y + 1) * scale)
+            ]
+

+ 171 - 20
pypog/Piece.py → pypog/grid_objects.py

@@ -1,9 +1,161 @@
 '''
-Created on 6 dec. 2016
-    Piece could represent any object on the grid: player, creature, pawn...etc
-@author: olinox
+    Game Grid
+
+    ** By Cro-Ki l@b, 2017 **
 '''
 from pypog import geometry
+from pypog import pathfinding
+
+
+ORIGIN_HPOSITION_LEFT = 0
+ORIGIN_HPOSITION_MIDDLE = 1
+ORIGIN_HPOSITION_RIGHT = 2
+ORIGIN_VPOSITION_TOP = 10
+ORIGIN_VPOSITION_MIDDLE = 11
+ORIGIN_VPOSITION_BOTTOM = 12
+
+
+class Grid(object):
+    def __init__(self, cell_shape, width, height, roof=None):
+        self._cell_shape = None
+        self.cell_shape = cell_shape
+
+        self._width = 0
+        self.width = width
+        self._height = 0
+        self.height = height
+
+        self._roof = roof
+
+        self._cells = {}
+        self._build()
+
+    def _build(self):
+        for x in range(self.width):
+            for y in range(self.height):
+                cell = Cell(self.cell_shape, x, y)
+                self._cells[(x, y)] = cell
+
+    # properties
+    @property
+    def cell_shape(self):
+        return self._cell_shape
+
+    @cell_shape.setter
+    def cell_shape(self, cell_shape):
+        if not cell_shape in geometry.CELL_SHAPES:
+            raise ValueError("'cell_shape' has to be a value from CELL_SHAPES")
+        self._cell_shape = cell_shape
+
+    @property
+    def width(self):
+        return self._width
+
+    @width.setter
+    def width(self, width):
+        if not isinstance(width, int) or not width > 0:
+            raise ValueError("'width' has to be a strictly positive integer")
+        self._width = width
+
+    @property
+    def height(self):
+        return self._height
+
+    @height.setter
+    def height(self, height):
+        if not isinstance(height, int) or not height > 0:
+            raise ValueError("'width' has to be a strictly positive integer")
+        self._height = height
+
+    @property
+    def roof(self):
+        return self._roof
+
+    def cell(self, x, y):
+        return self._cells[(x, y)]
+
+    @property
+    def cells(self):
+        return self._cells
+
+
+    # geometric methods
+    def cases_number(self):
+        return self.height * self.width
+
+    def in_grid(self, x, y):
+        """return True if the coordinates are in the grid"""
+        return (x > 0 and x <= self._width and y > 0 and y <= self._height)
+
+    def line(self, x1, y1, x2, y2):
+        return geometry.line2d(self.cell_shape, x1, y1, x2, y2)
+
+    def line3d(self, x1, y1, z1, x2, y2, z2):
+        return geometry.line3d(self.cell_shape, x1, y1, z1, x2, y2, z2)
+
+    def zone(self, x, y, radius):
+        return geometry.zone(self.cell_shape, x, y, radius)
+
+    def triangle(self, xa, ya, xh, yh, iAngle):
+        return geometry.triangle(self.cell_shape, xa, ya, xh, yh, iAngle)
+
+    def triangle3d(self, xa, ya, za, xh, yh, zh, iAngle):
+        return geometry.triangle3d(self.cell_shape, xa, ya, za, xh, yh, zh, iAngle)
+
+    def rect(self, x1, y1, x2, y2):
+        return geometry.rectangle(x1, y1, x2, y2)
+
+    def hollow_rect(self, x1, y1, x2, y2):
+        return geometry.hollow_rectangle(x1, y1, x2, y2)
+
+
+    # pathfinding methods
+    def moving_cost(self, *args):
+        return 1
+
+    def path(self, x1, y1, x2, y2):
+        return pathfinding.path(self, (x1, y1), (x2, y2), self.moving_cost_function)
+
+
+class HexGrid(Grid):
+    def __init__(self, width, height):
+        Grid.__init__(self, geometry.FLAT_HEX, width, height)
+
+class SquareGrid(Grid):
+    def __init__(self, width, height):
+        Grid.__init__(self, geometry.SQUARE, width, height)
+
+
+
+class Cell(object):
+    def __init__(self, cell_shape, x, y, z=0):
+        self._cell_shape = cell_shape
+        self._x = x
+        self._y = y
+        self._z = z
+
+    @property
+    def x(self):
+        return self._x
+
+    @property
+    def y(self):
+        return self._y
+
+    @property
+    def z(self):
+        return self._z
+
+    @property
+    def coord(self):
+        return (self._x, self._y)
+
+    @property
+    def coord3d(self):
+        return (self._x, self._y, self._z)
+
+    def __repr__(self):
+        return "Cell {}".format(self.coord)
 
 
 class Piece(object):
@@ -14,14 +166,14 @@ class Piece(object):
         self._height = 1
         self._zR = 0
         self._cell_shape = None
-        self._shape = [ (0,0) ]
-        
+        self._shape = [ (0, 0) ]
+
     # properties
     @property
     def position(self):
         """return the (x, y) position of the Piece"""
         return self._position
-        
+
     @position.setter
     def position(self, position):
         """update the (x, y) position of the Piece
@@ -33,7 +185,7 @@ class Piece(object):
         except ValueError:
             raise ValueError("'position' have to be an (x, y) tuple, with x and y integers")
         self._position = position
-    
+
     @property
     def x(self):
         """x coordinate of the Piece"""
@@ -99,12 +251,12 @@ class Piece(object):
         """return the 'cell_shape' from GRID_GEOMETRIES for which the Piece is made for.
         Cell's shape is needed for pivot algorithm, and for graphical purposes"""
         return self._cell_shape
-    
+
     @cell_shape.setter
     def cell_shape(self, cell_shape):
         """set the 'cell_shape' of the piece (see cell_shape property for more informations)"""
         self._cell_shape = cell_shape
-    
+
     @property
     def rotation(self):
         """return the current rotation of the piece.
@@ -115,35 +267,34 @@ class Piece(object):
     def rotation(self, rotation):
         """set the 'rotation' of the piece (see rotation property for more informations)"""
         if not isinstance(rotation, int):
-            raise TypeError("'rotation' has to be an integer")        
-        self._rotation = rotation       
-    
+            raise TypeError("'rotation' has to be an integer")
+        self._rotation = rotation
+
     # methods
     def occupation(self):
         """return the list of the (x, y) coordinates currently occupied by the Piece"""
         result = [ (xr + self.x, yr + self.y) for xr, yr in self._shape ]
         if self._rotation != 0:
-            result = geometry.pivot( self._cell_shape, self.position, result, self.rotation )
+            result = geometry.pivot(self._cell_shape, self.position, result, self.rotation)
         return result
 
-    def occupation3d(self, dz = 0):
+    def occupation3d(self, dz=0):
         """return the list of the (x, y, z) coordinates currently occupied by the Piece
-        
+
         Because Pieces altitude zR is relative, the z coordinate returned is not an absolute altitude
         If you want an absolute altitude, use the 'dz' modifier to correct the result."""
         occupation = self.occupation()
         return [(x, y, z) for x, y in occupation for z in range(dz + self.zR, dz + self.zR + self.height)]
 
-    def move_to(self, x, y, zR = None):
+    def move_to(self, x, y, zR=None):
         """move the piece to (x, y) position and is requested to zR relative altitude"""
         self.position = x, y
         if zR != None:
             self.zR = zR
-    
+
     def rotate(self, i):
         """pivot the Piece i times (counterclockwise rotation, i can be negative)"""
         new_rotation = self.rotation + i
         self.rotation = new_rotation % self.cell_shape
-        
-        
-        
+
+

+ 78 - 79
pypog/pencil.py → pypog/painting.py

@@ -1,43 +1,42 @@
 '''
-Created on 5 dec. 2016
     Pencil classes allow you to get an evolving selection of coordinates, according to the type of pencil you use.
-    
+
     Start it, set size (default: 1), update it as many times you want with new positions on the grid, then
     get the list of selected coordinates, or the list of added / removed coorinates by the last update.
-    
+
     Connect those functions to your graphical grid, and let it do the job.
-    
+
     Example of use:
         On mouse left button down on a (x, y) cell: pencil.start(x, y)
         On hover event on cells: pencil.update(), then display the result on your grid
         On mouse left button up: Apply the modification on your grid, then delete your pencil object
-        
-@author: olinox
+
+    ** By Cro-Ki l@b, 2017 **
 '''
 
-from pypog import geometry, Grid
+from pypog import geometry, grid_objects
 
-class NotStartedException(Exception): 
+class NotStartedException(Exception):
     pass
 
-class AlreadyStartedException(Exception): 
+class AlreadyStartedException(Exception):
     pass
 
 
 class BasePencil(object):
     """Base class of all pencils
     This class does not  paint anything: override it!"""
-    
+
     def __init__(self, grid):
-        
+
         # do we really need the grid ref? cell_shape could be enough?
-        if not isinstance(grid, Grid.Grid):
+        if not isinstance(grid, grid_objects.Grid):
             raise TypeError("'grid' should be a Grid object (given: {})".format(grid))
         self._grid = grid
-        
+
         self._origin = None
         self._position = None
-        
+
         self._size = 1
         self._selection = set()
 
@@ -74,25 +73,25 @@ class BasePencil(object):
     @property
     def selection(self):
         """return the current list of coordinates selected by the pencil (read-only)"""
-        return list( self._selection )
-        
+        return list(self._selection)
+
     @property
     def added(self):
         """return the list of coordinates added to the last selection by the last update (read-only)
-        
-        This property exists for performances reasons: you probably don't want to update your whole graphic scene if 
+
+        This property exists for performances reasons: you probably don't want to update your whole graphic scene if
         just one or two cells where added or removed from the selection!
         """
-        return list( self._added )
+        return list(self._added)
 
     @property
     def removed(self):
         """return the list of coordinates removed from the last selection by the last update (read-only)
-                
-        This property exists for performances reasons: you probably don't want to update your whole graphic scene if 
+
+        This property exists for performances reasons: you probably don't want to update your whole graphic scene if
         just one or two cells where added or removed from the selection!
         """
-        return list( self._removed )
+        return list(self._removed)
 
     def _update(self):
         """this method update the selection, added and removed lists, according to the current size, position, origin
@@ -117,26 +116,26 @@ class BasePencil(object):
             raise NotStartedException("Pencil has to be started before any update: use 'start' method")
         self._position = (x, y)
         self._update()
-        
+
 class LinePencil(BasePencil):
     """Paint a 2d line between origin and position"""
     def __init__(self, *args):
         BasePencil.__init__(self, *args)
-        
+
     def _update(self):
         x0, y0 = self._origin
         x, y = self._position
-        
+
         # use a set because of performance (should we generalize the use of sets for coordinates lists?)
         result = set([])
-        
-        line = set( geometry.line2d(self._grid.cell_shape, x0, y0, x, y) )
-        
+
+        line = set(geometry.line2d(self._grid.cell_shape, x0, y0, x, y))
+
         # apply size with geometry.zone
-        if self._grid.size >= 1: 
+        if self._grid.size >= 1:
             for x, y in line:
-                result |= set( geometry.zone(self._grid.cell_shape, x, y, self.size - 1) )
-        
+                result |= set(geometry.zone(self._grid.cell_shape, x, y, self.size - 1))
+
         self._added = result - self._selection
         self._removed = self._selection - result
         self._selection = result
@@ -146,43 +145,43 @@ class FreePencil(BasePencil):
     """Free handed pencil"""
     def __init__(self, *args):
         BasePencil.__init__(self, *args)
-        
+
     def _update(self):
         x, y = self.position
-        zone_set = set( geometry.zone(self._grid.cell_shape, x, y, self.size) )
+        zone_set = set(geometry.zone(self._grid.cell_shape, x, y, self.size))
 
         self._added = zone_set - self._selection
         # there can't be any removed coordinates with this pencil
         self._selection = self._selection + zone_set
-        
-        
+
+
 class PaintPotPencil(BasePencil):
     """This particular pencil selects all cells of same nature from nearest to nearest
-    
+
     The 'same nature' is confirmed by passing a comparaison method to the pencil
     This method should take x1, y1, x2, y2 as args, and return a boolean: True for similar cells, False in other case
-    
+
     WARNING: this pencil result is not modified by the update method, because the result is already defined when it starts
-    
+
     WARNING 2: take care of what you put in your comparing method, it could end with a infinite loop if you are not working on a finite grid!
-    
+
     example of use:
-        
+
         # your comparison function
         my_grid = HexGrid(30,30)
-        
+
         def identical_cells_function(x1, y1, x2, y2):
             if my_grid.cell(x1, y1).color == my_grid.cell(x2, y2).color:
                 return True
             return False
-            
+
         my_pencil = PaintPotPencil(my_grid)
         my_pencil.start(3, 3, identical_cells_function)
-        
+
         print(my_pencil.selection)
-    
+
     """
-    
+
     def __init__(self, *args):
         BasePencil.__init__(self, *args)
         self._comparing_method = (lambda x: False)
@@ -200,89 +199,89 @@ class PaintPotPencil(BasePencil):
         return self._selection
 
     def _update(self):
-        x0, y0 = self._origin 
+        x0, y0 = self._origin
         current_selection = { (x0, y0) }
-        buffer = set( geometry.neighbours_of(self._grid._cell_shape, x0, y0) )
-        
+        buffer = set(geometry.neighbours(self._grid._cell_shape, x0, y0))
+
         while len(buffer) > 0:
             x, y = buffer.pop()
             if self._comparing_method_pointer(x0, y0, x, y):
-                current_selection.add( (x, y) )
-                buffer |= ( set( geometry.neighbours_of(self._grid._cell_shape, x, y) ) - current_selection)
-                
+                current_selection.add((x, y))
+                buffer |= (set(geometry.neighbours(self._grid._cell_shape, x, y)) - current_selection)
+
         self._selection = current_selection
-        
-        
+
+
 class RectanglePencil(BasePencil):
-    """ RectanglePencil draw a plain rectangle with origin being the 
+    """ RectanglePencil draw a plain rectangle with origin being the
     top left corner, and position the bottom right corner"""
     def __init__(self, *args):
         BasePencil.__init__(self, *args)
-        
+
     def _update(self):
         x1, y1 = self._origin
         x2, y2 = self._position
-        
-        new_selection = set( geometry.rect(x1, y1, x2, y2) )
-        
+
+        new_selection = set(geometry.rectangle(x1, y1, x2, y2))
+
         self._added = new_selection - self._selection
         self._removed = self._selection - new_selection
         self._selection = new_selection
-        
+
 class HollowRectanglePencil(BasePencil):
-    """ HollowRectanglePencil draw an hollow rectangle with origin being the 
+    """ HollowRectanglePencil draw an hollow rectangle with origin being the
     top left corner, and position the bottom right corner"""
     def __init__(self, *args):
         BasePencil.__init__(self, *args)
-        
+
     def _update(self):
         x1, y1 = self._origin
         x2, y2 = self._position
-        
-        new_selection = set( geometry.hollow_rect(x1, y1, x2, y2) )
-        
+
+        new_selection = set(geometry.hollow_rectangle(x1, y1, x2, y2))
+
         self._added = new_selection - self._selection
         self._removed = self._selection - new_selection
         self._selection = new_selection
 
 class BoundaryPencil(BasePencil):
-    """ BoundaryPencil is a particular pencil which select all the cells 
+    """ BoundaryPencil is a particular pencil which select all the cells
     on the left of a straight line which could be oriented from 0, 45, 90, 135, 180, 225, 270, 315 degrees
     Orientation  of the boudary depends on position and origin."""
-    
+
     def __init__(self, *args):
         BasePencil.__init__(self, *args)
 
     def _update(self):
-        
+
         if self._position == self._origin:
             self._removed = self._selection.copy()
             self._selection = set()
             self._added = set()
             return
-        
+
         x0, y0 = self._origin
         x, y = self._position
         dx, dy = x - x0, y - y0
 
-        if dx == 0:   # vertical boudary
+        if dx == 0:  # vertical boudary
             selection = {(x, y) for x, y in self._grid.cells.keys() if (x - x0) * dy >= 0}
 
         elif dy == 0:  # horizontal boundary
-            selection = {(x, y) for x, y in self._grid.cells.keys() if (y - y0) * (-dx) >= 0} 
-            
+            selection = {(x, y) for x, y in self._grid.cells.keys() if (y - y0) * (-dx) >= 0}
+
         elif dx > 0 and dy < 0:  # normal vector to the top left
-            selection = {(x , y) for x, y in self._grid.cells.keys() if (x - x0) + (y - y0) <= 0} 
-            
-        elif dx > 0 and dy > 0: # normal vector to the top right
+            selection = {(x , y) for x, y in self._grid.cells.keys() if (x - x0) + (y - y0) <= 0}
+
+        elif dx > 0 and dy > 0:  # normal vector to the top right
             selection = {(x , y) for x, y in self._grid.cells.keys() if (x - x0) - (y - y0) >= 0}
-            
+
         elif dx < 0 and dy < 0:  # normal vector to bottom left
-            selection = {(x , y) for x, y in self._grid.cells.keys() if - (x - x0) + (y - y0) >= 0}
-            
+            selection = {(x , y) for x, y in self._grid.cells.keys() if -(x - x0) + (y - y0) >= 0}
+
         elif dx < 0 and dy > 0:  # normal vector to bottom right
-            selection = {(x , y) for x, y in self._grid.cells.keys() if - (x - x0) - (y - y0) <= 0}
-        
+            selection = {(x , y) for x, y in self._grid.cells.keys() if -(x - x0) - (y - y0) <= 0}
+
         self._added = selection - self._selection
         self._removed = self._selection - selection
         self._selection = selection

+ 33 - 34
pypog/pathfinder.py → pypog/pathfinding.py

@@ -1,35 +1,34 @@
 '''
-Created on 17 déc. 2015
    Implement the A* algorithm
-   
+
    Use the path function like that:
-   
+
        path(my_grid, (xs, ys), (xt, yt), my_moving_cost_function)
        >> [(xs, ys), (x1, y1), (x2, y2), ...(xt, yt)]
-       
+
        where:
         - my_grid is a Grid, HexGrid, or SquareGrid object
         - (xs, ys) is the starting cell
         - (xt, yt) is the targeted cell
         - my_moving_cost_function is a pointer to your custom function. This function should be like:
-        
+
         def my_moving_cost_function((x0, y0), (x1, y1)):
             ...
             return cost
-            
-        this function should return an INTEGER which represent the cost of a move from (x0, y0) to (x1, y1), 
+
+        this function should return an INTEGER which represent the cost of a move from (x0, y0) to (x1, y1),
         where (x0, y0) and (x1, y1) are adjacent cells
-        
+
         If cost is negative, move is impossible.
-        If move is strictly positive, it represents the difficulty to move from 0 to 1: 
-        the returned path will be the easiest from (xs, ys) to (xt, yt) 
-   
+        If move is strictly positive, it represents the difficulty to move from 0 to 1:
+        the returned path will be the easiest from (xs, ys) to (xt, yt)
+
     3D paths:
-        The path method takes account of the differents altitudes of the cells, but it is not designed to 
-        work for a flying mover. 
+        The path method takes account of the differents altitudes of the cells, but it is not designed to
+        work for a flying mover.
         More clearly: the path will be on the ground: walking, climbing, but no flying for instance.
 
-@author: olivier.massot
+    ** By Cro-Ki l@b, 2017 **
 '''
 from pypog import geometry
 
@@ -43,7 +42,7 @@ def distance(coord1, coord2):
 
 def square_distance(coord1, coord2):
     """distance between 1 and 2 (quicker than distance)"""
-    return (coord1[0] - coord2[0])**2 + (coord1[1] - coord2[1])**2
+    return (coord1[0] - coord2[0]) ** 2 + (coord1[1] - coord2[1]) ** 2
 
 class Path(object):
     def __init__(self):
@@ -54,44 +53,44 @@ class Path(object):
 
 class Node():
     def __init__(self, coord):
-        self.parent = None    # coords of the previous node
+        self.parent = None  # coords of the previous node
         self.coord = coord
         self.k_dep = 1
         self.g_cost = 0
         self.h_cost = 0
         self.cost = 0
- 
+
     def create(self, parent, target, k_dep):
         self.parent = parent
         self.k_dep = k_dep
         self.h_cost = self.distance(self.coord, target)
         self.g_cost = self.parent.g_cost + self.k_dep
-        self.cout =  self.g_cost + self.h_cost
-    
+        self.cout = self.g_cost + self.h_cost
+
     def parent(self):
         return self.parent
-    
+
     def distance(self, coord1, coord2):
         """distance (en cases) entre deux coordonnees"""
         x1, y1 = coord1
         x2, y2 = coord2
         return geometry.distance_off(x1, y1, x2, y2)
-    
+
 def _default_moving_cost_function(from_coord, to_coord):
     return 1
 
-def path(grid, origin, target, moving_cost_function = None):
+def path(grid, origin, target, moving_cost_function=None):
     """return the shorter path from origin to target on the Grid object
     the path is estimated following:
     - geometry of the grid
     - altitudes of the cells
-    - cost of the move returned by the 'moving_cost_function' 
-    
+    - cost of the move returned by the 'moving_cost_function'
+
     origin and target should be Cell objects
     """
     if moving_cost_function == None:
         moving_cost_function = _default_moving_cost_function
-    
+
     nodes = {}  # coord: node
 
     nO = Node(origin)
@@ -101,20 +100,20 @@ def path(grid, origin, target, moving_cost_function = None):
 #     kept = [nO]
     path = []
     position = nO
-    
+
     while position.coord != target:
 
         # we maybe could avoid the re-computing by storing the neighbours coordinates?
         neighbours = grid.cell(position.coord).neighbours
-        
+
         for coord in [coord for coord in neighbours if not coord in nodes.keys()]:
 
                 cost = moving_cost_function(position.coord, coord)
                 if cost < 0:
                     continue
-                
+
                 node = Node(coord)
-                
+
                 node.create(position, target, cost)
 
                 try:
@@ -123,21 +122,21 @@ def path(grid, origin, target, moving_cost_function = None):
                         continue
                 except KeyError:
                     pass
-                
+
                 nodes[coord] = node
 
         if len(nodes) == 0:
             print("No path found")
             return []
-        
+
         best = min(nodes.values(), key=lambda x: x.cost)
         del nodes[best.coord]
         position = best
-    
+
     else:
         # build the result
         while position.coord != origin:
             path.insert(0, (position.coord, position.k_dep))
             position = position.parent
-    
-    return path   
+
+    return path

+ 0 - 0
tests/geometry/__init__.py


+ 0 - 93
tests/geometry/test_line.py

@@ -1,93 +0,0 @@
-'''
-Created on 20 nov. 2016
-
-@author: olinox
-'''
-import unittest
-
-from pypog import geometry
-
-
-class Test(unittest.TestCase):
-    """test line algorithms"""
-
-    def test_line_errors(self):
-        self.assertRaises( TypeError, geometry.line2d, geometry.HEX, "a", 1, 1, 1)
-        self.assertRaises( TypeError, geometry.line2d, geometry.HEX, 1, "a", 1, 1)
-        self.assertRaises( TypeError, geometry.line2d, geometry.HEX, 1, 1, "a", 1)
-        self.assertRaises( TypeError, geometry.line2d, geometry.HEX, 1, 1, 1, "a")
-        self.assertRaises( ValueError, geometry.line2d, 0, 1, 1, 1, 1)
-
-        self.assertRaises( TypeError, geometry.line3d, geometry.HEX, 1, 1, "a", 1, 1, 1)
-        self.assertRaises( TypeError, geometry.line3d, geometry.HEX, 1, 1, 1, 1, 1, "a")
-        
-    def test_line(self):
-        """ 2d line on square or hexagonal grid """
-        cell_shape = geometry.HEX
-        
-        attended = {
-                    geometry.HEX:    {
-                                      (1,1,1,1): [(1,1)], 
-                                      (0,0,1,1): [(0,0), (0,1), (1,1)], 
-                                      (1,1,0,0): [(1,1), (0,1), (0,0)], 
-                                      (0,0,7,3): [(0,0), (1,0), (2,1), (3,1), (4,2), (5,2), (6,3), (7,3)], 
-                                      (7,3,0,0): [(7,3), (6,3), (5,2), (4,2), (3,1), (2,1), (1,0), (0,0)], 
-                                      (4,3,0,3): [(4,3), (3,2), (2,3), (1,2), (0,3)], 
-                                      (0,3,4,3): [(0,3), (1,2), (2,3), (3,2), (4,3)], 
-                                      (3,0,3,3): [(3,0), (3,1), (3,2), (3,3)], 
-                                      (3,3,3,0): [(3,3), (3,2), (3,1), (3,0)]
-                                     }, 
-                    
-                    geometry.SQUARE: {
-                                      (1,1,1,1): [(1,1)], 
-                                      (0,0,0,1): [(0,0), (0,1)], 
-                                      (0,1,0,0): [(0,1), (0,0)], 
-                                      (0,0,1,1): [(0,0), (1,1)], 
-                                      (1,1,0,0): [(1,1), (0,0)], 
-                                      (0,0,7,3): [(0,0), (1,0), (2,1), (3,1), (4,2), (5,2), (6,3), (7,3)], 
-                                      (7,3,0,0): [(7,3), (6,3), (5,2), (4,2), (3,1), (2,1), (1,0), (0,0)], 
-                                      (4,3,0,3): [(4,3), (3,3), (2,3), (1,3), (0,3)], 
-                                      (0,3,4,3): [(0,3), (1,3), (2,3), (3,3), (4,3)], 
-                                      (3,0,3,3): [(3,0), (3,1), (3,2), (3,3)],                      
-                                      (3,3,3,0): [(3,3), (3,2), (3,1), (3,0)]                    
-                                     }
-                   }
-        
-        for cell_shape, tests in attended.items():
-            for args, result in tests.items():
-                line = geometry.line2d(cell_shape, *args)
-                self.assertEqual(line, result)
-        
-    
-    def test_line_3d(self):
-        """ 3d line on hexagonal and square grid """
-        cell_shape = geometry.HEX
-        
-        attended = {
-                    geometry.HEX:    {
-                                      (1,1,1,1,1,1) : [(1,1,1)], 
-                                      (1,1,0,1,1,1) : [(1,1,0), (1,1,1)], 
-                                      (0,0,0,1,1,1) : [(0,0,0), (0,1,0), (1,1,1)], 
-                                      (0,0,0,7,3,7) : [(0,0,0), (1,0,1), (2,1,2), (3,1,3), (4,2,4), (5,2,5), (6,3,6), (7,3,7)], 
-                                      (4,3,10,0,3,3): [(4,3,10), (3,2,9), (3,2,8), (2,3,7), (2,3,6), (1,2,5), (1,2,4), (0,3,3)], 
-                                      (3,0,0,3,3,0) : [(3,0,0), (3,1,0), (3,2,0), (3,3,0)]
-                                     }, 
-                    
-                    geometry.SQUARE: {
-                                      (1,1,1,1,1,1) : [(1,1,1)], 
-                                      (1,1,0,1,1,1) : [(1,1,0), (1,1,1)], 
-                                      (0,0,0,1,1,1) : [(0,0,0), (1,1,1)], 
-                                      (0,0,0,7,3,7) : [(0,0,0), (1,0,1), (2,1,2), (3,1,3), (4,2,4), (5,2,5), (6,3,6), (7,3,7)], 
-                                      (4,3,10,0,3,3): [(4,3,10), (3,3,9), (3,3,8), (2,3,7), (2,3,6), (1,3,5), (1,3,4), (0,3,3)], 
-                                      (3,0,0,3,3,0) : [(3,0,0), (3,1,0), (3,2,0), (3,3,0)]
-                                     }
-                   }
-        
-        for cell_shape, tests in attended.items():
-            for args, result in tests.items():
-                line = geometry.line3d(cell_shape, *args)
-                self.assertEqual(line, result)
-        
-
-if __name__ == "__main__":
-    unittest.main()

+ 0 - 29
tests/geometry/test_neighbours.py

@@ -1,29 +0,0 @@
-'''
-Created on 25 nov. 2016
-
-@author: olinox
-'''
-import unittest
-
-from pypog import geometry
-
-
-class Test(unittest.TestCase):
-
-
-    def test_neighbours_of(self):
-        for coord in ( (0,0), (-10,-10), (10,10) ):
-            x, y = coord
-            self.assertEqual( geometry.neighbours_of(geometry.HEX, x, y), geometry.hex_neighbours_of(x, y) )
-            self.assertEqual( geometry.neighbours_of(geometry.SQUARE, x, y), geometry.squ_neighbours_of(x, y) )
-
-    def test_hex_neighbours_of(self):
-        self.assertCountEqual( geometry.hex_neighbours_of(3,3), [(3,2), (4,3), (4,4), (3,4), (2,4), (2,3)] )
-        self.assertCountEqual( geometry.hex_neighbours_of(4,4), [(4,3), (5,3), (5,4), (4,5), (3,4), (3,3)] )
-
-    def test_squ_neighbours_of(self):
-        self.assertCountEqual( geometry.squ_neighbours_of(3,3), [(2,3), (2,2), (3,2), (4,2), (4,3), (4,4), (3,4), (2,4)] )
-        
-
-if __name__ == "__main__":
-    unittest.main()

+ 0 - 59
tests/geometry/test_pivot.py

@@ -1,59 +0,0 @@
-'''
-Created on 6 dec. 2016
-
-@author: olinox
-'''
-import unittest
-from pypog import geometry
-
-class Test(unittest.TestCase):
-
-    def test_pivot_errors(self):
-        # invalid cell shape
-        self.assertRaises(ValueError, geometry.pivot, 0, (0,0), [(0,0)], 1)
-        
-        self.assertRaises(TypeError, geometry.pivot, 0,  "a"    , [(0,0)], 1)
-        self.assertRaises(ValueError, geometry.pivot, 0, ("a",0), [(0,0)], 1)
-        
-        self.assertRaises(TypeError, geometry.pivot, 0, (0,0), 0, 1)
-        self.assertRaises(ValueError, geometry.pivot, 0, (0,0), ["a", (0,0)], 1)
-        self.assertRaises(ValueError, geometry.pivot, 0, (0,0), [("a",0), (0,0)], 1)
-
-        self.assertRaises(TypeError, geometry.pivot, 0, (0,0), 1, "a")
-
-    def test_hex_pivot(self):
-        """ pivot on hexagonal grid """
-        
-        attended = [
-                    [(5, 5), (4, 5), (6, 6)], 
-                    [(5, 6), (4, 7), (6, 6)],
-                    [(6, 7), (6, 8), (6, 6)],
-                    [(7, 6), (8, 7), (6, 6)],
-                    [(7, 5), (8, 5), (6, 6)],
-                    [(6, 5), (6, 4), (6, 6)],
-                    [(5, 5), (4, 5), (6, 6)]
-                   ]
-        
-        
-        for i in range( len(attended) ):
-            self.assertCountEqual(geometry.pivot( geometry.HEX, (6,6), [(6,6)], i), [(6,6)])
-            result = geometry.pivot(geometry.HEX, (6,6), [(5,5), (4,5), (6,6)], i)
-            self.assertCountEqual(result, attended[i])
-
-    def test_squ_pivot(self):
-        """ pivot on square grid """
-        attended = [
-                    [(6, 6), (6, 5), (5, 5), (5, 6)],
-                    [(6, 6), (5, 6), (5, 7), (6, 7)],
-                    [(6, 6), (6, 7), (7, 7), (7, 6)],
-                    [(6, 6), (7, 6), (7, 5), (6, 5)],
-                    [(6, 6), (6, 5), (5, 5), (5, 6)]
-                   ]
-
-        for i in range( len(attended) ):
-            self.assertCountEqual(geometry.pivot( geometry.SQUARE, (6,6), [(6,6)], i), [(6,6)])
-            result = geometry.pivot(geometry.SQUARE, (6,6), [(6,6), (6,5), (5,5), (5,6)], i)
-            self.assertCountEqual(result, attended[i])
-     
-if __name__ == "__main__":
-    unittest.main()

+ 0 - 42
tests/geometry/test_rect.py

@@ -1,42 +0,0 @@
-'''
-Created on 11 dec. 2016
-
-@author: olinox
-'''
-import unittest
-
-from pypog import geometry
-
-
-class Test(unittest.TestCase):
-
-    def test_rect_errors(self):
-        for method in (geometry.rect, geometry.hollow_rect):
-            self.assertRaises( TypeError, method, "a", 1, 1, 1)
-            self.assertRaises( TypeError, method, 1, "a", 1, 1)
-            self.assertRaises( TypeError, method, 1, 1, "a", 1)
-            self.assertRaises( TypeError, method, 1, 1, 1, "a")
-
-    def test_rect(self):
-        
-        self.assertEquals(geometry.rect(0,0,0,0), [(0,0)])
-        self.assertCountEqual(geometry.rect(0,0,1,1), [(0,0), (0,1), (1,1), (1,0)])
-        self.assertCountEqual(geometry.rect(1,1,0,0), [(0,0), (0,1), (1,1), (1,0)])
-        self.assertCountEqual(geometry.rect(4,3,7,5), [(4, 3), (4, 4), (4, 5), (5, 5), (6, 5), (7, 5), (7, 4), (7, 3), (6, 3), (5, 3), (6, 4), (5, 4)])
-        self.assertCountEqual(geometry.rect(3,3,9,9), [(3, 3), (9, 9), (9, 8), (9, 7), (9, 5), (9, 6), (9, 4), (9, 3), (8, 4), (7, 3), (6, 4), (4, 4), 
-                                                       (3, 4), (3, 5), (3, 6), (3, 7), (3, 8), (3, 9), (4, 9), (5, 9), (6, 9), (7, 9), (8, 9), (4, 5), 
-                                                       (5, 4), (6, 5), (7, 4), (8, 5), (4, 6), (5, 5), (6, 6), (7, 5), (8, 6), (4, 7), (5, 6), (6, 7), 
-                                                       (7, 6), (8, 7), (4, 8), (5, 7), (6, 8), (7, 7), (8, 8), (7, 8), (5, 8), (8, 3), (6, 3), (4, 3), 
-                                                       (5, 3)])
-
-        self.assertEquals(geometry.hollow_rect(0,0,0,0), [(0,0)])
-        self.assertCountEqual(geometry.hollow_rect(0,0,1,1), [(0,0), (0,1), (1,1), (1,0)])
-        self.assertCountEqual(geometry.hollow_rect(1,1,0,0), [(0,0), (0,1), (1,1), (1,0)])
-        self.assertCountEqual(geometry.hollow_rect(4,3,7,5), [(4, 3), (4, 4), (4, 5), (5, 5), (6, 5), (7, 5), (7, 4), (7, 3), (6, 3), (5, 3)])
-        self.assertCountEqual(geometry.hollow_rect(3,3,9,9), [(3, 3), (9, 9), (9, 8), (9, 7), (9, 5), (9, 6), (9, 4), (9, 3), (7, 3), (3, 4), 
-                                                              (3, 5), (3, 6), (3, 7), (3, 8), (3, 9), (4, 9), (5, 9), (6, 9), (7, 9), (8, 9), 
-                                                              (8, 3), (6, 3), (4, 3), (5, 3)])
-
-
-if __name__ == "__main__":
-    unittest.main()

+ 0 - 157
tests/geometry/test_triangle.py

@@ -1,157 +0,0 @@
-'''
-Created on 22 nov. 2016
-
-@author: olinox
-'''
-import unittest
-
-from pypog import geometry
-
-
-class Test(unittest.TestCase):
-    """test triangle algorithms"""
-
-    def test_triangle_errors(self):
-        
-        for cell_shape in (geometry.HEX, geometry.SQUARE):
-            self.assertRaises( TypeError, geometry.triangle, cell_shape, "a", 1, 1, 1, 1)
-            self.assertRaises( TypeError, geometry.triangle, cell_shape, 1, "a", 1, 1, 1)
-            self.assertRaises( TypeError, geometry.triangle, cell_shape, 1, 1, "a", 1, 1)
-            self.assertRaises( TypeError, geometry.triangle, cell_shape, 1, 1, 1, "a", 1)
-            self.assertRaises( ValueError, geometry.triangle, cell_shape, 1, 1, 1, 1, -1)
-    
-            self.assertRaises( TypeError, geometry.triangle3d, cell_shape, "a", 1, 1, 1, 1, 1, 1)
-            self.assertRaises( TypeError, geometry.triangle3d, cell_shape, 1, "a", 1, 1, 1, 1, 1)
-            self.assertRaises( TypeError, geometry.triangle3d, cell_shape, 1, 1, "a", 1, 1, 1, 1)
-            self.assertRaises( TypeError, geometry.triangle3d, cell_shape, 1, 1, 1, "a", 1, 1, 1)
-            self.assertRaises( TypeError, geometry.triangle3d, cell_shape, 1, 1, 1, 1, "a", 1, 1)
-            self.assertRaises( TypeError, geometry.triangle3d, cell_shape, 1, 1, 1, 1, 1, "a", 1)
-            self.assertRaises( ValueError, geometry.triangle3d, cell_shape, 1, 1, 1, 1, 1, 1, -1)
-            
-        self.assertRaises( ValueError, geometry.triangle, 0, 1, 1, 1, 1, 1)
-        self.assertRaises( ValueError, geometry.triangle3d, 0, 1, 1, 1, 1, 1, 1, 1)
-
-    def test_sq_triangle(self):
-        """test triangle algorithms on square grid"""
-        cell_shape = geometry.SQUARE
-        
-        for i in geometry.ANGLES:
-            self.assertCountEqual(geometry.triangle(cell_shape, 0, 0, 0, 0, i), [(0,0)])
-
-        # TODO: check and validate
-#         # left to right
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
-#         
-#         # top to bottom
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
-#         
-#         # right to left
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
-#         
-#         # bottom to top
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
-#         
-#         # top left to bottom right
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
-#         
-#         # bottom right to top left
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
-#         
-#         # top right to bottom left
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
-#         
-#         # bottom right to top left
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
-
-        
-
-    def test_hex_triangle(self):
-        """test triangle algorithms on hexagonal grid"""
-        cell_shape = geometry.HEX
-        for i in geometry.ANGLES:
-            self.assertCountEqual(geometry.triangle(cell_shape, 0, 0, 0, 0, i), [(0,0)])
-
-        # left to right
-        self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [(3, 3), (3, 4), (3, 3), (4, 5), (4, 4), (4, 3), (4, 2), (4, 1), (4, 1), (3, 1), (3, 2), (2, 3)])
-        self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [(3, 3), (4, 4), (4, 3), (4, 2), (4, 2), (3, 2), (2, 3)])
-        self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [(3, 3), (4, 4), (4, 3), (4, 2), (4, 2), (3, 2), (2, 3)])
-        
-        # TODO: check and validate
-        
-#         # top to bottom
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
-#         
-        # right to left
-        self.assertCountEqual(geometry.triangle(cell_shape, 4, 3, 2, 3, 1), [(3, 2), (3, 1), (3, 2), (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (2, 5), (3, 4), (3, 3), (4, 3)])
-        self.assertCountEqual(geometry.triangle(cell_shape, 4, 3, 2, 3, 2), [(3, 2), (2, 2), (2, 3), (2, 4), (2, 4), (3, 3), (4, 3)])
-        self.assertCountEqual(geometry.triangle(cell_shape, 4, 3, 2, 3, 3), [(3, 2), (2, 2), (2, 3), (2, 4), (2, 4), (3, 3), (4, 3)])
-         
-#         # bottom to top
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
-#         
-#         # top left to bottom right
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
-#         
-#         # bottom right to top left
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
-#         
-#         # top right to bottom left
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
-#         
-#         # bottom right to top left
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
-#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
-
-        
-    
-    def test_sq_triangle_3d(self):
-        """test triangle3d algorithms on square grid"""
-        cell_shape = geometry.SQUARE
-        #TODO: complete
-     
-    def test_hex_triangle_3d(self):
-        """test triangle3d algorithms on hexagonal grid"""
-        cell_shape = geometry.HEX
-        #TODO: complete
-
-    def test_errors(self):
-        
-        for cell_shape in (geometry.HEX, geometry.SQUARE):
-            self.assertRaises(ValueError, geometry.triangle, cell_shape, 0, 0, 0, 0, 0)
-            self.assertRaises(TypeError, geometry.triangle, cell_shape, "a", 0, 0, 0, 1)
-            self.assertRaises(TypeError, geometry.triangle, cell_shape, 0, "a", 0, 0, 1)
-            self.assertRaises(TypeError, geometry.triangle, cell_shape, 0, 0, "a", 0, 1)
-            self.assertRaises(TypeError, geometry.triangle, cell_shape, 0, 0, 0, "a", 1)
-            self.assertRaises(ValueError, geometry.triangle, cell_shape, 0, 0, 0, 0, "a")
-    
-
-if __name__ == "__main__":
-    #import sys;sys.argv = ['', 'Test.test_sq_triangle']
-    unittest.main()

+ 0 - 44
tests/geometry/test_zone.py

@@ -1,44 +0,0 @@
-'''
-Created on 20 nov. 2016
-
-@author: olinox
-'''
-import unittest
-
-from pypog import geometry
-
-
-
-class Test(unittest.TestCase):
-    """ test the zone algorithm """
-
-
-    def test_hex_zone(self):
-        """ test the zone algo for hexagonal grid """
-        cell_shape = geometry.HEX
-        self.assertCountEqual( geometry.zone( cell_shape, 3, 3, 0 ), [(3,3)])
-        self.assertCountEqual( geometry.zone( cell_shape, 3, 3, 1 ), [(3, 2), (2, 3), (3, 3), (4, 3), (4, 4), (3, 4), (2, 4)])
-        self.assertCountEqual( geometry.zone( cell_shape, 3, 3, 2 ), [(3, 2), (1, 3), (5, 4), (4, 5), (1, 4), (2, 3), (4, 2), \
-                                                            (2, 5), (5, 3), (1, 2), (3, 5), (3, 3), (4, 4), (3, 1), \
-                                                            (4, 3), (2, 2), (3, 4), (2, 4), (5, 2)] )
-
-    def test_squ_zone(self):
-        """ test the zone algo for square grid """
-        cell_shape = geometry.SQUARE
-        self.assertCountEqual( geometry.zone( cell_shape, 3, 3, 0 ), [(3,3)])
-        self.assertCountEqual( geometry.zone( cell_shape, 3, 3, 1 ), [(3, 2), (3, 3), (4, 4), (2, 3), (4, 3), (2, 2), (4, 2), (3, 4), (2, 4)])
-        self.assertCountEqual( geometry.zone( cell_shape, 3, 3, 2 ), [(2, 4), (3, 2), (5, 4), (1, 3), (4, 5), (2, 1), (1, 4), (2, 3), (4, 2), \
-                                                                    (5, 1), (2, 5), (3, 5), (5, 3), (1, 2), (3, 3), (5, 5), (4, 4), (3, 1), \
-                                                                    (1, 5), (4, 3), (2, 2), (4, 1), (5, 2), (3, 4), (1, 1)])
-
-    def test_errors(self):
-        """test the errors due to bad parameters"""
-        self.assertRaises( TypeError, geometry.zone, 5, 0, 0, "a")
-        self.assertRaises( TypeError, geometry.zone, 5, "a", 0, 1)
-        self.assertRaises( TypeError, geometry.zone, 5, 0, "a", 1)
-        self.assertRaises( ValueError, geometry.zone, 5, 0, 0, -1)
-        self.assertRaises( ValueError, geometry.zone, -1, 0, 0, 1)
-        self.assertRaises( ValueError, geometry.zone, "a", 0, 0, 1)
-
-if __name__ == "__main__":
-    unittest.main()

+ 335 - 0
tests/test_geometry.py

@@ -0,0 +1,335 @@
+'''
+
+    ** By Cro-Ki l@b, 2017 **
+'''
+import unittest
+
+from pypog import geometry
+
+
+class Test(unittest.TestCase):
+
+    def test_line_errors(self):
+        self.assertRaises(TypeError, geometry.line2d, geometry.FLAT_HEX, "a", 1, 1, 1)
+        self.assertRaises(TypeError, geometry.line2d, geometry.FLAT_HEX, 1, "a", 1, 1)
+        self.assertRaises(TypeError, geometry.line2d, geometry.FLAT_HEX, 1, 1, "a", 1)
+        self.assertRaises(TypeError, geometry.line2d, geometry.FLAT_HEX, 1, 1, 1, "a")
+        self.assertRaises(ValueError, geometry.line2d, 0, 1, 1, 1, 1)
+
+        self.assertRaises(TypeError, geometry.line3d, geometry.FLAT_HEX, 1, 1, "a", 1, 1, 1)
+        self.assertRaises(TypeError, geometry.line3d, geometry.FLAT_HEX, 1, 1, 1, 1, 1, "a")
+
+    def test_line2d(self):
+        """ 2d line on square or hexagonal grid """
+        cell_shape = geometry.FLAT_HEX
+
+        attended = {
+                    geometry.FLAT_HEX:    {
+                                      (1, 1, 1, 1): [(1, 1)],
+                                      (0, 0, 1, 1): [(0, 0), (0, 1), (1, 1)],
+                                      (1, 1, 0, 0): [(1, 1), (0, 1), (0, 0)],
+                                      (0, 0, 7, 3): [(0, 0), (1, 0), (2, 1), (3, 1), (4, 2), (5, 2), (6, 3), (7, 3)],
+                                      (7, 3, 0, 0): [(7, 3), (6, 3), (5, 2), (4, 2), (3, 1), (2, 1), (1, 0), (0, 0)],
+                                      (4, 3, 0, 3): [(4, 3), (3, 2), (2, 3), (1, 2), (0, 3)],
+                                      (0, 3, 4, 3): [(0, 3), (1, 2), (2, 3), (3, 2), (4, 3)],
+                                      (3, 0, 3, 3): [(3, 0), (3, 1), (3, 2), (3, 3)],
+                                      (3, 3, 3, 0): [(3, 3), (3, 2), (3, 1), (3, 0)]
+                                     },
+
+                    geometry.SQUARE: {
+                                      (1, 1, 1, 1): [(1, 1)],
+                                      (0, 0, 0, 1): [(0, 0), (0, 1)],
+                                      (0, 1, 0, 0): [(0, 1), (0, 0)],
+                                      (0, 0, 1, 1): [(0, 0), (1, 1)],
+                                      (1, 1, 0, 0): [(1, 1), (0, 0)],
+                                      (0, 0, 7, 3): [(0, 0), (1, 0), (2, 1), (3, 1), (4, 2), (5, 2), (6, 3), (7, 3)],
+                                      (7, 3, 0, 0): [(7, 3), (6, 3), (5, 2), (4, 2), (3, 1), (2, 1), (1, 0), (0, 0)],
+                                      (4, 3, 0, 3): [(4, 3), (3, 3), (2, 3), (1, 3), (0, 3)],
+                                      (0, 3, 4, 3): [(0, 3), (1, 3), (2, 3), (3, 3), (4, 3)],
+                                      (3, 0, 3, 3): [(3, 0), (3, 1), (3, 2), (3, 3)],
+                                      (3, 3, 3, 0): [(3, 3), (3, 2), (3, 1), (3, 0)]
+                                     }
+                   }
+
+        for cell_shape, tests in attended.items():
+            for args, result in tests.items():
+                line = geometry.line2d(cell_shape, *args)
+                self.assertEqual(line, result)
+
+    def test_line3d(self):
+        """ 3d line on hexagonal and square grid """
+        cell_shape = geometry.FLAT_HEX
+
+        attended = {
+                    geometry.FLAT_HEX:    {
+                                      (1, 1, 1, 1, 1, 1) : [(1, 1, 1)],
+                                      (1, 1, 0, 1, 1, 1) : [(1, 1, 0), (1, 1, 1)],
+                                      (0, 0, 0, 1, 1, 1) : [(0, 0, 0), (0, 1, 0), (1, 1, 1)],
+                                      (0, 0, 0, 7, 3, 7) : [(0, 0, 0), (1, 0, 1), (2, 1, 2), (3, 1, 3), (4, 2, 4), (5, 2, 5), (6, 3, 6), (7, 3, 7)],
+                                      (4, 3, 10, 0, 3, 3): [(4, 3, 10), (3, 2, 9), (3, 2, 8), (2, 3, 7), (2, 3, 6), (1, 2, 5), (1, 2, 4), (0, 3, 3)],
+                                      (3, 0, 0, 3, 3, 0) : [(3, 0, 0), (3, 1, 0), (3, 2, 0), (3, 3, 0)]
+                                     },
+
+                    geometry.SQUARE: {
+                                      (1, 1, 1, 1, 1, 1) : [(1, 1, 1)],
+                                      (1, 1, 0, 1, 1, 1) : [(1, 1, 0), (1, 1, 1)],
+                                      (0, 0, 0, 1, 1, 1) : [(0, 0, 0), (1, 1, 1)],
+                                      (0, 0, 0, 7, 3, 7) : [(0, 0, 0), (1, 0, 1), (2, 1, 2), (3, 1, 3), (4, 2, 4), (5, 2, 5), (6, 3, 6), (7, 3, 7)],
+                                      (4, 3, 10, 0, 3, 3): [(4, 3, 10), (3, 3, 9), (3, 3, 8), (2, 3, 7), (2, 3, 6), (1, 3, 5), (1, 3, 4), (0, 3, 3)],
+                                      (3, 0, 0, 3, 3, 0) : [(3, 0, 0), (3, 1, 0), (3, 2, 0), (3, 3, 0)]
+                                     }
+                   }
+
+        for cell_shape, tests in attended.items():
+            for args, result in tests.items():
+                line = geometry.line3d(cell_shape, *args)
+                self.assertEqual(line, result)
+
+    def test_neighbours(self):
+        for coord in ((0, 0), (-10, -10), (10, 10)):
+            x, y = coord
+            self.assertEqual(geometry.neighbours(geometry.FLAT_HEX, x, y), geometry.fhex_neighbours(x, y))
+            self.assertEqual(geometry.neighbours(geometry.SQUARE, x, y), geometry.squ_neighbours(x, y))
+
+    def test_fhex_neighbours(self):
+        self.assertCountEqual(geometry.fhex_neighbours(3, 3), [(3, 2), (4, 3), (4, 4), (3, 4), (2, 4), (2, 3)])
+        self.assertCountEqual(geometry.fhex_neighbours(4, 4), [(4, 3), (5, 3), (5, 4), (4, 5), (3, 4), (3, 3)])
+
+    def test_squ_neighbours(self):
+        self.assertCountEqual(geometry.squ_neighbours(3, 3), [(2, 3), (2, 2), (3, 2), (4, 2), (4, 3), (4, 4), (3, 4), (2, 4)])
+
+    def test_pivot_errors(self):
+
+        # invalid cell shape
+        self.assertRaises(ValueError, geometry.pivot, 0, (0, 0), [(0, 0)], 1)
+
+        self.assertRaises(TypeError, geometry.pivot, 0, "a"    , [(0, 0)], 1)
+        self.assertRaises(ValueError, geometry.pivot, 0, ("a", 0), [(0, 0)], 1)
+
+        self.assertRaises(TypeError, geometry.pivot, 0, (0, 0), 0, 1)
+        self.assertRaises(ValueError, geometry.pivot, 0, (0, 0), ["a", (0, 0)], 1)
+        self.assertRaises(ValueError, geometry.pivot, 0, (0, 0), [("a", 0), (0, 0)], 1)
+
+        self.assertRaises(TypeError, geometry.pivot, 0, (0, 0), 1, "a")
+
+    def test_hex_pivot(self):
+        """ pivot on hexagonal grid """
+
+        attended = [
+                    [(5, 5), (4, 5), (6, 6)],
+                    [(5, 6), (4, 7), (6, 6)],
+                    [(6, 7), (6, 8), (6, 6)],
+                    [(7, 6), (8, 7), (6, 6)],
+                    [(7, 5), (8, 5), (6, 6)],
+                    [(6, 5), (6, 4), (6, 6)],
+                    [(5, 5), (4, 5), (6, 6)]
+                   ]
+
+
+        for i in range(len(attended)):
+            self.assertCountEqual(geometry.pivot(geometry.FLAT_HEX, (6, 6), [(6, 6)], i), [(6, 6)])
+            result = geometry.pivot(geometry.FLAT_HEX, (6, 6), [(5, 5), (4, 5), (6, 6)], i)
+            self.assertCountEqual(result, attended[i])
+
+    def test_squ_pivot(self):
+        """ pivot on square grid """
+        attended = [
+                    [(6, 6), (6, 5), (5, 5), (5, 6)],
+                    [(6, 6), (5, 6), (5, 7), (6, 7)],
+                    [(6, 6), (6, 7), (7, 7), (7, 6)],
+                    [(6, 6), (7, 6), (7, 5), (6, 5)],
+                    [(6, 6), (6, 5), (5, 5), (5, 6)]
+                   ]
+
+        for i in range(len(attended)):
+            self.assertCountEqual(geometry.pivot(geometry.SQUARE, (6, 6), [(6, 6)], i), [(6, 6)])
+            result = geometry.pivot(geometry.SQUARE, (6, 6), [(6, 6), (6, 5), (5, 5), (5, 6)], i)
+            self.assertCountEqual(result, attended[i])
+
+
+    def test_rectangle_errors(self):
+        for method in (geometry.rectangle, geometry.hollow_rectangle):
+            self.assertRaises(TypeError, method, "a", 1, 1, 1)
+            self.assertRaises(TypeError, method, 1, "a", 1, 1)
+            self.assertRaises(TypeError, method, 1, 1, "a", 1)
+            self.assertRaises(TypeError, method, 1, 1, 1, "a")
+
+    def test_rectangle(self):
+
+        self.assertEquals(geometry.rectangle(0, 0, 0, 0), [(0, 0)])
+        self.assertCountEqual(geometry.rectangle(0, 0, 1, 1), [(0, 0), (0, 1), (1, 1), (1, 0)])
+        self.assertCountEqual(geometry.rectangle(1, 1, 0, 0), [(0, 0), (0, 1), (1, 1), (1, 0)])
+        self.assertCountEqual(geometry.rectangle(4, 3, 7, 5), [(4, 3), (4, 4), (4, 5), (5, 5), (6, 5), (7, 5), (7, 4), (7, 3), (6, 3), (5, 3), (6, 4), (5, 4)])
+        self.assertCountEqual(geometry.rectangle(3, 3, 9, 9), [(3, 3), (9, 9), (9, 8), (9, 7), (9, 5), (9, 6), (9, 4), (9, 3), (8, 4), (7, 3), (6, 4), (4, 4),
+                                                       (3, 4), (3, 5), (3, 6), (3, 7), (3, 8), (3, 9), (4, 9), (5, 9), (6, 9), (7, 9), (8, 9), (4, 5),
+                                                       (5, 4), (6, 5), (7, 4), (8, 5), (4, 6), (5, 5), (6, 6), (7, 5), (8, 6), (4, 7), (5, 6), (6, 7),
+                                                       (7, 6), (8, 7), (4, 8), (5, 7), (6, 8), (7, 7), (8, 8), (7, 8), (5, 8), (8, 3), (6, 3), (4, 3),
+                                                       (5, 3)])
+
+        self.assertEquals(geometry.hollow_rectangle(0, 0, 0, 0), [(0, 0)])
+        self.assertCountEqual(geometry.hollow_rectangle(0, 0, 1, 1), [(0, 0), (0, 1), (1, 1), (1, 0)])
+        self.assertCountEqual(geometry.hollow_rectangle(1, 1, 0, 0), [(0, 0), (0, 1), (1, 1), (1, 0)])
+        self.assertCountEqual(geometry.hollow_rectangle(4, 3, 7, 5), [(4, 3), (4, 4), (4, 5), (5, 5), (6, 5), (7, 5), (7, 4), (7, 3), (6, 3), (5, 3)])
+        self.assertCountEqual(geometry.hollow_rectangle(3, 3, 9, 9), [(3, 3), (9, 9), (9, 8), (9, 7), (9, 5), (9, 6), (9, 4), (9, 3), (7, 3), (3, 4),
+                                                              (3, 5), (3, 6), (3, 7), (3, 8), (3, 9), (4, 9), (5, 9), (6, 9), (7, 9), (8, 9),
+                                                              (8, 3), (6, 3), (4, 3), (5, 3)])
+
+    def test_triangle_errors(self):
+
+        for cell_shape in (geometry.FLAT_HEX, geometry.SQUARE):
+            self.assertRaises(TypeError, geometry.triangle, cell_shape, "a", 1, 1, 1, 1)
+            self.assertRaises(TypeError, geometry.triangle, cell_shape, 1, "a", 1, 1, 1)
+            self.assertRaises(TypeError, geometry.triangle, cell_shape, 1, 1, "a", 1, 1)
+            self.assertRaises(TypeError, geometry.triangle, cell_shape, 1, 1, 1, "a", 1)
+            self.assertRaises(ValueError, geometry.triangle, cell_shape, 1, 1, 1, 1, -1)
+
+            self.assertRaises(TypeError, geometry.triangle3d, cell_shape, "a", 1, 1, 1, 1, 1, 1)
+            self.assertRaises(TypeError, geometry.triangle3d, cell_shape, 1, "a", 1, 1, 1, 1, 1)
+            self.assertRaises(TypeError, geometry.triangle3d, cell_shape, 1, 1, "a", 1, 1, 1, 1)
+            self.assertRaises(TypeError, geometry.triangle3d, cell_shape, 1, 1, 1, "a", 1, 1, 1)
+            self.assertRaises(TypeError, geometry.triangle3d, cell_shape, 1, 1, 1, 1, "a", 1, 1)
+            self.assertRaises(TypeError, geometry.triangle3d, cell_shape, 1, 1, 1, 1, 1, "a", 1)
+            self.assertRaises(ValueError, geometry.triangle3d, cell_shape, 1, 1, 1, 1, 1, 1, -1)
+
+        self.assertRaises(ValueError, geometry.triangle, 0, 1, 1, 1, 1, 1)
+        self.assertRaises(ValueError, geometry.triangle3d, 0, 1, 1, 1, 1, 1, 1, 1)
+
+    def test_squ2_triangle(self):
+        """test triangle algorithms on square grid"""
+        cell_shape = geometry.SQUARE
+
+        for i in geometry.ANGLES:
+            self.assertCountEqual(geometry.triangle(cell_shape, 0, 0, 0, 0, i), [(0, 0)])
+
+        # TODO: check and validate
+#         # left to right
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
+#
+#         # top to bottom
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
+#
+#         # right to left
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
+#
+#         # bottom to top
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
+#
+#         # top left to bottom right
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
+#
+#         # bottom right to top left
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
+#
+#         # top right to bottom left
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
+#
+#         # bottom right to top left
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
+
+
+
+    def test_fhex2_triangle(self):
+        """test triangle algorithms on hexagonal grid"""
+        cell_shape = geometry.FLAT_HEX
+        for i in geometry.ANGLES:
+            self.assertCountEqual(geometry.triangle(cell_shape, 0, 0, 0, 0, i), [(0, 0)])
+
+        # left to right
+        self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [(3, 3), (3, 4), (3, 3), (4, 5), (4, 4), (4, 3), (4, 2), (4, 1), (4, 1), (3, 1), (3, 2), (2, 3)])
+        self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [(3, 3), (4, 4), (4, 3), (4, 2), (4, 2), (3, 2), (2, 3)])
+        self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [(3, 3), (4, 4), (4, 3), (4, 2), (4, 2), (3, 2), (2, 3)])
+
+        # TODO: check and validate
+
+#         # top to bottom
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
+#
+        # right to left
+        self.assertCountEqual(geometry.triangle(cell_shape, 4, 3, 2, 3, 1), [(3, 2), (3, 1), (3, 2), (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (2, 5), (3, 4), (3, 3), (4, 3)])
+        self.assertCountEqual(geometry.triangle(cell_shape, 4, 3, 2, 3, 2), [(3, 2), (2, 2), (2, 3), (2, 4), (2, 4), (3, 3), (4, 3)])
+        self.assertCountEqual(geometry.triangle(cell_shape, 4, 3, 2, 3, 3), [(3, 2), (2, 2), (2, 3), (2, 4), (2, 4), (3, 3), (4, 3)])
+
+#         # bottom to top
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
+#
+#         # top left to bottom right
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
+#
+#         # bottom right to top left
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
+#
+#         # top right to bottom left
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
+#
+#         # bottom right to top left
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 1), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 2), [])
+#         self.assertCountEqual(geometry.triangle(cell_shape, 2, 3, 4, 3, 3), [])
+
+
+
+    def test_squ3_triangle(self):
+        """test triangle3d algorithms on square grid"""
+        cell_shape = geometry.SQUARE
+        # TODO: complete
+
+    def test_fhex3_triangle(self):
+        """test triangle3d algorithms on hexagonal grid"""
+        cell_shape = geometry.FLAT_HEX
+        # TODO: complete
+
+    def test_zone(self):
+        """test the errors due to bad parameters"""
+        self.assertRaises(TypeError, geometry.zone, 5, 0, 0, "a")
+        self.assertRaises(TypeError, geometry.zone, 5, "a", 0, 1)
+        self.assertRaises(TypeError, geometry.zone, 5, 0, "a", 1)
+        self.assertRaises(ValueError, geometry.zone, 5, 0, 0, -1)
+        self.assertRaises(ValueError, geometry.zone, -1, 0, 0, 1)
+        self.assertRaises(ValueError, geometry.zone, "a", 0, 0, 1)
+
+    def test_hex_zone(self):
+        """ test the zone algo for hexagonal grid """
+        cell_shape = geometry.FLAT_HEX
+        self.assertCountEqual(geometry.zone(cell_shape, 3, 3, 0), [(3, 3)])
+        self.assertCountEqual(geometry.zone(cell_shape, 3, 3, 1), [(3, 2), (2, 3), (3, 3), (4, 3), (4, 4), (3, 4), (2, 4)])
+        self.assertCountEqual(geometry.zone(cell_shape, 3, 3, 2), [(3, 2), (1, 3), (5, 4), (4, 5), (1, 4), (2, 3), (4, 2), \
+                                                            (2, 5), (5, 3), (1, 2), (3, 5), (3, 3), (4, 4), (3, 1), \
+                                                            (4, 3), (2, 2), (3, 4), (2, 4), (5, 2)])
+
+    def test_squ_zone(self):
+        """ test the zone algo for square grid """
+        cell_shape = geometry.SQUARE
+        self.assertCountEqual(geometry.zone(cell_shape, 3, 3, 0), [(3, 3)])
+        self.assertCountEqual(geometry.zone(cell_shape, 3, 3, 1), [(3, 2), (3, 3), (4, 4), (2, 3), (4, 3), (2, 2), (4, 2), (3, 4), (2, 4)])
+        self.assertCountEqual(geometry.zone(cell_shape, 3, 3, 2), [(2, 4), (3, 2), (5, 4), (1, 3), (4, 5), (2, 1), (1, 4), (2, 3), (4, 2), \
+                                                                    (5, 1), (2, 5), (3, 5), (5, 3), (1, 2), (3, 3), (5, 5), (4, 4), (3, 1), \
+                                                                    (1, 5), (4, 3), (2, 2), (4, 1), (5, 2), (3, 4), (1, 1)])
+
+if __name__ == "__main__":
+    unittest.main()

+ 59 - 60
tests/test_grid.py

@@ -1,101 +1,100 @@
 '''
-Created on 20 nov. 2016
 
-@author: olinox
+    ** By Cro-Ki l@b, 2017 **
 '''
 import unittest
 
 from pypog import geometry
-from pypog.Grid import Grid, SquareGrid, HexGrid
+from pypog.grid_objects import Grid, SquareGrid, HexGrid
 
 
 class Test(unittest.TestCase):
 
     def test_init(self):
-        #square grid
-        _ = Grid( geometry.SQUARE, 1, 1 )
-        _ = SquareGrid( 1, 1 )
-        
-        #hex grid
-        _ = Grid( geometry.HEX, 1, 1 )
-        _ = HexGrid( 1, 1 )
+        # square grid
+        _ = Grid(geometry.SQUARE, 1, 1)
+        _ = SquareGrid(1, 1)
+
+        # hex grid
+        _ = Grid(geometry.FLAT_HEX, 1, 1)
+        _ = HexGrid(1, 1)
 
     def test_cell_shape(self):
-        grid = Grid( geometry.SQUARE, 1, 1 )
-        self.assertEqual( grid.cell_shape, geometry.SQUARE )
-        
-        grid.cell_shape = geometry.HEX
-        self.assertEqual( grid.cell_shape, geometry.HEX )
+        grid = Grid(geometry.SQUARE, 1, 1)
+        self.assertEqual(grid.cell_shape, geometry.SQUARE)
+
+        grid.cell_shape = geometry.FLAT_HEX
+        self.assertEqual(grid.cell_shape, geometry.FLAT_HEX)
 
         def _set_invalid_cell_shape():
             grid.cell_shape = -1
-        self.assertRaises( ValueError, _set_invalid_cell_shape )
+        self.assertRaises(ValueError, _set_invalid_cell_shape)
 
     def test_dimensions(self):
-        
+
         for cls in (SquareGrid, HexGrid):
-            grid = cls( 1, 1 )
-            self.assertEqual( grid.height, 1 )
-            self.assertEqual( grid.width, 1 )
-    
+            grid = cls(1, 1)
+            self.assertEqual(grid.height, 1)
+            self.assertEqual(grid.width, 1)
+
             grid.height = 1000
-            self.assertEqual( grid.height, 1000 )
+            self.assertEqual(grid.height, 1000)
             grid.width = 1000
-            self.assertEqual( grid.width, 1000 )
-    
+            self.assertEqual(grid.width, 1000)
+
             def _set_invalid_height():
                 grid.height = -1
-            self.assertRaises( ValueError, _set_invalid_height )
-            
+            self.assertRaises(ValueError, _set_invalid_height)
+
             def _set_invalid_width():
                 grid.height = -1
-            self.assertRaises( ValueError, _set_invalid_width )
-        
+            self.assertRaises(ValueError, _set_invalid_width)
+
     def test_cases_number(self):
         for cls in (SquareGrid, HexGrid):
-            grid = cls( 1, 1 )
-            self.assertEqual( grid.cases_number(), 1 )
-            
+            grid = cls(1, 1)
+            self.assertEqual(grid.cases_number(), 1)
+
             grid.width = 100
             grid.height = 100
-            
-            self.assertEqual( grid.cases_number(), 10000 )
-    
+
+            self.assertEqual(grid.cases_number(), 10000)
+
     def test_in_grid(self):
-        
+
         for cls in (SquareGrid, HexGrid):
-            grid = cls( 10, 10 )
-            self.assertTrue( grid.in_grid(5, 5) )
-            self.assertFalse( grid.in_grid(11, 5) )
-            self.assertFalse( grid.in_grid(5, 11) )
+            grid = cls(10, 10)
+            self.assertTrue(grid.in_grid(5, 5))
+            self.assertFalse(grid.in_grid(11, 5))
+            self.assertFalse(grid.in_grid(5, 11))
 
     def test_line(self):
-         
-        #line algorithm is tested in tests.geometry.test_line
-        
+
+        # line algorithm is tested in tests.geometry.test_line
+
         grid = SquareGrid(10, 10)
-         
-        line = grid.line(0,0,0,1)
-        self.assertEqual(line, [(0,0), (0,1)])
-        
+
+        line = grid.line(0, 0, 0, 1)
+        self.assertEqual(line, [(0, 0), (0, 1)])
+
     def test_line_3d(self):
-        
-        #line algorithm is tested in tests.geometry.test_line
-        
+
+        # line algorithm is tested in tests.geometry.test_line
+
         grid = HexGrid(10, 10)
-    
-        line = grid.line3d(1,1,1,1,1,1)
-        self.assertEqual(line, [(1,1,1)])
-    
+
+        line = grid.line3d(1, 1, 1, 1, 1, 1)
+        self.assertEqual(line, [(1, 1, 1)])
+
 
     def test_hex_zone(self):
-        
-        #zone algorithm is tested in tests.geometry.test_zone
-        
-        grid = HexGrid(10,10)
-        zone = grid.zone( 0, 0, 0 )
-        self.assertCountEqual(zone, [(0,0)])
+
+        # zone algorithm is tested in tests.geometry.test_zone
+
+        grid = HexGrid(10, 10)
+        zone = grid.zone(0, 0, 0)
+        self.assertCountEqual(zone, [(0, 0)])
 
 
 if __name__ == "__main__":
-    unittest.main()
+    unittest.main()

+ 27 - 28
tests/test_pencils.py

@@ -1,69 +1,68 @@
 '''
-Created on 12 dec. 2016
 
-@author: olinox
+    ** By Cro-Ki l@b, 2017 **
 '''
 import unittest
 
-from pypog import geometry, Grid, pencil
+from pypog import geometry, grid_objects, painting
 
 class Test(unittest.TestCase):
 
     def test_base_pencil(self):
-        for cell_shape in (geometry.HEX, geometry.SQUARE):
-        
-            self.assertRaises(TypeError, pencil.BasePencil, "invalid arg")
-            
-            grid = Grid.Grid(cell_shape, 30, 30)
-            my_pencil = pencil.BasePencil(grid)
-            
+        for cell_shape in (geometry.FLAT_HEX, geometry.SQUARE):
+
+            self.assertRaises(TypeError, painting.BasePencil, "invalid arg")
+
+            grid = grid_objects.Grid(cell_shape, 30, 30)
+            my_pencil = painting.BasePencil(grid)
+
             # default origin and position
             self.assertEqual(my_pencil.origin, None)
             self.assertEqual(my_pencil.position, None)
-            self.assertRaises(AttributeError, my_pencil.origin, (1,1))
-            self.assertRaises(AttributeError, my_pencil.position, (1,1))
-            
+            self.assertRaises(AttributeError, my_pencil.origin, (1, 1))
+            self.assertRaises(AttributeError, my_pencil.position, (1, 1))
+
             # size
             self.assertRaises(TypeError, setattr, my_pencil, "size", "a")
             self.assertRaises(ValueError, setattr, my_pencil, "size", -1)
             self.assertEqual(my_pencil.size, 1)
-            
+
             # selection, added, removed
             self.assertEqual(my_pencil.selection, [])
             self.assertEqual(my_pencil.added, [])
             self.assertEqual(my_pencil.removed, [])
-            
+
             # pencil methods
             self.assertRaises(TypeError, my_pencil.start, "a")
-            self.assertRaises(pencil.NotStartedException, my_pencil.update, 1,1)
+            self.assertRaises(painting.NotStartedException, my_pencil.update, 1, 1)
             self.assertRaises(NotImplementedError, my_pencil._update)
-            
+
             try:
-                my_pencil.start(0,0)
+                my_pencil.start(0, 0)
             except NotImplementedError:
                 pass
             self.assertRaises(TypeError, my_pencil.update, "a")
-            self.assertEqual(my_pencil.origin, (0,0))
-            
+            self.assertEqual(my_pencil.origin, (0, 0))
+
     def test_line_pencil(self):
         pass
-    
+
     def test_free_pencil(self):
         pass
-    
+
     def test_pot_pencil(self):
         pass
-    
+
     def test_rect_pencil(self):
         pass
-    
+
     def test_hrect_pencil(self):
         pass
-    
+
     def test_boundary_pencil(self):
         pass
-    
-    
+
+
 
 if __name__ == "__main__":
-    unittest.main()
+    unittest.main()