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)