Browse Source

refactoring

olinox 9 years ago
parent
commit
a56ad32afe

+ 0 - 60
core/Cell.py

@@ -1,60 +0,0 @@
-'''
-Created on 8 nov. 2016
-    Cell of a board game
-@author: olinox
-'''
-from core import geometry
-
-
-class Cell(object):
-    def __init__(self, geometry, x, y, z = 0):
-        if not all(isinstance(value, int) for value in [x, y, z]):
-            raise TypeError("x, y and z should be integers")
-        self._geometry = geometry
-        self._x = x
-        self._y = y
-        self._z = z
-        self._neighbours = ()
-        self.__update_neighbours()
-
-    @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)
-    
-    @property
-    def neighbours(self):
-        return self._neighbours
-    
-    def __update_neighbours(self):
-        """update the tuple of neighbours cells"""
-        x, y = self._x, self._y
-        if self._geometry == geometry.HEX:
-            if 1 == (x % 2):
-                self._neighbours = ( (x, y-1), (x+1, y), (x+1, y+1), (x,  y+1), (x-1, y+1), (x-1, y) )
-            else:
-                self._neighbours = ( (x, y-1), (x+1, y-1), (x+1, y), (x,  y+1), (x-1, y), (x-1, y-1) )
-        elif self._geometry == geometry.SQUARE:
-            self._neighbours = ( (x-1, y-1), (x, y-1), (x+1, y-1), \
-                                (x-1, y)  , (x, y-1), (x+1, y)  , \
-                                (x-1, y+1), (x, y+1),(x+1, y+1) )
-    
-    

+ 0 - 109
core/Grid.py

@@ -1,109 +0,0 @@
-'''
-Created on 7 nov. 2016
-    Game Grid
-@author: olinox
-'''
-from core import geometry
-from core.Cell import Cell
-from core.geometry import gline, gzone, gtriangle, grectangle
-from core.pathfinder import pathfinder
-
-
-class Grid(object):
-    def __init__(self, grid_shape, width, height):
-        self._grid_shape = None
-        self.grid_shape = grid_shape
-        
-        self._width = 0
-        self.width = width
-        self._height = 0
-        self.height = height
-        
-        self._cells = {}
-        
-    # properties
-    @property
-    def grid_shape(self):
-        return self._grid_shape
-    
-    @grid_shape.setter
-    def grid_shape(self, grid_shape):
-        if not grid_shape in geometry.GRID_GEOMETRIES:
-            raise ValueError("'grid_shape' has to be a value from GRID_GEOMETRIES")
-        self._grid_shape = grid_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    
-    
-    def cell(self, coord):
-        # temporary
-        try:
-            return self._cells[coord]
-        except KeyError:
-            x, y = coord
-            cell = Cell(self._geometry, x, y)
-            self._cells[coord] = cell
-            return cell
-    
-    # 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 gline.line2d(self.grid_shape, x1, y1, x2, y2)
-    
-    def line3d(self, x1, y1, z1, x2, y2, z2):
-        return gline.line3d(self.grid_shape, x1, y1, z1, x2, y2, z2)
-    
-    def zone(self, x, y, radius):
-        return gzone.zone(self.grid_shape, x, y, radius)
-    
-    def triangle(self, xa, ya, xh, yh, iAngle):
-        return gtriangle.triangle(self.grid_shape, xa, ya, xh, yh, iAngle)
-    
-    def triangle3d(self, xa, ya, za, xh, yh, zh, iAngle):
-        return gtriangle.triangle3d(self.grid_shape, xa, ya, za, xh, yh, zh, iAngle)
-
-    def rect(self, x1, y1, x2, y2):
-        return grectangle.rect(x1, y1, x2, y2)
-    
-    def hollow_rect(self, x1, y1, x2, y2):
-        return grectangle.hollow_rect(x1, y1, x2, y2)
-
-    def path(self, x1, y1, x2, y2):
-        return pathfinder.path( self, (x1, y1), (x2,y2) )
-
-    
-    
-    
-    
-class HexGrid(Grid):
-    def __init__(self, width, height):
-        Grid.__init__(self, 5, width, height)
-
-class SquareGrid(Grid):
-    def __init__(self, width, height):
-        Grid.__init__(self, 4, width, height)
-
-    

+ 0 - 6
core/constants.py

@@ -1,6 +0,0 @@
-'''
-Created on 7 nov. 2016
-
-@author: olinox
-'''
-

+ 0 - 8
core/geometry/__init__.py

@@ -1,8 +0,0 @@
-"""
-    Geometric algorithms on square or hexagonal grids
-"""
-
-# geometry
-GRID_GEOMETRIES = [4, 5]
-SQUARE = 4
-HEX = 5

+ 0 - 48
core/geometry/cube_coords.py

@@ -1,48 +0,0 @@
-'''
-Created on 8 nov. 2016
-    Cubic coordinates are used in some algorythms about hexagonal grids
-@author: olinox
-'''
-
-
-def cv_cube_off(xu, yu, zu):
-    """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 )
-    x = zu
-    return (x, y)        
-
-def cv_off_cube(x, y):
-    """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
-    xu = int( y - ( x - (x & 1) ) / 2 )
-    yu = int( -xu -zu )
-    return (xu, yu, zu)    
-
-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
-    elif y_diff > z_diff:
-        ry = -rx-rz
-    else:
-        rz = -rx-ry
-    return (rx, ry, rz)
-
-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))
-
-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))
-    

+ 0 - 206
core/geometry/gline.py

@@ -1,206 +0,0 @@
-'''
-Created on 7 nov. 2016
-    Implementation of bresenham algorithm
-    
-    Compute lines between coordinates in 2d or 3d, on hexagonal or square grid
-@author: olinox
-'''
-from math import sqrt
-
-from core import geometry
-
-
-def line2d(grid_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 (x1, y1) == (x2, y2):
-        return [(x1, y1)]
-    if grid_shape == geometry.HEX:
-        return hex_2d_line(x1, y1, x2, y2)
-    elif grid_shape == geometry.SQUARE: 
-        return squ_2d_line(x1, y1, x2, y2)
-    else:
-        raise ValueError("'geometry' has to be a value from GRID_GEOMETRIES")
-
-def line3d(grid_shape, x1, y1, z1, x2, y2, z2):
-    """returns a line from x1,y1,z1 to x2,y2,z2
-    grid could be one of the GRIDTYPES values"""
-    if not all(isinstance(c, int) for c in [x1, y1, z1, x2, y2, z2]):
-        raise TypeError("x1, y1, z1, x2, y2, z2 have to be integers")
-    hoLine = line2d(grid_shape, x1, y1, x2, y2)
-    if z1 == z2:
-        return [(x, y, z1) for x, y in hoLine]
-    else:
-        ligneZ = _brC(0, z1, (len(hoLine)-1), z2)
-        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 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):
-        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
-    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:  
-        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 )
-    
-    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: 
-        result.reverse()
-    return result
-    
-def _brH(x1, y1, x2, y2):
-    """Line Bresenham algorithm for hexagonal grid"""
-    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 
-            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   
-            else:
-                pos = x + 1, y         
-            result.append(pos)
-            d += 0.5
-        
-        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
-            else:
-                pos = x + 1, y + direction            
-            result.append(pos)
-            offset -= 1.5
-        
-        if direction*pos[1] > direction*y2:
-            result = []
-            break
- 
-    return result 

+ 0 - 29
core/geometry/gneighbours.py

@@ -1,29 +0,0 @@
-'''
-Created on 19 nov. 2016
-
-@author: olinox
-'''
-from core import geometry
-
-
-def neighbours_of(grid_shape, x, y):
-    if grid_shape == geometry.SQUARE:
-        return squ_neighbours_of(x, y)
-    elif grid_shape == geometry.HEX: 
-        return hex_neighbours_of(x, y)
-    else:
-        raise ValueError("'geometry' has to be a value from GRID_GEOMETRIES")
-
-def hex_neighbours_of(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)]
-    else:
-        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):
-    """ 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)]
-    

+ 0 - 22
core/geometry/grectangle.py

@@ -1,22 +0,0 @@
-'''
-Created on 8 nov. 2016
-    Rectangle algorythms
-    rectangles are assumed to be composed of vertical/horizontal sides
-@author: olinox
-'''
-
-def rect(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):
-    """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)
-            if (x == x1 or x == x2 or y == y1 or y == y2)]
-    
-    

+ 0 - 233
core/geometry/gtriangle.py

@@ -1,233 +0,0 @@
-'''
-Created on 8 nov. 2016
-    Triangle algorithms
-@author: olinox
-'''
-
-from math import sqrt
-
-from core import geometry
-from core.geometry import gline
-from core.geometry.cube_coords import cv_off_cube, cube_round, cv_cube_off
-
-
-ANGLES = (1, 2, 3)
-
-def triangle(grid_shape, xa, ya, xh, yh, iAngle):
-    if grid_shape == geometry.SQUARE:
-        return triangle_sq(xa, ya, xh, yh, iAngle)
-    elif grid_shape == geometry.HEX: 
-        return triangle_hex(xa, ya, xh, yh, iAngle)
-    else:
-        raise ValueError("'geometry' has to be a value from GRID_GEOMETRIES")
-
-def triangle3d(grid_shape, xa, ya, za, xh, yh, zh, iAngle):
-    if grid_shape == geometry.SQUARE:
-        return triangle_sq_3d(xa, ya, za, xh, yh, zh, iAngle)
-    elif grid_shape == geometry.HEX: 
-        return triangle_hex_3d(xa, ya, za, xh, yh, zh, iAngle)
-    else:
-        raise ValueError("'geometry' 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)
-    """
-    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):
-        return [(xa, ya)]    
-    
-    result = []
-    
-    # direction vector
-    dx_dir, dy_dir = xh - xa, yh - ya
-    
-    # normal vector
-    dx_n, dy_n = - dy_dir, dx_dir
-
-    # B and C positions
-    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))
-    base = gline.squ_2d_line(x1, y1, x2, y2)
-    y_base = y1
-    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: 
-            y_top = y2
-        hat.extend( gline.squ_2d_line(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) )
-            y += sense
-    result.extend(hat)
-
-    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 = gline.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)
-    """
-    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):
-        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 )
-    
-    # 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)        
-
-    # B and C positions
-    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 = gline.hex_2d_line(x1, y1, x2, y2)
-    y_base = y1
-    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: 
-            y_sommet = y2
-        chapeau.extend( gline.hex_2d_line(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) )
-            y += sens
-    result.extend(chapeau)
-
-    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)
-    
-    #TODO: review result form
-    
-    if (xa, ya) == (xh, yh):
-        return [(xa, ya)]   
-    result = {} 
-    
-    k = 1 / ( iAngle * sqrt(3) )
-    
-    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) )
-
-    vertical_line = gline.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_trangle:
-        xu, yu, zu = cv_off_cube(x, y)
-        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) ) 
-    return result
-
-    

+ 0 - 23
core/geometry/gzone.py

@@ -1,23 +0,0 @@
-'''
-Created on 19 nov. 2016
-
-@author: olinox
-'''
-from core.geometry import gneighbours
-
-
-def zone(grid_shape, x0, y0, radius):
-    """ returns the list of the coordinates of the cells in the zone around (x0, y0)
-    """
-    if not all(isinstance(c, int) for c in [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) ] )
-
-    for _ in range(0, radius):
-        current = buffer
-        for x, y in current:
-            buffer |= frozenset( gneighbours.neighbours_of( grid_shape, x, y ) )
-
-    return list(buffer)

+ 0 - 0
core/graphic/__init__.py


+ 0 - 0
core/pathfinder/__init__.py


+ 0 - 0
core/pencil/__init__.py


+ 0 - 25
core/pencil/pline.py

@@ -1,25 +0,0 @@
-'''
-Created on 25 nov. 2016
-
-@author: olinox
-'''
-from core import geometry
-from core.pencil.pbase import BasePencil
-
-
-class LinePencil(BasePencil):
-    def __init__(self, *args):
-        BasePencil.__init__(*args)
-        
-    def _update(self):
-        x0, y0 = self.origin
-        x, y = self.position
-        
-        result = set([])
-        line = geometry.gline.line2d(self._grid.grid_shape, x0, y0, x, y)
-        for x, y in line:
-            result |= set( geometry.gzone.zone(self._grid.grid_shape, x, y, self.size) )
-        
-        self._added = list( result - self._selection )
-        self._removed = list( self._selection - result ) 
-        self._selection = list( result )

+ 0 - 19
core/pencil/psimple.py

@@ -1,19 +0,0 @@
-'''
-Created on 25 nov. 2016
-
-@author: olinox
-'''
-from core import geometry
-from core.pencil.pbase import BasePencil
-
-
-class SimplePencil(BasePencil):
-    def __init__(self, *args):
-        BasePencil.__init__(*args)
-        
-    def _update(self):
-        x, y = self.position
-        zone = geometry.gzone.zone(self._grid.grid_shape, x, y, self.size)
-
-        self._added = list( set(zone) - set(self._selection) )
-        self._selection = list( set(self._selection) + set(zone))

+ 147 - 0
pypog/Grid.py

@@ -0,0 +1,147 @@
+'''
+Created on 7 nov. 2016
+    Game Grid
+@author: olinox
+'''
+from pypog import geometry
+from pypog import pathfinder
+
+
+class Grid(object):
+    def __init__(self, cell_shape, width, height):
+        self._cell_shape = None
+        self.cell_shape = cell_shape
+        
+        self._width = 0
+        self.width = width
+        self._height = 0
+        self.height = height
+        
+        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    
+    
+    
+    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, x, y):
+        return 1
+    
+    def path(self, x1, y1, x2, y2):
+        return pathfinder.path( self, (x1, y1), (x2,y2), self.moving_cost )
+
+    
+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)
+

+ 0 - 0
core/__init__.py → pypog/__init__.py


+ 544 - 0
pypog/geometry.py

@@ -0,0 +1,544 @@
+'''
+Created on 5 dec. 2016
+    Implementation of bresenham algorithm
+    
+    Compute lines between coordinates in 2d or 3d, on hexagonal or square grid
+@author: olinox
+'''
+from math import sqrt
+
+
+GRID_GEOMETRIES = [4, 5]
+SQUARE = 4
+HEX = 5
+ANGLES = (1, 2, 3)
+
+## neigbours
+
+def neighbours_of(cell_shape, x, y):
+    if cell_shape == SQUARE:
+        return squ_neighbours_of(x, y)
+    elif cell_shape == HEX: 
+        return hex_neighbours_of(x, y)
+    else:
+        raise ValueError("'geometry' has to be a value from GRID_GEOMETRIES")
+
+def hex_neighbours_of(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)]
+    else:
+        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):
+    """ 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
+
+def zone(grid_shape, x0, y0, radius):
+    """ returns the list of the coordinates of the cells in the zone around (x0, y0)
+    """
+    if not all(isinstance(c, int) for c in [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) ] )
+
+    for _ in range(0, radius):
+        current = buffer
+        for x, y in current:
+            buffer |= frozenset( neighbours_of( grid_shape, x, y ) )
+
+    return list(buffer)
+
+
+## line : bresenham algorithm
+
+def line2d(grid_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 (x1, y1) == (x2, y2):
+        return [(x1, y1)]
+    if grid_shape == HEX:
+        return hex_2d_line(x1, y1, x2, y2)
+    elif grid_shape == SQUARE: 
+        return squ_2d_line(x1, y1, x2, y2)
+    else:
+        raise ValueError("'geometry' has to be a value from GRID_GEOMETRIES")
+
+def line3d(grid_shape, x1, y1, z1, x2, y2, z2):
+    """returns a line from x1,y1,z1 to x2,y2,z2
+    grid could be one of the GRIDTYPES values"""
+    if not all(isinstance(c, int) for c in [x1, y1, z1, x2, y2, z2]):
+        raise TypeError("x1, y1, z1, x2, y2, z2 have to be integers")
+    hoLine = line2d(grid_shape, x1, y1, x2, y2)
+    if z1 == z2:
+        return [(x, y, z1) for x, y in hoLine]
+    else:
+        ligneZ = _brC(0, z1, (len(hoLine)-1), z2)
+        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 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):
+        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
+    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:  
+        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 )
+    
+    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: 
+        result.reverse()
+    return result
+    
+def _brH(x1, y1, x2, y2):
+    """Line Bresenham algorithm for hexagonal grid"""
+    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 
+            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   
+            else:
+                pos = x + 1, y         
+            result.append(pos)
+            d += 0.5
+        
+        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
+            else:
+                pos = x + 1, y + direction            
+            result.append(pos)
+            offset -= 1.5
+        
+        if direction*pos[1] > direction*y2:
+            result = []
+            break
+ 
+    return result 
+
+
+## rectangles
+
+def rect(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):
+    """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)
+            if (x == x1 or x == x2 or y == y1 or y == y2)]
+    
+    
+    
+## triangles
+    
+
+
+def triangle(cell_shape, xa, ya, xh, yh, iAngle):
+    if cell_shape == SQUARE:
+        return triangle_sq(xa, ya, xh, yh, iAngle)
+    elif cell_shape == HEX: 
+        return triangle_hex(xa, ya, xh, yh, iAngle)
+    else:
+        raise ValueError("'geometry' has to be a value from GRID_GEOMETRIES")
+
+def triangle3d(cell_shape, xa, ya, za, xh, yh, zh, iAngle):
+    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)
+    else:
+        raise ValueError("'geometry' 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)
+    """
+    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):
+        return [(xa, ya)]    
+    
+    result = []
+    
+    # direction vector
+    dx_dir, dy_dir = xh - xa, yh - ya
+    
+    # normal vector
+    dx_n, dy_n = - dy_dir, dx_dir
+
+    # B and C positions
+    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))
+    base = squ_2d_line(x1, y1, x2, y2)
+    y_base = y1
+    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: 
+            y_top = y2
+        hat.extend( squ_2d_line(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) )
+            y += sense
+    result.extend(hat)
+
+    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)
+    """
+    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):
+        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 )
+    
+    # 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)        
+
+    # B and C positions
+    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 = hex_2d_line(x1, y1, x2, y2)
+    y_base = y1
+    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: 
+            y_sommet = y2
+        chapeau.extend( hex_2d_line(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) )
+            y += sens
+    result.extend(chapeau)
+
+    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)
+    
+    #TODO: review result form
+    
+    if (xa, ya) == (xh, yh):
+        return [(xa, ya)]   
+    result = {} 
+    
+    k = 1 / ( iAngle * sqrt(3) )
+    
+    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) )
+
+    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_trangle:
+        xu, yu, zu = cv_off_cube(x, y)
+        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) ) 
+    return result
+
+
+## cubic coordinates
+def cv_cube_off(xu, yu, zu):
+    """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 )
+    x = zu
+    return (x, y)        
+
+def cv_off_cube(x, y):
+    """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
+    xu = int( y - ( x - (x & 1) ) / 2 )
+    yu = int( -xu -zu )
+    return (xu, yu, zu)    
+
+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
+    elif y_diff > z_diff:
+        ry = -rx-rz
+    else:
+        rz = -rx-ry
+    return (rx, ry, rz)
+
+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))
+
+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))
+    
+
+
+    
+    
+    

+ 4 - 3
core/graphic/cells.py → pypog/graphic.py

@@ -1,9 +1,10 @@
 '''
-Created on 26 nov. 2016
+Created on 5 dec. 2016
 
 @author: olinox
 '''
-from core import geometry
+
+from pypog import geometry
 
 
 def polygon(shape, x, y, scale = 120):
@@ -20,7 +21,7 @@ def polygon(shape, x, y, scale = 120):
                    ( ((x*0.866)+0.2886) * scale ,   (y+1) * scale),  \
                    ( (x*0.866) * scale          ,   (y+0.5) * scale) 
                 ]
-        
+
     elif shape == geometry.SQUARE :
         
         return  [ 

+ 15 - 11
core/pathfinder/pathfinder.py → pypog/pathfinder.py

@@ -3,15 +3,14 @@ Created on 17 déc. 2015
    Implement the A* algorithm
 @author: olivier.massot
 '''
-from core.geometry import cube_coords
-
+from pypog import geometry
 
 def distance(coord1, coord2):
     """distance between 1 and 2"""
     x1, y1 = coord1
-    xu1, yu1, zu1 = cube_coords.cv_off_cube(x1, y1)
+    xu1, yu1, zu1 = geometry.cv_off_cube(x1, y1)
     x2, y2 = coord2
-    xu2, yu2, zu2 = cube_coords.cv_off_cube(x2, y2)
+    xu2, yu2, zu2 = geometry.cv_off_cube(x2, y2)
     return max(abs(xu1 - xu2), abs(yu1 - yu2), abs(zu1 - zu2))
 
 def square_distance(coord1, coord2):
@@ -48,10 +47,13 @@ class Node():
         """distance (en cases) entre deux coordonnees"""
         x1, y1 = coord1
         x2, y2 = coord2
-        return cube_coords.distance_off(x1, y1, x2, y2)
+        return geometry.distance_off(x1, y1, x2, y2)
     
+def _default_moving_cost_function(x, y):
+    return 1
+
 
-def path(grid, origin, target, abilities = 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
@@ -62,6 +64,9 @@ def path(grid, origin, target, abilities = None):
     
     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)
@@ -79,8 +84,8 @@ def path(grid, origin, target, abilities = None):
         
         for coord in [coord for coord in neighbours if not coord in nodes.keys()]:
 
-                # !!! need a calculate cost function !!!
-                cost = 1
+                x, y = coord
+                cost = moving_cost_function(x, y)
                 if cost < 0:
                     continue
                 
@@ -90,7 +95,7 @@ def path(grid, origin, target, abilities = None):
 
                 try:
                     existing = nodes[coord]
-                    if existing.cout <= node.cout:
+                    if existing.cost <= node.cost:
                         continue
                 except KeyError:
                     pass
@@ -101,8 +106,7 @@ def path(grid, origin, target, abilities = None):
             print("No path found")
             return []
         
-        best = min(nodes.values(), key=lambda x: x.cout)
-#         retenus.append(meilleur)
+        best = min(nodes.values(), key=lambda x: x.cost)
         del nodes[best.coord]
         position = best
     

+ 35 - 1
core/pencil/pbase.py → pypog/pencil.py

@@ -1,8 +1,10 @@
 '''
-Created on 25 nov. 2016
+Created on 5 déc. 2016
 
 @author: olinox
 '''
+from pypog.geometry import line2d, zone
+
 
 class BasePencil(object):
     
@@ -60,3 +62,35 @@ class BasePencil(object):
     def _update(self):
         pass
         
+        
+class LinePencil(BasePencil):
+    def __init__(self, *args):
+        BasePencil.__init__(*args)
+        
+    def _update(self):
+        x0, y0 = self.origin
+        x, y = self.position
+        
+        result = set([])
+        line = line2d(self._grid.cell_shape, x0, y0, x, y)
+        for x, y in line:
+            result |= set( zone(self._grid.cell_shape, x, y, self.size) )
+        
+        self._added = list( result - self._selection )
+        self._removed = list( self._selection - result ) 
+        self._selection = list( result )
+
+
+class SimplePencil(BasePencil):
+    def __init__(self, *args):
+        BasePencil.__init__(*args)
+        
+    def _update(self):
+        x, y = self.position
+        zone = zone(self._grid.cell_shape, x, y, self.size)
+
+        self._added = list( set(zone) - set(self._selection) )
+        self._selection = list( set(self._selection) + set(zone))
+        
+        
+

+ 24 - 24
tests/geometry/test_line.py

@@ -5,8 +5,7 @@ Created on 20 nov. 2016
 '''
 import unittest
 
-from core import geometry
-from core.geometry import gline
+from pypog import geometry
 
 
 class Test(unittest.TestCase):
@@ -15,80 +14,81 @@ class Test(unittest.TestCase):
     def test_hex_line(self):
         """ 2d line on hexagonal grid """
         grid_shape = geometry.HEX
-        line = gline.line2d(grid_shape, 1,1,1,1)
+        
+        line = geometry.line2d(grid_shape, 1,1,1,1)
         self.assertEqual(line, [(1,1)])
         
-        line = gline.line2d(grid_shape, 0,0,1,1)
+        line = geometry.line2d(grid_shape, 0,0,1,1)
         self.assertEqual(line, [(0,0), (0,1), (1,1)])
  
-        line = gline.line2d(grid_shape, 0,0,7,3)
+        line = geometry.line2d(grid_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 = gline.line2d(grid_shape, 4,3,0,3)
+        line = geometry.line2d(grid_shape, 4,3,0,3)
         self.assertEqual(line, [(4,3), (3,2), (2,3), (1,2), (0,3)] )
  
-        line = gline.line2d(grid_shape, 3,0,3,3)
+        line = geometry.line2d(grid_shape, 3,0,3,3)
         self.assertEqual(line, [(3,0), (3,1), (3,2), (3,3)] )
         
         
     def test_squ_line(self):
         """ 2d line on square grid """
         grid_shape = geometry.SQUARE
-        line = gline.line2d(grid_shape,0,0,0,1)
+        line = geometry.line2d(grid_shape,0,0,0,1)
         self.assertEqual(line, [(0,0), (0,1)])
         
-        line = gline.line2d(grid_shape,0,0,1,1)
+        line = geometry.line2d(grid_shape,0,0,1,1)
         self.assertEqual(line, [(0,0), (1,1)])
         
-        line = gline.line2d(grid_shape,0,0,7,3)
+        line = geometry.line2d(grid_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 = gline.line2d(grid_shape,4,3,0,3)
+        line = geometry.line2d(grid_shape,4,3,0,3)
         self.assertEqual(line, [(4,3), (3,3), (2,3), (1,3), (0,3)] )
  
-        line = gline.line2d(grid_shape,3,0,3,3)
+        line = geometry.line2d(grid_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 """
         grid_shape = geometry.HEX
-        line = gline.line3d(grid_shape,1,1,1,1,1,1)
+        line = geometry.line3d(grid_shape,1,1,1,1,1,1)
         self.assertEqual(line, [(1,1,1)])
     
-        line = gline.line3d(grid_shape,1,1,0,1,1,1)
+        line = geometry.line3d(grid_shape,1,1,0,1,1,1)
         self.assertEqual(line, [(1,1,0), (1,1,1)])
     
-        line = gline.line3d(grid_shape,0,0,0,1,1,1)
+        line = geometry.line3d(grid_shape,0,0,0,1,1,1)
         self.assertEqual(line, [(0,0,0), (0,1,0), (1,1,1)])
     
-        line = gline.line3d(grid_shape,0,0,0,7,3,7)
+        line = geometry.line3d(grid_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 = gline.line3d(grid_shape,4,3,10,0,3,3)
+        line = geometry.line3d(grid_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 = gline.line3d(grid_shape,3,0,0,3,3,0)
+        line = geometry.line3d(grid_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 """
         grid_shape = geometry.SQUARE
-        line = gline.line3d(grid_shape,1,1,1,1,1,1)
+        line = geometry.line3d(grid_shape,1,1,1,1,1,1)
         self.assertEqual(line, [(1,1,1)])
     
-        line = gline.line3d(grid_shape,1,1,0,1,1,1)
+        line = geometry.line3d(grid_shape,1,1,0,1,1,1)
         self.assertEqual(line, [(1,1,0), (1,1,1)])
     
-        line = gline.line3d(grid_shape,0,0,0,1,1,1)
+        line = geometry.line3d(grid_shape,0,0,0,1,1,1)
         self.assertEqual(line, [(0,0,0), (1,1,1)])
     
-        line = gline.line3d(grid_shape,0,0,0,7,3,7)
+        line = geometry.line3d(grid_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 = gline.line3d(grid_shape,4,3,10,0,3,3)
+        line = geometry.line3d(grid_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 = gline.line3d(grid_shape,3,0,0,3,3,0)
+        line = geometry.line3d(grid_shape,3,0,0,3,3,0)
         self.assertEqual(line, [(3,0,0), (3,1,0), (3,2,0), (3,3,0)] )
 
 

+ 6 - 7
tests/geometry/test_neighbours.py

@@ -5,8 +5,7 @@ Created on 25 nov. 2016
 '''
 import unittest
 
-from core import geometry
-from core.geometry import gneighbours
+from pypog import geometry
 
 
 class Test(unittest.TestCase):
@@ -15,15 +14,15 @@ class Test(unittest.TestCase):
     def test_neighbours_of(self):
         for coord in ( (0,0), (-10,-10), (10,10) ):
             x, y = coord
-            self.assertEqual( gneighbours.neighbours_of(geometry.HEX, x, y), gneighbours.hex_neighbours_of(x, y) )
-            self.assertEqual( gneighbours.neighbours_of(geometry.SQUARE, x, y), gneighbours.squ_neighbours_of(x, y) )
+            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( gneighbours.hex_neighbours_of(3,3), [(3,2), (4,3), (4,4), (3,4), (2,4), (2,3)] )
-        self.assertCountEqual( gneighbours.hex_neighbours_of(4,4), [(4,3), (5,3), (5,4), (4,5), (3,4), (3,3)] )
+        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( gneighbours.squ_neighbours_of(3,3), [(2,3), (2,2), (3,2), (4,2), (4,3), (4,4), (3,4), (2,4)] )
+        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__":

+ 11 - 12
tests/geometry/test_triangle.py

@@ -5,8 +5,7 @@ Created on 22 nov. 2016
 '''
 import unittest
 
-from core import geometry
-from core.geometry import gtriangle
+from pypog import geometry
 
 
 class Test(unittest.TestCase):
@@ -16,8 +15,8 @@ class Test(unittest.TestCase):
         """test triangle algorithms on square grid"""
         grid_shape = geometry.SQUARE
         
-        for i in gtriangle.ANGLES:
-            self.assertCountEqual(gtriangle.triangle(grid_shape, 0, 0, 0, 0, i), [(0,0)])
+        for i in geometry.ANGLES:
+            self.assertCountEqual(geometry.triangle(grid_shape, 0, 0, 0, 0, i), [(0,0)])
 
         #TODO: complete
 
@@ -25,8 +24,8 @@ class Test(unittest.TestCase):
     def test_hex_triangle(self):
         """test triangle algorithms on hexagonal grid"""
         grid_shape = geometry.HEX
-        for i in gtriangle.ANGLES:
-            self.assertCountEqual(gtriangle.triangle(grid_shape, 0, 0, 0, 0, i), [(0,0)])
+        for i in geometry.ANGLES:
+            self.assertCountEqual(geometry.triangle(grid_shape, 0, 0, 0, 0, i), [(0,0)])
         #TODO: complete
     
     def test_sq_triangle_3d(self):
@@ -42,12 +41,12 @@ class Test(unittest.TestCase):
     def test_errors(self):
         
         for grid_shape in (geometry.HEX, geometry.SQUARE):
-            self.assertRaises(ValueError, gtriangle.triangle, grid_shape, 0, 0, 0, 0, 0)
-            self.assertRaises(TypeError, gtriangle.triangle, grid_shape, "a", 0, 0, 0, 1)
-            self.assertRaises(TypeError, gtriangle.triangle, grid_shape, 0, "a", 0, 0, 1)
-            self.assertRaises(TypeError, gtriangle.triangle, grid_shape, 0, 0, "a", 0, 1)
-            self.assertRaises(TypeError, gtriangle.triangle, grid_shape, 0, 0, 0, "a", 1)
-            self.assertRaises(ValueError, gtriangle.triangle, grid_shape, 0, 0, 0, 0, "a")
+            self.assertRaises(ValueError, geometry.triangle, grid_shape, 0, 0, 0, 0, 0)
+            self.assertRaises(TypeError, geometry.triangle, grid_shape, "a", 0, 0, 0, 1)
+            self.assertRaises(TypeError, geometry.triangle, grid_shape, 0, "a", 0, 0, 1)
+            self.assertRaises(TypeError, geometry.triangle, grid_shape, 0, 0, "a", 0, 1)
+            self.assertRaises(TypeError, geometry.triangle, grid_shape, 0, 0, 0, "a", 1)
+            self.assertRaises(ValueError, geometry.triangle, grid_shape, 0, 0, 0, 0, "a")
     
 
 if __name__ == "__main__":

+ 15 - 16
tests/geometry/test_zone.py

@@ -5,8 +5,7 @@ Created on 20 nov. 2016
 '''
 import unittest
 
-from core import geometry
-from core.geometry import gzone
+from pypog import geometry
 
 
 
@@ -16,30 +15,30 @@ class Test(unittest.TestCase):
 
     def test_hex_zone(self):
         """ test the zone algo for hexagonal grid """
-        grid_shape = geometry.HEX
-        self.assertCountEqual( gzone.zone( grid_shape, 3, 3, 0 ), [(3,3)])
-        self.assertCountEqual( gzone.zone( grid_shape, 3, 3, 1 ), [(3, 2), (2, 3), (3, 3), (4, 3), (4, 4), (3, 4), (2, 4)])
-        self.assertCountEqual( gzone.zone( grid_shape, 3, 3, 2 ), [(3, 2), (1, 3), (5, 4), (4, 5), (1, 4), (2, 3), (4, 2), \
+        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 """
-        grid_shape = geometry.SQUARE
-        self.assertCountEqual( gzone.zone( grid_shape, 3, 3, 0 ), [(3,3)])
-        self.assertCountEqual( gzone.zone( grid_shape, 3, 3, 1 ), [(3, 2), (3, 3), (4, 4), (2, 3), (4, 3), (2, 2), (4, 2), (3, 4), (2, 4)])
-        self.assertCountEqual( gzone.zone( grid_shape, 3, 3, 2 ), [(2, 4), (3, 2), (5, 4), (1, 3), (4, 5), (2, 1), (1, 4), (2, 3), (4, 2), \
+        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, gzone.zone, 5, 0, 0, "a")
-        self.assertRaises( TypeError, gzone.zone, 5, "a", 0, 1)
-        self.assertRaises( TypeError, gzone.zone, 5, 0, "a", 1)
-        self.assertRaises( ValueError, gzone.zone, 5, 0, 0, -1)
-        self.assertRaises( ValueError, gzone.zone, -1, 0, 0, 1)
-        self.assertRaises( ValueError, gzone.zone, "a", 0, 0, 1)
+        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()

+ 1 - 1
tests/gridviewer/GridViewer.py

@@ -14,7 +14,7 @@ from PyQt5.QtWidgets import QMainWindow, \
     QApplication, QGraphicsScene, QGraphicsView, QMessageBox
 import ipdb  # until I find another way to print traceback with pyqt5
 
-from core import geometry
+from pypog import geometry
 from tests.gridviewer.GridViewerCell import GridViewerCell
 from tests.gridviewer.main import Ui_window
 

+ 4 - 5
tests/gridviewer/GridViewerCell.py

@@ -3,14 +3,13 @@ Created on 26 nov. 2016
 
 @author: olinox
 '''
-from PyQt5.QtCore import QPointF, pyqtSignal, QObject
+from PyQt5.QtCore import QPointF
 from PyQt5.QtGui import QPolygonF, QPen, QBrush, QColor, QFont
 from PyQt5.QtWidgets import QGraphicsPolygonItem, QGraphicsItem, \
     QGraphicsSimpleTextItem
 
-from core import geometry
-from core.graphic.cells import polygon
-
+from pypog import geometry
+from pypog import graphic
 
 class GridViewerCell(QGraphicsPolygonItem):
     
@@ -23,7 +22,7 @@ class GridViewerCell(QGraphicsPolygonItem):
         
     def generate(self, shape, scale=120):
         
-        points = [QPointF(xp, yp) for xp, yp in polygon(shape, self.x, self.y, scale)]
+        points = [QPointF(xp, yp) for xp, yp in graphic.polygon(shape, self.x, self.y, scale)]
         qpolygon = QPolygonF( points )
         
         self.setPolygon(qpolygon)

+ 9 - 9
tests/test_grid.py

@@ -5,8 +5,8 @@ Created on 20 nov. 2016
 '''
 import unittest
 
-from core import geometry
-from core.Grid import Grid, SquareGrid, HexGrid
+from pypog import geometry
+from pypog.Grid import Grid, SquareGrid, HexGrid
 
 
 class Test(unittest.TestCase):
@@ -20,16 +20,16 @@ class Test(unittest.TestCase):
         _ = Grid( geometry.HEX, 1, 1 )
         _ = HexGrid( 1, 1 )
 
-    def test_grid_shape(self):
+    def test_cell_shape(self):
         grid = Grid( geometry.SQUARE, 1, 1 )
-        self.assertEqual( grid.grid_shape, geometry.SQUARE )
+        self.assertEqual( grid.cell_shape, geometry.SQUARE )
         
-        grid.grid_shape = geometry.HEX
-        self.assertEqual( grid.grid_shape, geometry.HEX )
+        grid.cell_shape = geometry.HEX
+        self.assertEqual( grid.cell_shape, geometry.HEX )
 
-        def _set_invalid_grid_shape():
-            grid.grid_shape = -1
-        self.assertRaises( ValueError, _set_invalid_grid_shape )
+        def _set_invalid_cell_shape():
+            grid.cell_shape = -1
+        self.assertRaises( ValueError, _set_invalid_cell_shape )
 
     def test_dimensions(self):