#!/usr/bin/env python3
"""Geometry of the MeerK40t galvo calibration test (ComMarker B4, black construction paper).
All coordinates in mm, centred on the field centre, x to the right, y DOWN (MeerK40t/SVG scene convention).
Shared by gen_mk_cal.py (writes the job files) and mk_cal_analyze.py (measures the scan). Grok 2026-10-31."""
import math

OUTER = 100.0            # outer square side (field is ~110)
INNER = 78.49            # inner square side (= old LightBurn 111 mm-diagonal test)
R_BIG = 35.0             # big circle (bulge / ovality)
R_CEN = 7.5              # centre circle
R_CORNER = 4.0           # corner circles
CORNER_C = 44.6          # corner circle centre |x|=|y|
TICKS = [-44, -30, -20, -10, 10, 20, 30, 44]   # ruler tick positions on X and Y axes
TICK_HALF = 2.0          # tick half length
AXIS_HALF = 46.0         # ruler axis line half length
PIP_R = 0.8
PIP_SLOTS = [37.6, 34.6, 31.6]   # |y| of pip slots, at |x| = CORNER_C (left/right bands)
PIP_COUNT = {(-1, -1): 3, (1, -1): 2, (-1, 1): 1, (1, 1): 0}   # (sx,sy): TL=3, TR=2, BL=1, BR=0


def circle(cx, cy, r, n=None):
    n = n or max(48, int(2 * math.pi * r / 0.25))
    return [(cx + r * math.cos(2 * math.pi * i / n), cy + r * math.sin(2 * math.pi * i / n)) for i in range(n + 1)]


def square(h):
    return [(-h, -h), (h, -h), (h, h), (-h, h), (-h, -h)]


def pip_positions():
    out = []
    for (sx, sy), n in PIP_COUNT.items():
        for i in range(3):
            out.append(((sx, sy, i), (sx * CORNER_C, sy * PIP_SLOTS[i]), i < n))
    return out


def geometry():
    """list of (layer, polyline) ; layer in {'geom','text','frame'}"""
    g = []
    g.append(('geom', square(OUTER / 2)))
    g.append(('geom', square(INNER / 2)))
    h = INNER / 2
    g.append(('geom', [(-h, -h), (h, h)]))
    g.append(('geom', [(h, -h), (-h, h)]))
    g.append(('geom', [(-AXIS_HALF, 0), (AXIS_HALF, 0)]))
    g.append(('geom', [(0, -AXIS_HALF), (0, AXIS_HALF)]))
    for t in TICKS:
        g.append(('geom', [(t, -TICK_HALF), (t, TICK_HALF)]))
        g.append(('geom', [(-TICK_HALF, t), (TICK_HALF, t)]))
    g.append(('geom', circle(0, 0, R_BIG)))
    g.append(('geom', circle(0, 0, R_CEN)))
    for sx in (-1, 1):
        for sy in (-1, 1):
            g.append(('geom', circle(sx * CORNER_C, sy * CORNER_C, R_CORNER)))
    for key, (x, y), present in pip_positions():
        if present:
            g.append(('geom', circle(x, y, PIP_R, 24)))
    # arrow pointing at the TOP-LEFT corner (top band)
    y = -(OUTER / 2 + INNER / 2) / 2
    g.append(('text', [(-25.5, y), (-38.0, y)]))
    g.append(('text', [(-35.5, y - 1.6), (-38.0, y), (-35.5, y + 1.6)]))
    g.append(('frame', square(OUTER / 2)))
    return g


def text_items(sheet_label):
    """(text, x_left, y_baseline, height) ; drawn as outline text in the bands"""
    yb_top = -(OUTER / 2 + INNER / 2) / 2 + 1.75
    yb_bot = (OUTER / 2 + INNER / 2) / 2 + 1.5
    return [
        ('TOP-LEFT', -24.0, yb_top, 2.8),
        ('X+', 41.0, -3.0, 2.8),
        ('Y+', 2.0, 44.5 + 1.4, 2.8),
        (sheet_label, -34.0, yb_bot, 2.5),
    ]
