main
John Lauer Add symbol, footprint, Fusion and Altium libraries, viewers, provenance, README ab389b2 6h ago
#!/usr/bin/env python3
"""Add the footprint's isolation keepout to the hero GLB as its own declared layer (not part of the adom-hero
artwork root, so the hero stays adom-hero conformant): an amber translucent fill and a dashed amber border over
the KiCad rule area (x -4.05 .. 4.05, y -5.5 .. 5.5 mm: between the inner pad edges of TI's HV / isolation land
pattern, 8.1 mm, SBOSA24D p.46), flat at z = -0.03 mm, unlit, BLEND, double-sided, with a tooltip.
usage: hero_post_keepout.py hero.glb [out.glb]"""
import sys, json, struct, numpy as np
from pygltflib import GLTF2, Buffer, BufferView, Accessor, Mesh, Primitive, Node, Material, PbrMetallicRoughness, Attributes
src = sys.argv[1]; out = sys.argv[2] if len(sys.argv) > 2 else src
g = GLTF2().load(src); blob = bytearray(g.binary_blob())
if any(n.name == "Adom.IsolationKeepout" for n in g.nodes): sys.exit("already has Adom.IsolationKeepout")
X, Y, Z = 4.05, 5.5, -0.03
def quads(rects):
    P, I = [], []
    for x0, y0, x1, y1 in rects:
        b = len(P); P += [(x0, y0, Z), (x1, y0, Z), (x1, y1, Z), (x0, y1, Z)]; I += [b, b + 1, b + 2, b, b + 2, b + 3]
    return np.array(P, np.float32) / 1000, np.array(I, np.uint32)
fillP, fillI = quads([(-X, -Y, X, Y)])
w, dash, gap = 0.08, 0.45, 0.27
rects = []
def dashed(a, b, horiz, fixed):
    t = a
    while t < b:
        e = min(t + dash, b)
        rects.append((t, fixed - w / 2, e, fixed + w / 2) if horiz else (fixed - w / 2, t, fixed + w / 2, e)); t = e + gap
dashed(-X, X, True, -Y); dashed(-X, X, True, Y); dashed(-Y, Y, False, -X); dashed(-Y, Y, False, X)
lineP, lineI = quads(rects)
def add(arr, target, comp, typ, mm=False):
    while len(blob) % 4: blob.append(0)
    off = len(blob); b = arr.tobytes(); blob.extend(b)
    g.bufferViews.append(BufferView(buffer=0, byteOffset=off, byteLength=len(b), target=target))
    a = Accessor(bufferView=len(g.bufferViews) - 1, componentType=comp, count=len(arr), type=typ)
    if mm: a.min = arr.min(0).tolist(); a.max = arr.max(0).tolist()
    g.accessors.append(a); return len(g.accessors) - 1
def mat(name, rgba):
    g.materials.append(Material(name=name, alphaMode="BLEND", doubleSided=True, extensions={"KHR_materials_unlit": {}},
        pbrMetallicRoughness=PbrMetallicRoughness(baseColorFactor=rgba, metallicFactor=0.0, roughnessFactor=1.0)))
    return len(g.materials) - 1
prims = []
for P, I, m in ((fillP, fillI, mat("Isolation keepout fill - 12 percent opacity", [1.0, 0.70, 0.15, 0.12])),
                (lineP, lineI, mat("Isolation keepout border - 60 percent opacity", [1.0, 0.70, 0.15, 0.6]))):
    up = np.tile(np.array([[0, 0, 1]], np.float32), (len(P), 1))
    prims.append(Primitive(attributes=Attributes(POSITION=add(P, 34962, 5126, "VEC3", True), NORMAL=add(up, 34962, 5126, "VEC3")),
                           indices=add(I, 34963, 5125, "SCALAR"), material=m))
g.meshes.append(Mesh(name="Adom.IsolationKeepout", primitives=prims))
tip = ("Isolation keepout (footprint rule area, all copper layers)\nNo copper, tracks or vias between the primary pads (IN+, IN-)\n"
       "and the low-voltage pads: 8.1 mm pad-to-pad, TI HV / isolation land pattern\n(SBOSA24D p.46). The device itself is rated 8 mm creepage and clearance (p.6).")
g.nodes.append(Node(name="Adom.IsolationKeepout", mesh=len(g.meshes) - 1, extras={
    "adomLayer": {"key": "isolation_keepout", "label": "Isolation keepout (8.1 mm)", "group": "Footprint"}, "tooltip": tip}))
g.scenes[g.scene or 0].nodes.append(len(g.nodes) - 1)
g.buffers[0].byteLength = len(blob); g.set_binary_blob(bytes(blob))
def chunks(path):
    b = open(path, "rb").read(); n = struct.unpack_from("<I", b, 12)[0]
    return json.loads(b[20:20 + n]), b[20 + n:]
def write(path, j, rest):
    t = json.dumps(j, separators=(",", ":")).encode(); t += b" " * (-len(t) % 4)
    body = struct.pack("<II", len(t), 0x4E4F534A) + t + rest
    open(path, "wb").write(struct.pack("<III", 0x46546C67, 2, 12 + len(body)) + body)
extras = chunks(src)[0].get("asset", {}).get("extras", {})   # pygltflib drops asset.extras on save: carry it over
g.save(out)
j, rest = chunks(out)
extras.setdefault("adomHero", {}).setdefault("post", []).append(
    {"tool": "provenance/tools/hero_post_keepout.py", "node": "Adom.IsolationKeepout", "rect_mm": [-X, -Y, X, Y], "z_mm": Z})
j["asset"]["extras"] = extras; write(out, j, rest); print("OK", out)