@tscircuit/checks 0.0.207 → 0.0.209
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +7 -4
- package/dist/index.js +550 -285
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,3 +1,321 @@
|
|
|
1
|
+
// lib/check-pcb-trace-self-shorts.ts
|
|
2
|
+
import { cju } from "@tscircuit/circuit-json-util";
|
|
3
|
+
import { segmentToSegmentMinDistance } from "@tscircuit/math-utils";
|
|
4
|
+
import {
|
|
5
|
+
all_layers
|
|
6
|
+
} from "circuit-json";
|
|
7
|
+
|
|
8
|
+
// lib/check-each-pcb-trace-non-overlapping/getClosestPointBetweenSegments.ts
|
|
9
|
+
var getClosestPointBetweenSegments = (segmentA, segmentB) => {
|
|
10
|
+
const a1 = { x: segmentA.x1, y: segmentA.y1 };
|
|
11
|
+
const a2 = { x: segmentA.x2, y: segmentA.y2 };
|
|
12
|
+
const b1 = { x: segmentB.x1, y: segmentB.y1 };
|
|
13
|
+
const b2 = { x: segmentB.x2, y: segmentB.y2 };
|
|
14
|
+
const va = { x: a2.x - a1.x, y: a2.y - a1.y };
|
|
15
|
+
const vb = { x: b2.x - b1.x, y: b2.y - b1.y };
|
|
16
|
+
const lenSqrA = va.x * va.x + va.y * va.y;
|
|
17
|
+
const lenSqrB = vb.x * vb.x + vb.y * vb.y;
|
|
18
|
+
if (lenSqrA === 0 || lenSqrB === 0) {
|
|
19
|
+
if (lenSqrA === 0 && lenSqrB === 0) {
|
|
20
|
+
return {
|
|
21
|
+
x: (a1.x + b1.x) / 2,
|
|
22
|
+
y: (a1.y + b1.y) / 2
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
if (lenSqrA === 0) {
|
|
26
|
+
const t2 = clamp(
|
|
27
|
+
((a1.x - b1.x) * vb.x + (a1.y - b1.y) * vb.y) / lenSqrB,
|
|
28
|
+
0,
|
|
29
|
+
1
|
|
30
|
+
);
|
|
31
|
+
const closestOnB2 = {
|
|
32
|
+
x: b1.x + t2 * vb.x,
|
|
33
|
+
y: b1.y + t2 * vb.y
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
x: (a1.x + closestOnB2.x) / 2,
|
|
37
|
+
y: (a1.y + closestOnB2.y) / 2
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const t = clamp(
|
|
41
|
+
((b1.x - a1.x) * va.x + (b1.y - a1.y) * va.y) / lenSqrA,
|
|
42
|
+
0,
|
|
43
|
+
1
|
|
44
|
+
);
|
|
45
|
+
const closestOnA2 = {
|
|
46
|
+
x: a1.x + t * va.x,
|
|
47
|
+
y: a1.y + t * va.y
|
|
48
|
+
};
|
|
49
|
+
return {
|
|
50
|
+
x: (closestOnA2.x + b1.x) / 2,
|
|
51
|
+
y: (closestOnA2.y + b1.y) / 2
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const w = { x: a1.x - b1.x, y: a1.y - b1.y };
|
|
55
|
+
const dotAA = va.x * va.x + va.y * va.y;
|
|
56
|
+
const dotAB = va.x * vb.x + va.y * vb.y;
|
|
57
|
+
const dotAW = va.x * w.x + va.y * w.y;
|
|
58
|
+
const dotBB = vb.x * vb.x + vb.y * vb.y;
|
|
59
|
+
const dotBW = vb.x * w.x + vb.y * w.y;
|
|
60
|
+
const denominator = dotAA * dotBB - dotAB * dotAB;
|
|
61
|
+
if (denominator < 1e-10) {
|
|
62
|
+
return closestPointsParallelSegments(
|
|
63
|
+
a1,
|
|
64
|
+
a2,
|
|
65
|
+
b1,
|
|
66
|
+
b2,
|
|
67
|
+
va,
|
|
68
|
+
vb,
|
|
69
|
+
lenSqrA,
|
|
70
|
+
lenSqrB
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
let tA = (dotAB * dotBW - dotBB * dotAW) / denominator;
|
|
74
|
+
let tB = (dotAA * dotBW - dotAB * dotAW) / denominator;
|
|
75
|
+
tA = clamp(tA, 0, 1);
|
|
76
|
+
tB = clamp(tB, 0, 1);
|
|
77
|
+
tB = (tA * dotAB + dotBW) / dotBB;
|
|
78
|
+
tB = clamp(tB, 0, 1);
|
|
79
|
+
tA = (tB * dotAB - dotAW) / dotAA;
|
|
80
|
+
tA = clamp(tA, 0, 1);
|
|
81
|
+
const closestOnA = {
|
|
82
|
+
x: a1.x + tA * va.x,
|
|
83
|
+
y: a1.y + tA * va.y
|
|
84
|
+
};
|
|
85
|
+
const closestOnB = {
|
|
86
|
+
x: b1.x + tB * vb.x,
|
|
87
|
+
y: b1.y + tB * vb.y
|
|
88
|
+
};
|
|
89
|
+
const dx = closestOnA.x - closestOnB.x;
|
|
90
|
+
const dy = closestOnA.y - closestOnB.y;
|
|
91
|
+
const distance3 = Math.sqrt(dx * dx + dy * dy);
|
|
92
|
+
const averagePoint = {
|
|
93
|
+
x: (closestOnA.x + closestOnB.x) / 2,
|
|
94
|
+
y: (closestOnA.y + closestOnB.y) / 2
|
|
95
|
+
};
|
|
96
|
+
return averagePoint;
|
|
97
|
+
};
|
|
98
|
+
var closestPointsParallelSegments = (a1, a2, b1, b2, va, vb, lenSqrA, lenSqrB) => {
|
|
99
|
+
let tA = ((b1.x - a1.x) * va.x + (b1.y - a1.y) * va.y) / lenSqrA;
|
|
100
|
+
tA = clamp(tA, 0, 1);
|
|
101
|
+
const pointOnA1 = { x: a1.x + tA * va.x, y: a1.y + tA * va.y };
|
|
102
|
+
let tA2 = ((b2.x - a1.x) * va.x + (b2.y - a1.y) * va.y) / lenSqrA;
|
|
103
|
+
tA2 = clamp(tA2, 0, 1);
|
|
104
|
+
const pointOnA2 = { x: a1.x + tA2 * va.x, y: a1.y + tA2 * va.y };
|
|
105
|
+
let tB = ((a1.x - b1.x) * vb.x + (a1.y - b1.y) * vb.y) / lenSqrB;
|
|
106
|
+
tB = clamp(tB, 0, 1);
|
|
107
|
+
const pointOnB1 = { x: b1.x + tB * vb.x, y: b1.y + tB * vb.y };
|
|
108
|
+
let tB2 = ((a2.x - b1.x) * vb.x + (a2.y - b1.y) * vb.y) / lenSqrB;
|
|
109
|
+
tB2 = clamp(tB2, 0, 1);
|
|
110
|
+
const pointOnB2 = { x: b1.x + tB2 * vb.x, y: b1.y + tB2 * vb.y };
|
|
111
|
+
const distances = [
|
|
112
|
+
{
|
|
113
|
+
pointA: pointOnA1,
|
|
114
|
+
pointB: b1,
|
|
115
|
+
distance: Math.sqrt(
|
|
116
|
+
(pointOnA1.x - b1.x) ** 2 + (pointOnA1.y - b1.y) ** 2
|
|
117
|
+
)
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
pointA: pointOnA2,
|
|
121
|
+
pointB: b2,
|
|
122
|
+
distance: Math.sqrt(
|
|
123
|
+
(pointOnA2.x - b2.x) ** 2 + (pointOnA2.y - b2.y) ** 2
|
|
124
|
+
)
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
pointA: a1,
|
|
128
|
+
pointB: pointOnB1,
|
|
129
|
+
distance: Math.sqrt(
|
|
130
|
+
(a1.x - pointOnB1.x) ** 2 + (a1.y - pointOnB1.y) ** 2
|
|
131
|
+
)
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
pointA: a2,
|
|
135
|
+
pointB: pointOnB2,
|
|
136
|
+
distance: Math.sqrt(
|
|
137
|
+
(a2.x - pointOnB2.x) ** 2 + (a2.y - pointOnB2.y) ** 2
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
];
|
|
141
|
+
const closestPair = distances.reduce(
|
|
142
|
+
(closest, current) => current.distance < closest.distance ? current : closest
|
|
143
|
+
);
|
|
144
|
+
return {
|
|
145
|
+
x: (closestPair.pointA.x + closestPair.pointB.x) / 2,
|
|
146
|
+
y: (closestPair.pointA.y + closestPair.pointB.y) / 2
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
var clamp = (value, min, max) => {
|
|
150
|
+
return Math.max(min, Math.min(max, value));
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// lib/check-each-pcb-trace-non-overlapping/getPcbPortIdsConnectedToTraces.ts
|
|
154
|
+
function getPcbPortIdsConnectedToRoutePoint(routePoint) {
|
|
155
|
+
if (routePoint.route_type !== "wire") return [];
|
|
156
|
+
return [routePoint.start_pcb_port_id, routePoint.end_pcb_port_id].filter(
|
|
157
|
+
(portId) => Boolean(portId)
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
function getPcbPortIdsConnectedToTrace(trace) {
|
|
161
|
+
const connectedPcbPorts = /* @__PURE__ */ new Set();
|
|
162
|
+
for (const segment of trace.route) {
|
|
163
|
+
for (const portId of getPcbPortIdsConnectedToRoutePoint(segment)) {
|
|
164
|
+
connectedPcbPorts.add(portId);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return Array.from(connectedPcbPorts);
|
|
168
|
+
}
|
|
169
|
+
function getPcbPortIdsConnectedToTraces(traces) {
|
|
170
|
+
const connectedPorts = /* @__PURE__ */ new Set();
|
|
171
|
+
for (const trace of traces) {
|
|
172
|
+
for (const portId of getPcbPortIdsConnectedToTrace(trace)) {
|
|
173
|
+
connectedPorts.add(portId);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return Array.from(connectedPorts);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// lib/check-pcb-trace-self-shorts.ts
|
|
180
|
+
function checkPcbTraceSelfShorts(circuitJson) {
|
|
181
|
+
const matchedSourceTraceIds = new Set(
|
|
182
|
+
circuitJson.flatMap(
|
|
183
|
+
(element) => element.type === "source_bus" && element.max_length_skew !== void 0 ? element.source_trace_ids : []
|
|
184
|
+
)
|
|
185
|
+
);
|
|
186
|
+
const db = cju(circuitJson);
|
|
187
|
+
const errors = [];
|
|
188
|
+
for (const trace of circuitJson) {
|
|
189
|
+
if (trace.type !== "pcb_trace") continue;
|
|
190
|
+
if (!trace.source_trace_id || !matchedSourceTraceIds.has(trace.source_trace_id))
|
|
191
|
+
continue;
|
|
192
|
+
const segments = [];
|
|
193
|
+
const routeDistances = [];
|
|
194
|
+
let distance3 = 0;
|
|
195
|
+
let run = 0;
|
|
196
|
+
for (let i = 0; i < trace.route.length - 1; i++) {
|
|
197
|
+
routeDistances[i] = distance3;
|
|
198
|
+
const a = trace.route[i];
|
|
199
|
+
const b = trace.route[i + 1];
|
|
200
|
+
if (a.route_type !== "wire" || b.route_type !== "wire" || a.layer !== b.layer) {
|
|
201
|
+
run++;
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
const length = Math.hypot(b.x - a.x, b.y - a.y);
|
|
205
|
+
if (length === 0) continue;
|
|
206
|
+
segments.push({
|
|
207
|
+
type: "pcb_trace_segment",
|
|
208
|
+
_pcbTrace: trace,
|
|
209
|
+
pcb_trace_id: trace.pcb_trace_id,
|
|
210
|
+
thickness: a.width,
|
|
211
|
+
layer: a.layer,
|
|
212
|
+
x1: a.x,
|
|
213
|
+
y1: a.y,
|
|
214
|
+
x2: b.x,
|
|
215
|
+
y2: b.y,
|
|
216
|
+
run,
|
|
217
|
+
startDistance: distance3,
|
|
218
|
+
endDistance: distance3 + length
|
|
219
|
+
});
|
|
220
|
+
distance3 += length;
|
|
221
|
+
}
|
|
222
|
+
routeDistances[trace.route.length - 1] = distance3;
|
|
223
|
+
let shortCenter;
|
|
224
|
+
pairs: for (let i = 0; i < segments.length; i++) {
|
|
225
|
+
const a = segments[i];
|
|
226
|
+
for (let j = i + 1; j < segments.length; j++) {
|
|
227
|
+
const b = segments[j];
|
|
228
|
+
if (a.layer !== b.layer) continue;
|
|
229
|
+
const contactDistance = (a.thickness + b.thickness) / 2;
|
|
230
|
+
const dot = (a.x2 - a.x1) * (b.x2 - b.x1) + (a.y2 - a.y1) * (b.y2 - b.y1);
|
|
231
|
+
const cross = (a.x2 - a.x1) * (b.y2 - b.y1) - (a.y2 - a.y1) * (b.x2 - b.x1);
|
|
232
|
+
const adjacent = a.run === b.run && b.startDistance === a.endDistance;
|
|
233
|
+
if (adjacent && !(dot < 0 && Math.abs(cross) < 1e-9)) continue;
|
|
234
|
+
if (!adjacent && a.run === b.run && b.startDistance - a.endDistance < contactDistance && dot >= 0)
|
|
235
|
+
continue;
|
|
236
|
+
const gap = segmentToSegmentMinDistance(
|
|
237
|
+
{ x: a.x1, y: a.y1 },
|
|
238
|
+
{ x: a.x2, y: a.y2 },
|
|
239
|
+
{ x: b.x1, y: b.y1 },
|
|
240
|
+
{ x: b.x2, y: b.y2 }
|
|
241
|
+
) - contactDistance;
|
|
242
|
+
if (gap > 1e-9) continue;
|
|
243
|
+
shortCenter = getClosestPointBetweenSegments(a, b);
|
|
244
|
+
break pairs;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (!shortCenter) {
|
|
248
|
+
const board = circuitJson.find((e) => e.type === "pcb_board");
|
|
249
|
+
const stack = [
|
|
250
|
+
"top",
|
|
251
|
+
...all_layers.filter((layer) => layer.startsWith("inner")).slice(0, board ? Math.max(0, board.num_layers - 2) : void 0),
|
|
252
|
+
...board?.num_layers === 1 ? [] : ["bottom"]
|
|
253
|
+
];
|
|
254
|
+
vias: for (let i = 0; i < trace.route.length; i++) {
|
|
255
|
+
const point2 = trace.route[i];
|
|
256
|
+
if (point2.route_type !== "via") continue;
|
|
257
|
+
const materialized = db.pcb_via.list().find(
|
|
258
|
+
(via) => (via.pcb_trace_id === trace.pcb_trace_id || !via.pcb_trace_id && via.source_trace_id === trace.source_trace_id) && Math.hypot(via.x - point2.x, via.y - point2.y) <= 1e-9
|
|
259
|
+
);
|
|
260
|
+
const diameter = materialized?.outer_diameter ?? point2.outer_diameter;
|
|
261
|
+
if (diameter === void 0 || !Number.isFinite(diameter) || diameter <= 0)
|
|
262
|
+
continue;
|
|
263
|
+
const from = stack.indexOf(point2.from_layer);
|
|
264
|
+
const to = stack.indexOf(point2.to_layer);
|
|
265
|
+
const layers = materialized?.layers ?? (from >= 0 && to >= 0 ? stack.slice(Math.min(from, to), Math.max(from, to) + 1) : []);
|
|
266
|
+
for (const segment of segments) {
|
|
267
|
+
if (!layers.includes(segment.layer)) continue;
|
|
268
|
+
const reach = (diameter + segment.thickness) / 2;
|
|
269
|
+
const alongRouteGap = Math.max(
|
|
270
|
+
segment.startDistance - routeDistances[i],
|
|
271
|
+
routeDistances[i] - segment.endDistance,
|
|
272
|
+
0
|
|
273
|
+
);
|
|
274
|
+
if (alongRouteGap <= reach + 1e-9) continue;
|
|
275
|
+
const viaSegment = {
|
|
276
|
+
...segment,
|
|
277
|
+
x1: point2.x,
|
|
278
|
+
y1: point2.y,
|
|
279
|
+
x2: point2.x,
|
|
280
|
+
y2: point2.y
|
|
281
|
+
};
|
|
282
|
+
const gap = segmentToSegmentMinDistance(
|
|
283
|
+
point2,
|
|
284
|
+
point2,
|
|
285
|
+
{ x: segment.x1, y: segment.y1 },
|
|
286
|
+
{ x: segment.x2, y: segment.y2 }
|
|
287
|
+
) - reach;
|
|
288
|
+
if (gap > 1e-9) continue;
|
|
289
|
+
shortCenter = getClosestPointBetweenSegments(segment, viaSegment);
|
|
290
|
+
break vias;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (shortCenter) {
|
|
295
|
+
const sourceTrace = db.source_trace.get(trace.source_trace_id);
|
|
296
|
+
const endpointNames = (sourceTrace?.connected_source_port_ids ?? []).map((id) => {
|
|
297
|
+
const port = db.source_port.get(id);
|
|
298
|
+
const component = port?.source_component_id ? db.source_component.get(port.source_component_id) : void 0;
|
|
299
|
+
return component?.name && port?.name ? `${component.name}.${port.name}` : void 0;
|
|
300
|
+
}).filter((name) => Boolean(name));
|
|
301
|
+
const traceName = sourceTrace?.name || sourceTrace?.display_name || endpointNames.join(" \u2192 ") || "unnamed";
|
|
302
|
+
errors.push({
|
|
303
|
+
type: "pcb_trace_error",
|
|
304
|
+
error_type: "pcb_trace_error",
|
|
305
|
+
pcb_trace_error_id: `self_short_${trace.pcb_trace_id}`,
|
|
306
|
+
pcb_trace_id: trace.pcb_trace_id,
|
|
307
|
+
source_trace_id: trace.source_trace_id,
|
|
308
|
+
message: `PCB trace "${traceName}" shorts to itself, bypassing part of its length-matched route`,
|
|
309
|
+
center: shortCenter,
|
|
310
|
+
pcb_component_ids: [],
|
|
311
|
+
pcb_port_ids: getPcbPortIdsConnectedToTraces([trace]),
|
|
312
|
+
subcircuit_id: trace.subcircuit_id
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return errors;
|
|
317
|
+
}
|
|
318
|
+
|
|
1
319
|
// lib/check-copper-pour-shorts.ts
|
|
2
320
|
import Flatbush from "flatbush";
|
|
3
321
|
import "@flatten-js/core";
|
|
@@ -696,25 +1014,190 @@ function checkCopperPourShorts(circuitJson, { connMap } = {}) {
|
|
|
696
1014
|
const id = `copper_pour_short_${[pour.elementId, other.elementId].sort().join("_")}`;
|
|
697
1015
|
if (errors.has(id) || !pour.shapes.some((shape) => touchesPour(shape, otherShape)))
|
|
698
1016
|
continue;
|
|
699
|
-
errors.set(id, {
|
|
700
|
-
type: "pcb_placement_error",
|
|
701
|
-
pcb_placement_error_id: id,
|
|
702
|
-
error_type: "pcb_placement_error",
|
|
703
|
-
message: `Copper pour ${pour.elementId} (${netNames.get(netId) ?? netId ?? "unassigned net"}) shorts to ${getReadableNameForElement(circuitJson, other.elementId)} (${other.elementId}) on ${pour.layer} (accidental copper contact)`,
|
|
704
|
-
subcircuit_id: element.subcircuit_id
|
|
705
|
-
});
|
|
1017
|
+
errors.set(id, {
|
|
1018
|
+
type: "pcb_placement_error",
|
|
1019
|
+
pcb_placement_error_id: id,
|
|
1020
|
+
error_type: "pcb_placement_error",
|
|
1021
|
+
message: `Copper pour ${pour.elementId} (${netNames.get(netId) ?? netId ?? "unassigned net"}) shorts to ${getReadableNameForElement(circuitJson, other.elementId)} (${other.elementId}) on ${pour.layer} (accidental copper contact)`,
|
|
1022
|
+
subcircuit_id: element.subcircuit_id
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
return [...errors.values()];
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
// lib/util/create-indexed-pcb-connectivity-map.ts
|
|
1030
|
+
import {
|
|
1031
|
+
ConnectivityMap,
|
|
1032
|
+
findConnectedNetworks,
|
|
1033
|
+
PcbConnectivityMap
|
|
1034
|
+
} from "circuit-json-to-connectivity-map";
|
|
1035
|
+
import Flatbush3 from "flatbush";
|
|
1036
|
+
|
|
1037
|
+
// lib/util/get-via-and-pour-connections.ts
|
|
1038
|
+
import { all_layers as all_layers2 } from "circuit-json";
|
|
1039
|
+
import {
|
|
1040
|
+
getPourPolygon,
|
|
1041
|
+
getTraceSegmentPolygon,
|
|
1042
|
+
getViaPolygon
|
|
1043
|
+
} from "@tscircuit/circuit-json-util";
|
|
1044
|
+
import Flatbush2 from "flatbush";
|
|
1045
|
+
|
|
1046
|
+
// lib/copper-pour-connectivity/create-copper-polygon-contact-tester.ts
|
|
1047
|
+
import {
|
|
1048
|
+
Box
|
|
1049
|
+
} from "@flatten-js/core";
|
|
1050
|
+
function createCopperPolygonContactTester(tolerance) {
|
|
1051
|
+
const cache = /* @__PURE__ */ new WeakMap();
|
|
1052
|
+
const getGeometry = (polygon) => {
|
|
1053
|
+
let geometry = cache.get(polygon);
|
|
1054
|
+
if (!geometry) {
|
|
1055
|
+
geometry = {
|
|
1056
|
+
box: polygon.box,
|
|
1057
|
+
facePoints: [...polygon.faces].map((face) => face.first.start),
|
|
1058
|
+
edges: [...polygon.edges].map((edge) => edge.shape),
|
|
1059
|
+
edgeIndex: polygon.edges
|
|
1060
|
+
};
|
|
1061
|
+
cache.set(polygon, geometry);
|
|
1062
|
+
}
|
|
1063
|
+
return geometry;
|
|
1064
|
+
};
|
|
1065
|
+
return (a, b) => {
|
|
1066
|
+
if (a.isEmpty() || b.isEmpty()) return false;
|
|
1067
|
+
const ga = getGeometry(a);
|
|
1068
|
+
const gb = getGeometry(b);
|
|
1069
|
+
if (ga.box.xmin > gb.box.xmax + tolerance || ga.box.xmax < gb.box.xmin - tolerance || ga.box.ymin > gb.box.ymax + tolerance || ga.box.ymax < gb.box.ymin - tolerance)
|
|
1070
|
+
return false;
|
|
1071
|
+
if (ga.facePoints.some(
|
|
1072
|
+
(point2) => gb.box.contains(point2) && b.contains(point2)
|
|
1073
|
+
) || gb.facePoints.some((point2) => ga.box.contains(point2) && a.contains(point2)))
|
|
1074
|
+
return true;
|
|
1075
|
+
const [small, large] = ga.edges.length <= gb.edges.length ? [ga, gb] : [gb, ga];
|
|
1076
|
+
for (const edge of small.edges) {
|
|
1077
|
+
const box = edge.box;
|
|
1078
|
+
const queryBox = new Box(
|
|
1079
|
+
box.xmin - tolerance,
|
|
1080
|
+
box.ymin - tolerance,
|
|
1081
|
+
box.xmax + tolerance,
|
|
1082
|
+
box.ymax + tolerance
|
|
1083
|
+
);
|
|
1084
|
+
const candidates = large.edgeIndex.search(
|
|
1085
|
+
queryBox
|
|
1086
|
+
);
|
|
1087
|
+
for (const candidate of candidates) {
|
|
1088
|
+
if (edge.distanceTo(candidate.shape)[0] <= tolerance) return true;
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
return false;
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
// lib/util/get-via-and-pour-connections.ts
|
|
1096
|
+
function getViaAndPourConnections(circuit) {
|
|
1097
|
+
if (!circuit.some(
|
|
1098
|
+
(e) => e.type === "pcb_via" || e.type === "pcb_copper_pour" || e.type === "pcb_trace" && e.route.some((p) => p.route_type === "via")
|
|
1099
|
+
))
|
|
1100
|
+
return [];
|
|
1101
|
+
const conductors = [];
|
|
1102
|
+
const connections = [];
|
|
1103
|
+
const scale = 1e6;
|
|
1104
|
+
const tolerance = 1e-7 * scale;
|
|
1105
|
+
const touches = createCopperPolygonContactTester(tolerance);
|
|
1106
|
+
const add = (id, layers, polygon, bridges = false) => {
|
|
1107
|
+
if (!polygon.isEmpty())
|
|
1108
|
+
conductors.push({
|
|
1109
|
+
id,
|
|
1110
|
+
layers,
|
|
1111
|
+
polygon: polygon.scale(scale, scale),
|
|
1112
|
+
bridges
|
|
1113
|
+
});
|
|
1114
|
+
};
|
|
1115
|
+
const vias = circuit.filter((e) => e.type === "pcb_via");
|
|
1116
|
+
const board = circuit.find((e) => e.type === "pcb_board");
|
|
1117
|
+
const stack = [
|
|
1118
|
+
"top",
|
|
1119
|
+
...all_layers2.filter((l) => l.startsWith("inner")).slice(0, board ? Math.max(0, board.num_layers - 2) : void 0),
|
|
1120
|
+
...board?.num_layers === 1 ? [] : ["bottom"]
|
|
1121
|
+
];
|
|
1122
|
+
for (const copper of circuit) {
|
|
1123
|
+
if (copper.type === "pcb_via") {
|
|
1124
|
+
add(
|
|
1125
|
+
copper.pcb_via_id,
|
|
1126
|
+
copper.layers,
|
|
1127
|
+
getViaPolygon(copper, copper.outer_diameter, 0),
|
|
1128
|
+
true
|
|
1129
|
+
);
|
|
1130
|
+
if (copper.pcb_trace_id)
|
|
1131
|
+
connections.push([copper.pcb_via_id, copper.pcb_trace_id]);
|
|
1132
|
+
} else if (copper.type === "pcb_copper_pour") {
|
|
1133
|
+
add(
|
|
1134
|
+
copper.pcb_copper_pour_id,
|
|
1135
|
+
[copper.layer],
|
|
1136
|
+
getPourPolygon(copper),
|
|
1137
|
+
true
|
|
1138
|
+
);
|
|
1139
|
+
} else if (copper.type === "pcb_trace") {
|
|
1140
|
+
if (copper.route_thickness_mode === "interpolated") continue;
|
|
1141
|
+
for (let i = 0; i < copper.route.length; i++) {
|
|
1142
|
+
const a = copper.route[i], b = copper.route[i + 1];
|
|
1143
|
+
if (a.route_type === "wire" && b?.route_type === "wire" && a.layer === b.layer && (a.x !== b.x || a.y !== b.y)) {
|
|
1144
|
+
add(
|
|
1145
|
+
copper.pcb_trace_id,
|
|
1146
|
+
[a.layer],
|
|
1147
|
+
getTraceSegmentPolygon(a, b, a.width)
|
|
1148
|
+
);
|
|
1149
|
+
} else if (a.route_type === "via") {
|
|
1150
|
+
if (vias.some(
|
|
1151
|
+
(v) => v.pcb_trace_id === copper.pcb_trace_id && Math.hypot(v.x - a.x, v.y - a.y) < 1e-9
|
|
1152
|
+
))
|
|
1153
|
+
continue;
|
|
1154
|
+
const from = stack.indexOf(a.from_layer), to = stack.indexOf(a.to_layer);
|
|
1155
|
+
if (from < 0 || to < 0 || !a.outer_diameter) continue;
|
|
1156
|
+
const via = {
|
|
1157
|
+
type: "pcb_via",
|
|
1158
|
+
pcb_via_id: `${copper.pcb_trace_id}_via_${i}`,
|
|
1159
|
+
x: a.x,
|
|
1160
|
+
y: a.y,
|
|
1161
|
+
outer_diameter: a.outer_diameter,
|
|
1162
|
+
hole_diameter: 0,
|
|
1163
|
+
layers: stack.slice(Math.min(from, to), Math.max(from, to) + 1)
|
|
1164
|
+
};
|
|
1165
|
+
add(
|
|
1166
|
+
copper.pcb_trace_id,
|
|
1167
|
+
via.layers,
|
|
1168
|
+
getViaPolygon(via, via.outer_diameter, 0),
|
|
1169
|
+
true
|
|
1170
|
+
);
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
if (!conductors.length) return connections;
|
|
1176
|
+
const index = new Flatbush2(conductors.length);
|
|
1177
|
+
for (const { polygon } of conductors) {
|
|
1178
|
+
const b = polygon.box;
|
|
1179
|
+
index.add(b.xmin, b.ymin, b.xmax, b.ymax);
|
|
1180
|
+
}
|
|
1181
|
+
index.finish();
|
|
1182
|
+
for (const [i, a] of conductors.entries()) {
|
|
1183
|
+
if (!a.bridges) continue;
|
|
1184
|
+
const box = a.polygon.box;
|
|
1185
|
+
for (const j of index.search(
|
|
1186
|
+
box.xmin - tolerance,
|
|
1187
|
+
box.ymin - tolerance,
|
|
1188
|
+
box.xmax + tolerance,
|
|
1189
|
+
box.ymax + tolerance
|
|
1190
|
+
)) {
|
|
1191
|
+
const b = conductors[j];
|
|
1192
|
+
if (i === j || a.id === b.id || b.bridges && j < i || !a.layers.some((l) => b.layers.includes(l)))
|
|
1193
|
+
continue;
|
|
1194
|
+
if (touches(a.polygon, b.polygon)) connections.push([a.id, b.id]);
|
|
706
1195
|
}
|
|
707
1196
|
}
|
|
708
|
-
return
|
|
1197
|
+
return connections;
|
|
709
1198
|
}
|
|
710
1199
|
|
|
711
1200
|
// lib/util/create-indexed-pcb-connectivity-map.ts
|
|
712
|
-
import {
|
|
713
|
-
ConnectivityMap,
|
|
714
|
-
findConnectedNetworks,
|
|
715
|
-
PcbConnectivityMap
|
|
716
|
-
} from "circuit-json-to-connectivity-map";
|
|
717
|
-
import Flatbush2 from "flatbush";
|
|
718
1201
|
function createIndexedPcbConnectivityMap(circuitJson) {
|
|
719
1202
|
const map = new PcbConnectivityMap();
|
|
720
1203
|
map.circuitJson = circuitJson;
|
|
@@ -756,7 +1239,7 @@ function createIndexedPcbConnectivityMap(circuitJson) {
|
|
|
756
1239
|
}
|
|
757
1240
|
const candidates = traces.map(() => /* @__PURE__ */ new Set());
|
|
758
1241
|
for (const bounds of boundsByLayer.values()) {
|
|
759
|
-
const index = new
|
|
1242
|
+
const index = new Flatbush3(bounds.length);
|
|
760
1243
|
for (const box of bounds) index.add(box.minX, box.minY, box.maxX, box.maxY);
|
|
761
1244
|
index.finish();
|
|
762
1245
|
for (const box of bounds) {
|
|
@@ -795,6 +1278,7 @@ function createIndexedPcbConnectivityMap(circuitJson) {
|
|
|
795
1278
|
for (const connection of connectionsByPort.get(portId) ?? [])
|
|
796
1279
|
connections.push(connection);
|
|
797
1280
|
}
|
|
1281
|
+
connections.push(...getViaAndPourConnections(circuitJson));
|
|
798
1282
|
map.connMap = new ConnectivityMap(findConnectedNetworks(connections));
|
|
799
1283
|
return map;
|
|
800
1284
|
}
|
|
@@ -808,7 +1292,7 @@ import {
|
|
|
808
1292
|
getSegmentIntersection,
|
|
809
1293
|
isPointInsidePolygon,
|
|
810
1294
|
pointToSegmentClosestPoint,
|
|
811
|
-
segmentToSegmentMinDistance
|
|
1295
|
+
segmentToSegmentMinDistance as segmentToSegmentMinDistance2
|
|
812
1296
|
} from "@tscircuit/math-utils";
|
|
813
1297
|
var rotatePoint = (point2, angleDegrees) => {
|
|
814
1298
|
const angle = angleDegrees * Math.PI / 180;
|
|
@@ -908,7 +1392,7 @@ var getClosestPointsBetweenSegments = (a1, a2, b1, b2) => {
|
|
|
908
1392
|
}
|
|
909
1393
|
}
|
|
910
1394
|
return {
|
|
911
|
-
distance:
|
|
1395
|
+
distance: segmentToSegmentMinDistance2(a1, a2, b1, b2),
|
|
912
1396
|
pointOnA: best.pointOnA,
|
|
913
1397
|
pointOnB: best.pointOnB,
|
|
914
1398
|
center: {
|
|
@@ -1264,63 +1748,14 @@ function getReadableNameForFootprintPad(circuitJson, pad, ordinal) {
|
|
|
1264
1748
|
return `${padKind} #${ordinal + 1} at ${location}`;
|
|
1265
1749
|
}
|
|
1266
1750
|
|
|
1267
|
-
// lib/copper-pour-connectivity/create-copper-polygon-contact-tester.ts
|
|
1268
|
-
import {
|
|
1269
|
-
Box
|
|
1270
|
-
} from "@flatten-js/core";
|
|
1271
|
-
function createCopperPolygonContactTester(tolerance) {
|
|
1272
|
-
const cache = /* @__PURE__ */ new WeakMap();
|
|
1273
|
-
const getGeometry = (polygon) => {
|
|
1274
|
-
let geometry = cache.get(polygon);
|
|
1275
|
-
if (!geometry) {
|
|
1276
|
-
geometry = {
|
|
1277
|
-
box: polygon.box,
|
|
1278
|
-
facePoints: [...polygon.faces].map((face) => face.first.start),
|
|
1279
|
-
edges: [...polygon.edges].map((edge) => edge.shape),
|
|
1280
|
-
edgeIndex: polygon.edges
|
|
1281
|
-
};
|
|
1282
|
-
cache.set(polygon, geometry);
|
|
1283
|
-
}
|
|
1284
|
-
return geometry;
|
|
1285
|
-
};
|
|
1286
|
-
return (a, b) => {
|
|
1287
|
-
if (a.isEmpty() || b.isEmpty()) return false;
|
|
1288
|
-
const ga = getGeometry(a);
|
|
1289
|
-
const gb = getGeometry(b);
|
|
1290
|
-
if (ga.box.xmin > gb.box.xmax + tolerance || ga.box.xmax < gb.box.xmin - tolerance || ga.box.ymin > gb.box.ymax + tolerance || ga.box.ymax < gb.box.ymin - tolerance)
|
|
1291
|
-
return false;
|
|
1292
|
-
if (ga.facePoints.some(
|
|
1293
|
-
(point2) => gb.box.contains(point2) && b.contains(point2)
|
|
1294
|
-
) || gb.facePoints.some((point2) => ga.box.contains(point2) && a.contains(point2)))
|
|
1295
|
-
return true;
|
|
1296
|
-
const [small, large] = ga.edges.length <= gb.edges.length ? [ga, gb] : [gb, ga];
|
|
1297
|
-
for (const edge of small.edges) {
|
|
1298
|
-
const box = edge.box;
|
|
1299
|
-
const queryBox = new Box(
|
|
1300
|
-
box.xmin - tolerance,
|
|
1301
|
-
box.ymin - tolerance,
|
|
1302
|
-
box.xmax + tolerance,
|
|
1303
|
-
box.ymax + tolerance
|
|
1304
|
-
);
|
|
1305
|
-
const candidates = large.edgeIndex.search(
|
|
1306
|
-
queryBox
|
|
1307
|
-
);
|
|
1308
|
-
for (const candidate of candidates) {
|
|
1309
|
-
if (edge.distanceTo(candidate.shape)[0] <= tolerance) return true;
|
|
1310
|
-
}
|
|
1311
|
-
}
|
|
1312
|
-
return false;
|
|
1313
|
-
};
|
|
1314
|
-
}
|
|
1315
|
-
|
|
1316
1751
|
// lib/copper-pour-connectivity/get-copper-pour-connectivity.ts
|
|
1317
1752
|
import {
|
|
1318
1753
|
getPrimaryId,
|
|
1319
1754
|
getPlatedHolePolygon,
|
|
1320
|
-
getPourPolygon,
|
|
1755
|
+
getPourPolygon as getPourPolygon2,
|
|
1321
1756
|
getSmtPadPolygon,
|
|
1322
|
-
getTraceSegmentPolygon,
|
|
1323
|
-
getViaPolygon
|
|
1757
|
+
getTraceSegmentPolygon as getTraceSegmentPolygon2,
|
|
1758
|
+
getViaPolygon as getViaPolygon2
|
|
1324
1759
|
} from "@tscircuit/circuit-json-util";
|
|
1325
1760
|
function getCopperPourConnectivity(circuitJson, connectivity) {
|
|
1326
1761
|
const scale = 1e6;
|
|
@@ -1343,7 +1778,7 @@ function getCopperPourConnectivity(circuitJson, connectivity) {
|
|
|
1343
1778
|
if (!netId) continue;
|
|
1344
1779
|
pouredNets.add(netId);
|
|
1345
1780
|
add({
|
|
1346
|
-
polygon:
|
|
1781
|
+
polygon: getPourPolygon2(pour),
|
|
1347
1782
|
layers: [pour.layer],
|
|
1348
1783
|
netId,
|
|
1349
1784
|
isPour: true
|
|
@@ -1364,7 +1799,7 @@ function getCopperPourConnectivity(circuitJson, connectivity) {
|
|
|
1364
1799
|
if (start.route_type !== "wire" || end.route_type !== "wire" || start.layer !== end.layer)
|
|
1365
1800
|
continue;
|
|
1366
1801
|
add({
|
|
1367
|
-
polygon:
|
|
1802
|
+
polygon: getTraceSegmentPolygon2(start, end, start.width),
|
|
1368
1803
|
layers: [start.layer],
|
|
1369
1804
|
netId: netId2
|
|
1370
1805
|
});
|
|
@@ -1394,7 +1829,7 @@ function getCopperPourConnectivity(circuitJson, connectivity) {
|
|
|
1394
1829
|
});
|
|
1395
1830
|
} else {
|
|
1396
1831
|
add({
|
|
1397
|
-
polygon:
|
|
1832
|
+
polygon: getViaPolygon2(
|
|
1398
1833
|
copper,
|
|
1399
1834
|
copper.outer_diameter,
|
|
1400
1835
|
copper.hole_diameter
|
|
@@ -1578,20 +2013,20 @@ function checkEachPcbPortConnectedToPcbTraces(circuitJson, {
|
|
|
1578
2013
|
}
|
|
1579
2014
|
|
|
1580
2015
|
// lib/check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping.ts
|
|
1581
|
-
import { cju as
|
|
2016
|
+
import { cju as cju3, getReadableNameForElement as getReadableNameForElement3 } from "@tscircuit/circuit-json-util";
|
|
1582
2017
|
import { getPrimaryId as getPrimaryId2 } from "@tscircuit/circuit-json-util";
|
|
1583
2018
|
import {
|
|
1584
2019
|
segmentToBoundsMinDistance,
|
|
1585
2020
|
segmentToCircleMinDistance as segmentToCircleMinDistance2
|
|
1586
2021
|
} from "@tscircuit/math-utils";
|
|
1587
|
-
import { segmentToSegmentMinDistance as
|
|
2022
|
+
import { segmentToSegmentMinDistance as segmentToSegmentMinDistance4 } from "@tscircuit/math-utils";
|
|
1588
2023
|
import {
|
|
1589
2024
|
getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson3
|
|
1590
2025
|
} from "circuit-json-to-connectivity-map";
|
|
1591
2026
|
|
|
1592
2027
|
// lib/check-pad-clearance/common.ts
|
|
1593
2028
|
import {
|
|
1594
|
-
cju,
|
|
2029
|
+
cju as cju2,
|
|
1595
2030
|
distanceBetweenCircleAndCircle,
|
|
1596
2031
|
distanceBetweenCircleAndPolygon,
|
|
1597
2032
|
distanceBetweenPolygonAndPolygon,
|
|
@@ -1601,7 +2036,7 @@ import {
|
|
|
1601
2036
|
midpoint,
|
|
1602
2037
|
pointToSegmentClosestPoint as pointToSegmentClosestPoint2,
|
|
1603
2038
|
segmentToCircleMinDistance,
|
|
1604
|
-
segmentToSegmentMinDistance as
|
|
2039
|
+
segmentToSegmentMinDistance as segmentToSegmentMinDistance3
|
|
1605
2040
|
} from "@tscircuit/math-utils";
|
|
1606
2041
|
|
|
1607
2042
|
// node_modules/@tscircuit/jlcpcb-manufacturing-specs/lib/jlcpcb-manufacturing-specs.ts
|
|
@@ -1768,7 +2203,7 @@ var getPadToPadGap = (padA, padB) => {
|
|
|
1768
2203
|
if (isPillPad(padA) && isPillPad(padB)) {
|
|
1769
2204
|
const pillA = getPillCenterLineForPad(padA);
|
|
1770
2205
|
const pillB = getPillCenterLineForPad(padB);
|
|
1771
|
-
return
|
|
2206
|
+
return segmentToSegmentMinDistance3(
|
|
1772
2207
|
pillA.start,
|
|
1773
2208
|
pillA.end,
|
|
1774
2209
|
pillB.start,
|
|
@@ -1823,11 +2258,11 @@ var getPadToPadGap = (padA, padB) => {
|
|
|
1823
2258
|
);
|
|
1824
2259
|
};
|
|
1825
2260
|
var getPads = (circuitJson) => [
|
|
1826
|
-
...
|
|
1827
|
-
...
|
|
2261
|
+
...cju2(circuitJson).pcb_smtpad.list(),
|
|
2262
|
+
...cju2(circuitJson).pcb_plated_hole.list()
|
|
1828
2263
|
];
|
|
1829
2264
|
var getTraceSegments = (circuitJson) => {
|
|
1830
|
-
const pcbTraces =
|
|
2265
|
+
const pcbTraces = cju2(circuitJson).pcb_trace.list();
|
|
1831
2266
|
return pcbTraces.flatMap((pcbTrace) => {
|
|
1832
2267
|
const segments = [];
|
|
1833
2268
|
for (let i = 0; i < pcbTrace.route.length - 1; i++) {
|
|
@@ -2045,7 +2480,7 @@ var SpatialObjectIndex = class {
|
|
|
2045
2480
|
};
|
|
2046
2481
|
|
|
2047
2482
|
// lib/util/getLayersOfPcbElement.ts
|
|
2048
|
-
import { all_layers } from "circuit-json";
|
|
2483
|
+
import { all_layers as all_layers3 } from "circuit-json";
|
|
2049
2484
|
function getLayersOfPcbElement(obj) {
|
|
2050
2485
|
if (obj.type === "pcb_trace_segment") {
|
|
2051
2486
|
return [obj.layer];
|
|
@@ -2054,13 +2489,13 @@ function getLayersOfPcbElement(obj) {
|
|
|
2054
2489
|
return [obj.layer];
|
|
2055
2490
|
}
|
|
2056
2491
|
if (obj.type === "pcb_plated_hole") {
|
|
2057
|
-
return Array.isArray(obj.layers) ? obj.layers : [...
|
|
2492
|
+
return Array.isArray(obj.layers) ? obj.layers : [...all_layers3];
|
|
2058
2493
|
}
|
|
2059
2494
|
if (obj.type === "pcb_hole") {
|
|
2060
|
-
return [...
|
|
2495
|
+
return [...all_layers3];
|
|
2061
2496
|
}
|
|
2062
2497
|
if (obj.type === "pcb_via") {
|
|
2063
|
-
return Array.isArray(obj.layers) ? obj.layers : [...
|
|
2498
|
+
return Array.isArray(obj.layers) ? obj.layers : [...all_layers3];
|
|
2064
2499
|
}
|
|
2065
2500
|
if (obj.type === "pcb_keepout") {
|
|
2066
2501
|
return Array.isArray(obj.layers) ? obj.layers : [];
|
|
@@ -2161,151 +2596,6 @@ var getClosestPointBetweenSegmentAndBounds = (segment, bounds) => {
|
|
|
2161
2596
|
return closestPoint;
|
|
2162
2597
|
};
|
|
2163
2598
|
|
|
2164
|
-
// lib/check-each-pcb-trace-non-overlapping/getClosestPointBetweenSegments.ts
|
|
2165
|
-
var getClosestPointBetweenSegments = (segmentA, segmentB) => {
|
|
2166
|
-
const a1 = { x: segmentA.x1, y: segmentA.y1 };
|
|
2167
|
-
const a2 = { x: segmentA.x2, y: segmentA.y2 };
|
|
2168
|
-
const b1 = { x: segmentB.x1, y: segmentB.y1 };
|
|
2169
|
-
const b2 = { x: segmentB.x2, y: segmentB.y2 };
|
|
2170
|
-
const va = { x: a2.x - a1.x, y: a2.y - a1.y };
|
|
2171
|
-
const vb = { x: b2.x - b1.x, y: b2.y - b1.y };
|
|
2172
|
-
const lenSqrA = va.x * va.x + va.y * va.y;
|
|
2173
|
-
const lenSqrB = vb.x * vb.x + vb.y * vb.y;
|
|
2174
|
-
if (lenSqrA === 0 || lenSqrB === 0) {
|
|
2175
|
-
if (lenSqrA === 0 && lenSqrB === 0) {
|
|
2176
|
-
return {
|
|
2177
|
-
x: (a1.x + b1.x) / 2,
|
|
2178
|
-
y: (a1.y + b1.y) / 2
|
|
2179
|
-
};
|
|
2180
|
-
}
|
|
2181
|
-
if (lenSqrA === 0) {
|
|
2182
|
-
const t2 = clamp(
|
|
2183
|
-
((a1.x - b1.x) * vb.x + (a1.y - b1.y) * vb.y) / lenSqrB,
|
|
2184
|
-
0,
|
|
2185
|
-
1
|
|
2186
|
-
);
|
|
2187
|
-
const closestOnB2 = {
|
|
2188
|
-
x: b1.x + t2 * vb.x,
|
|
2189
|
-
y: b1.y + t2 * vb.y
|
|
2190
|
-
};
|
|
2191
|
-
return {
|
|
2192
|
-
x: (a1.x + closestOnB2.x) / 2,
|
|
2193
|
-
y: (a1.y + closestOnB2.y) / 2
|
|
2194
|
-
};
|
|
2195
|
-
}
|
|
2196
|
-
const t = clamp(
|
|
2197
|
-
((b1.x - a1.x) * va.x + (b1.y - a1.y) * va.y) / lenSqrA,
|
|
2198
|
-
0,
|
|
2199
|
-
1
|
|
2200
|
-
);
|
|
2201
|
-
const closestOnA2 = {
|
|
2202
|
-
x: a1.x + t * va.x,
|
|
2203
|
-
y: a1.y + t * va.y
|
|
2204
|
-
};
|
|
2205
|
-
return {
|
|
2206
|
-
x: (closestOnA2.x + b1.x) / 2,
|
|
2207
|
-
y: (closestOnA2.y + b1.y) / 2
|
|
2208
|
-
};
|
|
2209
|
-
}
|
|
2210
|
-
const w = { x: a1.x - b1.x, y: a1.y - b1.y };
|
|
2211
|
-
const dotAA = va.x * va.x + va.y * va.y;
|
|
2212
|
-
const dotAB = va.x * vb.x + va.y * vb.y;
|
|
2213
|
-
const dotAW = va.x * w.x + va.y * w.y;
|
|
2214
|
-
const dotBB = vb.x * vb.x + vb.y * vb.y;
|
|
2215
|
-
const dotBW = vb.x * w.x + vb.y * w.y;
|
|
2216
|
-
const denominator = dotAA * dotBB - dotAB * dotAB;
|
|
2217
|
-
if (denominator < 1e-10) {
|
|
2218
|
-
return closestPointsParallelSegments(
|
|
2219
|
-
a1,
|
|
2220
|
-
a2,
|
|
2221
|
-
b1,
|
|
2222
|
-
b2,
|
|
2223
|
-
va,
|
|
2224
|
-
vb,
|
|
2225
|
-
lenSqrA,
|
|
2226
|
-
lenSqrB
|
|
2227
|
-
);
|
|
2228
|
-
}
|
|
2229
|
-
let tA = (dotAB * dotBW - dotBB * dotAW) / denominator;
|
|
2230
|
-
let tB = (dotAA * dotBW - dotAB * dotAW) / denominator;
|
|
2231
|
-
tA = clamp(tA, 0, 1);
|
|
2232
|
-
tB = clamp(tB, 0, 1);
|
|
2233
|
-
tB = (tA * dotAB + dotBW) / dotBB;
|
|
2234
|
-
tB = clamp(tB, 0, 1);
|
|
2235
|
-
tA = (tB * dotAB - dotAW) / dotAA;
|
|
2236
|
-
tA = clamp(tA, 0, 1);
|
|
2237
|
-
const closestOnA = {
|
|
2238
|
-
x: a1.x + tA * va.x,
|
|
2239
|
-
y: a1.y + tA * va.y
|
|
2240
|
-
};
|
|
2241
|
-
const closestOnB = {
|
|
2242
|
-
x: b1.x + tB * vb.x,
|
|
2243
|
-
y: b1.y + tB * vb.y
|
|
2244
|
-
};
|
|
2245
|
-
const dx = closestOnA.x - closestOnB.x;
|
|
2246
|
-
const dy = closestOnA.y - closestOnB.y;
|
|
2247
|
-
const distance3 = Math.sqrt(dx * dx + dy * dy);
|
|
2248
|
-
const averagePoint = {
|
|
2249
|
-
x: (closestOnA.x + closestOnB.x) / 2,
|
|
2250
|
-
y: (closestOnA.y + closestOnB.y) / 2
|
|
2251
|
-
};
|
|
2252
|
-
return averagePoint;
|
|
2253
|
-
};
|
|
2254
|
-
var closestPointsParallelSegments = (a1, a2, b1, b2, va, vb, lenSqrA, lenSqrB) => {
|
|
2255
|
-
let tA = ((b1.x - a1.x) * va.x + (b1.y - a1.y) * va.y) / lenSqrA;
|
|
2256
|
-
tA = clamp(tA, 0, 1);
|
|
2257
|
-
const pointOnA1 = { x: a1.x + tA * va.x, y: a1.y + tA * va.y };
|
|
2258
|
-
let tA2 = ((b2.x - a1.x) * va.x + (b2.y - a1.y) * va.y) / lenSqrA;
|
|
2259
|
-
tA2 = clamp(tA2, 0, 1);
|
|
2260
|
-
const pointOnA2 = { x: a1.x + tA2 * va.x, y: a1.y + tA2 * va.y };
|
|
2261
|
-
let tB = ((a1.x - b1.x) * vb.x + (a1.y - b1.y) * vb.y) / lenSqrB;
|
|
2262
|
-
tB = clamp(tB, 0, 1);
|
|
2263
|
-
const pointOnB1 = { x: b1.x + tB * vb.x, y: b1.y + tB * vb.y };
|
|
2264
|
-
let tB2 = ((a2.x - b1.x) * vb.x + (a2.y - b1.y) * vb.y) / lenSqrB;
|
|
2265
|
-
tB2 = clamp(tB2, 0, 1);
|
|
2266
|
-
const pointOnB2 = { x: b1.x + tB2 * vb.x, y: b1.y + tB2 * vb.y };
|
|
2267
|
-
const distances = [
|
|
2268
|
-
{
|
|
2269
|
-
pointA: pointOnA1,
|
|
2270
|
-
pointB: b1,
|
|
2271
|
-
distance: Math.sqrt(
|
|
2272
|
-
(pointOnA1.x - b1.x) ** 2 + (pointOnA1.y - b1.y) ** 2
|
|
2273
|
-
)
|
|
2274
|
-
},
|
|
2275
|
-
{
|
|
2276
|
-
pointA: pointOnA2,
|
|
2277
|
-
pointB: b2,
|
|
2278
|
-
distance: Math.sqrt(
|
|
2279
|
-
(pointOnA2.x - b2.x) ** 2 + (pointOnA2.y - b2.y) ** 2
|
|
2280
|
-
)
|
|
2281
|
-
},
|
|
2282
|
-
{
|
|
2283
|
-
pointA: a1,
|
|
2284
|
-
pointB: pointOnB1,
|
|
2285
|
-
distance: Math.sqrt(
|
|
2286
|
-
(a1.x - pointOnB1.x) ** 2 + (a1.y - pointOnB1.y) ** 2
|
|
2287
|
-
)
|
|
2288
|
-
},
|
|
2289
|
-
{
|
|
2290
|
-
pointA: a2,
|
|
2291
|
-
pointB: pointOnB2,
|
|
2292
|
-
distance: Math.sqrt(
|
|
2293
|
-
(a2.x - pointOnB2.x) ** 2 + (a2.y - pointOnB2.y) ** 2
|
|
2294
|
-
)
|
|
2295
|
-
}
|
|
2296
|
-
];
|
|
2297
|
-
const closestPair = distances.reduce(
|
|
2298
|
-
(closest, current) => current.distance < closest.distance ? current : closest
|
|
2299
|
-
);
|
|
2300
|
-
return {
|
|
2301
|
-
x: (closestPair.pointA.x + closestPair.pointB.x) / 2,
|
|
2302
|
-
y: (closestPair.pointA.y + closestPair.pointB.y) / 2
|
|
2303
|
-
};
|
|
2304
|
-
};
|
|
2305
|
-
var clamp = (value, min, max) => {
|
|
2306
|
-
return Math.max(min, Math.min(max, value));
|
|
2307
|
-
};
|
|
2308
|
-
|
|
2309
2599
|
// lib/check-each-pcb-trace-non-overlapping/getCollidableBounds.ts
|
|
2310
2600
|
import { getBoundsOfPcbElements as getBoundsOfPcbElements3 } from "@tscircuit/circuit-json-util";
|
|
2311
2601
|
var getCollidableBounds = (collidable) => {
|
|
@@ -2341,32 +2631,6 @@ var getCollidableBounds = (collidable) => {
|
|
|
2341
2631
|
return getBoundsOfPcbElements3([collidable]);
|
|
2342
2632
|
};
|
|
2343
2633
|
|
|
2344
|
-
// lib/check-each-pcb-trace-non-overlapping/getPcbPortIdsConnectedToTraces.ts
|
|
2345
|
-
function getPcbPortIdsConnectedToRoutePoint(routePoint) {
|
|
2346
|
-
if (routePoint.route_type !== "wire") return [];
|
|
2347
|
-
return [routePoint.start_pcb_port_id, routePoint.end_pcb_port_id].filter(
|
|
2348
|
-
(portId) => Boolean(portId)
|
|
2349
|
-
);
|
|
2350
|
-
}
|
|
2351
|
-
function getPcbPortIdsConnectedToTrace(trace) {
|
|
2352
|
-
const connectedPcbPorts = /* @__PURE__ */ new Set();
|
|
2353
|
-
for (const segment of trace.route) {
|
|
2354
|
-
for (const portId of getPcbPortIdsConnectedToRoutePoint(segment)) {
|
|
2355
|
-
connectedPcbPorts.add(portId);
|
|
2356
|
-
}
|
|
2357
|
-
}
|
|
2358
|
-
return Array.from(connectedPcbPorts);
|
|
2359
|
-
}
|
|
2360
|
-
function getPcbPortIdsConnectedToTraces(traces) {
|
|
2361
|
-
const connectedPorts = /* @__PURE__ */ new Set();
|
|
2362
|
-
for (const trace of traces) {
|
|
2363
|
-
for (const portId of getPcbPortIdsConnectedToTrace(trace)) {
|
|
2364
|
-
connectedPorts.add(portId);
|
|
2365
|
-
}
|
|
2366
|
-
}
|
|
2367
|
-
return Array.from(connectedPorts);
|
|
2368
|
-
}
|
|
2369
|
-
|
|
2370
2634
|
// lib/check-each-pcb-trace-non-overlapping/getRadiusOfCircuitJsonElement.ts
|
|
2371
2635
|
var getRadiusOfCircuitJsonElement = (obj) => {
|
|
2372
2636
|
if (obj.type === "pcb_via") {
|
|
@@ -2396,12 +2660,12 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
|
|
|
2396
2660
|
connMap,
|
|
2397
2661
|
minClearance
|
|
2398
2662
|
} = {}) {
|
|
2399
|
-
const errors =
|
|
2663
|
+
const errors = checkPcbTraceSelfShorts(circuitJson);
|
|
2400
2664
|
addStartAndEndPortIdsIfMissing(circuitJson);
|
|
2401
2665
|
connMap ??= getFullConnectivityMapFromCircuitJson3(circuitJson);
|
|
2402
2666
|
const board = getPcbBoard(circuitJson);
|
|
2403
2667
|
minClearance ??= getBoardDrcValue(board, "min_trace_to_pad_edge_clearance") ?? DEFAULT_TRACE_MARGIN;
|
|
2404
|
-
const pcbTraces =
|
|
2668
|
+
const pcbTraces = cju3(circuitJson).pcb_trace.list();
|
|
2405
2669
|
const pcbTraceSegments = pcbTraces.flatMap((pcbTrace) => {
|
|
2406
2670
|
const segments = [];
|
|
2407
2671
|
for (let i = 0; i < pcbTrace.route.length - 1; i++) {
|
|
@@ -2424,12 +2688,12 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
|
|
|
2424
2688
|
}
|
|
2425
2689
|
return segments;
|
|
2426
2690
|
});
|
|
2427
|
-
const pcbSmtPads =
|
|
2428
|
-
const pcbPlatedHoles =
|
|
2429
|
-
const pcbPorts =
|
|
2430
|
-
const pcbHoles =
|
|
2431
|
-
const pcbVias =
|
|
2432
|
-
const pcbKeepouts =
|
|
2691
|
+
const pcbSmtPads = cju3(circuitJson).pcb_smtpad.list();
|
|
2692
|
+
const pcbPlatedHoles = cju3(circuitJson).pcb_plated_hole.list();
|
|
2693
|
+
const pcbPorts = cju3(circuitJson).pcb_port.list();
|
|
2694
|
+
const pcbHoles = cju3(circuitJson).pcb_hole.list();
|
|
2695
|
+
const pcbVias = cju3(circuitJson).pcb_via.list();
|
|
2696
|
+
const pcbKeepouts = cju3(circuitJson).pcb_keepout.list().filter((keepout) => !keepout.allow_traces);
|
|
2433
2697
|
const pcbComponentConnectionElements = [
|
|
2434
2698
|
...pcbPorts,
|
|
2435
2699
|
...pcbSmtPads,
|
|
@@ -2490,7 +2754,7 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
|
|
|
2490
2754
|
if (segmentA.layer !== segmentB.layer) continue;
|
|
2491
2755
|
if (connMap.areIdsConnected(segmentA.pcb_trace_id, segmentB.pcb_trace_id))
|
|
2492
2756
|
continue;
|
|
2493
|
-
const gap2 =
|
|
2757
|
+
const gap2 = segmentToSegmentMinDistance4(
|
|
2494
2758
|
{ x: segmentA.x1, y: segmentA.y1 },
|
|
2495
2759
|
{ x: segmentA.x2, y: segmentA.y2 },
|
|
2496
2760
|
{ x: segmentB.x1, y: segmentB.y1 },
|
|
@@ -3149,7 +3413,7 @@ function checkPcbComponentOverCutout(circuitJson) {
|
|
|
3149
3413
|
}
|
|
3150
3414
|
|
|
3151
3415
|
// lib/check-pcb-copper-over-keepout.ts
|
|
3152
|
-
import { cju as
|
|
3416
|
+
import { cju as cju4, getPrimaryId as getPrimaryId3 } from "@tscircuit/circuit-json-util";
|
|
3153
3417
|
var getErrorOwnerId = (copper) => "pcb_component_id" in copper && copper.pcb_component_id ? copper.pcb_component_id : getPrimaryId3(copper);
|
|
3154
3418
|
var getReadableCopperName = (circuitJson, copper) => {
|
|
3155
3419
|
if ("pcb_component_id" in copper && copper.pcb_component_id) {
|
|
@@ -3165,11 +3429,11 @@ var getReadableCopperName = (circuitJson, copper) => {
|
|
|
3165
3429
|
return copper.type === "pcb_via" ? `via ${copper.pcb_via_id}` : `${copper.type} ${getPrimaryId3(copper)}`;
|
|
3166
3430
|
};
|
|
3167
3431
|
function checkPcbCopperOverKeepout(circuitJson) {
|
|
3168
|
-
const keepouts =
|
|
3432
|
+
const keepouts = cju4(circuitJson).pcb_keepout.list();
|
|
3169
3433
|
if (keepouts.length === 0) return [];
|
|
3170
3434
|
const copper = [
|
|
3171
3435
|
...getPads(circuitJson),
|
|
3172
|
-
...
|
|
3436
|
+
...cju4(circuitJson).pcb_via.list()
|
|
3173
3437
|
];
|
|
3174
3438
|
const errors = /* @__PURE__ */ new Map();
|
|
3175
3439
|
for (const keepout of keepouts) {
|
|
@@ -3346,11 +3610,11 @@ function checkDifferentNetViaSpacing(circuitJson, {
|
|
|
3346
3610
|
}
|
|
3347
3611
|
|
|
3348
3612
|
// lib/check-source-traces-match-pcb-trace-thickness.ts
|
|
3349
|
-
import { cju as
|
|
3613
|
+
import { cju as cju5 } from "@tscircuit/circuit-json-util";
|
|
3350
3614
|
import { getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson6 } from "circuit-json-to-connectivity-map";
|
|
3351
3615
|
function checkSourceTracesMatchPcbTraceThickness(circuitJson) {
|
|
3352
3616
|
const warnings = [];
|
|
3353
|
-
const db =
|
|
3617
|
+
const db = cju5(circuitJson);
|
|
3354
3618
|
const sourceTraces = db.source_trace.list();
|
|
3355
3619
|
const pcbTraces = db.pcb_trace.list();
|
|
3356
3620
|
const pcbPorts = db.pcb_port.list();
|
|
@@ -3489,7 +3753,7 @@ import {
|
|
|
3489
3753
|
|
|
3490
3754
|
// lib/check-traces-are-contiguous/via-contact-index.ts
|
|
3491
3755
|
import {
|
|
3492
|
-
all_layers as
|
|
3756
|
+
all_layers as all_layers4
|
|
3493
3757
|
} from "circuit-json";
|
|
3494
3758
|
import { getPrimaryId as getPrimaryId4 } from "@tscircuit/circuit-json-util";
|
|
3495
3759
|
import { pointToSegmentDistance as pointToSegmentDistance2 } from "@tscircuit/math-utils";
|
|
@@ -3501,7 +3765,7 @@ function getViaContactIndex(circuitJson, connectivity) {
|
|
|
3501
3765
|
const vias = circuitJson.filter((element) => element.type === "pcb_via");
|
|
3502
3766
|
const board = circuitJson.find((element) => element.type === "pcb_board");
|
|
3503
3767
|
const layerCount = board?.num_layers;
|
|
3504
|
-
const innerLayers =
|
|
3768
|
+
const innerLayers = all_layers4.filter((layer) => layer.startsWith("inner"));
|
|
3505
3769
|
const stack = [
|
|
3506
3770
|
"top",
|
|
3507
3771
|
...innerLayers.slice(
|
|
@@ -4027,8 +4291,8 @@ function checkTracesAreContiguous(circuitJson, {
|
|
|
4027
4291
|
}
|
|
4028
4292
|
|
|
4029
4293
|
// lib/check-trace-out-of-board/checkTraceOutOfBoard.ts
|
|
4030
|
-
import { cju as
|
|
4031
|
-
import { segmentToSegmentMinDistance as
|
|
4294
|
+
import { cju as cju6 } from "@tscircuit/circuit-json-util";
|
|
4295
|
+
import { segmentToSegmentMinDistance as segmentToSegmentMinDistance5 } from "@tscircuit/math-utils";
|
|
4032
4296
|
function getBoardPolygonPoints(board) {
|
|
4033
4297
|
if (board.outline && board.outline.length > 0) {
|
|
4034
4298
|
return board.outline.map((p) => ({ x: p.x, y: p.y }));
|
|
@@ -4058,7 +4322,7 @@ function checkPcbTracesOutOfBoard(circuitJson, config = {}) {
|
|
|
4058
4322
|
const margin = config.margin ?? getBoardDrcValue(board, "min_board_edge_clearance") ?? jlcMinTolerances.min_board_edge_clearance;
|
|
4059
4323
|
const boardPoints = getBoardPolygonPoints(board);
|
|
4060
4324
|
if (!boardPoints) return errors;
|
|
4061
|
-
const pcbTraces =
|
|
4325
|
+
const pcbTraces = cju6(circuitJson).pcb_trace.list();
|
|
4062
4326
|
for (const trace of pcbTraces) {
|
|
4063
4327
|
if (trace.route.length < 2) continue;
|
|
4064
4328
|
for (let i = 0; i < trace.route.length - 1; i++) {
|
|
@@ -4072,7 +4336,7 @@ function checkPcbTracesOutOfBoard(circuitJson, config = {}) {
|
|
|
4072
4336
|
for (let j = 0; j < boardPoints.length; j++) {
|
|
4073
4337
|
const edgeStart = boardPoints[j];
|
|
4074
4338
|
const edgeEnd = boardPoints[(j + 1) % boardPoints.length];
|
|
4075
|
-
const distance3 =
|
|
4339
|
+
const distance3 = segmentToSegmentMinDistance5(
|
|
4076
4340
|
segmentStart,
|
|
4077
4341
|
segmentEnd,
|
|
4078
4342
|
edgeStart,
|
|
@@ -4107,7 +4371,7 @@ function checkPcbTracesOutOfBoard(circuitJson, config = {}) {
|
|
|
4107
4371
|
|
|
4108
4372
|
// lib/check-pcb-components-overlap/checkPcbComponentOverlap.ts
|
|
4109
4373
|
import {
|
|
4110
|
-
cju as
|
|
4374
|
+
cju as cju7,
|
|
4111
4375
|
getBoundsOfPcbElements as getBoundsOfPcbElements5,
|
|
4112
4376
|
getPrimaryId as getPrimaryId5
|
|
4113
4377
|
} from "@tscircuit/circuit-json-util";
|
|
@@ -4205,9 +4469,9 @@ function checkPcbComponentOverlap(circuitJson) {
|
|
|
4205
4469
|
)
|
|
4206
4470
|
);
|
|
4207
4471
|
const connMap = getFullConnectivityMapFromCircuitJson9(circuitJson);
|
|
4208
|
-
const smtPads =
|
|
4209
|
-
const platedHoles =
|
|
4210
|
-
const holes =
|
|
4472
|
+
const smtPads = cju7(circuitJson).pcb_smtpad.list();
|
|
4473
|
+
const platedHoles = cju7(circuitJson).pcb_plated_hole.list();
|
|
4474
|
+
const holes = cju7(circuitJson).pcb_hole.list();
|
|
4211
4475
|
const courtyards = circuitJson.filter(isCourtyardElement2);
|
|
4212
4476
|
const componentMap = /* @__PURE__ */ new Map();
|
|
4213
4477
|
for (const pad of smtPads) {
|
|
@@ -4949,7 +5213,7 @@ function checkTwoTerminalSwitchContactsOnDifferentNets(circuitJson) {
|
|
|
4949
5213
|
}
|
|
4950
5214
|
|
|
4951
5215
|
// lib/check-all-pins-in-component-are-underspecified.ts
|
|
4952
|
-
import { cju as
|
|
5216
|
+
import { cju as cju8 } from "@tscircuit/circuit-json-util";
|
|
4953
5217
|
var PIN_ATTRIBUTE_KEYS = [
|
|
4954
5218
|
"must_be_connected",
|
|
4955
5219
|
"provides_power",
|
|
@@ -4994,7 +5258,7 @@ function hasAnyPinAttribute(port) {
|
|
|
4994
5258
|
}
|
|
4995
5259
|
function checkAllPinsInComponentAreUnderspecified(circuitJson) {
|
|
4996
5260
|
const warnings = [];
|
|
4997
|
-
const db =
|
|
5261
|
+
const db = cju8(circuitJson);
|
|
4998
5262
|
const sourceComponents = db.source_component.list();
|
|
4999
5263
|
const sourcePorts = db.source_port.list();
|
|
5000
5264
|
const portsByComponent = /* @__PURE__ */ new Map();
|
|
@@ -5026,7 +5290,7 @@ function checkAllPinsInComponentAreUnderspecified(circuitJson) {
|
|
|
5026
5290
|
}
|
|
5027
5291
|
|
|
5028
5292
|
// lib/check-no-power-pin-defined.ts
|
|
5029
|
-
import { cju as
|
|
5293
|
+
import { cju as cju9 } from "@tscircuit/circuit-json-util";
|
|
5030
5294
|
|
|
5031
5295
|
// lib/util/should-check-chip-power-ground-pins.ts
|
|
5032
5296
|
var shouldCheckChipPowerGroundPins = (component, ports) => component.ftype === "simple_chip" && ports.filter((port) => port.do_not_connect !== true).length >= 2;
|
|
@@ -5034,7 +5298,7 @@ var shouldCheckChipPowerGroundPins = (component, ports) => component.ftype === "
|
|
|
5034
5298
|
// lib/check-no-power-pin-defined.ts
|
|
5035
5299
|
function checkNoPowerPinDefined(circuitJson) {
|
|
5036
5300
|
const warnings = [];
|
|
5037
|
-
const db =
|
|
5301
|
+
const db = cju9(circuitJson);
|
|
5038
5302
|
const sourceComponents = db.source_component.list();
|
|
5039
5303
|
const sourcePorts = db.source_port.list();
|
|
5040
5304
|
const portsByComponent = /* @__PURE__ */ new Map();
|
|
@@ -5065,10 +5329,10 @@ function checkNoPowerPinDefined(circuitJson) {
|
|
|
5065
5329
|
}
|
|
5066
5330
|
|
|
5067
5331
|
// lib/check-no-ground-pin-defined.ts
|
|
5068
|
-
import { cju as
|
|
5332
|
+
import { cju as cju10 } from "@tscircuit/circuit-json-util";
|
|
5069
5333
|
function checkNoGroundPinDefined(circuitJson) {
|
|
5070
5334
|
const warnings = [];
|
|
5071
|
-
const db =
|
|
5335
|
+
const db = cju10(circuitJson);
|
|
5072
5336
|
const sourceComponents = db.source_component.list();
|
|
5073
5337
|
const sourcePorts = db.source_port.list();
|
|
5074
5338
|
const portsByComponent = /* @__PURE__ */ new Map();
|
|
@@ -5929,6 +6193,7 @@ export {
|
|
|
5929
6193
|
checkPcbComponentsOutOfBoard,
|
|
5930
6194
|
checkPcbCopperOverKeepout,
|
|
5931
6195
|
checkPcbTraceLengths,
|
|
6196
|
+
checkPcbTraceSelfShorts,
|
|
5932
6197
|
checkPcbTraceViaCounts,
|
|
5933
6198
|
checkPcbTracesOutOfBoard,
|
|
5934
6199
|
checkPinMustBeConnected,
|