#!/home/sam/dev/perso/flsun/cad-env/bin/python
"""Bac ouvert pour ESP32-C3 Super Mini (carte 22,5 x 18 mm, picots vers le haut).

    ./cad/esp32-supermini-tray.py            -> cad/esp32-supermini-tray.stl (+ apercu PNG)

La carte se clipse au fond, sur deux nervures qui laissent la place aux
moignons de soudure ; deux bossages arrondis par grand cote la retiennent.
Encoche USB-C au bout, mur abaisse au bout antenne, trou au centre du fond
pour la pousser hors du bac. Tout est parametre ci-dessous, en mm.
Tolerances prevues pour le profil qqs-pla-prod.ini (XY -0,1 deja compense).
"""
import os, struct
import numpy as np
from manifold3d import Manifold

# ---- carte ------------------------------------------------------------------
L, W, PCB = 22.6, 18.0, 1.0      # longueur (USB -> antenne), largeur, epaisseur
C = 0.25                          # jeu par cote autour de la carte
# ---- bac --------------------------------------------------------------------
WALL, FLOOR = 1.6, 1.2
STANDOFF = 2.2                    # hauteur des nervures : degage les moignons de soudure
ABOVE = 4.0                       # mur au-dessus de la carte (picots ~6 mm : ils depassent, c'est voulu)
RIB = 1.6                         # largeur des nervures (aux deux bouts)
CLIP_R, CLIP_LEN = 0.6, 4.0       # bossages de retenue (demi-cylindres dans les grands murs)
USB_W = 12.0                      # encoche cote USB-C (prise + surmoulage du cable)
USB_DOWN = 0.6                    # l'encoche descend un peu sous le dessus de la carte
ANT_W, ANT_ABOVE = 14.0, 1.5      # mur abaisse cote antenne : largeur, hauteur restante sur la carte
PUSH_D = 6.0                      # trou au fond pour pousser la carte

LO, WO = L + 2 * C + 2 * WALL, W + 2 * C + 2 * WALL
HO = FLOOR + STANDOFF + PCB + ABOVE
Z_PCB_TOP = FLOOR + STANDOFF + PCB


def box(x, y, z, cx=0, cy=0, z0=0):
    """Pave x*y*z, centre en (cx, cy), base a z0."""
    return Manifold.cube([x, y, z]).translate([cx - x / 2, cy - y / 2, z0])


def build():
    tray = box(LO, WO, HO)
    tray -= box(L + 2 * C, W + 2 * C, HO, z0=FLOOR)                     # cavite
    for sx in (-1, 1):                                                   # nervures aux deux bouts
        tray += box(RIB, W + 2 * C, STANDOFF, cx=sx * (L / 2 + C - RIB / 2), z0=FLOOR)
    for sy in (-1, 1):                                                   # bossages de retenue
        for cx in (-L / 4, L / 4):
            bump = Manifold.cylinder(CLIP_LEN, CLIP_R, CLIP_R, 24, True).rotate([0, 90, 0])
            tray += bump.translate([cx, sy * (W / 2 + C), Z_PCB_TOP + CLIP_R - 0.1])
    tray -= box(WALL + 2, USB_W, HO, cx=-(L / 2 + C + WALL / 2), z0=Z_PCB_TOP - USB_DOWN)   # encoche USB-C
    tray -= box(WALL + 2, ANT_W, HO, cx=(L / 2 + C + WALL / 2), z0=Z_PCB_TOP + ANT_ABOVE)    # mur abaisse antenne
    tray -= Manifold.cylinder(FLOOR + 2, PUSH_D / 2, PUSH_D / 2, 48).translate([0, 0, -1])    # trou pousse-carte
    return tray


def write_stl(m, path):
    mesh = m.to_mesh()
    v = np.asarray(mesh.vert_properties)[:, :3]
    t = np.asarray(mesh.tri_verts)
    tri = v[t]                                                           # (n, 3, 3)
    n = np.cross(tri[:, 1] - tri[:, 0], tri[:, 2] - tri[:, 0])
    n /= np.maximum(np.linalg.norm(n, axis=1), 1e-12)[:, None]
    with open(path, "wb") as f:
        f.write(b"esp32 super mini tray".ljust(80, b"\0"))
        f.write(struct.pack("<I", len(t)))
        rec = np.zeros(len(t), dtype=[("n", "<3f4"), ("v", "<9f4"), ("a", "<u2")])
        rec["n"] = n; rec["v"] = tri.reshape(-1, 9)
        f.write(rec.tobytes())
    return len(t)


def preview(m, path):
    try:
        import matplotlib; matplotlib.use("Agg")
        import matplotlib.pyplot as plt
        from mpl_toolkits.mplot3d.art3d import Poly3DCollection
    except ImportError:
        return False
    mesh = m.to_mesh(); v = np.asarray(mesh.vert_properties)[:, :3]; t = np.asarray(mesh.tri_verts)
    fig = plt.figure(figsize=(12, 4.5))
    for i, (el, az, titre) in enumerate(((35, -50, "3/4 dessus"), (90, -90, "dessus"), (0, -90, "bout USB"))):
        ax = fig.add_subplot(1, 3, i + 1, projection="3d")
        pc = Poly3DCollection(v[t], facecolor="#d9d2c5", edgecolor="#555", linewidth=0.15)
        ax.add_collection3d(pc)
        r = max(LO, WO, HO) / 2
        ax.set_xlim(-r, r); ax.set_ylim(-r, r); ax.set_zlim(0, 2 * r)
        ax.view_init(el, az); ax.set_title(titre); ax.set_axis_off()
    fig.tight_layout(); fig.savefig(path, dpi=110); return True


if __name__ == "__main__":
    here = os.path.dirname(os.path.abspath(__file__))
    m = build()
    n = write_stl(m, os.path.join(here, "esp32-supermini-tray.stl"))
    print("bac %.1f x %.1f x %.1f mm, cavite %.2f x %.2f, %d triangles, volume %.1f cm3"
          % (LO, WO, HO, L + 2 * C, W + 2 * C, n, m.volume() / 1000))
    print("apercu :", preview(m, os.path.join(here, "esp32-supermini-tray.png")))
