#!/usr/bin/env python3
"""Cedar Cup Studio - FICTIONAL demo loop.
4-second 720x720 silent seamless loop: three clay-coloured circles orbiting.
Exactly 120 frames at 30 fps. Phase = i / N, so frame N would equal frame 0;
frame N is never rendered (no duplicated endpoint).
"""
import math, subprocess, json
from PIL import Image, ImageDraw

W = H = 720; FPS = 30; N = 120; SS = 4  # 4x supersampling for antialiasing
BG = (245, 239, 227)                     # warm ivory
CLAY = [(184, 100, 62), (201, 139, 94), (158, 91, 60)]
ORBIT_R = 150; DOT_R = 54
OUT = "cedar_cup_orbit_loop_720_120f.mp4"

def render(i):
    ph = i / N                            # 0 <= ph < 1, index-derived only
    im = Image.new("RGB", (W * SS, H * SS), BG); d = ImageDraw.Draw(im)
    cx = cy = W * SS / 2
    # faint orbit guide
    d.ellipse([cx - ORBIT_R * SS, cy - ORBIT_R * SS, cx + ORBIT_R * SS, cy + ORBIT_R * SS],
              outline=(226, 214, 196), width=3 * SS)
    for k, col in enumerate(CLAY):
        a = 2 * math.pi * (ph + k / 3) - math.pi / 2
        # gentle size breathing, two cycles per loop (still periodic in ph)
        r = DOT_R * (1 + 0.06 * math.sin(2 * math.pi * (2 * ph + k / 3)))
        x = cx + ORBIT_R * SS * math.cos(a); y = cy + ORBIT_R * SS * math.sin(a)
        d.ellipse([x - r * SS, y - r * SS, x + r * SS, y + r * SS], fill=col)
    return im.resize((W, H), Image.LANCZOS)

def main():
    cmd = ["ffmpeg", "-y", "-loglevel", "error", "-f", "rawvideo", "-pix_fmt", "rgb24",
           "-s", f"{W}x{H}", "-r", str(FPS), "-i", "-", "-an", "-c:v", "libx264",
           "-preset", "slow", "-crf", "14", "-pix_fmt", "yuv420p", "-movflags", "+faststart", OUT]
    pr = subprocess.Popen(cmd, stdin=subprocess.PIPE)
    for i in range(N):
        f = render(i)
        if i in (117, 118, 119, 0, 1, 2):
            f.save(f"loop_{i:03d}.png")
        pr.stdin.write(f.tobytes())
    pr.stdin.close(); pr.wait()
    # seam check: wrap step 119->0 vs ordinary step 0->1 (mean abs pixel diff)
    import numpy as np
    A = lambda j: np.asarray(render(j % N), dtype=np.int16)
    seam = float(np.abs(A(119) - A(0)).mean()); step = float(np.abs(A(0) - A(1)).mean())
    dup = float(np.abs(A(N) - A(0)).mean())  # virtual frame 120 vs frame 0 (should be 0)
    meta = {"frames_rendered": N, "seam_diff_119_to_0": seam, "normal_diff_0_to_1": step,
            "virtual_frame120_vs_frame0": dup}
    json.dump(meta, open("loop_meta.json", "w"), indent=2); print(json.dumps(meta))

if __name__ == "__main__":
    main()
