@tscircuit/checks 0.0.208 → 0.0.210

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.js CHANGED
@@ -1,3 +1,325 @@
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 directionLengthProduct = Math.hypot(a.x2 - a.x1, a.y2 - a.y1) * Math.hypot(b.x2 - b.x1, b.y2 - b.y1);
233
+ const isForwardOrRightAngle = dot >= -1e-9 * directionLengthProduct;
234
+ const adjacent = a.run === b.run && b.startDistance === a.endDistance;
235
+ if (adjacent && !(dot < 0 && Math.abs(cross) < 1e-9)) continue;
236
+ if (!adjacent && a.run === b.run && // A right-angle connector can have sqrt(2) times the straight-line
237
+ // distance. Its local copper overlap is still part of the same bend.
238
+ b.startDistance - a.endDistance <= Math.SQRT2 * contactDistance && isForwardOrRightAngle)
239
+ continue;
240
+ const gap = segmentToSegmentMinDistance(
241
+ { x: a.x1, y: a.y1 },
242
+ { x: a.x2, y: a.y2 },
243
+ { x: b.x1, y: b.y1 },
244
+ { x: b.x2, y: b.y2 }
245
+ ) - contactDistance;
246
+ if (gap > 1e-9) continue;
247
+ shortCenter = getClosestPointBetweenSegments(a, b);
248
+ break pairs;
249
+ }
250
+ }
251
+ if (!shortCenter) {
252
+ const board = circuitJson.find((e) => e.type === "pcb_board");
253
+ const stack = [
254
+ "top",
255
+ ...all_layers.filter((layer) => layer.startsWith("inner")).slice(0, board ? Math.max(0, board.num_layers - 2) : void 0),
256
+ ...board?.num_layers === 1 ? [] : ["bottom"]
257
+ ];
258
+ vias: for (let i = 0; i < trace.route.length; i++) {
259
+ const point2 = trace.route[i];
260
+ if (point2.route_type !== "via") continue;
261
+ const materialized = db.pcb_via.list().find(
262
+ (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
263
+ );
264
+ const diameter = materialized?.outer_diameter ?? point2.outer_diameter;
265
+ if (diameter === void 0 || !Number.isFinite(diameter) || diameter <= 0)
266
+ continue;
267
+ const from = stack.indexOf(point2.from_layer);
268
+ const to = stack.indexOf(point2.to_layer);
269
+ const layers = materialized?.layers ?? (from >= 0 && to >= 0 ? stack.slice(Math.min(from, to), Math.max(from, to) + 1) : []);
270
+ for (const segment of segments) {
271
+ if (!layers.includes(segment.layer)) continue;
272
+ const reach = (diameter + segment.thickness) / 2;
273
+ const alongRouteGap = Math.max(
274
+ segment.startDistance - routeDistances[i],
275
+ routeDistances[i] - segment.endDistance,
276
+ 0
277
+ );
278
+ if (alongRouteGap <= reach + 1e-9) continue;
279
+ const viaSegment = {
280
+ ...segment,
281
+ x1: point2.x,
282
+ y1: point2.y,
283
+ x2: point2.x,
284
+ y2: point2.y
285
+ };
286
+ const gap = segmentToSegmentMinDistance(
287
+ point2,
288
+ point2,
289
+ { x: segment.x1, y: segment.y1 },
290
+ { x: segment.x2, y: segment.y2 }
291
+ ) - reach;
292
+ if (gap > 1e-9) continue;
293
+ shortCenter = getClosestPointBetweenSegments(segment, viaSegment);
294
+ break vias;
295
+ }
296
+ }
297
+ }
298
+ if (shortCenter) {
299
+ const sourceTrace = db.source_trace.get(trace.source_trace_id);
300
+ const endpointNames = (sourceTrace?.connected_source_port_ids ?? []).map((id) => {
301
+ const port = db.source_port.get(id);
302
+ const component = port?.source_component_id ? db.source_component.get(port.source_component_id) : void 0;
303
+ return component?.name && port?.name ? `${component.name}.${port.name}` : void 0;
304
+ }).filter((name) => Boolean(name));
305
+ const traceName = sourceTrace?.name || sourceTrace?.display_name || endpointNames.join(" \u2192 ") || "unnamed";
306
+ errors.push({
307
+ type: "pcb_trace_error",
308
+ error_type: "pcb_trace_error",
309
+ pcb_trace_error_id: `self_short_${trace.pcb_trace_id}`,
310
+ pcb_trace_id: trace.pcb_trace_id,
311
+ source_trace_id: trace.source_trace_id,
312
+ message: `PCB trace "${traceName}" shorts to itself, bypassing part of its length-matched route`,
313
+ center: shortCenter,
314
+ pcb_component_ids: [],
315
+ pcb_port_ids: getPcbPortIdsConnectedToTraces([trace]),
316
+ subcircuit_id: trace.subcircuit_id
317
+ });
318
+ }
319
+ }
320
+ return errors;
321
+ }
322
+
1
323
  // lib/check-copper-pour-shorts.ts
2
324
  import Flatbush from "flatbush";
3
325
  import "@flatten-js/core";
@@ -717,7 +1039,7 @@ import {
717
1039
  import Flatbush3 from "flatbush";
718
1040
 
719
1041
  // lib/util/get-via-and-pour-connections.ts
720
- import { all_layers } from "circuit-json";
1042
+ import { all_layers as all_layers2 } from "circuit-json";
721
1043
  import {
722
1044
  getPourPolygon,
723
1045
  getTraceSegmentPolygon,
@@ -798,7 +1120,7 @@ function getViaAndPourConnections(circuit) {
798
1120
  const board = circuit.find((e) => e.type === "pcb_board");
799
1121
  const stack = [
800
1122
  "top",
801
- ...all_layers.filter((l) => l.startsWith("inner")).slice(0, board ? Math.max(0, board.num_layers - 2) : void 0),
1123
+ ...all_layers2.filter((l) => l.startsWith("inner")).slice(0, board ? Math.max(0, board.num_layers - 2) : void 0),
802
1124
  ...board?.num_layers === 1 ? [] : ["bottom"]
803
1125
  ];
804
1126
  for (const copper of circuit) {
@@ -974,7 +1296,7 @@ import {
974
1296
  getSegmentIntersection,
975
1297
  isPointInsidePolygon,
976
1298
  pointToSegmentClosestPoint,
977
- segmentToSegmentMinDistance
1299
+ segmentToSegmentMinDistance as segmentToSegmentMinDistance2
978
1300
  } from "@tscircuit/math-utils";
979
1301
  var rotatePoint = (point2, angleDegrees) => {
980
1302
  const angle = angleDegrees * Math.PI / 180;
@@ -1074,7 +1396,7 @@ var getClosestPointsBetweenSegments = (a1, a2, b1, b2) => {
1074
1396
  }
1075
1397
  }
1076
1398
  return {
1077
- distance: segmentToSegmentMinDistance(a1, a2, b1, b2),
1399
+ distance: segmentToSegmentMinDistance2(a1, a2, b1, b2),
1078
1400
  pointOnA: best.pointOnA,
1079
1401
  pointOnB: best.pointOnB,
1080
1402
  center: {
@@ -1695,20 +2017,20 @@ function checkEachPcbPortConnectedToPcbTraces(circuitJson, {
1695
2017
  }
1696
2018
 
1697
2019
  // lib/check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping.ts
1698
- import { cju as cju2, getReadableNameForElement as getReadableNameForElement3 } from "@tscircuit/circuit-json-util";
2020
+ import { cju as cju3, getReadableNameForElement as getReadableNameForElement3 } from "@tscircuit/circuit-json-util";
1699
2021
  import { getPrimaryId as getPrimaryId2 } from "@tscircuit/circuit-json-util";
1700
2022
  import {
1701
2023
  segmentToBoundsMinDistance,
1702
2024
  segmentToCircleMinDistance as segmentToCircleMinDistance2
1703
2025
  } from "@tscircuit/math-utils";
1704
- import { segmentToSegmentMinDistance as segmentToSegmentMinDistance3 } from "@tscircuit/math-utils";
2026
+ import { segmentToSegmentMinDistance as segmentToSegmentMinDistance4 } from "@tscircuit/math-utils";
1705
2027
  import {
1706
2028
  getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson3
1707
2029
  } from "circuit-json-to-connectivity-map";
1708
2030
 
1709
2031
  // lib/check-pad-clearance/common.ts
1710
2032
  import {
1711
- cju,
2033
+ cju as cju2,
1712
2034
  distanceBetweenCircleAndCircle,
1713
2035
  distanceBetweenCircleAndPolygon,
1714
2036
  distanceBetweenPolygonAndPolygon,
@@ -1718,7 +2040,7 @@ import {
1718
2040
  midpoint,
1719
2041
  pointToSegmentClosestPoint as pointToSegmentClosestPoint2,
1720
2042
  segmentToCircleMinDistance,
1721
- segmentToSegmentMinDistance as segmentToSegmentMinDistance2
2043
+ segmentToSegmentMinDistance as segmentToSegmentMinDistance3
1722
2044
  } from "@tscircuit/math-utils";
1723
2045
 
1724
2046
  // node_modules/@tscircuit/jlcpcb-manufacturing-specs/lib/jlcpcb-manufacturing-specs.ts
@@ -1885,7 +2207,7 @@ var getPadToPadGap = (padA, padB) => {
1885
2207
  if (isPillPad(padA) && isPillPad(padB)) {
1886
2208
  const pillA = getPillCenterLineForPad(padA);
1887
2209
  const pillB = getPillCenterLineForPad(padB);
1888
- return segmentToSegmentMinDistance2(
2210
+ return segmentToSegmentMinDistance3(
1889
2211
  pillA.start,
1890
2212
  pillA.end,
1891
2213
  pillB.start,
@@ -1940,11 +2262,11 @@ var getPadToPadGap = (padA, padB) => {
1940
2262
  );
1941
2263
  };
1942
2264
  var getPads = (circuitJson) => [
1943
- ...cju(circuitJson).pcb_smtpad.list(),
1944
- ...cju(circuitJson).pcb_plated_hole.list()
2265
+ ...cju2(circuitJson).pcb_smtpad.list(),
2266
+ ...cju2(circuitJson).pcb_plated_hole.list()
1945
2267
  ];
1946
2268
  var getTraceSegments = (circuitJson) => {
1947
- const pcbTraces = cju(circuitJson).pcb_trace.list();
2269
+ const pcbTraces = cju2(circuitJson).pcb_trace.list();
1948
2270
  return pcbTraces.flatMap((pcbTrace) => {
1949
2271
  const segments = [];
1950
2272
  for (let i = 0; i < pcbTrace.route.length - 1; i++) {
@@ -2162,7 +2484,7 @@ var SpatialObjectIndex = class {
2162
2484
  };
2163
2485
 
2164
2486
  // lib/util/getLayersOfPcbElement.ts
2165
- import { all_layers as all_layers2 } from "circuit-json";
2487
+ import { all_layers as all_layers3 } from "circuit-json";
2166
2488
  function getLayersOfPcbElement(obj) {
2167
2489
  if (obj.type === "pcb_trace_segment") {
2168
2490
  return [obj.layer];
@@ -2171,13 +2493,13 @@ function getLayersOfPcbElement(obj) {
2171
2493
  return [obj.layer];
2172
2494
  }
2173
2495
  if (obj.type === "pcb_plated_hole") {
2174
- return Array.isArray(obj.layers) ? obj.layers : [...all_layers2];
2496
+ return Array.isArray(obj.layers) ? obj.layers : [...all_layers3];
2175
2497
  }
2176
2498
  if (obj.type === "pcb_hole") {
2177
- return [...all_layers2];
2499
+ return [...all_layers3];
2178
2500
  }
2179
2501
  if (obj.type === "pcb_via") {
2180
- return Array.isArray(obj.layers) ? obj.layers : [...all_layers2];
2502
+ return Array.isArray(obj.layers) ? obj.layers : [...all_layers3];
2181
2503
  }
2182
2504
  if (obj.type === "pcb_keepout") {
2183
2505
  return Array.isArray(obj.layers) ? obj.layers : [];
@@ -2278,151 +2600,6 @@ var getClosestPointBetweenSegmentAndBounds = (segment, bounds) => {
2278
2600
  return closestPoint;
2279
2601
  };
2280
2602
 
2281
- // lib/check-each-pcb-trace-non-overlapping/getClosestPointBetweenSegments.ts
2282
- var getClosestPointBetweenSegments = (segmentA, segmentB) => {
2283
- const a1 = { x: segmentA.x1, y: segmentA.y1 };
2284
- const a2 = { x: segmentA.x2, y: segmentA.y2 };
2285
- const b1 = { x: segmentB.x1, y: segmentB.y1 };
2286
- const b2 = { x: segmentB.x2, y: segmentB.y2 };
2287
- const va = { x: a2.x - a1.x, y: a2.y - a1.y };
2288
- const vb = { x: b2.x - b1.x, y: b2.y - b1.y };
2289
- const lenSqrA = va.x * va.x + va.y * va.y;
2290
- const lenSqrB = vb.x * vb.x + vb.y * vb.y;
2291
- if (lenSqrA === 0 || lenSqrB === 0) {
2292
- if (lenSqrA === 0 && lenSqrB === 0) {
2293
- return {
2294
- x: (a1.x + b1.x) / 2,
2295
- y: (a1.y + b1.y) / 2
2296
- };
2297
- }
2298
- if (lenSqrA === 0) {
2299
- const t2 = clamp(
2300
- ((a1.x - b1.x) * vb.x + (a1.y - b1.y) * vb.y) / lenSqrB,
2301
- 0,
2302
- 1
2303
- );
2304
- const closestOnB2 = {
2305
- x: b1.x + t2 * vb.x,
2306
- y: b1.y + t2 * vb.y
2307
- };
2308
- return {
2309
- x: (a1.x + closestOnB2.x) / 2,
2310
- y: (a1.y + closestOnB2.y) / 2
2311
- };
2312
- }
2313
- const t = clamp(
2314
- ((b1.x - a1.x) * va.x + (b1.y - a1.y) * va.y) / lenSqrA,
2315
- 0,
2316
- 1
2317
- );
2318
- const closestOnA2 = {
2319
- x: a1.x + t * va.x,
2320
- y: a1.y + t * va.y
2321
- };
2322
- return {
2323
- x: (closestOnA2.x + b1.x) / 2,
2324
- y: (closestOnA2.y + b1.y) / 2
2325
- };
2326
- }
2327
- const w = { x: a1.x - b1.x, y: a1.y - b1.y };
2328
- const dotAA = va.x * va.x + va.y * va.y;
2329
- const dotAB = va.x * vb.x + va.y * vb.y;
2330
- const dotAW = va.x * w.x + va.y * w.y;
2331
- const dotBB = vb.x * vb.x + vb.y * vb.y;
2332
- const dotBW = vb.x * w.x + vb.y * w.y;
2333
- const denominator = dotAA * dotBB - dotAB * dotAB;
2334
- if (denominator < 1e-10) {
2335
- return closestPointsParallelSegments(
2336
- a1,
2337
- a2,
2338
- b1,
2339
- b2,
2340
- va,
2341
- vb,
2342
- lenSqrA,
2343
- lenSqrB
2344
- );
2345
- }
2346
- let tA = (dotAB * dotBW - dotBB * dotAW) / denominator;
2347
- let tB = (dotAA * dotBW - dotAB * dotAW) / denominator;
2348
- tA = clamp(tA, 0, 1);
2349
- tB = clamp(tB, 0, 1);
2350
- tB = (tA * dotAB + dotBW) / dotBB;
2351
- tB = clamp(tB, 0, 1);
2352
- tA = (tB * dotAB - dotAW) / dotAA;
2353
- tA = clamp(tA, 0, 1);
2354
- const closestOnA = {
2355
- x: a1.x + tA * va.x,
2356
- y: a1.y + tA * va.y
2357
- };
2358
- const closestOnB = {
2359
- x: b1.x + tB * vb.x,
2360
- y: b1.y + tB * vb.y
2361
- };
2362
- const dx = closestOnA.x - closestOnB.x;
2363
- const dy = closestOnA.y - closestOnB.y;
2364
- const distance3 = Math.sqrt(dx * dx + dy * dy);
2365
- const averagePoint = {
2366
- x: (closestOnA.x + closestOnB.x) / 2,
2367
- y: (closestOnA.y + closestOnB.y) / 2
2368
- };
2369
- return averagePoint;
2370
- };
2371
- var closestPointsParallelSegments = (a1, a2, b1, b2, va, vb, lenSqrA, lenSqrB) => {
2372
- let tA = ((b1.x - a1.x) * va.x + (b1.y - a1.y) * va.y) / lenSqrA;
2373
- tA = clamp(tA, 0, 1);
2374
- const pointOnA1 = { x: a1.x + tA * va.x, y: a1.y + tA * va.y };
2375
- let tA2 = ((b2.x - a1.x) * va.x + (b2.y - a1.y) * va.y) / lenSqrA;
2376
- tA2 = clamp(tA2, 0, 1);
2377
- const pointOnA2 = { x: a1.x + tA2 * va.x, y: a1.y + tA2 * va.y };
2378
- let tB = ((a1.x - b1.x) * vb.x + (a1.y - b1.y) * vb.y) / lenSqrB;
2379
- tB = clamp(tB, 0, 1);
2380
- const pointOnB1 = { x: b1.x + tB * vb.x, y: b1.y + tB * vb.y };
2381
- let tB2 = ((a2.x - b1.x) * vb.x + (a2.y - b1.y) * vb.y) / lenSqrB;
2382
- tB2 = clamp(tB2, 0, 1);
2383
- const pointOnB2 = { x: b1.x + tB2 * vb.x, y: b1.y + tB2 * vb.y };
2384
- const distances = [
2385
- {
2386
- pointA: pointOnA1,
2387
- pointB: b1,
2388
- distance: Math.sqrt(
2389
- (pointOnA1.x - b1.x) ** 2 + (pointOnA1.y - b1.y) ** 2
2390
- )
2391
- },
2392
- {
2393
- pointA: pointOnA2,
2394
- pointB: b2,
2395
- distance: Math.sqrt(
2396
- (pointOnA2.x - b2.x) ** 2 + (pointOnA2.y - b2.y) ** 2
2397
- )
2398
- },
2399
- {
2400
- pointA: a1,
2401
- pointB: pointOnB1,
2402
- distance: Math.sqrt(
2403
- (a1.x - pointOnB1.x) ** 2 + (a1.y - pointOnB1.y) ** 2
2404
- )
2405
- },
2406
- {
2407
- pointA: a2,
2408
- pointB: pointOnB2,
2409
- distance: Math.sqrt(
2410
- (a2.x - pointOnB2.x) ** 2 + (a2.y - pointOnB2.y) ** 2
2411
- )
2412
- }
2413
- ];
2414
- const closestPair = distances.reduce(
2415
- (closest, current) => current.distance < closest.distance ? current : closest
2416
- );
2417
- return {
2418
- x: (closestPair.pointA.x + closestPair.pointB.x) / 2,
2419
- y: (closestPair.pointA.y + closestPair.pointB.y) / 2
2420
- };
2421
- };
2422
- var clamp = (value, min, max) => {
2423
- return Math.max(min, Math.min(max, value));
2424
- };
2425
-
2426
2603
  // lib/check-each-pcb-trace-non-overlapping/getCollidableBounds.ts
2427
2604
  import { getBoundsOfPcbElements as getBoundsOfPcbElements3 } from "@tscircuit/circuit-json-util";
2428
2605
  var getCollidableBounds = (collidable) => {
@@ -2458,32 +2635,6 @@ var getCollidableBounds = (collidable) => {
2458
2635
  return getBoundsOfPcbElements3([collidable]);
2459
2636
  };
2460
2637
 
2461
- // lib/check-each-pcb-trace-non-overlapping/getPcbPortIdsConnectedToTraces.ts
2462
- function getPcbPortIdsConnectedToRoutePoint(routePoint) {
2463
- if (routePoint.route_type !== "wire") return [];
2464
- return [routePoint.start_pcb_port_id, routePoint.end_pcb_port_id].filter(
2465
- (portId) => Boolean(portId)
2466
- );
2467
- }
2468
- function getPcbPortIdsConnectedToTrace(trace) {
2469
- const connectedPcbPorts = /* @__PURE__ */ new Set();
2470
- for (const segment of trace.route) {
2471
- for (const portId of getPcbPortIdsConnectedToRoutePoint(segment)) {
2472
- connectedPcbPorts.add(portId);
2473
- }
2474
- }
2475
- return Array.from(connectedPcbPorts);
2476
- }
2477
- function getPcbPortIdsConnectedToTraces(traces) {
2478
- const connectedPorts = /* @__PURE__ */ new Set();
2479
- for (const trace of traces) {
2480
- for (const portId of getPcbPortIdsConnectedToTrace(trace)) {
2481
- connectedPorts.add(portId);
2482
- }
2483
- }
2484
- return Array.from(connectedPorts);
2485
- }
2486
-
2487
2638
  // lib/check-each-pcb-trace-non-overlapping/getRadiusOfCircuitJsonElement.ts
2488
2639
  var getRadiusOfCircuitJsonElement = (obj) => {
2489
2640
  if (obj.type === "pcb_via") {
@@ -2513,12 +2664,12 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
2513
2664
  connMap,
2514
2665
  minClearance
2515
2666
  } = {}) {
2516
- const errors = [];
2667
+ const errors = checkPcbTraceSelfShorts(circuitJson);
2517
2668
  addStartAndEndPortIdsIfMissing(circuitJson);
2518
2669
  connMap ??= getFullConnectivityMapFromCircuitJson3(circuitJson);
2519
2670
  const board = getPcbBoard(circuitJson);
2520
2671
  minClearance ??= getBoardDrcValue(board, "min_trace_to_pad_edge_clearance") ?? DEFAULT_TRACE_MARGIN;
2521
- const pcbTraces = cju2(circuitJson).pcb_trace.list();
2672
+ const pcbTraces = cju3(circuitJson).pcb_trace.list();
2522
2673
  const pcbTraceSegments = pcbTraces.flatMap((pcbTrace) => {
2523
2674
  const segments = [];
2524
2675
  for (let i = 0; i < pcbTrace.route.length - 1; i++) {
@@ -2541,12 +2692,12 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
2541
2692
  }
2542
2693
  return segments;
2543
2694
  });
2544
- const pcbSmtPads = cju2(circuitJson).pcb_smtpad.list();
2545
- const pcbPlatedHoles = cju2(circuitJson).pcb_plated_hole.list();
2546
- const pcbPorts = cju2(circuitJson).pcb_port.list();
2547
- const pcbHoles = cju2(circuitJson).pcb_hole.list();
2548
- const pcbVias = cju2(circuitJson).pcb_via.list();
2549
- const pcbKeepouts = cju2(circuitJson).pcb_keepout.list().filter((keepout) => !keepout.allow_traces);
2695
+ const pcbSmtPads = cju3(circuitJson).pcb_smtpad.list();
2696
+ const pcbPlatedHoles = cju3(circuitJson).pcb_plated_hole.list();
2697
+ const pcbPorts = cju3(circuitJson).pcb_port.list();
2698
+ const pcbHoles = cju3(circuitJson).pcb_hole.list();
2699
+ const pcbVias = cju3(circuitJson).pcb_via.list();
2700
+ const pcbKeepouts = cju3(circuitJson).pcb_keepout.list().filter((keepout) => !keepout.allow_traces);
2550
2701
  const pcbComponentConnectionElements = [
2551
2702
  ...pcbPorts,
2552
2703
  ...pcbSmtPads,
@@ -2607,7 +2758,7 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
2607
2758
  if (segmentA.layer !== segmentB.layer) continue;
2608
2759
  if (connMap.areIdsConnected(segmentA.pcb_trace_id, segmentB.pcb_trace_id))
2609
2760
  continue;
2610
- const gap2 = segmentToSegmentMinDistance3(
2761
+ const gap2 = segmentToSegmentMinDistance4(
2611
2762
  { x: segmentA.x1, y: segmentA.y1 },
2612
2763
  { x: segmentA.x2, y: segmentA.y2 },
2613
2764
  { x: segmentB.x1, y: segmentB.y1 },
@@ -3266,7 +3417,7 @@ function checkPcbComponentOverCutout(circuitJson) {
3266
3417
  }
3267
3418
 
3268
3419
  // lib/check-pcb-copper-over-keepout.ts
3269
- import { cju as cju3, getPrimaryId as getPrimaryId3 } from "@tscircuit/circuit-json-util";
3420
+ import { cju as cju4, getPrimaryId as getPrimaryId3 } from "@tscircuit/circuit-json-util";
3270
3421
  var getErrorOwnerId = (copper) => "pcb_component_id" in copper && copper.pcb_component_id ? copper.pcb_component_id : getPrimaryId3(copper);
3271
3422
  var getReadableCopperName = (circuitJson, copper) => {
3272
3423
  if ("pcb_component_id" in copper && copper.pcb_component_id) {
@@ -3282,11 +3433,11 @@ var getReadableCopperName = (circuitJson, copper) => {
3282
3433
  return copper.type === "pcb_via" ? `via ${copper.pcb_via_id}` : `${copper.type} ${getPrimaryId3(copper)}`;
3283
3434
  };
3284
3435
  function checkPcbCopperOverKeepout(circuitJson) {
3285
- const keepouts = cju3(circuitJson).pcb_keepout.list();
3436
+ const keepouts = cju4(circuitJson).pcb_keepout.list();
3286
3437
  if (keepouts.length === 0) return [];
3287
3438
  const copper = [
3288
3439
  ...getPads(circuitJson),
3289
- ...cju3(circuitJson).pcb_via.list()
3440
+ ...cju4(circuitJson).pcb_via.list()
3290
3441
  ];
3291
3442
  const errors = /* @__PURE__ */ new Map();
3292
3443
  for (const keepout of keepouts) {
@@ -3463,11 +3614,11 @@ function checkDifferentNetViaSpacing(circuitJson, {
3463
3614
  }
3464
3615
 
3465
3616
  // lib/check-source-traces-match-pcb-trace-thickness.ts
3466
- import { cju as cju4 } from "@tscircuit/circuit-json-util";
3617
+ import { cju as cju5 } from "@tscircuit/circuit-json-util";
3467
3618
  import { getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson6 } from "circuit-json-to-connectivity-map";
3468
3619
  function checkSourceTracesMatchPcbTraceThickness(circuitJson) {
3469
3620
  const warnings = [];
3470
- const db = cju4(circuitJson);
3621
+ const db = cju5(circuitJson);
3471
3622
  const sourceTraces = db.source_trace.list();
3472
3623
  const pcbTraces = db.pcb_trace.list();
3473
3624
  const pcbPorts = db.pcb_port.list();
@@ -3606,7 +3757,7 @@ import {
3606
3757
 
3607
3758
  // lib/check-traces-are-contiguous/via-contact-index.ts
3608
3759
  import {
3609
- all_layers as all_layers3
3760
+ all_layers as all_layers4
3610
3761
  } from "circuit-json";
3611
3762
  import { getPrimaryId as getPrimaryId4 } from "@tscircuit/circuit-json-util";
3612
3763
  import { pointToSegmentDistance as pointToSegmentDistance2 } from "@tscircuit/math-utils";
@@ -3618,7 +3769,7 @@ function getViaContactIndex(circuitJson, connectivity) {
3618
3769
  const vias = circuitJson.filter((element) => element.type === "pcb_via");
3619
3770
  const board = circuitJson.find((element) => element.type === "pcb_board");
3620
3771
  const layerCount = board?.num_layers;
3621
- const innerLayers = all_layers3.filter((layer) => layer.startsWith("inner"));
3772
+ const innerLayers = all_layers4.filter((layer) => layer.startsWith("inner"));
3622
3773
  const stack = [
3623
3774
  "top",
3624
3775
  ...innerLayers.slice(
@@ -4144,8 +4295,8 @@ function checkTracesAreContiguous(circuitJson, {
4144
4295
  }
4145
4296
 
4146
4297
  // lib/check-trace-out-of-board/checkTraceOutOfBoard.ts
4147
- import { cju as cju5 } from "@tscircuit/circuit-json-util";
4148
- import { segmentToSegmentMinDistance as segmentToSegmentMinDistance4 } from "@tscircuit/math-utils";
4298
+ import { cju as cju6 } from "@tscircuit/circuit-json-util";
4299
+ import { segmentToSegmentMinDistance as segmentToSegmentMinDistance5 } from "@tscircuit/math-utils";
4149
4300
  function getBoardPolygonPoints(board) {
4150
4301
  if (board.outline && board.outline.length > 0) {
4151
4302
  return board.outline.map((p) => ({ x: p.x, y: p.y }));
@@ -4175,7 +4326,7 @@ function checkPcbTracesOutOfBoard(circuitJson, config = {}) {
4175
4326
  const margin = config.margin ?? getBoardDrcValue(board, "min_board_edge_clearance") ?? jlcMinTolerances.min_board_edge_clearance;
4176
4327
  const boardPoints = getBoardPolygonPoints(board);
4177
4328
  if (!boardPoints) return errors;
4178
- const pcbTraces = cju5(circuitJson).pcb_trace.list();
4329
+ const pcbTraces = cju6(circuitJson).pcb_trace.list();
4179
4330
  for (const trace of pcbTraces) {
4180
4331
  if (trace.route.length < 2) continue;
4181
4332
  for (let i = 0; i < trace.route.length - 1; i++) {
@@ -4189,7 +4340,7 @@ function checkPcbTracesOutOfBoard(circuitJson, config = {}) {
4189
4340
  for (let j = 0; j < boardPoints.length; j++) {
4190
4341
  const edgeStart = boardPoints[j];
4191
4342
  const edgeEnd = boardPoints[(j + 1) % boardPoints.length];
4192
- const distance3 = segmentToSegmentMinDistance4(
4343
+ const distance3 = segmentToSegmentMinDistance5(
4193
4344
  segmentStart,
4194
4345
  segmentEnd,
4195
4346
  edgeStart,
@@ -4224,7 +4375,7 @@ function checkPcbTracesOutOfBoard(circuitJson, config = {}) {
4224
4375
 
4225
4376
  // lib/check-pcb-components-overlap/checkPcbComponentOverlap.ts
4226
4377
  import {
4227
- cju as cju6,
4378
+ cju as cju7,
4228
4379
  getBoundsOfPcbElements as getBoundsOfPcbElements5,
4229
4380
  getPrimaryId as getPrimaryId5
4230
4381
  } from "@tscircuit/circuit-json-util";
@@ -4322,9 +4473,9 @@ function checkPcbComponentOverlap(circuitJson) {
4322
4473
  )
4323
4474
  );
4324
4475
  const connMap = getFullConnectivityMapFromCircuitJson9(circuitJson);
4325
- const smtPads = cju6(circuitJson).pcb_smtpad.list();
4326
- const platedHoles = cju6(circuitJson).pcb_plated_hole.list();
4327
- const holes = cju6(circuitJson).pcb_hole.list();
4476
+ const smtPads = cju7(circuitJson).pcb_smtpad.list();
4477
+ const platedHoles = cju7(circuitJson).pcb_plated_hole.list();
4478
+ const holes = cju7(circuitJson).pcb_hole.list();
4328
4479
  const courtyards = circuitJson.filter(isCourtyardElement2);
4329
4480
  const componentMap = /* @__PURE__ */ new Map();
4330
4481
  for (const pad of smtPads) {
@@ -5066,7 +5217,7 @@ function checkTwoTerminalSwitchContactsOnDifferentNets(circuitJson) {
5066
5217
  }
5067
5218
 
5068
5219
  // lib/check-all-pins-in-component-are-underspecified.ts
5069
- import { cju as cju7 } from "@tscircuit/circuit-json-util";
5220
+ import { cju as cju8 } from "@tscircuit/circuit-json-util";
5070
5221
  var PIN_ATTRIBUTE_KEYS = [
5071
5222
  "must_be_connected",
5072
5223
  "provides_power",
@@ -5111,7 +5262,7 @@ function hasAnyPinAttribute(port) {
5111
5262
  }
5112
5263
  function checkAllPinsInComponentAreUnderspecified(circuitJson) {
5113
5264
  const warnings = [];
5114
- const db = cju7(circuitJson);
5265
+ const db = cju8(circuitJson);
5115
5266
  const sourceComponents = db.source_component.list();
5116
5267
  const sourcePorts = db.source_port.list();
5117
5268
  const portsByComponent = /* @__PURE__ */ new Map();
@@ -5143,7 +5294,7 @@ function checkAllPinsInComponentAreUnderspecified(circuitJson) {
5143
5294
  }
5144
5295
 
5145
5296
  // lib/check-no-power-pin-defined.ts
5146
- import { cju as cju8 } from "@tscircuit/circuit-json-util";
5297
+ import { cju as cju9 } from "@tscircuit/circuit-json-util";
5147
5298
 
5148
5299
  // lib/util/should-check-chip-power-ground-pins.ts
5149
5300
  var shouldCheckChipPowerGroundPins = (component, ports) => component.ftype === "simple_chip" && ports.filter((port) => port.do_not_connect !== true).length >= 2;
@@ -5151,7 +5302,7 @@ var shouldCheckChipPowerGroundPins = (component, ports) => component.ftype === "
5151
5302
  // lib/check-no-power-pin-defined.ts
5152
5303
  function checkNoPowerPinDefined(circuitJson) {
5153
5304
  const warnings = [];
5154
- const db = cju8(circuitJson);
5305
+ const db = cju9(circuitJson);
5155
5306
  const sourceComponents = db.source_component.list();
5156
5307
  const sourcePorts = db.source_port.list();
5157
5308
  const portsByComponent = /* @__PURE__ */ new Map();
@@ -5182,10 +5333,10 @@ function checkNoPowerPinDefined(circuitJson) {
5182
5333
  }
5183
5334
 
5184
5335
  // lib/check-no-ground-pin-defined.ts
5185
- import { cju as cju9 } from "@tscircuit/circuit-json-util";
5336
+ import { cju as cju10 } from "@tscircuit/circuit-json-util";
5186
5337
  function checkNoGroundPinDefined(circuitJson) {
5187
5338
  const warnings = [];
5188
- const db = cju9(circuitJson);
5339
+ const db = cju10(circuitJson);
5189
5340
  const sourceComponents = db.source_component.list();
5190
5341
  const sourcePorts = db.source_port.list();
5191
5342
  const portsByComponent = /* @__PURE__ */ new Map();
@@ -6046,6 +6197,7 @@ export {
6046
6197
  checkPcbComponentsOutOfBoard,
6047
6198
  checkPcbCopperOverKeepout,
6048
6199
  checkPcbTraceLengths,
6200
+ checkPcbTraceSelfShorts,
6049
6201
  checkPcbTraceViaCounts,
6050
6202
  checkPcbTracesOutOfBoard,
6051
6203
  checkPinMustBeConnected,