imrishabh18/rp2040-motor-controller

A library of PCB component definitions covering MOSFETs, motor drivers, USB-PD control, sensors, current monitors, transistors, diodes, resistors, speakers, connectors, and mirrored bottom-side logos, with pin mappings, footprints, and 3D models.

Version
1.0.55
License
unset
Stars
5

scripts/find-pg-placement.py

"""Search real top-side courtyard space and report copper that must be rerouted."""
import json, math
from pathlib import Path
from shapely.geometry import Point, Polygon, LineString, box
from shapely.affinity import rotate, translate
from shapely.strtree import STRtree
from copper_geometry import trace_segments

j=json.loads(Path('reports/pg-sense-2026-10-22/baseline/circuit.json').read_text())
board=next(e for e in j if e['type']=='pcb_board')
outline=Polygon([(p['x'],p['y']) for p in board['outline']]).buffer(-.3)
courts=[]
for e in j:
    if e['type'] not in ['pcb_courtyard_outline','pcb_courtyard_rect'] or e.get('layer')!='top':continue
    if e['type']=='pcb_courtyard_outline':g=Polygon([(p['x'],p['y']) for p in e['outline']])
    else:g=translate(rotate(box(-e['width']/2,-e['height']/2,e['width']/2,e['height']/2),e.get('ccw_rotation',0)),e['center']['x'],e['center']['y'])
    courts.append(g)
for e in j:
    if e['type']=='pcb_plated_hole':
        if e.get('shape',e.get('hole_shape'))=='circle':courts.append(Point(e['x'],e['y']).buffer(e.get('outer_diameter',5.6)/2+.2))
        else:courts.append(translate(rotate(box(-e['outer_width']/2-.2,-e['outer_height']/2-.2,e['outer_width']/2+.2,e['outer_height']/2+.2),e.get('ccw_rotation',0)),e['x'],e['y']))
    elif e['type']=='pcb_hole':
        if e.get('shape',e.get('hole_shape','circle'))=='circle':courts.append(Point(e['x'],e['y']).buffer(e.get('hole_diameter',e.get('diameter',.5))/2+.2))
ct=STRtree(courts)
shapes=[];owners=[];fixed=[]
for e in j:
    if e['type']=='pcb_trace':
        for a,b,l,w in trace_segments(e['route']):
            if l=='top':shapes.append(LineString([a,b]).buffer(w/2+.108));owners.append(e['pcb_trace_id']);fixed.append(False)
    elif e['type']=='pcb_via':
        shapes.append(Point(e['x'],e['y']).buffer(e['outer_diameter']/2+.133));owners.append(e.get('pcb_trace_id','manual'));fixed.append(not e.get('pcb_trace_id'))
tree=STRtree(shapes)
def scan(court,pads,target,limit=50):
    choices=[]
    for angle in [0,90,180,270]:
        bc=rotate(court,angle);bp=[rotate(p,angle,origin=(0,0)) for p in pads]
        for xi in range(-80,81):
            for yi in range(-80,81):
                x,y=xi*.25,yi*.25;c=translate(bc,x,y)
                if not outline.covers(c) or any(c.distance(courts[i])<.04 for i in ct.query(c.buffer(.04))):continue
                hits=set();valid=True
                for p in bp:
                    g=translate(p,x,y)
                    for i in tree.query(g):
                        if not g.intersects(shapes[i]):continue
                        if fixed[i]:valid=False;break
                        hits.add(owners[i])
                    if not valid:break
                if valid:choices.append((len(hits)*100+math.hypot(x-target[0],y-target[1]),x,y,angle,sorted(hits)))
    return sorted(choices)[:limit]
if __name__ == '__main__':
    import sys
    if '--passives' not in sys.argv:
        chip=scan(box(-1.05005,-1.230085,1.05005,1.230085),[translate(box(-.140005,-.340005,.140005,.340005),x,y) for x in [-.5,0,.5] for y in [-.64008,.64008]],(-10,-3))
        print(json.dumps(chip[:20],indent=2))
        Path('reports/pg-sense-2026-10-22/placement-candidates.json').write_text(json.dumps(chip,indent=2))
    else:
        p=Path('mechanical/next-placement.json');j=json.loads(p.read_text())
        q=j['U_PD_PG'];courts.append(translate(rotate(box(-1.05005,-1.230085,1.05005,1.230085),q['pcbRotation']),q['pcbX'],q['pcbY']));ct=STRtree(courts)
        for name,target in [('C_PD_PG',(-19.5,-15)),('R_PD_VDD_PD',(-19.5,-11)),('R_PD_GOOD_PD',(-18,-14))]:
            opts=scan(box(-.95,-.5,.95,.5),[translate(box(-.27,-.32,.27,.32),x,0) for x in [-.51,.51]],target,10)
            best=opts[0];_,x,y,angle,hits=best;print(name,best,flush=True);j[name]={'pcbX':x,'pcbY':y,'pcbRotation':angle}
            courts.append(translate(rotate(box(-.95,-.5,.95,.5),angle),x,y));ct=STRtree(courts)
        p.write_text(json.dumps(j,indent=2)+'\n')