imrishabh18/rp2040-motor-controller

An RP2040-based NEMA17 stepper-motor controller with USB-C programming and PD motor-power negotiation, DRV8825 dual H-bridge drive, current/temperature telemetry, protected power filtering, status RGB LED, buzzer alarm, and optional magnetic encoder.

Version
1.0.42
License
unset
Stars
3

scripts/freerouting_geometry.py

"""Export actual placed copper geometry to a local Specctra routing job.
The result must be imported and independently checked before use.
"""
import json,sys,math,re
from pathlib import Path
from shapely.geometry import Point,LineString,box,Polygon
from shapely.affinity import translate,rotate
from copper_geometry import trace_segments
j=json.load(open('work/freerouting/placement.json'));plan=json.load(open('dist/revision/route-plan.json'))
parent={}
def root(x):
 parent.setdefault(x,x)
 if parent[x]!=x:parent[x]=root(parent[x])
 return parent[x]
for e in j:
 ids=e.get('connected_source_port_ids',[])+e.get('connected_source_net_ids',[]) if e['type']=='source_trace' else e.get('source_port_ids',[]) if e['type']=='source_component_internal_connection' else []
 for x in ids[1:]:parent[root(x)]=root(ids[0])
pp={e['pcb_port_id']:e for e in j if e['type']=='pcb_port'}
sc={e['source_component_id']:e for e in j if e['type']=='source_component'}
sp={e['source_port_id']:e for e in j if e['type']=='source_port'}
st={e['source_trace_id']:e for e in j if e['type']=='source_trace'}
pt={e['pcb_trace_id']:e for e in j if e['type']=='pcb_trace'}
def enet(e):
 if e.get('pcb_port_id') in pp:return root(pp[e['pcb_port_id']]['source_port_id'])
 if e.get('source_trace_id') in st:
  t=st[e['source_trace_id']];return root((t.get('connected_source_port_ids',[])+t.get('connected_source_net_ids',[]))[0])
 if e.get('source_net_id'):return root(e['source_net_id'])
 if e.get('pcb_trace_id') in pt:return enet(pt[e['pcb_trace_id']])
 return 'UNCONNECTED_'+e.get(e['type']+'_id','unknown')
layers=['top','inner1','inner2','bottom'];u=lambda n: str(round(n*1000,4));xy=lambda x,y:u(x)+' '+u(y)

def geom(e,prefix=''):
 shape=e.get('shape',e.get('hole_shape','circle'));x,y=e['x'],e['y']
 if shape=='circle':g=Point(0,0).buffer(e.get(prefix+'diameter',e.get('radius',0)*2)/2,quad_segs=12)
 else:
  w,h=e[prefix+'width'],e[prefix+'height'];r=min(w,h)/2 if 'pill' in shape or shape=='oval' else e.get('corner_radius',0)
  g=box(-w/2,-h/2,w/2,h/2) if not r else (LineString([(-w/2+r,-h/2+r),(w/2-r,h/2-r)]).buffer(r,quad_segs=12) if min(w,h)<=2*r+1e-8 else box(-w/2+r,-h/2+r,w/2-r,h/2-r).buffer(r,quad_segs=12))
 return translate(rotate(g,e.get('ccw_rotation',0),origin=(0,0)),x,y)
def polygon(g,l):return f'(polygon {l} 0 '+' '.join(xy(x,y) for x,y in list(g.exterior.coords)[:-1])+')'