hrithik18k/punck-tscircuit-recreation
A four-layer Eurorack drum/sampler controller PCB built around an STM32H743, dual QSPI flash, PCM5100 stereo DAC, TL074 analog audio circuitry, input clamps, 12V-to-3.3V power regulation, MIDI/USB/SWD connectors, controls, LEDs, and trigger/audio I/O.
- Version
- 1.0.3
- License
- unset
- Stars
- 0
scripts/generate-index.ts
import fs from 'node:fs'
const d=JSON.parse(fs.readFileSync('native/components.json','utf8'))
const sheetFor=(p:any)=>p.section==='Passives'?(p.name.startsWith('C')?'Capacitors':'Resistors'):p.section
const sheets=[...new Set(d.parts.map(sheetFor))]
const attr=(k:string,v:any)=>v===undefined?'':` ${k}={${JSON.stringify(v)}}`
const lines=[`import { punckRouter } from './native/punck-router.browser.js'`,`import data from './native/components.json'`,`import { ExactFootprint } from './native/ExactFootprint'`,``, `// Original hardware: https://github.com/dchwebb/Punck (Hardware_v1_1).`, `// Native schematic and fresh autorouting. Run build:native for the two validated via repairs.`, `const footprints = Object.fromEntries(data.parts.map(part => [part.name, part]))`,``, `export default function Punck() {`, ` return <board width={91.2} height={100} thickness={1.59} layers={4}`, ` autorouter={{algorithmFn:punckRouter,allowViaInPad:false}} minViaEdgeToPadEdgeClearance={0.4} minTraceWidth={0.15}>`]
for(const [i,s] of sheets.entries())lines.push(` <schematicsheet name={${JSON.stringify(s)}} displayName={${JSON.stringify(s.replaceAll('_',' '))}} sheetIndex={${i}} />`)
for(const s of d.sections)lines.push(` <schematicsection${attr('name','section_'+s)}${attr('displayName',s.replaceAll('_',' '))} />`)
const nets=[...new Set(d.parts.flatMap((p:any)=>p.pins.filter((v:any)=>v.net&&!v.nc).map((v:any)=>v.net)))]
for(const n of nets)lines.push(` <net${attr('name',n)} />`)
for(const p of d.parts){
const common={name:p.name,pcbX:p.x,pcbY:p.y,pcbRotation:['C24','R16','C26','C23'].includes(p.name)?180:0,layer:p.layer,schX:p.schX,schY:p.schY,schRotation:p.schRotation??0,schSectionName:'section_'+p.section,schSheetName:sheetFor(p)}
let type='chip',props:any={...common}
if(p.ftype==='simple_resistor'&&p.pins.length===2){type='resistor';props.resistance=p.value.replace(/^(\d+)([kmunp])(\d+)$/,'$1.$3$2')}
else if(p.ftype==='simple_capacitor'&&p.pins.length===2){type='capacitor';props.capacitance=p.value.replace(/^(\d+)([kmunp])(\d+)$/,'$1.$3$2');if(['C1','C34','C20'].includes(p.name))props.schOrientation='vertical'}
else {const half=Math.ceil(p.pins.length/2);Object.assign(props,{manufacturerPartNumber:p.value,pinLabels:Object.fromEntries(p.pins.map((v:any)=>[v.selector,[v.label.replace(/\+/g,'_PLUS').replace(/-/g,'_MINUS').replace(/[^a-zA-Z0-9_]/g,'_')]])),pinAttributes:Object.fromEntries(p.pins.filter((v:any)=>v.nc).map((v:any)=>[v.selector,{doNotConnect:true}])),schWidth:p.schWidth??(p.pins.length>20?9:4),schHeight:p.verticalPins?1:(half+1)*.2,schPinArrangement:p.verticalPins?{topSide:{direction:'left-to-right',pins:[p.verticalPins[0]]},bottomSide:{direction:'left-to-right',pins:[p.verticalPins[1]]}}:{leftSide:{direction:'top-to-bottom',pins:p.pins.slice(0,half).map((v:any)=>v.selector)},rightSide:{direction:'top-to-bottom',pins:p.pins.slice(half).map((v:any)=>v.selector)}}})}
lines.push(`\n <${type}${Object.entries(props).map(([k,v])=>attr(k,v)).join('')}\n footprint={<ExactFootprint part={footprints[${JSON.stringify(p.name)}]} />} />`)
}
lines.push('\n {/* Named connections drive both the schematic and PCB routing. */}')
for(const p of d.parts)for(const v of p.pins.filter((v:any)=>v.net&&!v.nc))lines.push(` <netlabel${attr('connection',`.${p.name} > .${v.selector}`)}${attr('net',v.net)}${attr('schSheetName',sheetFor(p))} />`)
lines.push(' </board>','}','')
fs.writeFileSync('index.circuit.tsx',lines.join('\n'))