"""Parametric metric fasteners (ISO dims) — THE reusable screw/nut models for viz + clearance work.
socket_screw(d, L): ISO 4762 socket head cap screw (correct head OD/height, hex socket recess, tip chamfer).
hex_nut(d): ISO 4032 nut (across-flats, height, chamfered corners). All along +z, head at z=0 plane."""
import numpy as np, trimesh

ISO4762 = {2.5: (4.5, 2.5, 2.0), 3: (5.5, 3.0, 2.5), 4: (7.0, 4.0, 3.0), 5: (8.5, 5.0, 4.0), 6: (10.0, 6.0, 5.0)}
ISO4032 = {2.5: (5.0, 2.0), 3: (5.5, 2.4), 4: (7.0, 3.2), 5: (8.0, 4.7), 6: (10.0, 5.2)}

def _hex_prism(af, h):
    r = af / np.sqrt(3.0)
    p = trimesh.creation.cylinder(radius=r, height=h, sections=6)
    return p

def socket_screw(d=5.0, L=40.0):
    """Head sits z -H..0, shaft z 0..L (insertion direction +z)."""
    hd, hh, sock_af = ISO4762[d]
    head = trimesh.creation.cylinder(radius=hd / 2, height=hh, sections=48)
    head.apply_translation([0, 0, -hh / 2])
    sock = _hex_prism(sock_af, hh * 1.2)
    sock.apply_translation([0, 0, -hh + hh * 0.5 - 0.01])
    head = trimesh.boolean.difference([head, sock], engine="manifold")
    shaft = trimesh.creation.cylinder(radius=d / 2, height=L - d * 0.15, sections=36)
    shaft.apply_translation([0, 0, (L - d * 0.15) / 2])
    tip = trimesh.creation.cone(radius=d / 2, height=d * 0.15, sections=36)
    tip.apply_transform(trimesh.transformations.rotation_matrix(np.pi, [1, 0, 0]))
    tip.apply_translation([0, 0, L])
    return trimesh.boolean.union([head, shaft, tip], engine="manifold")

def hex_nut(d=5.0):
    af, h = ISO4032[d]
    n = _hex_prism(af, h)
    n.apply_translation([0, 0, h / 2])
    bore = trimesh.creation.cylinder(radius=d / 2 * 0.85, height=h * 2, sections=24)
    bore.apply_translation([0, 0, h / 2])
    return trimesh.boolean.difference([n, bore], engine="manifold")

if __name__ == "__main__":
    s = socket_screw(5.0, 45.0); s.export("fastener_m5x45_socket.stl")
    n = hex_nut(5.0); n.export("fastener_m5_nut.stl")
    print(f"M5x45 socket screw: wt={s.is_watertight} vol={s.volume:.0f}mm3; M5 nut: wt={n.is_watertight}")


def drop_in_tnut(d=5.0):
    """2020-series drop-in T-nut: body 10x6x3.4 + wings that grip under the slot lips, M5 bore.
    Origin at the thread axis, top face (slot-opening side) at z=0, body hangs z<0."""
    body = trimesh.creation.box(extents=[10.0, 6.0, 3.4])
    body.apply_translation([0, 0, -1.7])
    wing = trimesh.creation.box(extents=[10.0, 8.8, 1.4])
    wing.apply_translation([0, 0, -0.7])
    n = trimesh.boolean.union([body, wing], engine="manifold")
    bore = trimesh.creation.cylinder(radius=d / 2 * 0.85, height=8, sections=24)
    return trimesh.boolean.difference([n, bore], engine="manifold")
