Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,26 @@ export function computeCrossings(
const crossLen = opts.crossSegmentLength ?? 0.075
const tol = opts.tolerance ?? TOL

// Precompute endpoint orientations for all traces to help choose which
// trace receives a crossing when a junction is also present at the
// intersection point.
const endpointInfo = new Map<
string,
Array<{ traceIdx: number; orient: "horizontal" | "vertical" }>
>()
for (let ti = 0; ti < traces.length; ti++) {
const trace = traces[ti]
for (const e of trace.edges) {
const orient = isHorizontalEdge(e) ? "horizontal" : "vertical"
for (const p of [e.from, e.to]) {
const key = `${p.x.toFixed(6)},${p.y.toFixed(6)}`
const arr = endpointInfo.get(key) ?? []
arr.push({ traceIdx: ti, orient })
endpointInfo.set(key, arr)
}
}
}

// Collect intersections per edge reference
type EdgeRef = { traceIdx: number; edgeIdx: number }
const crossingsByEdge = new Map<string, number[]>()
Expand Down Expand Up @@ -196,12 +216,25 @@ export function computeCrossings(
// Crossing is when both are away from endpoints
if (!nearEndpointA && !nearEndpointB) {
// Only one of the traces should receive the crossing marker.
// Prefer assigning the crossing to the more horizontal edge.
// Prefer assigning the crossing to the more horizontal edge, but if
// there's a junction at this point choose the edge that doesn't
// participate in that junction to avoid overlap.

const pointKey = `${P.x.toFixed(6)},${P.y.toFixed(6)}`
const otherOrients = new Set(
(endpointInfo.get(pointKey) ?? [])
.filter((info) => info.traceIdx !== ti && info.traceIdx !== tj)
.map((info) => info.orient),
)

const aIsHorizontal = isHorizontalEdge(eA)
const bIsHorizontal = isHorizontalEdge(eB)

let assignToA: boolean
if (aIsHorizontal !== bIsHorizontal) {
if (otherOrients.size === 1 && aIsHorizontal !== bIsHorizontal) {
const orient = otherOrients.values().next().value
assignToA = orient === "horizontal" ? aIsHorizontal : !aIsHorizontal
} else if (aIsHorizontal !== bIsHorizontal) {
// If exactly one is horizontal, prefer the horizontal one
assignToA = aIsHorizontal
} else {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
73 changes: 73 additions & 0 deletions tests/repros/repro51-junction-hop-overlap.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { test, expect } from "bun:test"
import { getTestFixture } from "../fixtures/get-test-fixture"

test("repro51: junction and hop over traces do not overlap", () => {
const { circuit } = getTestFixture()

circuit.add(
<board pcbPack>
{/* --- 555 timer as a generic 8‑pin chip --- */}
<chip
name="U1"
footprint="soic8"
pinLabels={{
pin1: "GND",
pin2: "TRIG",
pin3: "OUT",
pin4: "RESET",
pin5: "CTRL",
pin6: "THRES",
pin7: "DISCH",
pin8: "VCC",
}}
schPinArrangement={{
leftSide: {
direction: "top-to-bottom",
pins: ["RESET", "CTRL", "THRES", "TRIG"],
},
rightSide: {
direction: "top-to-bottom",
pins: ["VCC", "OUT", "DISCH", "GND"],
},
}}
/>
{/* VCC -> DISCH */}
<resistor name="R2" resistance="10k" footprint="0805" />{" "}
{/* DISCH -> node */}
<capacitor name="C1" capacitance="10uF" footprint="1206" />{" "}
{/* node -> GND */}
<capacitor name="C2" capacitance="10nF" footprint="0805" />{" "}
{/* CTRL -> GND (stability) */}
{/* 3-pin header for power + output */}
<pinheader
name="J1"
pinCount={3}
footprint="pinrow3"
gender="male"
schFacingDirection="left"
pinLabels={{ pin1: "VCC", pin2: "OUT", pin3: "GND" }}
connections={{ VCC: "net.VCC", OUT: "net.OUT", GND: "net.GND" }}
/>
{/* Power & housekeeping */}
<trace from="U1.VCC" to="net.VCC" />
<trace from="U1.GND" to="net.GND" />
<trace from="U1.RESET" to="net.VCC" />
<trace from="U1.CTRL" to="C2.pin1" />
<trace from="C2.pin2" to="net.GND" />
{/* Astable wiring: tie THRES & TRIG; R1, R2, C1 form RC network */}
<trace from="U1.THRES" to="net.NODE" />
<trace from="U1.TRIG" to="net.NODE" />
<trace from="R2.pin2" to="net.NODE" />
<trace from="C1.pin1" to="net.NODE" />
<trace from="C1.pin2" to="net.GND" />
{/* R1 from VCC to DISCH; R2 from DISCH to node */}
<trace from="U1.DISCH" to="R2.pin1" />
{/* Output to header */}
<trace from="U1.OUT" to="net.OUT" />
</board>,
)

circuit.render()

expect(circuit).toMatchSchematicSnapshot(import.meta.path)
})