"""The deepening fold - revision 4.

One constant-width band folds across the square, space-filling-curve
style, stepping down in fold size by a fixed ratio three times. The
scale regions are rebalanced along the diagonal: the broadest red folds
hold the whole upper-left half and reach past the middle of the square,
the magenta and violet hand-offs are compressed into one narrow
transition band astride the diagonal, and the fine blue weave is
deepened so it fills the whole lower-right quadrant and climbs the
right and bottom edges toward the middle. The scale regions are
deliberately unequal along the diagonal, so the fold reads as one
decisive gesture rather than even blocks marching corner to corner.
Ragged fringes carry residual coarse turns into each finer region;
every hand-off is routed as an orthogonal elbow.

Python 3 + Pillow. Standalone. Writes render-1.png beside itself.
"""

import math
import os
from PIL import Image, ImageDraw

SEED = 20261029

# Canvas
W = 2500
FIELD = 2048                      # drawing field, centred
OFF = (W - FIELD) // 2            # 226 -> ink stays >= 200 px from edges (> 125)
WIDTH = 22                        # constant band width (>= 9; finest pitch 32, gap 10)

# Four scales, fold size steps down by the fixed ratio 2 three times.
SIZES = [256, 128, 64, 32]

# Diagonal coordinate thresholds (t in [0,1] from the coarse corner to the
# fine corner) where the target scale steps down. Revision 4: the coarse red
# opening is unchanged; the transition band is compressed a touch further and
# the fine blue territory is deepened from t > 0.78 to t > 0.70, so the weave
# holds the whole lower-right quadrant and the closing corner lands firmly
# against the coarse opening.
THRESHOLDS = [0.50, 0.62, 0.70]
FRINGE = 0.12                     # raggedness of each hand-off, in t units

# Flexoki swatches, inlined as literals.
GROUND = "#100F0F"                # flexoki black
INKS = ["#D14D41",                # red-400    (broadest folds)
        "#CE5D97",                # magenta-400
        "#8B7EC8",                # purple-400
        "#4385BE"]                # blue-400   (finest weave)


def noise(ix, iy):
    """Deterministic hash in [-1, 1) for the ragged hand-off fringe."""
    v = math.sin(ix * 127.1 + iy * 311.7 + SEED * 0.6180339887) * 43758.5453
    return (v - math.floor(v)) * 2.0 - 1.0


def raw_level(cx, cy):
    """Desired scale level (0 coarse .. 3 fine) from diagonal position."""
    t = (cx + cy - 2.0 * OFF) / (2.0 * FIELD)
    ix = round((cx - OFF) / 16.0)
    iy = round((cy - OFF) / 16.0)
    t += FRINGE * noise(ix, iy)
    return sum(t > th for th in THRESHOLDS)


def target_level(cx, cy, size):
    """Region allocation with one admissibility rule.

    A single-cell violet pocket wholly surrounded by coarser territory is
    absorbed into its surroundings: a pocket that small lets the band dive
    in, loop a two-by-two of fine cells and climb out over its own track,
    so the allocation refuses it and the ragged fringe stays retrace-free.
    """
    lev = raw_level(cx, cy)
    if lev == 2 and size <= 128:
        for dx, dy in ((128, 0), (-128, 0), (0, 128), (0, -128)):
            if raw_level(cx + dx, cy + dy) >= 2:
                return lev
        return 1
    return lev


def hilbert(x, y, xi, xj, yi, yj, size, out):
    """Adaptive Hilbert traversal; appends (cx, cy, size) of each leaf."""
    cx = x + (xi + yi) / 2.0
    cy = y + (xj + yj) / 2.0
    if size <= SIZES[0]:
        level = int(round(math.log2(SIZES[0] / size)))
        if size <= SIZES[-1] or level >= target_level(cx, cy, size):
            out.append((cx, cy, size))
            return
    h = size / 2.0
    hilbert(x, y, yi / 2, yj / 2, xi / 2, xj / 2, h, out)
    hilbert(x + xi / 2, y + xj / 2, xi / 2, xj / 2, yi / 2, yj / 2, h, out)
    hilbert(x + xi / 2 + yi / 2, y + xj / 2 + yj / 2,
            xi / 2, xj / 2, yi / 2, yj / 2, h, out)
    hilbert(x + xi / 2 + yi, y + xj / 2 + yj,
            -yi / 2, -yj / 2, -xi / 2, -xj / 2, h, out)


def level_of(size):
    return int(round(math.log2(SIZES[0] / size)))


def orthogonal_segments(pts):
    """Turn the leaf-centre polyline into axis-aligned segments only.

    Consecutive leaves are always edge-adjacent. Same size: one straight
    segment. Mixed sizes: the centres are offset in both axes, so route
    through one elbow waypoint - dominant axis first - and the corner of
    the L lands exactly on the shared cell edge. The fold never draws a
    diagonal at any scale.
    """
    segs = []
    for (x0, y0, s0), (x1, y1, s1) in zip(pts, pts[1:]):
        lev = max(level_of(s0), level_of(s1))  # coarse colour carries through
        dx, dy = x1 - x0, y1 - y0
        if dx == 0 or dy == 0:
            segs.append((lev, x0, y0, x1, y1))
        elif abs(dx) >= abs(dy):
            segs.append((lev, x0, y0, x1, y0))
            segs.append((lev, x1, y0, x1, y1))
        else:
            segs.append((lev, x0, y0, x0, y1))
            segs.append((lev, x0, y1, x1, y1))
    return segs


def main():
    pts = []
    hilbert(OFF, OFF, FIELD, 0, 0, FIELD, FIELD, pts)

    img = Image.new("RGB", (W, W), GROUND)
    dr = ImageDraw.Draw(img)
    r = WIDTH / 2.0
    inks = [tuple(int(h[i:i + 2], 16) for i in (1, 3, 5)) for h in INKS]

    segs = orthogonal_segments(pts)
    # Draw coarse levels first so finer weaves sit cleanly on top at meets.
    segs.sort(key=lambda s: s[0])
    for lev, x0, y0, x1, y1 in segs:
        c = inks[lev]
        dr.line([x0, y0, x1, y1], fill=c, width=WIDTH)
        dr.ellipse([x0 - r, y0 - r, x0 + r, y0 + r], fill=c)
        dr.ellipse([x1 - r, y1 - r, x1 + r, y1 + r], fill=c)

    out = os.path.join(os.path.dirname(os.path.abspath(__file__)), "render-1.png")
    img.save(out)
    print("points:", len(pts), "segments:", len(segs), "->", out)


if __name__ == "__main__":
    main()
