"""Fillet the OUTER edges of the holder box, then carve the servo cavity + bolt holes exactly as
carve_holder.py does -> fit + holes are identical, only the exterior is softened. -> holder_fillet.stl"""
import numpy as np, trimesh
R_FILLET = 2.5
box = trimesh.load("holder_approx.stl", force="mesh")
lo, hi = box.bounds
r = R_FILLET
cs = []
for x in (lo[0] + r, hi[0] - r):
    for y in (lo[1] + r, hi[1] - r):
        for z in (lo[2] + r, hi[2] - r):
            s = trimesh.creation.icosphere(radius=r, subdivisions=3); s.apply_translation([x, y, z]); cs.append(s)
rounded = trimesh.util.concatenate(cs).convex_hull           # box with all edges/corners rounded by r
# --- identical carve to carve_holder.py ---
hull = trimesh.load("st3215.stl", force="mesh").convex_hull
hull.vertices += hull.vertex_normals * 0.5                    # CLEAR=0.5
m = trimesh.boolean.difference([rounded, hull], engine="manifold")
def cylY(x, z, rr, h=60.0):
    c = trimesh.creation.cylinder(radius=rr, height=h)
    c.apply_transform(trimesh.transformations.rotation_matrix(np.pi / 2, [1, 0, 0])); c.apply_translation([x, 0, z]); return c
for x, z in [(3.5, 10.2), (3.5, -10.2), (7.2, 10.2), (7.2, -10.2)]:
    m = trimesh.boolean.difference([m, cylY(x, z, 1.4)], engine="manifold")
m.export("holder_fillet.stl")
ref = trimesh.load("holder_approx_drilled.stl", force="mesh")
print(f"holder_fillet: wt={m.is_watertight} vol={m.volume/1000:.1f}cm3 (drilled ref {ref.volume/1000:.1f}cm3) bounds={np.round(m.bounds,1).tolist()}")
