tscircuit / tscircuit/core

pcbPath chooses top when a plated-hole start connects to an explicit bottom via port

Open
#3,906 0 comments 0 reactions 0 assignees View on GitHub

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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.