pcbPath chooses top when a plated-hole start connects to an explicit bottom via port
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 58
- Forks
- 203
- Avg merge
- 7h 39m
- Merged PRs (30d)
- 286
Description
Environment: @tscircuit/core 0.0.1892, @tscircuit/props 0.0.649, circuit-json 0.0.484, Bun 1.4.2, Linux.
A manual pcbPath from a plated contact to an explicit via .bottom port is emitted entirely on top. Reversing the same endpoints emits it on bottom. In a two-layer board this silently changed saved bottom crossings into top copper shorts. Our independent saved-route comparison and native KiCad DRC caught it.
Expected: choose a layer available at both endpoint ports (bottom in this case), or reject an incompatible path. An explicit trace-level pcbLayer would also make PTH-to-PTH layer selection possible without adding a physical via.
Reproduction (bun repro.tsx):
import React from 'react'
import {Circuit} from '@tscircuit/core'
import {writeFileSync} from 'node:fs'
for(const reverse of [false,true]){
const circuit=new Circuit()
circuit.add(<board width={10} height={10} layers={2} schematicDisabled>
<net name="SIGNAL"/>
<chip name="J1" pcbX={-2} pinLabels={{pin1:['SIGNAL']}} footprint={<footprint>
<platedhole portHints={['1']} shape="circle" outerDiameter={1.4} holeDiameter={.8}/>
</footprint>}/>
<via name="V1" pcbX={2} holeDiameter={.3} outerDiameter={.6} fromLayer="top" toLayer="bottom" connectsTo="net.SIGNAL"/>
<trace from="J1.pin1" to="net.SIGNAL"/>
<trace name="PATH" from={reverse?'.V1 > .bottom':'J1.pin1'} to={reverse?'J1.pin1':'.V1 > .bottom'}
pcbPath={[{x:reverse?-2:2,y:1}]} thickness={.25}/>
</board>)
await circuit.renderUntilSettled()
const data=circuit.getCircuitJson()
const source=data.find((r:any)=>r.type==='source_trace'&&r.name==='PATH') as any
const trace=data.find((r:any)=>r.type==='pcb_trace'&&r.source_trace_id===source.source_trace_id) as any
const layers=[...new Set(trace.route.filter((p:any)=>p.route_type==='wire').map((p:any)=>p.layer))]
console.log(JSON.stringify({reverse,layers}))
writeFileSync(new URL(reverse?'via-first.json':'pth-first.json',import.meta.url),JSON.stringify(data,null,2)+'\n')
}
Actual output:
{"reverse":false,"layers":["top"]}
{"reverse":true,"layers":["bottom"]}
In the installed core implementation, manual path emission initializes currentLayer from anchorPort.getAvailablePcbLayers()[0]; the explicit bottom endpoint does not constrain that selection. Current props expose no trace-level layer override.
Local workaround: begin bottom paths at an explicit via bottom port, and restructure the bottom tree to avoid PTH-to-PTH edges. Saved route geometry/layer checks now fail closed before native export. This workaround is implemented locally; no upstream implementation PR yet.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Run bun repro.tsx and inspect the manual path emission where currentLayer is initialized from anchorPort.getAvailablePcbLayers()[0]. Add regression coverage for both endpoint orders, then verify that compatible endpoints use the shared layer or that incompatible paths are rejected, without changing the reported saved-route expectations.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- bun, react, typescript
- Domain
- embedded-iot
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 55/100