def ellipse(ctx: cairo.Context, cell_structure: mat.CellStructure, r: float = 2) -> None: """ Draw an ellipse in the given context. Drawing is centered relative to cell structure, and semi-major is aligned with vertical coordinate axis. :param ctx: Current context. :param cell_structure: Assumptions about structure of the cell being drawn. :param r: Semi-major over semi-minor, must be > 2. """ if not 2 <= r: raise ValueError() width, height = _get_dims(cell_structure) ctx.save() ctx.translate(cell_structure.width / 2., cell_structure.height / 2.) ctx.scale(width / (2 * r), height / 2) ctx.new_sub_path() ctx.arc(0., 0., 1., 0., 2 * math.pi) ctx.restore()
def rounded_rect(ctx: Context, x, y, width, height, radius): def deg(value): return value * math.pi / 180.0 ctx.new_sub_path() ctx.arc(x + width - radius, y + radius, radius, deg(-90), deg(0)) ctx.arc(x + width - radius, y + height - radius, radius, deg(0), deg(90)) ctx.arc(x + radius, y + height - radius, radius, deg(90), deg(180)) ctx.arc(x + radius, y + radius, radius, deg(180), deg(270)) ctx.close_path()
def _show_hand(self, cr: cairo.Context, x: float, y: float, angle: float, radius: float) -> None: if radius == 0.0: return cr.move_to(x, y) cr.rel_line_to(radius * math.cos(angle), radius * math.sin(angle)) cr.new_sub_path()
class Canvas: def __init__(self, width: int, height: Optional[int] = None, *, normalise: bool = True): if height is None: height = width self._width = width self._height = height self._surface = ImageSurface(FORMAT_ARGB32, width, height) self._context = Context(self._surface) self._normalise = normalise if self._normalise: self.scale(self._width, self._height) @property def width(self) -> int: return self._width if not self._normalise else 1 @property def height(self) -> int: return self._height if not self._normalise else 1 # TODO depreciate @property def context(self) -> Context: # pragma: no cover return self._context def save(self) -> None: self._context.save() def restore(self) -> None: self._context.restore() # Transformation def rotate(self, angle: float) -> None: self._context.rotate(angle) def translate(self, x: float, y: float) -> None: self._context.translate(x, y) def scale(self, width: int, height: int) -> None: self._context.scale(width, height) def set_line_width(self, width: float) -> None: self._context.set_line_width(width) # Colour functionality def set_colour(self, colour: Colour) -> None: self._context.set_source_rgba(colour.red, colour.blue, colour.green, colour.alpha) def set_grey(self, colour_value: float, alpha: float = 1) -> None: colour = Colour(*(colour_value, ) * 3, alpha) self.set_colour(colour) def set_black(self) -> None: self.set_colour(BLACK) def set_white(self) -> None: self.set_colour(WHITE) def set_background(self, colour: Colour) -> None: self._context.rectangle(0, 0, self.width, self.height) self.set_colour(colour) self._context.fill() def set_line_cap(self, line_cap: LineCap) -> None: self._context.set_line_cap(line_cap.value) def set_line_join(self, line_join: LineJoin) -> None: self._context.set_line_join(line_join.value) def set_fill_rule(self, file_rule: FillRule) -> None: self._context.set_fill_rule(file_rule.value) # Render methods def fill(self, preserve: bool = False) -> None: if not preserve: self._context.fill() else: self._context.fill_preserve() def stroke(self, preserve: bool = False) -> None: if not preserve: self._context.stroke() else: self._context.stroke_preserve() def clip(self) -> None: self._context.clip() def _draw_path(self, path: Iterable[Point], close_path: bool) -> None: self._context.new_sub_path() for p in path: self._context.line_to(*p) if close_path: self._context.close_path() def draw_path(self, path: Iterable[PointType], close_path: bool = False) -> None: points = (p if isinstance(p, Point) else Point(*p) for p in path) self._draw_path(points, close_path) def draw_polygon(self, polygon: Polygon) -> None: self._draw_path(polygon.points, close_path=True) def draw_circle(self, circle: Circle, fill: bool = False, stroke: bool = True) -> None: self._context.new_sub_path() self._context.arc(*circle.centre, circle.radius, 0, 2 * pi) def write_to_png(self, file_name: str) -> None: self._surface.write_to_png(file_name)