Skip to content

Commit a8d1706

Browse files
derekbredenclaude
andcommitted
Rename pcbFan -> pcbComb to match the upstreamed prop (tscircuit#2567)
The workbench branch carried this feature under its pre-PR name (pcbFan / computeFanWaypoints). The extracted PR names it pcbComb / computeCombWaypoints. Rename here so the board dogfoods the exact upstreamed API; the util is adopted from the PR verbatim (same algorithm, polished comments). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 116c34f commit a8d1706

5 files changed

Lines changed: 41 additions & 31 deletions

File tree

dist/index.d.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,7 @@ declare class Trace extends PrimitiveComponent<typeof traceProps> implements Tra
644644
}>, z.ZodString]>, "many">, "many">>;
645645
routingPhaseIndex: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
646646
pcbStraightLine: z.ZodOptional<z.ZodBoolean>;
647+
pcbComb: z.ZodOptional<z.ZodEnum<["columnToColumn", "rowToColumn", "columnToRow", "rowToRow"]>>;
647648
schDisplayLabel: z.ZodOptional<z.ZodString>;
648649
schStroke: z.ZodOptional<z.ZodString>;
649650
highlightColor: z.ZodOptional<z.ZodString>;
@@ -695,6 +696,7 @@ declare class Trace extends PrimitiveComponent<typeof traceProps> implements Tra
695696
toLayer?: "top" | "bottom" | "inner1" | "inner2" | "inner3" | "inner4" | "inner5" | "inner6" | undefined;
696697
})[][] | undefined;
697698
pcbStraightLine?: boolean | undefined;
699+
pcbComb?: "columnToColumn" | "rowToColumn" | "columnToRow" | "rowToRow" | undefined;
698700
schDisplayLabel?: string | undefined;
699701
schStroke?: string | undefined;
700702
}, {
@@ -747,6 +749,7 @@ declare class Trace extends PrimitiveComponent<typeof traceProps> implements Tra
747749
} | undefined;
748750
})[][] | undefined;
749751
pcbStraightLine?: boolean | undefined;
752+
pcbComb?: "columnToColumn" | "rowToColumn" | "columnToRow" | "rowToRow" | undefined;
750753
schDisplayLabel?: string | undefined;
751754
schStroke?: string | undefined;
752755
}>, z.ZodObject<{
@@ -909,6 +912,7 @@ declare class Trace extends PrimitiveComponent<typeof traceProps> implements Tra
909912
}>, z.ZodString]>, "many">, "many">>;
910913
routingPhaseIndex: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
911914
pcbStraightLine: z.ZodOptional<z.ZodBoolean>;
915+
pcbComb: z.ZodOptional<z.ZodEnum<["columnToColumn", "rowToColumn", "columnToRow", "rowToRow"]>>;
912916
schDisplayLabel: z.ZodOptional<z.ZodString>;
913917
schStroke: z.ZodOptional<z.ZodString>;
914918
highlightColor: z.ZodOptional<z.ZodString>;
@@ -968,6 +972,7 @@ declare class Trace extends PrimitiveComponent<typeof traceProps> implements Tra
968972
toLayer?: "top" | "bottom" | "inner1" | "inner2" | "inner3" | "inner4" | "inner5" | "inner6" | undefined;
969973
})[][] | undefined;
970974
pcbStraightLine?: boolean | undefined;
975+
pcbComb?: "columnToColumn" | "rowToColumn" | "columnToRow" | "rowToRow" | undefined;
971976
schDisplayLabel?: string | undefined;
972977
schStroke?: string | undefined;
973978
}, {
@@ -1023,6 +1028,7 @@ declare class Trace extends PrimitiveComponent<typeof traceProps> implements Tra
10231028
} | undefined;
10241029
})[][] | undefined;
10251030
pcbStraightLine?: boolean | undefined;
1031+
pcbComb?: "columnToColumn" | "rowToColumn" | "columnToRow" | "rowToRow" | undefined;
10261032
schDisplayLabel?: string | undefined;
10271033
schStroke?: string | undefined;
10281034
}>, z.ZodObject<{
@@ -1185,6 +1191,7 @@ declare class Trace extends PrimitiveComponent<typeof traceProps> implements Tra
11851191
}>, z.ZodString]>, "many">, "many">>;
11861192
routingPhaseIndex: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
11871193
pcbStraightLine: z.ZodOptional<z.ZodBoolean>;
1194+
pcbComb: z.ZodOptional<z.ZodEnum<["columnToColumn", "rowToColumn", "columnToRow", "rowToRow"]>>;
11881195
schDisplayLabel: z.ZodOptional<z.ZodString>;
11891196
schStroke: z.ZodOptional<z.ZodString>;
11901197
highlightColor: z.ZodOptional<z.ZodString>;
@@ -1244,6 +1251,7 @@ declare class Trace extends PrimitiveComponent<typeof traceProps> implements Tra
12441251
toLayer?: "top" | "bottom" | "inner1" | "inner2" | "inner3" | "inner4" | "inner5" | "inner6" | undefined;
12451252
})[][] | undefined;
12461253
pcbStraightLine?: boolean | undefined;
1254+
pcbComb?: "columnToColumn" | "rowToColumn" | "columnToRow" | "rowToRow" | undefined;
12471255
schDisplayLabel?: string | undefined;
12481256
schStroke?: string | undefined;
12491257
}, {
@@ -1299,6 +1307,7 @@ declare class Trace extends PrimitiveComponent<typeof traceProps> implements Tra
12991307
} | undefined;
13001308
})[][] | undefined;
13011309
pcbStraightLine?: boolean | undefined;
1310+
pcbComb?: "columnToColumn" | "rowToColumn" | "columnToRow" | "rowToRow" | undefined;
13021311
schDisplayLabel?: string | undefined;
13031312
schStroke?: string | undefined;
13041313
}>]>;

dist/index.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3570,9 +3570,9 @@ var getViaDiameterDefaultsWithOverrides = (overrides, pcbStyle) => {
35703570
};
35713571
};
35723572

3573-
// lib/utils/computeFanWaypoints.ts
3573+
// lib/utils/computeCombWaypoints.ts
35743574
var monotone = (vals, sgn) => vals.every((v, i) => i === 0 || sgn * (v - vals[i - 1]) >= -1e-6);
3575-
function computeFanWaypoints(s, t, orientation, stub = 1) {
3575+
function computeCombWaypoints(s, t, orientation, stub = 1) {
35763576
const sgx = Math.sign(t.x - s.x) || 1;
35773577
const sgy = Math.sign(t.y - s.y) || 1;
35783578
let p1;
@@ -3745,10 +3745,10 @@ function Trace_doInitialPcbManualTraceRender(trace) {
37453745
const subcircuit = trace.getSubcircuit();
37463746
const hasPcbPath = props.pcbPath !== void 0;
37473747
const wantsStraightLine = Boolean(props.pcbStraightLine);
3748-
const fanOrientation = props.pcbFan;
3749-
const wantsFan = Boolean(fanOrientation);
3748+
const combOrientation = props.pcbComb;
3749+
const wantsComb = Boolean(combOrientation);
37503750
const inflatedPcbTraces = trace._inflatedPcbTraces ?? [];
3751-
if (!hasPcbPath && !wantsStraightLine && !wantsFan && inflatedPcbTraces.length === 0)
3751+
if (!hasPcbPath && !wantsStraightLine && !wantsComb && inflatedPcbTraces.length === 0)
37523752
return;
37533753
let allPortsFound;
37543754
let ports;
@@ -3949,9 +3949,9 @@ function Trace_doInitialPcbManualTraceRender(trace) {
39493949
trace._insertErrorIfTraceIsOutsideBoard(route2, ports);
39503950
return;
39513951
}
3952-
if (wantsFan && !hasPcbPath) {
3952+
if (wantsComb && !hasPcbPath) {
39533953
if (!ports || ports.length < 2) {
3954-
trace.renderError("pcbFan requires exactly two connected ports");
3954+
trace.renderError("pcbComb requires exactly two connected ports");
39553955
return;
39563956
}
39573957
const [startPort, endPort] = ports;
@@ -3961,10 +3961,10 @@ function Trace_doInitialPcbManualTraceRender(trace) {
39613961
const layer2 = sharedLayer ?? startLayers[0] ?? endLayers[0] ?? "top";
39623962
const startPos = startPort._getGlobalPcbPositionAfterLayout();
39633963
const endPos = endPort._getGlobalPcbPositionAfterLayout();
3964-
const bends = computeFanWaypoints(startPos, endPos, fanOrientation);
3964+
const bends = computeCombWaypoints(startPos, endPos, combOrientation);
39653965
if (!bends) {
39663966
console.warn(
3967-
`[pcbFan] ${trace} (${fanOrientation}): fixed fan doesn't fit \u2014 leaving for the autorouter`
3967+
`[pcbComb] ${trace} (${combOrientation}): fixed comb doesn't fit \u2014 leaving for the autorouter`
39683968
);
39693969
return;
39703970
}
@@ -4199,7 +4199,7 @@ function Trace_doInitialPcbTraceRender(trace) {
41994199
if (props.pcbStraightLine) {
42004200
return;
42014201
}
4202-
if (props.pcbFan && trace.pcb_trace_id) {
4202+
if (props.pcbComb && trace.pcb_trace_id) {
42034203
return;
42044204
}
42054205
if (!subcircuit._shouldUseTraceByTraceRouting()) {

lib/components/primitive-components/Trace/Trace_doInitialPcbManualTraceRender.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ import { clipTraceEndAtPad } from "../../../utils/trace-clipping/clipTraceEndAtP
77
import { getViaDiameterDefaults } from "../../../utils/pcbStyle/getViaDiameterDefaults"
88
import type { ManualPcbPathPoint } from "lib/utils/pcbTraceRouteToPcbPath"
99
import {
10-
computeFanWaypoints,
11-
type FanOrientation,
12-
} from "lib/utils/computeFanWaypoints"
10+
computeCombWaypoints,
11+
type CombOrientation,
12+
} from "lib/utils/computeCombWaypoints"
1313
import { TraceConnectionError } from "lib/errors"
1414
import { getPcbSelectorErrorForTracePort } from "./getPcbSelectorErrorForTracePort"
1515
import { jlcMinTolerances } from "@tscircuit/jlcpcb-manufacturing-specs"
@@ -51,14 +51,14 @@ export function Trace_doInitialPcbManualTraceRender(trace: Trace) {
5151

5252
const hasPcbPath = props.pcbPath !== undefined
5353
const wantsStraightLine = Boolean(props.pcbStraightLine)
54-
const fanOrientation = (props as any).pcbFan as FanOrientation | undefined
55-
const wantsFan = Boolean(fanOrientation)
54+
const combOrientation = (props as any).pcbComb as CombOrientation | undefined
55+
const wantsComb = Boolean(combOrientation)
5656
const inflatedPcbTraces = trace._inflatedPcbTraces ?? []
5757

5858
if (
5959
!hasPcbPath &&
6060
!wantsStraightLine &&
61-
!wantsFan &&
61+
!wantsComb &&
6262
inflatedPcbTraces.length === 0
6363
)
6464
return
@@ -309,9 +309,9 @@ export function Trace_doInitialPcbManualTraceRender(trace: Trace) {
309309
return
310310
}
311311

312-
if (wantsFan && !hasPcbPath) {
312+
if (wantsComb && !hasPcbPath) {
313313
if (!ports || ports.length < 2) {
314-
trace.renderError("pcbFan requires exactly two connected ports")
314+
trace.renderError("pcbComb requires exactly two connected ports")
315315
return
316316
}
317317

@@ -324,17 +324,17 @@ export function Trace_doInitialPcbManualTraceRender(trace: Trace) {
324324
endLayers[0] ??
325325
"top") as LayerRef
326326

327-
// The two pads are the route endpoints; the fan is computed in global coordinates
327+
// The two pads are the route endpoints; the comb is computed in global coordinates
328328
// (both positions are known by this phase), so no per-component transform is needed.
329329
const startPos = startPort._getGlobalPcbPositionAfterLayout()
330330
const endPos = endPort._getGlobalPcbPositionAfterLayout()
331-
const bends = computeFanWaypoints(startPos, endPos, fanOrientation!)
331+
const bends = computeCombWaypoints(startPos, endPos, combOrientation!)
332332

333-
// A fan whose fixed shape would overshoot (offset exceeds gap) is left unrouted so the
333+
// A comb whose fixed shape would overshoot (offset exceeds gap) is left unrouted so the
334334
// autorouter handles it, rather than drawing backtracking copper.
335335
if (!bends) {
336336
console.warn(
337-
`[pcbFan] ${trace} (${fanOrientation}): fixed fan doesn't fit — leaving for the autorouter`,
337+
`[pcbComb] ${trace} (${combOrientation}): fixed comb doesn't fit — leaving for the autorouter`,
338338
)
339339
return
340340
}

lib/components/primitive-components/Trace/Trace_doInitialPcbTraceRender.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,9 @@ export function Trace_doInitialPcbTraceRender(trace: Trace) {
121121
return
122122
}
123123

124-
// A placed fan (see PcbManualTraceRender) is already fixed copper; an infeasible fan
124+
// A placed comb (see PcbManualTraceRender) is already fixed copper; an unfittable comb
125125
// wasn't placed (no pcb_trace_id) and falls through to the autorouter below.
126-
if ((props as any).pcbFan && trace.pcb_trace_id) {
126+
if ((props as any).pcbComb && trace.pcb_trace_id) {
127127
return
128128
}
129129

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export type FanOrientation =
1+
export type CombOrientation =
22
| "columnToColumn"
33
| "rowToColumn"
44
| "columnToRow"
@@ -10,22 +10,23 @@ const monotone = (vals: number[], sgn: number) =>
1010
vals.every((v, i) => i === 0 || sgn * (v - vals[i - 1]) >= -1e-6)
1111

1212
/**
13-
* The two interior bend points of a clean fan between two pads, in board coordinates: a
13+
* The two interior bend points of a comb trace between two pads, in board coordinates: a
1414
* perpendicular escape stub off the source, one 45° diagonal covering the offset, then a
1515
* perpendicular landing into the target. The pads are the route's endpoints (added by the
1616
* caller); only these two corners are returned, so the drawn path is straight → 45° → straight.
1717
*
1818
* `orientation` is <sourceLine>To<targetLine>: a COLUMN of pads (stacked, shared x) escapes
19-
* perpendicular in x, a ROW (spread, shared y) in y.
19+
* perpendicular in x, a ROW (spread, shared y) in y. The perpendicular escape is what lets a
20+
* bundle clear rectangular SMD lands before it turns, so N of these nest into an even comb.
2021
*
2122
* Returns null when the fixed shape can't reach the target without backtracking — e.g. a
2223
* diverging fan whose perpendicular offset exceeds the gap it has to close. The caller then
2324
* leaves the trace unrouted so the autorouter handles it, rather than drawing overshooting copper.
2425
*/
25-
export function computeFanWaypoints(
26+
export function computeCombWaypoints(
2627
s: Pt,
2728
t: Pt,
28-
orientation: FanOrientation,
29+
orientation: CombOrientation,
2930
stub = 1,
3031
): Pt[] | null {
3132
const sgx = Math.sign(t.x - s.x) || 1
@@ -55,8 +56,8 @@ export function computeFanWaypoints(
5556
} else {
5657
return null
5758
}
58-
// A clean fan never backtracks: x and y must each progress monotonically source→target
59-
// across pad → escape → diagonal → land. If not, the fixed shape doesn't fit here.
59+
// A comb never backtracks: x and y must each progress monotonically source→target across
60+
// pad → escape → diagonal → land. If not, the fixed shape doesn't fit here.
6061
if (
6162
!monotone([s.x, p1.x, p2.x, t.x], sgx) ||
6263
!monotone([s.y, p1.y, p2.y, t.y], sgy)

0 commit comments

Comments
 (0)