seveibar/am3352-dev-board-4layer-dogbone
Defines PCB footprints and pin mappings for a TI AM3352ZCZ100 processor, JST GH connectors, and assembly-part lookup helpers for capacitors, resistors, and headers.
- Version
- 0.1.5
- License
- unset
- Stars
- 0
scripts/patch-endpoint-inference.mjs
/** Fix @tscircuit/checks endpoint inference: matching XY is insufficient across layers/nets. */
import fs from 'node:fs'
const replacement = `var addStartAndEndPortIdsIfMissing = (soup) => {
// layerNetSafeEndpointTags: keep inferred contacts on the correct electrical net and layer.
const parent = new Map();
const find = (id) => { if (!parent.has(id)) parent.set(id,id); const p=parent.get(id); if(p!==id) parent.set(id,find(p)); return parent.get(id); };
const join = (ids) => { const clean=ids.filter(Boolean); for(const id of clean.slice(1)) parent.set(find(id),find(clean[0])); };
for(const e of soup) {
if(e.type==='source_trace') join([e.source_trace_id,...(e.connected_source_port_ids||[]),...(e.connected_source_net_ids||[])]);
if(e.type==='source_component') for(const ids of e.internally_connected_source_port_ids||[]) join(ids);
}
const ports=soup.filter(e=>e.type==='pcb_port');
const pads=soup.filter(e=>e.type==='pcb_smtpad');
const through=new Set(soup.filter(e=>e.type==='pcb_plated_hole').map(e=>e.pcb_port_id));
const portById=new Map(ports.map(p=>[p.pcb_port_id,p]));
const findContact=(point,trace,endpoint)=>{
if(!trace.source_trace_id) return null;
const root=find(trace.source_trace_id);
const eligible=(p)=>p && find(p.source_port_id)===root && ((p.layers||[]).includes(point.layer)||through.has(p.pcb_port_id));
const direct=ports.find(p=>eligible(p)&&Math.hypot(p.x-point.x,p.y-point.y)<.01);
if(direct) return direct.pcb_port_id;
if(!endpoint) return null;
const pad=pads.find(p=>{
if(p.layer!==point.layer||!eligible(portById.get(p.pcb_port_id)))return false;
const dx=point.x-p.x,dy=point.y-p.y;
if(p.shape==='circle')return Math.hypot(dx,dy)<p.radius;
if(p.shape==='rect'||p.shape==='rotated_rect'){
const a=-(p.ccw_rotation||0)*Math.PI/180;
return Math.abs(dx*Math.cos(a)-dy*Math.sin(a))<p.width/2 && Math.abs(dx*Math.sin(a)+dy*Math.cos(a))<p.height/2;
}
return false;
});
return pad?.pcb_port_id||null;
};
for(const trace of soup.filter(e=>e.type==='pcb_trace')) {
for(let i=0;i<trace.route.length;i++) {
const p=trace.route[i]; if(p.route_type!=='wire')continue;
if(i===0&&!p.start_pcb_port_id){const id=findContact(p,trace,true);if(id)p.start_pcb_port_id=id;}
if(i===trace.route.length-1&&!p.end_pcb_port_id){const id=findContact(p,trace,true);if(id)p.end_pcb_port_id=id;}
}
}
};`;
const files=['node_modules/@tscircuit/checks/dist/index.js','node_modules/@tscircuit/cli/dist/cli/main.js','node_modules/@tscircuit/cli/dist/lib/index.js'];
let patched=0;
for(const file of files){
if(!fs.existsSync(file))continue;
const source=fs.readFileSync(file,'utf8');
if(source.includes('layerNetSafeEndpointTags'))continue;
const start=source.indexOf('var addStartAndEndPortIdsIfMissing = ');
if(start<0)continue;
const end=source.indexOf('\n};',start)+3;
if(end<start)throw Error('Unrecognized endpoint helper in '+file);
fs.writeFileSync(file,source.slice(0,start)+replacement+source.slice(end));patched++;
}
if(!fs.readFileSync(files[0],'utf8').includes('layerNetSafeEndpointTags'))throw Error('Endpoint inference patch was not installed');
console.log('Layer/net-aware endpoint inference installed:',patched,'files patched');