fractal_sierpinski#

Sierpinski triangle rendered with WebGL patches.

Recursively subdivides a triangle into smaller triangles, demonstrating WebGL-accelerated rendering of many filled polygons.

import numpy as np

from bokeh.palettes import Viridis256
from bokeh.plotting import figure, show


def sierpinski(ax, ay, bx, by, cx, cy, depth, max_depth, xs, ys, colors):
    """Recursively generate Sierpinski triangle patches."""
    if depth >= max_depth:
        xs.append([ax, bx, cx])
        ys.append([ay, by, cy])
        # Color by depth and position
        idx = int(((ax + bx + cx) / 3 + 1) / 2 * 200 + depth * 10) % 256
        colors.append(Viridis256[idx])
        return

    # Midpoints
    abx, aby = (ax + bx) / 2, (ay + by) / 2
    bcx, bcy = (bx + cx) / 2, (by + cy) / 2
    acx, acy = (ax + cx) / 2, (ay + cy) / 2

    # Three sub-triangles (skip the middle one to create the hole)
    sierpinski(ax, ay, abx, aby, acx, acy, depth + 1, max_depth, xs, ys, colors)
    sierpinski(abx, aby, bx, by, bcx, bcy, depth + 1, max_depth, xs, ys, colors)
    sierpinski(acx, acy, bcx, bcy, cx, cy, depth + 1, max_depth, xs, ys, colors)


xs, ys, colors = [], [], []
sierpinski(-1, 0, 1, 0, 0, np.sqrt(3), 0, 9, xs, ys, colors)

p = figure(
    title=f"Sierpinski Triangle ({len(xs)} patches, WebGL)",
    width=800, height=700,
    output_backend="webgl",
    match_aspect=True,
    tools="pan,wheel_zoom,reset",
)
p.patches(xs=xs, ys=ys, fill_color=colors, line_color=None, fill_alpha=0.9)

show(p)