import re, sys from PIL import Image, ImageDraw import numpy as np from scipy import ndimage def regions(path): inreg=False; regs=[]; cur=[]; x=y=0; pol="D" for line in open(path, errors="replace"): s=line.strip() if s=="%LPC*%": pol="C" if s=="%LPD*%": pol="D" if s=="G36*": inreg=True; cur=[]; continue if s=="G37*": inreg=False; regs.append((pol,cur)); continue m=re.match(r"(?:X(-?\d+))?(?:Y(-?\d+))?D0([123])\*$",s) if m: nx=int(m.group(1)) if m.group(1) is not None else x; ny=int(m.group(2)) if m.group(2) is not None else y if inreg: cur.append((nx,ny)) x,y=nx,ny return regs def prims(path): ap=None; inreg=False; x=y=0; flashes=set(); draws=set(); apdefs={} for line in open(path, errors="replace"): s=line.strip() if s.startswith("G04") or s.startswith("%T"): continue m=re.match(r"%ADD(\d+)(.*)\*%",s) if m: apdefs[m.group(1)]=m.group(2); continue if s=="G36*": inreg=True; continue if s=="G37*": inreg=False; continue m=re.match(r"D(\d+)\*$",s) if m and int(m.group(1))>=10: ap=apdefs.get(m.group(1)); continue m=re.match(r"(?:X(-?\d+))?(?:Y(-?\d+))?D0([123])\*$",s) if m: nx=int(m.group(1)) if m.group(1) is not None else x; ny=int(m.group(2)) if m.group(2) is not None else y if not inreg: if m.group(3)=="3": flashes.add((ap,nx,ny)) elif m.group(3)=="1": draws.add((ap,x,y,nx,ny)) x,y=nx,ny return flashes,draws SC=50; W,H=int(141*SC),int(101*SC) def raster_regs(regs): im=Image.new("1",(W,H),0); d=ImageDraw.Draw(im) for pol,pts in regs: d.polygon([((x/1e6-60)*SC,(-y/1e6-40)*SC) for x,y in pts], fill=1 if pol=="D" else 0) return np.array(im) def compare(a,b): fa,da=prims(a); fb,db=prims(b) ra=raster_regs(regions(a)); rb=raster_regs(regions(b)) x=ra^rb er=ndimage.binary_erosion(x, structure=np.ones((3,3))) lab,n=ndimage.label(er) blobs=[] for i in range(1,n+1): ys,xs=np.nonzero(lab==i) if len(xs)<9: continue blobs.append((len(xs)/SC/SC, round(xs.min()/SC+60,1),round(ys.min()/SC+40,1),round(xs.max()/SC+60,1),round(ys.max()/SC+40,1), "A-only" if ra[ys[0],xs[0]] else "B-only")) blobs.sort(reverse=True) return fa==fb, da==db, x.sum()/SC/SC, blobs if __name__=="__main__": base="../in/03-Architecture-Hardware/kicad" for v,b,prod in (("v56","v5","HBOX_v5_Rev6_PROD"),("v57","v57","HBOX_v5_Rev7_PROD")): print("=====",v) for lay,e1 in (("F_Cu","gtl"),("In1_Cu","g1"),("In2_Cu","g2"),("B_Cu","gbl")): fs,ds,xa,blobs=compare(f"{base}/production/{prod}/hbox-watchdog-{b}-{lay}.{e1}", f"{base}/_to_delete/audit-20261022/{v}/rf/gerber/hbox-watchdog-{b}-{lay}.gbr") print(f" {lay:7s} flashes={fs} draws={ds} fill-xor={xa:.3f} mm2 blobs={len(blobs)}", [f"{a:.2f}@({x1},{y1})-({x2},{y2}){t}" for a,x1,y1,x2,y2,t in blobs[:5]])