imrishabh18/corne-keyboard

The code defines and renders two surface-mount chip components with SMT pads, silkscreen outlines, and 3D CAD models (OBJ and STEP) for PCB assembly.

Version
2.0.21
License
unset
Stars
1

lib/routing/replay-reviewed.ts

/** Rebase reviewed board-local copper into a translated panel board. */
export function replayReviewed(routes: any[], ports: {id:string,x:number,y:number}[]) {
  return async (input: any) => {
    const number = (id:string) => Number(id.match(/_(\d+)$/)![1])
    const points = input.connections.flatMap((c:any) => c.pointsToConnect).filter((p:any) => p.pcb_port_id)
    const portOffset = Math.min(...points.map((p:any) => number(p.pcb_port_id))) - Math.min(...ports.map(p => number(p.id)))
    const traceNumbers = input.connections.map((c:any) => c.name.match(/^source_trace_(\d+)$/)?.[1]).filter(Boolean).map(Number)
    const netNumbers = input.connections.map((c:any) => c.name.match(/^source_net_(\d+)$/)?.[1]).filter(Boolean).map(Number)
    const traceOffset = Math.min(...traceNumbers)
    const netOffset = Math.min(...netNumbers)
    if (!Number.isFinite(traceOffset) || !Number.isFinite(netOffset)) throw Error('Reviewed routing requires the complete board routing stage')
    const outline = input.outline
    const dx = (Math.min(...outline.map((p:any)=>p.x)) + Math.max(...outline.map((p:any)=>p.x))) / 2
    const dy = (Math.min(...outline.map((p:any)=>p.y)) + Math.max(...outline.map((p:any)=>p.y))) / 2
    const expected = new Map(ports.map(p=>[p.id,p]))
    for (const p of points) {
      const original = expected.get(`pcb_port_${number(p.pcb_port_id)-portOffset}`)
      if (!original || Math.hypot(p.x-dx-original.x,p.y-dy-original.y)>.001) throw Error(`Reviewed routing no longer matches ${p.pcb_port_id}; reroute after changing placement`)
    }
    const remap = (value:any):any => {
      if (typeof value==='string') return value.replace(/pcb_port_(\d+)/g,(_,n)=>`pcb_port_${Number(n)+portOffset}`).replace(/source_trace_(\d+)/g,(_,n)=>`source_trace_${Number(n)+traceOffset}`).replace(/source_net_(\d+)/g,(_,n)=>`source_net_${Number(n)+netOffset}`)
      if (Array.isArray(value)) return value.map(remap)
      if (value && typeof value==='object') return Object.fromEntries(Object.entries(value).map(([k,v])=>[k,remap(v)]))
      return value
    }
    const translated=routes.map(original=>{
      const trace=remap(original)
      // Core assigns the current board's subcircuit instead of the saved one.
      delete trace.subcircuit_id
      for(const p of trace.route){
        // Explicit saved sizes otherwise override the board minima in core.
        if(p.route_type === 'via'){
          const padMinimum = input.minViaPadDiameter ?? input.min_via_pad_diameter
          const holeMinimum = input.minViaHoleDiameter ?? input.min_via_hole_diameter
          if(padMinimum !== undefined) p.via_diameter = Math.max(p.via_diameter ?? p.outer_diameter ?? 0, padMinimum)
          if(holeMinimum !== undefined) p.via_hole_diameter = Math.max(p.via_hole_diameter ?? p.hole_diameter ?? 0, holeMinimum)
        }
        if(typeof p.x==='number'){p.x+=dx;p.y+=dy}
        for(const key of ['start','end']) if(p[key]){p[key].x+=dx;p[key].y+=dy}
      }
      return trace
    })
    const handlers:Record<string,Array<(e:any)=>void>>={}
    return {
      on(event:string,fn:(e:any)=>void){(handlers[event]??=[]).push(fn)},
      start(){queueMicrotask(()=>{for(const fn of handlers.complete??[])fn({traces:translated})})},
      stop(){},
    }
  }
}