renderer.py

7.1 kB · python · 174 lines

1import numpy as np2from PIL import Image, ImageDraw3from typing import Dict, List, Optional, TYPE_CHECKING4from mrlypy.core.colors import Color56if TYPE_CHECKING:7    from .models import Cell2d89# IMAGE1011def to_image(cell: "Cell2d", scale: int = 1) -> Image.Image:12    array = cell.colors13    if scale > 1:14        array = array.repeat(scale, axis=0).repeat(scale, axis=1)15    return Image.fromarray(array, "RGBA")1617def from_image(image: Image.Image) -> "Cell2d":18    from .models import Cell2d19    array = np.array(image)20    return Cell2d(colors=array)2122# TEXT2324def text_2d(grid: np.ndarray, mapping: Optional[Dict[int, str]] = None) -> List[str]:25    rows = []26    height, width = grid.shape27    for y in range(height):28        row_str = ""29        for x in range(width):30            val = grid[y, x]31            if mapping:32                row_str += mapping.get(val, str(val))33            else:34                row_str += str(val)35        rows.append(row_str)36    return rows3738# DRAW3940def draw_square(cell: "Cell2d", scale: int = 1, outline: Optional["Color"] = None, width: int = 1) -> Image.Image:41    padding = width if outline else 042    img_width = (cell.width * scale) + (padding * 2)43    img_height = (cell.height * scale) + (padding * 2)44    image = Image.new("RGBA", (int(img_width), int(img_height)), (0, 0, 0, 0))45    draw_ctx = ImageDraw.Draw(image)46    outline_rgba = outline.to_rgba() if outline else None47    colors_grid = cell.colors48    for y in range(cell.height):49        for x in range(cell.width):50            r, g, b, a = colors_grid[y, x]51            if a == 0:52                continue53            x0 = (x * scale) + padding54            y0 = (y * scale) + padding55            x1 = x0 + scale56            y1 = y0 + scale57            draw_ctx.rectangle([x0, y0, x1, y1], fill=(r, g, b, a), outline=outline_rgba, width=width)58    return image5960def draw_circle(cell: "Cell2d", scale: int = 1, outline: Optional["Color"] = None, width: int = 1) -> Image.Image:61    padding = width if outline else 062    img_width = (cell.width * scale) + (padding * 2)63    img_height = (cell.height * scale) + (padding * 2)64    image = Image.new("RGBA", (int(img_width), int(img_height)), (0, 0, 0, 0))65    draw_ctx = ImageDraw.Draw(image)66    outline_rgba = outline.to_rgba() if outline else None67    colors_grid = cell.colors68    for y in range(cell.height):69        for x in range(cell.width):70            r, g, b, a = colors_grid[y, x]71            if a == 0:72                continue73            x0 = (x * scale) + padding74            y0 = (y * scale) + padding75            x1 = x0 + scale76            y1 = y0 + scale77            draw_ctx.ellipse([x0, y0, x1, y1], fill=(r, g, b, a), outline=outline_rgba, width=width)78    return image7980def draw_diamond(cell: "Cell2d", scale: int = 1, outline: Optional["Color"] = None, width: int = 1) -> Image.Image:81    padding = width if outline else 082    img_width = (cell.width * scale) + (padding * 2)83    img_height = (cell.height * scale) + (padding * 2)84    image = Image.new("RGBA", (int(img_width), int(img_height)), (0, 0, 0, 0))85    draw_ctx = ImageDraw.Draw(image)86    outline_rgba = outline.to_rgba() if outline else None87    colors_grid = cell.colors88    half_scale = scale // 289    for y in range(cell.height):90        for x in range(cell.width):91            r, g, b, a = colors_grid[y, x]92            if a == 0:93                continue94            cx = (x * scale) + padding95            cy = (y * scale) + padding96            pts = [97                (cx + half_scale, cy),98                (cx + scale, cy + half_scale),99                (cx + half_scale, cy + scale),100                (cx, cy + half_scale)101            ]102            draw_ctx.polygon(pts, fill=(r, g, b, a), outline=outline_rgba, width=width)103    return image104105# SVG106107def svg_square(cell: "Cell2d", scale: int = 1, outline: Optional["Color"] = None, width: int = 1) -> str:108    padding = width if outline else 0109    img_width = (cell.width * scale) + (padding * 2)110    img_height = (cell.height * scale) + (padding * 2)111    outline_hex = outline.to_hex() if outline else None112    stroke_attr = f'stroke="{outline_hex}" stroke-width="{width}"' if outline_hex else 'stroke="none"'113    elements = [f'<svg width="{int(img_width)}" height="{int(img_height)}" xmlns="http://www.w3.org/2000/svg">']114    colors_grid = cell.colors115    for y in range(cell.height):116        for x in range(cell.width):117            r, g, b, a = colors_grid[y, x]118            if a == 0:119                continue120            fill_color = Color.rgba_to_hex(r, g, b, a)121            rect_x = (x * scale) + padding122            rect_y = (y * scale) + padding123            elements.append(f'<rect x="{int(rect_x)}" y="{int(rect_y)}" width="{int(scale)}" height="{int(scale)}" fill="{fill_color}" {stroke_attr}/>')124    elements.append('</svg>')125    return "\n".join(elements)126127def svg_circle(cell: "Cell2d", scale: int = 1, outline: Optional["Color"] = None, width: int = 1) -> str:128    padding = width if outline else 0129    img_width = (cell.width * scale) + (padding * 2)130    img_height = (cell.height * scale) + (padding * 2)131    outline_hex = outline.to_hex() if outline else None132    stroke_attr = f'stroke="{outline_hex}" stroke-width="{width}"' if outline_hex else 'stroke="none"'133    radius = scale // 2134    elements = [f'<svg width="{int(img_width)}" height="{int(img_height)}" xmlns="http://www.w3.org/2000/svg">']135    colors_grid = cell.colors136    for y in range(cell.height):137        for x in range(cell.width):138            r, g, b, a = colors_grid[y, x]139            if a == 0:140                continue141            cx = (x * scale) + padding + radius142            cy = (y * scale) + padding + radius143            fill_color = Color.rgba_to_hex(r, g, b, a)144            elements.append(f'<circle cx="{int(cx)}" cy="{int(cy)}" r="{int(radius)}" fill="{fill_color}" {stroke_attr}/>')145    elements.append('</svg>')146    return "\n".join(elements)147148def svg_diamond(cell: "Cell2d", scale: int = 1, outline: Optional["Color"] = None, width: int = 1) -> str:149    padding = width if outline else 0150    img_width = (cell.width * scale) + (padding * 2)151    img_height = (cell.height * scale) + (padding * 2)152    outline_hex = outline.to_hex() if outline else None153    stroke_attr = f'stroke="{outline_hex}" stroke-width="{width}"' if outline_hex else 'stroke="none"'154    half_scale = scale // 2155    elements = [f'<svg width="{int(img_width)}" height="{int(img_height)}" xmlns="http://www.w3.org/2000/svg">']156    colors_grid = cell.colors157    for y in range(cell.height):158        for x in range(cell.width):159            r, g, b, a = colors_grid[y, x]160            if a == 0:161                continue162            cx = (x * scale) + padding163            cy = (y * scale) + padding164            pts = [165                (cx + half_scale, cy),166                (cx + scale, cy + half_scale),167                (cx + half_scale, cy + scale),168                (cx, cy + half_scale)169            ]170            pts_str = " ".join([f"{int(p[0])},{int(p[1])}" for p in pts])171            fill_color = Color.rgba_to_hex(r, g, b, a)172            elements.append(f'<polygon points="{pts_str}" fill="{fill_color}" {stroke_attr}/>')173    elements.append('</svg>')174    return "\n".join(elements)