Преглед изворни кода

various corrections to geometry ; increase test coverage

olinox пре 9 година
родитељ
комит
6a1ba38440

+ 1 - 1
.travis.yml

@@ -8,6 +8,6 @@ install:
   - pip install nose2
   - pip install nose2
   - pip install coveralls
   - pip install coveralls
 script: 
 script: 
-  - nose2 --with-coverage
+  - nose2 --with-coverage --coverage pypog
 after_success:
 after_success:
   coveralls
   coveralls

+ 115 - 132
pypog/geometry.py

@@ -33,7 +33,7 @@ def neighbours_of(cell_shape, x, y):
     elif cell_shape == HEX: 
     elif cell_shape == HEX: 
         return hex_neighbours_of(x, y)
         return hex_neighbours_of(x, y)
     else:
     else:
-        raise ValueError("'geometry' has to be a value from GRID_GEOMETRIES")
+        raise ValueError("'cell_shape' has to be a value from GRID_GEOMETRIES")
 
 
 def hex_neighbours_of(x, y):
 def hex_neighbours_of(x, y):
     """ returns the list of coords of the neighbours of a cell on an hexagonal grid"""
     """ returns the list of coords of the neighbours of a cell on an hexagonal grid"""
@@ -76,19 +76,17 @@ def line2d(cell_shape, x1, y1, x2, y2):
     grid could be one of the GRIDTYPES values"""
     grid could be one of the GRIDTYPES values"""
     if not all(isinstance(c, int) for c in [x1, y1, x2, y2]):
     if not all(isinstance(c, int) for c in [x1, y1, x2, y2]):
         raise TypeError("x1, y1, x2, y2 have to be integers")
         raise TypeError("x1, y1, x2, y2 have to be integers")
-    if (x1, y1) == (x2, y2):
-        return [(x1, y1)]
     if cell_shape == HEX:
     if cell_shape == HEX:
-        return hex_2d_line(x1, y1, x2, y2)
+        return _brH(x1, y1, x2, y2)
     elif cell_shape == SQUARE: 
     elif cell_shape == SQUARE: 
-        return squ_2d_line(x1, y1, x2, y2)
+        return _brC(x1, y1, x2, y2)
     else:
     else:
-        raise ValueError("'geometry' has to be a value from GRID_GEOMETRIES")
+        raise ValueError("'cell_shape' has to be a value from GRID_GEOMETRIES")
 
 
 def line3d(cell_shape, x1, y1, z1, x2, y2, z2):
 def line3d(cell_shape, x1, y1, z1, x2, y2, z2):
     """returns a line from x1,y1,z1 to x2,y2,z2
     """returns a line from x1,y1,z1 to x2,y2,z2
     grid could be one of the GRIDTYPES values"""
     grid could be one of the GRIDTYPES values"""
-    if not all(isinstance(c, int) for c in [x1, y1, z1, x2, y2, z2]):
+    if not all(isinstance(c, int) for c in [z1, z2]):
         raise TypeError("x1, y1, z1, x2, y2, z2 have to be integers")
         raise TypeError("x1, y1, z1, x2, y2, z2 have to be integers")
     hoLine = line2d(cell_shape, x1, y1, x2, y2)
     hoLine = line2d(cell_shape, x1, y1, x2, y2)
     if z1 == z2:
     if z1 == z2:
@@ -97,48 +95,13 @@ def line3d(cell_shape, x1, y1, z1, x2, y2, z2):
         ligneZ = _brC(0, z1, (len(hoLine)-1), z2)
         ligneZ = _brC(0, z1, (len(hoLine)-1), z2)
         return [(hoLine[d][0], hoLine[d][1], z) for d, z in ligneZ]
         return [(hoLine[d][0], hoLine[d][1], z) for d, z in ligneZ]
 
 
-def hex_2d_line(x1, y1, x2, y2):
-    """returns a line from x1,y1 to x2,y2 on an hexagonal grid
-    line is a list of coordinates"""
-    if (x1, y1) == (x2, y2):
-        return [(x1, y1)]
-    return _brH(x1, y1, x2, y2)
+def _brC(x1, y1, x2, y2):
+    """Line Bresenham algorithm for square grid"""
+    result = []
 
 
-def hex_3d_line(x1, y1, z1, x2, y2, z2):
-    """returns a line from x1,y1,z1 to x2,y2,z2 on an hexagonal grid
-    line is a list of coordinates"""
-    hoLine = hex_2d_line(x1, y1, x2, y2)
-    if z1 == z2:
-        return [(x, y, z1) for x, y in hoLine]
-    else:
-        zLine = _brC(0, z1, (len(hoLine)-1), z2)
-        dicZ = {d:[] for d, z in zLine}
-        for d, z in zLine:
-            dicZ[d].append(z)
-        return [(hoLine[d][0], hoLine[d][1], z) for d, liste_z in dicZ.items() for z in liste_z]
-
-def squ_2d_line(x1, y1, x2, y2):
-    """returns a line from x1,y1 to x2,y2 on an square grid
-    line is a list of coordinates
-    """
     if (x1, y1) == (x2, y2):
     if (x1, y1) == (x2, y2):
         return [(x1, y1)]
         return [(x1, y1)]
-    return _brC(x1, y1, x2, y2)
-    
-def squ_3d_line(x1, y1, z1, x2, y2, z2):
-    """returns a line from x1,y1,z1 to x2,y2,z2 on an square grid
-    line is a list of coordinates"""
-    hoLine = squ_2d_line(x1, y1, x2, y2)
-    if z1 == z2:
-        return [(x, y, z1) for x, y in hoLine]
-    else:
-        zLine = _brC(0, z1, (len(hoLine)-1), z2)
-        return [(hoLine[d][0], hoLine[d][1], z) for d, z in zLine]
 
 
-def _brC(x1, y1, x2, y2):
-    """Line Bresenham algorithm for square grid"""
-    result = []
-    
     # DIAGONAL SYMETRY
     # 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
     if V: y1, x1, y2, x2 = x1, y1, x2, y2
@@ -169,6 +132,9 @@ def _brC(x1, y1, x2, y2):
     
     
 def _brH(x1, y1, x2, y2):
 def _brH(x1, y1, x2, y2):
     """Line Bresenham algorithm for hexagonal grid"""
     """Line Bresenham algorithm for hexagonal grid"""
+    if (x1, y1) == (x2, y2):
+        return [(x1, y1)]
+    
     reversed_sym = (x1 > x2)
     reversed_sym = (x1 > x2)
     if reversed_sym:
     if reversed_sym:
         x1, x2 = x2, x1
         x1, x2 = x2, x1
@@ -216,6 +182,7 @@ def _brH_h(x1, y1, x2, y2):
             result.append(pos)
             result.append(pos)
             d += 0.5
             d += 0.5
         
         
+        # in case of error in the algorithm, we should avoid infinite loop:
         if pos[0] > x2:
         if pos[0] > x2:
             result = []
             result = []
             break
             break
@@ -260,6 +227,7 @@ def _brH_v(x1, y1, x2, y2):
             result.append(pos)
             result.append(pos)
             offset -= 1.5
             offset -= 1.5
         
         
+        # in case of error in the algorithm, we should avoid infinite loop:
         if direction*pos[1] > direction*y2:
         if direction*pos[1] > direction*y2:
             result = []
             result = []
             break
             break
@@ -293,35 +261,48 @@ def triangle(cell_shape, xa, ya, xh, yh, iAngle):
     """Returns a list of (x, y) coordinates in a triangle
     """Returns a list of (x, y) coordinates in a triangle
     A is the top of the triangle, H if the middle of the base
     A is the top of the triangle, H if the middle of the base
     """
     """
+    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")   
+    
     if cell_shape == SQUARE:
     if cell_shape == SQUARE:
-        return triangle_sq(xa, ya, xh, yh, iAngle)
+        return _triangle_sq(xa, ya, xh, yh, iAngle)
     elif cell_shape == HEX: 
     elif cell_shape == HEX: 
-        return triangle_hex(xa, ya, xh, yh, iAngle)
+        return _triangle_hex(xa, ya, xh, yh, iAngle)
     else:
     else:
-        raise ValueError("'geometry' has to be a value from GRID_GEOMETRIES")
+        raise ValueError("'cell_shape' has to be a value from GRID_GEOMETRIES")
 
 
 def triangle3d(cell_shape, xa, ya, za, xh, yh, zh, iAngle):
 def triangle3d(cell_shape, xa, ya, za, xh, yh, zh, iAngle):
     """Returns a list of (x, y, z) coordinates in a 3d-cone
     """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
     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:
     if cell_shape == SQUARE:
-        return triangle_sq_3d(xa, ya, za, xh, yh, zh, iAngle)
+        return _triangle_sq_3d(xa, ya, za, xh, yh, zh, iAngle)
     elif cell_shape == HEX: 
     elif cell_shape == HEX: 
-        return triangle_hex_3d(xa, ya, za, xh, yh, zh, iAngle)
+        return _triangle_hex_3d(xa, ya, za, xh, yh, zh, iAngle)
     else:
     else:
-        raise ValueError("'geometry' has to be a value from GRID_GEOMETRIES")
+        raise ValueError("'cell_shape' has to be a value from GRID_GEOMETRIES")    
+    
 
 
-def triangle_sq(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
-    (square grid)
+def _triangle_sq(xa, ya, xh, yh, iAngle):   
+    """ triangle algorithm on square grid
     """
     """
-    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")
     if (xa, ya) == (xh, yh):
     if (xa, ya) == (xh, yh):
-        return [(xa, ya)]    
+        return [(xa, ya)] 
     
     
     result = []
     result = []
     
     
@@ -344,7 +325,7 @@ def triangle_sq(xa, ya, xh, yh, iAngle):
     
     
     # base (lower slope)
     # 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 = squ_2d_line(x1, y1, x2, y2)
+    base = line2d(SQUARE, x1, y1, x2, y2)
     y_base = y1
     y_base = y1
     lines.remove( (x1, y1, x2, y2) )
     lines.remove( (x1, y1, x2, y2) )
     
     
@@ -354,7 +335,7 @@ def triangle_sq(xa, ya, xh, yh, iAngle):
     for x1, y1, x2, y2 in lines:
     for x1, y1, x2, y2 in lines:
         if y_top == None: 
         if y_top == None: 
             y_top = y2
             y_top = y2
-        hat.extend( squ_2d_line(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 top is under base, -1 if not)
     sense = 1 if y_top > y_base else -1
     sense = 1 if y_top > y_base else -1
@@ -368,55 +349,9 @@ def triangle_sq(xa, ya, xh, yh, iAngle):
 
 
     return result
     return result
 
 
-def triangle_sq_3d(xa, ya, za, xh, yh, zh, iAngle):
-    """returns a dictionnary {coord: (-dh, +dh)}
-    coord keys are the cells in the triangle, (-dh, +dh) value is the vertical amplitude"""
-    
-    #TODO: review result form
-    
-    if not all(isinstance(c, int) for c in [xa, ya, xh, yh]):
-        raise TypeError("xa, ya, za, xh, yh have to be integers")
-    if not iAngle in ANGLES:
-        raise ValueError("iAngle should be one of the ANGLES values")
-    if (xa, ya) == (xh, yh):
-        return [(xa, ya)]  
-
-    result = {} 
-    
-    flat_triangle = triangle_sq(xa, ya, xh, yh, iAngle)
-    k = 1 / ( iAngle * sqrt(3) )
-
-    length = max( abs(xh - xa), abs(yh - ya) )
-
-    vertical_line = squ_2d_line(0, za, length, zh)
-    
-    #on cree un dictionnaire ou x est la cle, et ou la valeur est une liste de z
-    vertical_line_dict = {d:[] for d, z in vertical_line}
-    for d, z in vertical_line:
-        vertical_line_dict[d].append(z)
-        
-    #approximation: on met a jour la hauteur en fonction de la distance au centre
-    for x, y in flat_triangle:
-        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) ) 
-    return result
-
-
-def triangle_hex(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
-    (hexagonal grid)
+def _triangle_hex(xa, ya, xh, yh, iAngle):   
+    """  triangle algorithm on hexagonal grid
     """
     """
-    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 [1, 2, 3]:
-        raise ValueError("iAngle should be one of the ANGLES values")
     if (xa, ya) == (xh, yh):
     if (xa, ya) == (xh, yh):
         return [(xa, ya)]    
         return [(xa, ya)]    
     
     
@@ -449,7 +384,7 @@ def triangle_hex(xa, ya, xh, yh, iAngle):
     
     
     # base (lower slope)
     # 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))
     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 = hex_2d_line(x1, y1, x2, y2)
+    base = line2d(HEX, x1, y1, x2, y2)
     y_base = y1
     y_base = y1
     segments.remove( (x1, y1, x2, y2) )
     segments.remove( (x1, y1, x2, y2) )
     
     
@@ -459,7 +394,7 @@ def triangle_hex(xa, ya, xh, yh, iAngle):
     for x1, y1, x2, y2 in segments:
     for x1, y1, x2, y2 in segments:
         if y_sommet == None: 
         if y_sommet == None: 
             y_sommet = y2
             y_sommet = y2
-        chapeau.extend( hex_2d_line(x1, y1, x2, y2) )
+        chapeau.extend( line2d(HEX, x1, y1, x2, y2) )
     
     
     # sense (1 if top is under base, -1 if not)
     # sense (1 if top is under base, -1 if not)
     sens = 1 if y_sommet > y_base else -1
     sens = 1 if y_sommet > y_base else -1
@@ -473,34 +408,59 @@ def triangle_hex(xa, ya, xh, yh, iAngle):
 
 
     return result
     return result
 
 
-def triangle_hex_3d(xa, ya, za, xh, yh, zh, iAngle):
-    """returns a dictionnary {coord: (-dh, +dh)}
-    coord (x,y) keys are the cells in the triangle, 
-    (-dh, +dh) value is the vertical amplitude"""
-    flat_trangle = triangle_hex(xa, ya, xh, yh, iAngle)
+
+def _triangle_sq_3d(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) )
+
+    length = max( abs(xh - xa), abs(yh - ya) )
+
+    vertical_line = line2d(SQUARE, 0, za, length, zh)
     
     
-    #TODO: review result form
+    # 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) ) )
+        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) ) 
+    return result
+
+def _triangle_hex_3d(xa, ya, za, xh, yh, zh, iAngle):
+    """ 3d triangle algorithm on hexagonal grid """
     
     
-    if (xa, ya) == (xh, yh):
-        return [(xa, ya)]   
+    flat_triangle = triangle(HEX, xa, ya, xh, yh, iAngle)
+
     result = {} 
     result = {} 
     
     
     k = 1 / ( iAngle * sqrt(3) )
     k = 1 / ( iAngle * sqrt(3) )
     
     
+    # use cubic coordinates
     xua, yua, zua = cv_off_cube(xa, ya)
     xua, yua, zua = cv_off_cube(xa, ya)
     xuh, yuh, zuh = cv_off_cube(xh, yh)
     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 = squ_2d_line(0, za, length, zh)
+    vertical_line = line2d(SQUARE, 0, za, length, zh)
     
     
-    #on cree un dictionnaire ou x est la cle, et ou la valeur est une liste de z
+    # build a dict with X key and value is a list of Z values
     vertical_line_dict = {d:[] for d, z in vertical_line}
     vertical_line_dict = {d:[] for d, z in vertical_line}
     for d, z in vertical_line:
     for d, z in vertical_line:
         vertical_line_dict[d].append(z)
         vertical_line_dict[d].append(z)
         
         
-    #approximation: on met a jour la hauteur en fonction de la distance au centre
-    for x, y in flat_trangle:
+    # 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)
         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:
         try:
@@ -512,21 +472,45 @@ def triangle_hex_3d(xa, ya, za, xh, yh, zh, iAngle):
         result[ (x, y) ] = ( (min(z_list) - dh) , (max(z_list) + dh) ) 
         result[ (x, y) ] = ( (min(z_list) - dh) , (max(z_list) + dh) ) 
     return result
     return result
 
 
+
 ## pivot
 ## pivot
 
 
 def pivot(cell_shape, center, coordinates, 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)
     around the center coordinates (x,y)
     Rotation is counterclockwise"""
     Rotation is counterclockwise"""
+    # check the args:
+    try:
+        x, y = center
+    except ValueError:
+        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() 
+            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:
     if cell_shape == SQUARE:
-        return squ_pivot(center, coordinates, rotations)
+        return _squ_pivot(center, coordinates, rotations)
     elif cell_shape == HEX: 
     elif cell_shape == HEX: 
-        return hex_pivot(center, coordinates, rotations)
+        return _hex_pivot(center, coordinates, rotations)
     else:
     else:
         raise ValueError("'cell_shape' has to be a value from GRID_GEOMETRIES")
         raise ValueError("'cell_shape' has to be a value from GRID_GEOMETRIES")
 
 
 
 
-def hex_pivot(center, coordinates, rotations):
+def _hex_pivot(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)
     around the center coordinates (x,y)
     On hexagonal grid, rotates of 60 degrees each time"""
     On hexagonal grid, rotates of 60 degrees each time"""
@@ -548,7 +532,7 @@ def hex_pivot(center, coordinates, rotations):
         
         
     return result
     return result
 
 
-def squ_pivot(center, coordinates, rotations):
+def _squ_pivot(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)
     around the center coordinates (x,y)
     On square grid, rotates of 90 degrees each time"""
     On square grid, rotates of 90 degrees each time"""
@@ -572,21 +556,18 @@ def squ_pivot(center, coordinates, rotations):
 ## cubic coordinates
 ## cubic coordinates
 def cv_cube_off(xu, yu, zu):
 def cv_cube_off(xu, yu, zu):
     """convert cubic coordinates (xu, yu, zu) in standards coordinates (x, y) [offset]"""
     """convert cubic coordinates (xu, yu, zu) in standards coordinates (x, y) [offset]"""
-    if not all(isinstance(c, int) for c in [xu, yu, zu]):
-        raise TypeError("!err: xu, yu et zu doivent etre des entiers")
     y = int( xu + ( zu - (zu & 1) ) / 2 )
     y = int( xu + ( zu - (zu & 1) ) / 2 )
     x = zu
     x = zu
     return (x, y)        
     return (x, y)        
 
 
 def cv_off_cube(x, y):
 def cv_off_cube(x, y):
     """converts standards coordinates (x, y) [offset] in cubic coordinates (xu, yu, zu)"""
     """converts standards coordinates (x, y) [offset] in cubic coordinates (xu, yu, zu)"""
-    if not all(isinstance(c, int) for c in [x, y]):
-        raise TypeError("!err: x et y doivent etre des entiers")
     zu = x
     zu = x
     xu = int( y - ( x - (x & 1) ) / 2 )
     xu = int( y - ( x - (x & 1) ) / 2 )
     yu = int( -xu -zu )
     yu = int( -xu -zu )
     return (xu, yu, zu)    
     return (xu, yu, zu)    
 
 
+# unused
 def cube_round(x, y, z):
 def cube_round(x, y, z):
     """returns the nearest cell (in cubic coords)
     """returns the nearest cell (in cubic coords)
     x, y, z can be floating numbers, no problem."""
     x, y, z can be floating numbers, no problem."""
@@ -600,10 +581,12 @@ def cube_round(x, y, z):
         rz = -rx-ry
         rz = -rx-ry
     return (rx, ry, rz)
     return (rx, ry, rz)
 
 
+# unused
 def hex_distance_cube(xa, ya, za, xb, yb, zb):
 def hex_distance_cube(xa, ya, za, xb, yb, zb):
     """returns the manhattan distance between the two cells"""
     """returns the manhattan distance between the two cells"""
     return max(abs(xa - xb), abs(ya - yb), abs(za - zb))
     return max(abs(xa - xb), abs(ya - yb), abs(za - zb))
 
 
+# unused
 def distance_off(xa, ya, xb, yb):
 def distance_off(xa, ya, xb, yb):
     """ distance between A and B (offset coordinates)"""
     """ distance between A and B (offset coordinates)"""
     # 10 times quicker if no conversion...
     # 10 times quicker if no conversion...

+ 70 - 73
tests/geometry/test_line.py

@@ -11,86 +11,83 @@ from pypog import geometry
 class Test(unittest.TestCase):
 class Test(unittest.TestCase):
     """test line algorithms"""
     """test line algorithms"""
 
 
-    def test_hex_line(self):
-        """ 2d line on hexagonal grid """
-        cell_shape = geometry.HEX
-        
-        line = geometry.line2d(cell_shape, 1,1,1,1)
-        self.assertEqual(line, [(1,1)])
-        
-        line = geometry.line2d(cell_shape, 0,0,1,1)
-        self.assertEqual(line, [(0,0), (0,1), (1,1)])
- 
-        line = geometry.line2d(cell_shape, 0,0,7,3)
-        self.assertEqual(line, [(0,0), (1,0), (2,1), (3,1), (4,2), (5,2), (6,3), (7,3)] )
- 
-        line = geometry.line2d(cell_shape, 4,3,0,3)
-        self.assertEqual(line, [(4,3), (3,2), (2,3), (1,2), (0,3)] )
- 
-        line = geometry.line2d(cell_shape, 3,0,3,3)
-        self.assertEqual(line, [(3,0), (3,1), (3,2), (3,3)] )
+    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
         
         
-    def test_squ_line(self):
-        """ 2d line on square grid """
-        cell_shape = geometry.SQUARE
-        line = geometry.line2d(cell_shape,0,0,0,1)
-        self.assertEqual(line, [(0,0), (0,1)])
+        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)]                    
+                                     }
+                   }
         
         
-        line = geometry.line2d(cell_shape,0,0,1,1)
-        self.assertEqual(line, [(0,0), (1,1)])
+        for cell_shape, tests in attended.items():
+            for args, result in tests.items():
+                line = geometry.line2d(cell_shape, *args)
+                self.assertEqual(line, result)
         
         
-        line = geometry.line2d(cell_shape,0,0,7,3)
-        self.assertEqual(line, [(0,0), (1,0), (2,1), (3,1), (4,2), (5,2), (6,3), (7,3)] )
- 
-        line = geometry.line2d(cell_shape,4,3,0,3)
-        self.assertEqual(line, [(4,3), (3,3), (2,3), (1,3), (0,3)] )
- 
-        line = geometry.line2d(cell_shape,3,0,3,3)
-        self.assertEqual(line, [(3,0), (3,1), (3,2), (3,3)] )
     
     
-    def test_hex_line_3d(self):
-        """ 3d line on hexagonal grid """
+    def test_line_3d(self):
+        """ 3d line on hexagonal and square grid """
         cell_shape = geometry.HEX
         cell_shape = geometry.HEX
-        line = geometry.line3d(cell_shape,1,1,1,1,1,1)
-        self.assertEqual(line, [(1,1,1)])
-    
-        line = geometry.line3d(cell_shape,1,1,0,1,1,1)
-        self.assertEqual(line, [(1,1,0), (1,1,1)])
-    
-        line = geometry.line3d(cell_shape,0,0,0,1,1,1)
-        self.assertEqual(line, [(0,0,0), (0,1,0), (1,1,1)])
-    
-        line = geometry.line3d(cell_shape,0,0,0,7,3,7)
-        self.assertEqual(line, [(0,0,0), (1,0,1), (2,1,2), (3,1,3), (4,2,4), (5,2,5), (6,3,6), (7,3,7)] )
- 
-        line = geometry.line3d(cell_shape,4,3,10,0,3,3)
-        self.assertEqual(line, [(4,3,10), (3,2,9), (3,2,8), (2,3,7), (2,3,6), (1,2,5), (1,2,4), (0,3,3)] )
- 
-        line = geometry.line3d(cell_shape,3,0,0,3,3,0)
-        self.assertEqual(line, [(3,0,0), (3,1,0), (3,2,0), (3,3,0)] )
         
         
-    def test_squ_line_3d(self):
-        """ 3d line on square grid """
-        cell_shape = geometry.SQUARE
-        line = geometry.line3d(cell_shape,1,1,1,1,1,1)
-        self.assertEqual(line, [(1,1,1)])
-    
-        line = geometry.line3d(cell_shape,1,1,0,1,1,1)
-        self.assertEqual(line, [(1,1,0), (1,1,1)])
-    
-        line = geometry.line3d(cell_shape,0,0,0,1,1,1)
-        self.assertEqual(line, [(0,0,0), (1,1,1)])
-    
-        line = geometry.line3d(cell_shape,0,0,0,7,3,7)
-        self.assertEqual(line, [(0,0,0), (1,0,1), (2,1,2), (3,1,3), (4,2,4), (5,2,5), (6,3,6), (7,3,7)] )
- 
-        line = geometry.line3d(cell_shape,4,3,10,0,3,3)
-        self.assertEqual(line, [(4,3,10), (3,3,9), (3,3,8), (2,3,7), (2,3,6), (1,3,5), (1,3,4), (0,3,3)] )
- 
-        line = geometry.line3d(cell_shape,3,0,0,3,3,0)
-        self.assertEqual(line, [(3,0,0), (3,1,0), (3,2,0), (3,3,0)] )
-
+        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__":
 if __name__ == "__main__":
     unittest.main()
     unittest.main()

+ 18 - 8
tests/geometry/test_pivot.py

@@ -8,10 +8,22 @@ from pypog import geometry
 
 
 class Test(unittest.TestCase):
 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):
     def test_hex_pivot(self):
         """ pivot on hexagonal grid """
         """ pivot on hexagonal grid """
         
         
-        
         attended = [
         attended = [
                     [(5, 5), (4, 5), (6, 6)], 
                     [(5, 5), (4, 5), (6, 6)], 
                     [(5, 6), (4, 7), (6, 6)],
                     [(5, 6), (4, 7), (6, 6)],
@@ -24,12 +36,11 @@ class Test(unittest.TestCase):
         
         
         
         
         for i in range( len(attended) ):
         for i in range( len(attended) ):
-            self.assertCountEqual(geometry.hex_pivot( (6,6), [(6,6)], i), [(6,6)])
-            result = geometry.hex_pivot( (6,6), [(5,5), (4,5), (6,6)], i )
+            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])
             self.assertCountEqual(result, attended[i])
-            self.assertCountEqual(result, geometry.pivot(geometry.HEX, (6,6), [(5,5), (4,5), (6,6)], i))
 
 
-    def test_squ_line(self):
+    def test_squ_pivot(self):
         """ pivot on square grid """
         """ pivot on square grid """
         attended = [
         attended = [
                     [(6, 6), (6, 5), (5, 5), (5, 6)],
                     [(6, 6), (6, 5), (5, 5), (5, 6)],
@@ -40,10 +51,9 @@ class Test(unittest.TestCase):
                    ]
                    ]
 
 
         for i in range( len(attended) ):
         for i in range( len(attended) ):
-            self.assertCountEqual(geometry.hex_pivot( (6,6), [(6,6)], i), [(6,6)])
-            result = geometry.squ_pivot( (6,6), [(6,6), (6,5), (5,5), (5,6)], i )
+            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])
             self.assertCountEqual(result, attended[i])
-            self.assertCountEqual(result, geometry.pivot(geometry.SQUARE, (6,6), [(6,6), (6,5), (5,5), (5,6)], i))
      
      
 if __name__ == "__main__":
 if __name__ == "__main__":
     unittest.main()
     unittest.main()

+ 42 - 0
tests/geometry/test_rect.py

@@ -0,0 +1,42 @@
+'''
+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()

+ 105 - 2
tests/geometry/test_triangle.py

@@ -11,6 +11,26 @@ from pypog import geometry
 class Test(unittest.TestCase):
 class Test(unittest.TestCase):
     """test triangle algorithms"""
     """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):
     def test_sq_triangle(self):
         """test triangle algorithms on square grid"""
         """test triangle algorithms on square grid"""
         cell_shape = geometry.SQUARE
         cell_shape = geometry.SQUARE
@@ -18,15 +38,98 @@ class Test(unittest.TestCase):
         for i in geometry.ANGLES:
         for i in geometry.ANGLES:
             self.assertCountEqual(geometry.triangle(cell_shape, 0, 0, 0, 0, i), [(0,0)])
             self.assertCountEqual(geometry.triangle(cell_shape, 0, 0, 0, 0, i), [(0,0)])
 
 
-        #TODO: complete
+        # 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):
     def test_hex_triangle(self):
         """test triangle algorithms on hexagonal grid"""
         """test triangle algorithms on hexagonal grid"""
         cell_shape = geometry.HEX
         cell_shape = geometry.HEX
         for i in geometry.ANGLES:
         for i in geometry.ANGLES:
             self.assertCountEqual(geometry.triangle(cell_shape, 0, 0, 0, 0, i), [(0,0)])
             self.assertCountEqual(geometry.triangle(cell_shape, 0, 0, 0, 0, i), [(0,0)])
-        #TODO: complete
+
+        # 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):
     def test_sq_triangle_3d(self):
         """test triangle3d algorithms on square grid"""
         """test triangle3d algorithms on square grid"""

+ 2 - 0
tests/gridviewer/GridViewer.py

@@ -50,6 +50,8 @@ class GridViewer(QMainWindow):
         self.make_grid()
         self.make_grid()
         
         
     def make_grid(self):
     def make_grid(self):
+        
+        self.selection = []
         shape = geometry.HEX if self.ui.opt_hex.isChecked() else geometry.SQUARE
         shape = geometry.HEX if self.ui.opt_hex.isChecked() else geometry.SQUARE
         width = self.ui.spb_width.value()
         width = self.ui.spb_width.value()
         height = self.ui.spb_height.value()
         height = self.ui.spb_height.value()