"""[feetech_calib] Orthographic vertex-scatter of servo+holder+yoke from 3 axes,
to understand the yoke screw-boss region for a 180deg saddle fixture. PIL only."""
import numpy as np, trimesh
from PIL import Image, ImageDraw

servo  = trimesh.load("st3215.stl", force="mesh")
yoke   = trimesh.load("yoke_fillet.stl", force="mesh")   # net-zero mate (servo frame)
saddle = trimesh.load("servo_zero_fixture.stl", force="mesh")
parts = [("servo",(120,120,128),servo), ("yoke",(230,150,40),yoke), ("saddle",(60,220,90),saddle)]

# (name, kept-axes indices, axis labels)
views = [("down_+Z_XYplane",(0,1),("X","Y")), ("down_+Y_XZplane",(0,2),("X","Z")), ("down_+X_YZplane",(1,2),("Y","Z"))]
W = 760; M = 40
for vname, (a0,a1), (l0,l1) in views:
    allpts = np.vstack([p.vertices[:, [a0,a1]] for _,_,p in parts])
    lo = allpts.min(0); hi = allpts.max(0); span = (hi-lo).max()
    sc = (W-2*M)/span
    def px(pts):
        q = (pts-lo)*sc; q[:,1] = (W-2*M) - q[:,1]  # flip for image
        return (q + M).astype(int)
    img = Image.new("RGB",(W,W),(20,20,24)); d = ImageDraw.Draw(img)
    for name,col,p in parts:
        for x,y in px(p.vertices[:, [a0,a1]]):
            d.point((x,y), fill=col)
    # mark horn axis (x=-25.5, z=0) projected + screw circle r~7 in x-z
    axis_pt = np.array([[-25.5,0,0]])
    ap = px(axis_pt[:, [a0,a1]])[0]
    d.ellipse([ap[0]-3,ap[1]-3,ap[0]+3,ap[1]+3], outline=(255,60,60), width=2)
    # legend + axes
    d.text((M,8), f"{vname}   ({l0} right, {l1} up)   red=horn axis (-25.5,0,0)", fill=(255,255,255))
    d.text((M,24), "grey=servo  blue=holder  orange=yoke", fill=(200,200,200))
    out = f"ortho_{vname}.png"; img.save(out); print("wrote", out, "bounds_mm", np.round(lo,1), np.round(hi,1))
