def test_conversion(self): a = Pos(10, 15) b = Pos(12, 21) tmp = a.grid_cr() self.assertEquals(tmp.x, 1) self.assertEquals(tmp.y, 0) tmp = a.view_xy() self.assertEquals(tmp.x, 100) self.assertEquals(tmp.y, 240) a.snap_to_grid() self.assertEquals(a.x, 10) self.assertEquals(a.y, 0) b = Pos(10, 16) tmp = b.grid_cr() self.assertEquals(tmp.x, 1) self.assertEquals(tmp.y, 1) tmp = b.view_xy() self.assertEquals(tmp.x, 100) self.assertEquals(tmp.y, 256) b.snap_to_grid() self.assertEquals(b.x, 10) self.assertEquals(b.y, 16) c = Pos(11, 17) tmp = c.grid_cr() self.assertEquals(tmp.x, 1) self.assertEquals(tmp.y, 1) tmp = c.view_xy() self.assertEquals(tmp.x, 110) self.assertEquals(tmp.y, 272) c.snap_to_grid() self.assertEquals(c.x, 10) self.assertEquals(c.y, 16)
class Symbol(object): """ Symbol represented by a grid. :param id: the component id :param dict: the character-grid to create the symbol :param ori: orientation (0-3) :param mirrored: set to 1 to mirror the symbol vertically :param startpos: the upper-left corner (col,row) coordinate of the character-grid :param endpos: used in subclasses, e.g. Line """ ORIENTATION = {0: "N", 1: "E", 2: "S", 3: "W"} def __init__(self, id=0, grid=None, ori=None, mirrored=None, startpos=None, endpos=None): self._id = id self._has_pickpoint = True if ori is None: self._ori = 0 else: self._ori = ori if mirrored is None: self._mirrored = 0 else: self._mirrored = mirrored if grid is None: self._grid = self.default else: self._grid = grid if startpos is None: self._startpos = Pos(0, 0) else: self._startpos = startpos if endpos is None: self._endpos = Pos(0, 0) else: self._endpos = endpos self._is_symbol = True self._is_text = False self._is_line = False def __str__(self): str = _("Class: {0} id: {1} ori: {2} startpos: {3}").format( self.__class__.__name__, self._id, self.ORIENTATION[self._ori], self.startpos) return str def _representation(self): self._repr = dict() pos = self._startpos incr = Pos(1, 0) for row in self.grid: pos.x = self._startpos.x for char in row: if char != ' ': self._repr[pos] = char pos += incr pos += Pos(0, 1) @property def name(self): return self.__class__.__name__ @property def is_symbol(self): return self._is_symbol @property def is_text(self): return self._is_text @property def is_line(self): return self._is_line @property def has_pickpoint(self): return self._has_pickpoint @property def pickpoint_pos(self): """ Return the pick position. Examples, where '.' represents a space character and 'x' the pick-point: '.|.....' => 'x|.....' '....|..' => '...x|..' '|......' => 'x|......' '.......' => '.......x' => empty first line not expected, see the component library content """ first_row = self.grid[0] found = re.search(r'\S', first_row) if found: x_offset = found.start() x_offset -= 1 else: x_offset = 0 pos = Pos(self._startpos.x + x_offset, self._startpos.y) return pos @property def id(self): return self._id @property def ori_as_str(self): return Symbol.ORIENTATION[self._ori] @property def ori(self): return self._ori @ori.setter def ori(self, value): # orientation can be set as the grid is dynamically selected (in grid() method) if value in (0, 1, 2, 3): self._ori = value @property def mirrored(self): return self._mirrored @mirrored.setter def mirrored(self, value): """Set to True to show the symbol vertically mirrored, otherwise False.""" self._mirrored = value @property def startpos(self): return self._startpos @startpos.setter def startpos(self, value): self._startpos = value @property def endpos(self): return self._endpos @endpos.setter def endpos(self, value): self._endpos = value @property def default(self): # default provides one ("N" orientation) grid only grid = {'N': [' ___ ', '|__ \\', ' / /', ' |_| ', ' (_) ']} return grid @property def repr(self): """ The representation in ASCII characters of the symbol on a grid. :returns a dictionary of positions in grid (col,row) coordinates and the character to be shown on each position. """ return self._repr def memo(self): """Return entry for the actions as recorded in the memo.""" str = "{0}:{1},{2},{3},{4}".format(COMPONENT, self._id, self._ori, self._mirrored, self._startpos) return str def copy(self): ori = copy.deepcopy(self._ori) mirrored = copy.deepcopy(self._mirrored) startpos = copy.deepcopy(self._startpos) endpos = copy.deepcopy(self._endpos) return Symbol(id=self._id, grid=self._grid, ori=ori, mirrored=mirrored, startpos=startpos, endpos=endpos) @property def grid(self): try: if self._mirrored == 1: return self.mirror(self._grid[self.ORIENTATION[self._ori]]) else: return self._grid[self.ORIENTATION[self._ori]] except KeyError: return self.default[self.ORIENTATION[0]] def rotate(self): """Return the grid with the next (90 degrees clockwise rotated) orientation for this symbol.""" self._ori += 1 self._ori %= 4 return self.grid def draw(self, ctx, pos=None): """ Draw the symbol on the grid canvas. :param ctx: the Cairo context :param pos: target position in grid canvas (x,y) coordinates """ self._representation() if pos is None: pos = self._startpos.view_xy() offset = pos - self._startpos.view_xy() for pos, char in self._repr.items(): grid_pos = pos.view_xy() + offset show_text(ctx, grid_pos.x, grid_pos.y, char) def paste(self, grid): """Paste the symbol in the target grid at its start position.""" self._representation() for pos, value in self._repr.items(): grid.set_cell(pos, value) def remove(self, grid): """Remove the symbol from the target grid.""" self._representation() for pos in self._repr.keys(): grid.set_cell(pos, CELL_ERASE) def mirror(self, grid): """Return the symbol grid vertically mirrored.""" # mirror specific characters switcher = { '/': '\\', '\\': '/', '<': '>', '>': '<', '(': ')', ')': '(' } mir_grid = [] for r, row in enumerate(grid): rev = "" for c in reversed(row): try: rev += switcher[c] except KeyError: rev += c mir_grid.append(rev) return mir_grid