SVG Tile Renderer

Table of Contents

A simple SVG renderer for 2D grids. Grids cells can contain references to tiles defined as SVG elements of size 1x1.

Implementation

from dataclasses import dataclass, field

@dataclass
class Renderer:
    width: int
    height: int
    patterns: dict[str, str] = field(default_factory=dict)
    _grid: dict[(int, int), str] = field(default_factory=dict)

    def set_tile(self, x: int, y: int, key: str):
        self._grid[(x, y)] = key

    def _format_pattern(self, key: str, value: str):
        return f'''
        <symbol id="{key}" viewBox="0 0 1 1">
          {value}
        </symbol>
        '''

    def _format_outline(self, width: int, height: int):
        return f'''
        <rect x="0" y="0"
              width="{width}" height="{height}"
              stroke="gray" fill="none"
        />
        '''

    def _format_tile(self, x: int, y: int, key: str, tile_size: int):
        return f'''
        <use x="{x * tile_size}" y="{y * tile_size}"
             width="{tile_size}" height="{tile_size}"
             href="#{key}"
        />
        '''

    def _format(self, tile_size: int):
        width = self.width * tile_size
        height = self.height * tile_size

        yield f'<svg xmlns="http://www.w3.org/2000/svg" width="{width}" height="{height}">\n'

        yield '<defs>'
        for key, value in self.patterns.items():
            yield self._format_pattern(key, value)
        yield '</defs>'
        yield self._format_outline(width, height)

        for (x, y), key in self._grid.items():
            yield self._format_tile(x, y, key, tile_size)

        yield '</svg>'

    def render_file(self, path: str, tile_size: int):
        with open(path, "w") as f:
            for line in self._format(tile_size):
                f.write(line)

    def render_inline(self, tile_size: int) -> str:
        return "\n".join(self._format(tile_size))

Example

<<svg_tile_renderer>>

r = Renderer(6, 3)
r.patterns["v"] = '<line x1="0.5" y1="0" x2="0.5" y2="1" stroke="black" stroke-width="0.1"/>'
r.patterns["h"] = '<line x1="0" y1="0.5" x2="1" y2="0.5" stroke="black" stroke-width="0.1"/>'
r.patterns["c"] = '<circle cx="0.5" cy="0.5" r="0.5" fill="red"/>'

r.set_tile(0, 0, "v")
r.set_tile(0, 1, "c")
r.set_tile(1, 1, "h")
r.set_tile(2, 1, "h")
r.set_tile(3, 1, "h")
r.set_tile(4, 1, "c")
r.set_tile(5, 1, "h")
r.set_tile(4, 2, "v")
r.render_file("images/svg_tiles/test.svg", tile_size=40)
test.svg

If you have an idea how this page could be improved or a comment send me a mail.