shibosoftwaredev/f1c100s-linux-nema17-stepper-controller
Compact four-layer NEMA 17 carrier PCB combining an F1C100S Linux SoC, USB-C PD power and buck regulation, W5500 10/100 Ethernet, STM32 safety/motion control, and DRV8825 stepper-driver hardware with protection, sensing, connectors, and dual-sided SMT assembly.
- Version
- 0.4.15
- License
- unset
- Stars
- 0
routing-boundary.ts
import { applyPartMetadata, applyCadMetadata, applyAssemblyPasteMetadata } from "./assembly-metadata"
import { addGroundViaFanout, applyCompactComponentPlan, packCompactBoard } from "./compact-layout"
import {
CopperPourPipelineSolver,
convertCircuitJsonToInputProblem,
initializeManifoldGeometry,
} from "@tscircuit/copper-pour-solver"
async function regenerateGroundPours(board: any) {
const db = board.root.db
const ground = db.source_net.list().find((net: any) => net.name === "BOARD_GND")
const pcbBoard = board.pcb_board_id ? db.pcb_board.get(board.pcb_board_id) : undefined
if (!ground || !pcbBoard) return
for (const pour of db.pcb_copper_pour.list())
if ((pour.layer === "top" || pour.layer === "bottom") && pour.source_net_id === ground.source_net_id)
db.pcb_copper_pour.delete(pour.pcb_copper_pour_id)
// Remove stale annotations left by the preliminary pour. The regenerated
// B-rep is authoritative for final connectivity and DRC.
for (const trace of db.pcb_trace.list()) db.pcb_trace.update(trace.pcb_trace_id, {
route: trace.route.map((point: any) => {
const { copper_pour_id, is_inside_copper_pour, ...cleanPoint } = point
return cleanPoint
}),
})
const outline = pcbBoard.outline?.length ? pcbBoard.outline : [
{ x: pcbBoard.center.x - pcbBoard.width / 2, y: pcbBoard.center.y - pcbBoard.height / 2 },
{ x: pcbBoard.center.x + pcbBoard.width / 2, y: pcbBoard.center.y - pcbBoard.height / 2 },
{ x: pcbBoard.center.x + pcbBoard.width / 2, y: pcbBoard.center.y + pcbBoard.height / 2 },
{ x: pcbBoard.center.x - pcbBoard.width / 2, y: pcbBoard.center.y + pcbBoard.height / 2 },
]
await initializeManifoldGeometry()
// Keep the continuous ground reference on top. The imported SoC fanout has
// a dense set of intentional bottom-layer supply escapes; a global bottom
// fill is unnecessary here and can create fragile slivers around that fixed
// copper. Bottom-side ground pads still reach this plane through the local
// 0.30/0.45 mm stitching vias added immediately above.
for (const layer of ["top"] as const) {
const input = convertCircuitJsonToInputProblem(db.toArray(), {
layer,
subcircuit_id: board.subcircuit_id,
source_net_id: ground.source_net_id,
pad_margin: 0.3,
trace_margin: 0.3,
pour_margin: 0.3,
board_edge_margin: 0.25,
board_edge_outline: outline,
cutout_margin: 0.25,
use_thermal_reliefs: true,
thermal_relief_spoke_width: 0.3,
outline,
})
const solver = new CopperPourPipelineSolver(input)
solver.solve()
if (solver.failed) throw new Error(solver.error ?? `Final ${layer} ground-pour regeneration failed`)
for (const shape of solver.getOutput().brep_shapes) db.pcb_copper_pour.insert({
shape: "brep",
layer,
brep_shape: shape,
source_net_id: ground.source_net_id,
subcircuit_id: board.subcircuit_id,
covered_with_solder_mask: false,
})
}
}
/** Keep the reservation in the autorouter input, then remove the non-fabricated
* helper after routing and before copper generation / final DRC. */
export function routingKeepoutRef(keepout: any) {
if (!keepout) return
keepout.doInitialPcbCopperPourRender=()=>{
if (keepout.root && keepout.pcb_keepout_id)
keepout.root.db.pcb_keepout.delete(keepout.pcb_keepout_id)
}
}
/** Apply assembly metadata before the unmodified full DRC. */
export function routingBoundaryRef(board: any) {
// React 19 calls callback refs with null during cleanup.
if (!board?.doInitialPcbDesignRuleChecks) return
// The ref fires before composite child components have expanded. Apply the
// face/connector plan immediately before footprint emission, when the full
// component tree exists but no PCB primitives have been created yet.
const runChildren=board.runRenderPhaseForChildren.bind(board)
board.runRenderPhaseForChildren=(phase:any)=>{
if(phase==="PcbComponentRender") applyCompactComponentPlan(board)
return runChildren(phase)
}
const placementCheck=board.doInitialPcbPlacementDesignRuleChecks.bind(board)
board.doInitialPcbPlacementDesignRuleChecks=()=>{ packCompactBoard(board); return placementCheck() }
const check=board.doInitialPcbDesignRuleChecks.bind(board)
board.doInitialPcbDesignRuleChecks=()=>{
if (board.root?.pcbRoutingDisabled) {
applyPartMetadata(board)
check()
return
}
board._queueAsyncEffect("post-route-copper-and-drc", async()=>{
addGroundViaFanout(board)
await regenerateGroundPours(board)
applyPartMetadata(board)
check()
})
}
const parts=board.doInitialPartsEngineRender.bind(board)
board.doInitialPartsEngineRender=()=>{ applyCadMetadata(board); applyAssemblyPasteMetadata(board); return parts() }
}