fractal_koch#

Koch snowflake rendered with WebGL patch.

Builds the classic Koch snowflake boundary by recursively replacing each edge segment with the Koch curve motif, then renders as a single filled polygon to demonstrate complex polygon rendering with WebGL.

import math

from bokeh.plotting import figure, show


def koch_curve(x0, y0, x1, y1, depth):
    """Recursively generate Koch curve points between (x0,y0) and (x1,y1)."""
    if depth == 0:
        return [(x0, y0)]

    dx = x1 - x0
    dy = y1 - y0

    # Divide the segment into thirds
    ax, ay = x0 + dx / 3, y0 + dy / 3
    bx, by = x0 + 2 * dx / 3, y0 + 2 * dy / 3

    # Peak of the equilateral triangle
    px = (ax + bx) / 2 + math.sqrt(3) / 6 * (y0 - y1)
    py = (ay + by) / 2 + math.sqrt(3) / 6 * (x1 - x0)

    points = []
    points.extend(koch_curve(x0, y0, ax, ay, depth - 1))
    points.extend(koch_curve(ax, ay, px, py, depth - 1))
    points.extend(koch_curve(px, py, bx, by, depth - 1))
    points.extend(koch_curve(bx, by, x1, y1, depth - 1))

    return points


depth = 6

# Start with an equilateral triangle
angle = 2 * math.pi / 3
vertices = [(math.cos(math.pi / 2 + i * angle), math.sin(math.pi / 2 + i * angle)) for i in range(3)]

# Build the Koch snowflake boundary
boundary = []
for i in range(3):
    x0, y0 = vertices[i]
    x1, y1 = vertices[(i + 1) % 3]
    boundary.extend(koch_curve(x0, y0, x1, y1, depth))

xs = [pt[0] for pt in boundary]
ys = [pt[1] for pt in boundary]

n_points = len(xs)

p = figure(
    title=f"Koch Snowflake (depth={depth}, {n_points} boundary points, WebGL)",
    width=700, height=700,
    output_backend="webgl",
    match_aspect=True,
    tools="pan,wheel_zoom,reset",
)
p.patch(
    x=xs, y=ys,
    fill_color="steelblue", fill_alpha=0.7,
    line_color="navy", line_width=1,
)

show(p)