Source code for amaze.visu.maze

from abc import ABC, abstractmethod
from enum import Enum, auto
from logging import getLogger
from typing import Optional

from ..misc.resources import SignType
from ..simu.maze import Maze
from ..simu.robot import Robot
from ..simu.types import OutputType

logger = getLogger(__name__)


[docs] class Color(Enum): BACKGROUND = auto() FOREGROUND = auto() START = auto() FINISH = auto() PATH = auto() COLOR1 = auto() COLOR2 = auto() COLOR3 = auto()
[docs] class MazePainter(ABC): @abstractmethod def draw_line(self, x0: float, y0: float, x1: float, y1: float, c: Color): pass @abstractmethod def fill_rect( self, x: float, y: float, w: float, h: float, draw: Color, fill: Color, alpha: float = 1, ): pass @abstractmethod def fill_circle( self, x: float, y: float, r: float, draw: Optional[Color], fill: Optional[Color], ): pass @abstractmethod def draw_image(self, x: float, y: float, w: float, h: float, img): pass @abstractmethod def draw_start(self, x: int, y: int, w: float, h: float, c: Color): pass @abstractmethod def draw_finish(self, x: int, y: int, w: float, h: float, c: Color): pass def render(self, maze: Maze, options: dict): # noinspection PyPep8Naming EAST, NORTH, WEST, SOUTH = [d for d in Maze.Direction] w, h = maze.width, maze.height scale = options["scale"] wall = maze.wall self.draw_line(-1, -1, w * scale, -1, Color.FOREGROUND) self.draw_line(-1, h * scale, w * scale, h * scale, Color.FOREGROUND) self.draw_line(-1, -1, -1, h * scale, Color.FOREGROUND) self.draw_line(w * scale, -1, w * scale, h * scale, Color.FOREGROUND) for i in range(w): for j in range(h): if wall(i, j, EAST): self.draw_line( (i + 1) * scale - 1, j * scale - 1, (i + 1) * scale - 1, (j + 1) * scale, Color.FOREGROUND, ) if wall(i, j, NORTH): self.draw_line( i * scale - 1, (j + 1) * scale - 1, (i + 1) * scale, (j + 1) * scale - 1, Color.FOREGROUND, ) if wall(i, j, WEST): self.draw_line( i * scale, j * scale - 1, i * scale, (j + 1) * scale, Color.FOREGROUND, ) if wall(i, j, SOUTH): self.draw_line( i * scale - 1, j * scale, (i + 1) * scale, j * scale, Color.FOREGROUND, ) if options["solution"] and maze.solution is not None: if maze.unicursive(): ignored_cells = {(i_, j_) for i_ in range(w) for j_ in range(h)} for e in maze.solution: ignored_cells.remove(e) for i, j in ignored_cells: self.fill_rect( i * scale, j * scale, scale, scale, Color.FOREGROUND, Color.FOREGROUND, ) # painter.setPen(Qt.blue) i0, j0 = maze.solution[0] # painter.drawRect(QRectF((i0 + .5) * scale - 1, # (j0 + .5) * scale - 1, 2, 2)) for i1, j1 in maze.solution[1:]: self.draw_line( (i0 + 0.5) * scale, (j0 + 0.5) * scale - 1, (i1 + 0.5) * scale, (j1 + 0.5) * scale - 1, Color.PATH, ) i0, j0 = i1, j1 for t in SignType: positions = maze.signs_data[t] images = options[t.value.lower() + "s"] if images and positions is not None: for vix, six, d, _ in positions: c_i, c_j = maze.solution[six] img = images[vix][d.value] self.draw_image( c_i * scale + 1, c_j * scale + 1, scale - 2, scale - 2, img, ) for f, (i, j), c in [ (self.draw_start, maze.start, Color.START), (self.draw_finish, maze.end, Color.FINISH), ]: f(i * scale, j * scale, scale, scale, c) if r := options.get("robot"): i, j = r["pos"] if options["outputs"] is OutputType.DISCRETE: self.fill_rect( (i - 0.5) * scale, (j - 0.5) * scale, scale - 1, scale - 1, Color.COLOR1, Color.COLOR1, alpha=0.25, ) elif options["outputs"] is OutputType.CONTINUOUS: x, y = scale * r["pos"] self.fill_circle(x, y, Robot.RADIUS * scale, None, Color.COLOR1) if v := r.get("vel"): v *= scale self.draw_line(x, y, x + v.x, y + v.y, Color.COLOR3) if a := r.get("acc"): a *= scale self.draw_line(x, y, x + a.x, y + a.y, Color.COLOR1) else: # pragma: no cover raise ValueError