#!/usr/bin/env python3
"""Slice the STL at every 0.4mm and save each layer as PNG."""
import struct, numpy as np
from PIL import Image, ImageDraw

path = "/app/projects/esp32-box/original/Window 20x60mm.stl"

with open(path, "rb") as f:
    f.read(80)
    n = struct.unpack("<I", f.read(4))[0]
    tris = []
    for i in range(n):
        f.read(12)
        tri = np.array([struct.unpack("<fff", f.read(12)) for _ in range(3)])
        f.read(2)
        tris.append(tri)
tris = np.array(tris)

# Center XY
cx = tris[:,:,0].mean()
cy = tris[:,:,1].mean()
tris[:,:,0] -= cx
tris[:,:,1] -= cy

W = 33.9; H = 13.9; T = 4.8
MARGIN = 3

OUT = "/app/projects/esp32-box/exports/slices"
import os
os.makedirs(OUT, exist_ok=True)

# Slice at each Z level (0.2mm step for detail)
z_levels = np.arange(0, T + 0.01, 0.2)

def slice_at_z(tris, z, tol=0.05):
    """Get XY outline at a given Z level."""
    segments = []
    for tri in tris:
        zs = tri[:, 2]
        for i in range(3):
            j = (i+1) % 3
            if min(zs[i], zs[j]) - tol <= z <= max(zs[i], zs[j]) + tol:
                if abs(zs[j] - zs[i]) > 0.001:
                    t = (z - zs[i]) / (zs[j] - zs[i])
                    x = tri[i,0] + t * (tri[j,0] - tri[i,0])
                    y = tri[i,1] + t * (tri[j,1] - tri[i,1])
                    segments.append([x, y])
    return np.array(segments) if segments else None

def render_slice(segments, z, size=400):
    """Render segment points as image."""
    if segments is None or len(segments) < 3:
        return None
    
    xs = segments[:,0]; ys = segments[:,1]
    half = max(W, H) / 2 + MARGIN
    
    img = Image.new("RGB", (size, size), "white")
    draw = ImageDraw.Draw(img)
    
    # Scale
    scale = size / (2 * half)
    
    # Draw points
    pts = []
    for x, y in zip(xs, ys):
        px = int((x + half) * scale)
        py = int((y + half) * scale)
        px = np.clip(px, 0, size-1)
        py = np.clip(py, 0, size-1)
        pts.append((px, py))
    
    if pts:
        draw.point(pts, fill="blue")
    
    return img

print("Slicing at %d Z levels..." % len(z_levels))
for i, z in enumerate(z_levels):
    pts = slice_at_z(tris, z)
    if pts is not None and len(pts) >= 3:
        img = render_slice(pts, z)
        if img:
            fname = "z%03d_%.1f.png" % (i, z)
            img.save(os.path.join(OUT, fname))

# Also create a composite: multi-row image with key Z levels
print("Creating composite...")
key_z = [0, 0.4, 0.8, 1.2, 1.6, 2.0, 2.4, 2.8, 3.2, 3.6, 4.0, 4.4, 4.8]
n_cols = 4
n_rows = (len(key_z) + n_cols - 1) // n_cols
cell = 250
composite = Image.new("RGB", (n_cols * cell, n_rows * cell), "white")

for idx, z in enumerate(key_z):
    col = idx % n_cols
    row = idx // n_cols
    pts = slice_at_z(tris, z)
    img = render_slice(pts, z, cell)
    if img:
        composite.paste(img, (col * cell, row * cell))
        # Add label
        d = ImageDraw.Draw(composite)
        d.text((col * cell + 5, row * cell + 5), "Z=%.1f" % z, fill="red")

composite.save(os.path.join(OUT, "composite.png"))
print("Saved composite.png with %d slices" % len(key_z))

# Also analyze: at each Z, report X range and Y range
print("\nLayer analysis:")
for z in key_z:
    pts = slice_at_z(tris, z)
    if pts is not None and len(pts) >= 3:
        x0, x1 = pts[:,0].min(), pts[:,0].max()
        y0, y1 = pts[:,1].min(), pts[:,1].max()
        # Check if there's a central gap
        xs = np.unique(np.round(pts[:,0], 1))
        gaps = [(xs[i], xs[i+1]) for i in range(len(xs)-1) if xs[i+1]-xs[i] > 2]
        gp = " ".join("%.0f-%.0f" % g for g in gaps) if gaps else "-"
        print("Z=%.1f: X=[%.1f,%.1f] Y=[%.1f,%.1f] %d pts gaps=%s" % (z, x0, x1, y0, y1, len(pts), gp))

print("\nDONE - check %s/" % OUT)
