"""A Sierpinski triangle, drawn as SVG. Plain Python, nothing to install.

    python3 sierpinski.py

writes sierpinski.svg (7 steps) and sierpinski-steps.svg (steps 0 to 3).
From Geometridae's blog, "A simple fractal".
"""
import re


def midpoint(p, q):
    return ((p[0] + q[0]) / 2, (p[1] + q[1]) / 2)


def sierpinski(a, b, c, depth):
    """The triangles left after cutting out the middle, depth times."""
    if depth == 0:
        return [(a, b, c)]
    ab, bc, ca = midpoint(a, b), midpoint(b, c), midpoint(c, a)
    return (sierpinski(a, ab, ca, depth - 1)
            + sierpinski(ab, b, bc, depth - 1)
            + sierpinski(ca, bc, c, depth - 1))


def svg(triangles, size=300):
    shapes = ''.join(
        '<polygon points="' + ' '.join(f'{x:.1f},{y:.1f}' for x, y in t) + '" fill="#c9738f"/>'
        for t in triangles)
    return f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {size} {size}">{shapes}</svg>'


def side_by_side(drawings, size=300, gap=20):
    """Several square SVGs in one row."""
    width = len(drawings) * size + (len(drawings) - 1) * gap
    inside = ''.join(f'<g transform="translate({i * (size + gap)},0)">'
                     + re.sub(r'^<svg[^>]*>|</svg>$', '', d) + '</g>'
                     for i, d in enumerate(drawings))
    return f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {width} {size}">{inside}</svg>'


if __name__ == '__main__':
    a, b, c = (150, 20), (10, 262), (290, 262)
    with open('sierpinski.svg', 'w') as f:
        f.write(svg(sierpinski(a, b, c, 7)).replace('<svg ', '<svg width="300" ', 1))
    with open('sierpinski-steps.svg', 'w') as f:
        f.write(side_by_side([svg(sierpinski(a, b, c, n)) for n in range(4)]))
    # try your own: a different depth, other corners, another colour ...
