main
Caleb Publish 0.1.1 3c8a666 1d ago
#!/usr/bin/env python3
"""contact_proof.py <step> <kicad_mod>: step_pose 0.2.0's contact test (material in z -0.05..0.10 inside each pad grown by
0.5 mm, centroid must fall inside the pad) run twice: on the whole model (what step_pose does) and on the terminal solids
only. On a 1.7 mm side-view LED the 0.5 mm grow reaches past the 0.86 mm pad gap into the body and lens, so the
whole-model centroid lands between the pads although each terminal sits on its pad."""
import sys, os, json
sys.path.insert(0, os.path.expanduser('~/.claude/skills/component-page-verify/lib'))
import cadquery as cq, pad_geom as PGm
import step_pose as SP
step, kmod = sys.argv[1], sys.argv[2]
text = open(kmod).read()
gp = PGm.parse_pads(text)
def sbox(p):
    xs = [x for core, r, _ in p['shapes'] for x, _ in core for x in (x - r, x + r)]
    ys = [-y for core, r, _ in p['shapes'] for _, y in core for y in (y - r, y + r)]
    return min(xs), max(xs), min(ys), max(ys)
pads = [dict(number=p['number'], bbox=sbox(p), tht=False) for p in gp]
a = cq.Assembly.importStep(step)
allc = cq.Compound.makeCompound([s for s in cq.importers.importStep(step).solids().vals()])
terms = [o.obj.val() if hasattr(o.obj, 'val') else o.obj for n, o in a.traverse() if n.startswith('terminal') or (o.name or '').startswith('terminal')]
terms = [ (o.obj if isinstance(o.obj, cq.Shape) else o.obj.val()).moved(o.loc) for n, o in a.traverse() if (o.name or '').startswith('terminal') and o.obj is not None]
tc = cq.Compound.makeCompound(terms)
out = {}
for label, comp in (('whole model (step_pose)', allc), ('terminals only', tc)):
    bb = comp.BoundingBox(); res = []
    slab = comp.intersect(SP._box(bb.xmin - 1, bb.xmax + 1, bb.ymin - 1, bb.ymax + 1, *SP.SMD_SLAB))
    for p in pads:
        x0, x1, y0, y1 = p['bbox']; g = SP.CONTACT_GROW
        piece = slab.intersect(SP._box(x0 - g, x1 + g, y0 - g, y1 + g, SP.SMD_SLAB[0] - 0.01, SP.SMD_SLAB[1] + 0.01))
        c = cq.Shape.centerOfMass(piece)
        res.append({'pad': p['number'], 'pad_box_xy': [round(v, 3) for v in p['bbox']], 'centroid_xy': [round(c.x, 3), round(c.y, 3)],
                    'inside': bool(x0 <= c.x <= x1 and y0 <= c.y <= y1)})
    out[label] = res
print(json.dumps(out, indent=1))