@tscircuit/checks 0.0.208 → 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.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";
@@ -717,7 +1035,7 @@ import {
717
1035
  import Flatbush3 from "flatbush";
718
1036
 
719
1037
  // lib/util/get-via-and-pour-connections.ts
720
- import { all_layers } from "circuit-json";
1038
+ import { all_layers as all_layers2 } from "circuit-json";
721
1039
  import {
722
1040
  getPourPolygon,
723
1041
  getTraceSegmentPolygon,
@@ -798,7 +1116,7 @@ function getViaAndPourConnections(circuit) {
798
1116
  const board = circuit.find((e) => e.type === "pcb_board");
799
1117
  const stack = [
800
1118
  "top",
801
- ...all_layers.filter((l) => l.startsWith("inner")).slice(0, board ? Math.max(0, board.num_layers - 2) : void 0),
1119
+ ...all_layers2.filter((l) => l.startsWith("inner")).slice(0, board ? Math.max(0, board.num_layers - 2) : void 0),
802
1120
  ...board?.num_layers === 1 ? [] : ["bottom"]
803
1121
  ];
804
1122
  for (const copper of circuit) {
@@ -974,7 +1292,7 @@ import {
974
1292
  getSegmentIntersection,
975
1293
  isPointInsidePolygon,
976
1294
  pointToSegmentClosestPoint,
977
- segmentToSegmentMinDistance
1295
+ segmentToSegmentMinDistance as segmentToSegmentMinDistance2
978
1296
  } from "@tscircuit/math-utils";
979
1297
  var rotatePoint = (point2, angleDegrees) => {
980
1298
  const angle = angleDegrees * Math.PI / 180;
@@ -1074,7 +1392,7 @@ var getClosestPointsBetweenSegments = (a1, a2, b1, b2) => {
1074
1392
  }
1075
1393
  }
1076
1394
  return {
1077
- distance: segmentToSegmentMinDistance(a1, a2, b1, b2),
1395
+ distance: segmentToSegmentMinDistance2(a1, a2, b1, b2),
1078
1396
  pointOnA: best.pointOnA,
1079
1397
  pointOnB: best.pointOnB,
1080
1398
  center: {
@@ -1695,20 +2013,20 @@ function checkEachPcbPortConnectedToPcbTraces(circuitJson, {
1695
2013
  }
1696
2014
 
1697
2015
  // 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";
2016
+ import { cju as cju3, getReadableNameForElement as getReadableNameForElement3 } from "@tscircuit/circuit-json-util";
1699
2017
  import { getPrimaryId as getPrimaryId2 } from "@tscircuit/circuit-json-util";
1700
2018
  import {
1701
2019
  segmentToBoundsMinDistance,
1702
2020
  segmentToCircleMinDistance as segmentToCircleMinDistance2
1703
2021
  } from "@tscircuit/math-utils";
1704
- import { segmentToSegmentMinDistance as segmentToSegmentMinDistance3 } from "@tscircuit/math-utils";
2022
+ import { segmentToSegmentMinDistance as segmentToSegmentMinDistance4 } from "@tscircuit/math-utils";
1705
2023
  import {
1706
2024
  getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson3
1707
2025
  } from "circuit-json-to-connectivity-map";
1708
2026
 
1709
2027
  // lib/check-pad-clearance/common.ts
1710
2028
  import {
1711
- cju,
2029
+ cju as cju2,
1712
2030
  distanceBetweenCircleAndCircle,
1713
2031
  distanceBetweenCircleAndPolygon,
1714
2032
  distanceBetweenPolygonAndPolygon,
@@ -1718,7 +2036,7 @@ import {
1718
2036
  midpoint,
1719
2037
  pointToSegmentClosestPoint as pointToSegmentClosestPoint2,
1720
2038
  segmentToCircleMinDistance,
1721
- segmentToSegmentMinDistance as segmentToSegmentMinDistance2
2039
+ segmentToSegmentMinDistance as segmentToSegmentMinDistance3
1722
2040
  } from "@tscircuit/math-utils";
1723
2041
 
1724
2042
  // node_modules/@tscircuit/jlcpcb-manufacturing-specs/lib/jlcpcb-manufacturing-specs.ts
@@ -1885,7 +2203,7 @@ var getPadToPadGap = (padA, padB) => {
1885
2203
  if (isPillPad(padA) && isPillPad(padB)) {
1886
2204
  const pillA = getPillCenterLineForPad(padA);
1887
2205
  const pillB = getPillCenterLineForPad(padB);
1888
- return segmentToSegmentMinDistance2(
2206
+ return segmentToSegmentMinDistance3(
1889
2207
  pillA.start,
1890
2208
  pillA.end,
1891
2209
  pillB.start,
@@ -1940,11 +2258,11 @@ var getPadToPadGap = (padA, padB) => {
1940
2258
  );
1941
2259
  };
1942
2260
  var getPads = (circuitJson) => [
1943
- ...cju(circuitJson).pcb_smtpad.list(),
1944
- ...cju(circuitJson).pcb_plated_hole.list()
2261
+ ...cju2(circuitJson).pcb_smtpad.list(),
2262
+ ...cju2(circuitJson).pcb_plated_hole.list()
1945
2263
  ];
1946
2264
  var getTraceSegments = (circuitJson) => {
1947
- const pcbTraces = cju(circuitJson).pcb_trace.list();
2265
+ const pcbTraces = cju2(circuitJson).pcb_trace.list();
1948
2266
  return pcbTraces.flatMap((pcbTrace) => {
1949
2267
  const segments = [];
1950
2268
  for (let i = 0; i < pcbTrace.route.length - 1; i++) {
@@ -2162,7 +2480,7 @@ var SpatialObjectIndex = class {
2162
2480
  };
2163
2481
 
2164
2482
  // lib/util/getLayersOfPcbElement.ts
2165
- import { all_layers as all_layers2 } from "circuit-json";
2483
+ import { all_layers as all_layers3 } from "circuit-json";
2166
2484
  function getLayersOfPcbElement(obj) {
2167
2485
  if (obj.type === "pcb_trace_segment") {
2168
2486
  return [obj.layer];
@@ -2171,13 +2489,13 @@ function getLayersOfPcbElement(obj) {
2171
2489
  return [obj.layer];
2172
2490
  }
2173
2491
  if (obj.type === "pcb_plated_hole") {
2174
- return Array.isArray(obj.layers) ? obj.layers : [...all_layers2];
2492
+ return Array.isArray(obj.layers) ? obj.layers : [...all_layers3];
2175
2493
  }
2176
2494
  if (obj.type === "pcb_hole") {
2177
- return [...all_layers2];
2495
+ return [...all_layers3];
2178
2496
  }
2179
2497
  if (obj.type === "pcb_via") {
2180
- return Array.isArray(obj.layers) ? obj.layers : [...all_layers2];
2498
+ return Array.isArray(obj.layers) ? obj.layers : [...all_layers3];
2181
2499
  }
2182
2500
  if (obj.type === "pcb_keepout") {
2183
2501
  return Array.isArray(obj.layers) ? obj.layers : [];
@@ -2278,151 +2596,6 @@ var getClosestPointBetweenSegmentAndBounds = (segment, bounds) => {
2278
2596
  return closestPoint;
2279
2597
  };
2280
2598
 
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
2599
  // lib/check-each-pcb-trace-non-overlapping/getCollidableBounds.ts
2427
2600
  import { getBoundsOfPcbElements as getBoundsOfPcbElements3 } from "@tscircuit/circuit-json-util";
2428
2601
  var getCollidableBounds = (collidable) => {
@@ -2458,32 +2631,6 @@ var getCollidableBounds = (collidable) => {
2458
2631
  return getBoundsOfPcbElements3([collidable]);
2459
2632
  };
2460
2633
 
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
2634
  // lib/check-each-pcb-trace-non-overlapping/getRadiusOfCircuitJsonElement.ts
2488
2635
  var getRadiusOfCircuitJsonElement = (obj) => {
2489
2636
  if (obj.type === "pcb_via") {
@@ -2513,12 +2660,12 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
2513
2660
  connMap,
2514
2661
  minClearance
2515
2662
  } = {}) {
2516
- const errors = [];
2663
+ const errors = checkPcbTraceSelfShorts(circuitJson);
2517
2664
  addStartAndEndPortIdsIfMissing(circuitJson);
2518
2665
  connMap ??= getFullConnectivityMapFromCircuitJson3(circuitJson);
2519
2666
  const board = getPcbBoard(circuitJson);
2520
2667
  minClearance ??= getBoardDrcValue(board, "min_trace_to_pad_edge_clearance") ?? DEFAULT_TRACE_MARGIN;
2521
- const pcbTraces = cju2(circuitJson).pcb_trace.list();
2668
+ const pcbTraces = cju3(circuitJson).pcb_trace.list();
2522
2669
  const pcbTraceSegments = pcbTraces.flatMap((pcbTrace) => {
2523
2670
  const segments = [];
2524
2671
  for (let i = 0; i < pcbTrace.route.length - 1; i++) {
@@ -2541,12 +2688,12 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
2541
2688
  }
2542
2689
  return segments;
2543
2690
  });
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);
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);
2550
2697
  const pcbComponentConnectionElements = [
2551
2698
  ...pcbPorts,
2552
2699
  ...pcbSmtPads,
@@ -2607,7 +2754,7 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
2607
2754
  if (segmentA.layer !== segmentB.layer) continue;
2608
2755
  if (connMap.areIdsConnected(segmentA.pcb_trace_id, segmentB.pcb_trace_id))
2609
2756
  continue;
2610
- const gap2 = segmentToSegmentMinDistance3(
2757
+ const gap2 = segmentToSegmentMinDistance4(
2611
2758
  { x: segmentA.x1, y: segmentA.y1 },
2612
2759
  { x: segmentA.x2, y: segmentA.y2 },
2613
2760
  { x: segmentB.x1, y: segmentB.y1 },
@@ -3266,7 +3413,7 @@ function checkPcbComponentOverCutout(circuitJson) {
3266
3413
  }
3267
3414
 
3268
3415
  // lib/check-pcb-copper-over-keepout.ts
3269
- import { cju as cju3, getPrimaryId as getPrimaryId3 } from "@tscircuit/circuit-json-util";
3416
+ import { cju as cju4, getPrimaryId as getPrimaryId3 } from "@tscircuit/circuit-json-util";
3270
3417
  var getErrorOwnerId = (copper) => "pcb_component_id" in copper && copper.pcb_component_id ? copper.pcb_component_id : getPrimaryId3(copper);
3271
3418
  var getReadableCopperName = (circuitJson, copper) => {
3272
3419
  if ("pcb_component_id" in copper && copper.pcb_component_id) {
@@ -3282,11 +3429,11 @@ var getReadableCopperName = (circuitJson, copper) => {
3282
3429
  return copper.type === "pcb_via" ? `via ${copper.pcb_via_id}` : `${copper.type} ${getPrimaryId3(copper)}`;
3283
3430
  };
3284
3431
  function checkPcbCopperOverKeepout(circuitJson) {
3285
- const keepouts = cju3(circuitJson).pcb_keepout.list();
3432
+ const keepouts = cju4(circuitJson).pcb_keepout.list();
3286
3433
  if (keepouts.length === 0) return [];
3287
3434
  const copper = [
3288
3435
  ...getPads(circuitJson),
3289
- ...cju3(circuitJson).pcb_via.list()
3436
+ ...cju4(circuitJson).pcb_via.list()
3290
3437
  ];
3291
3438
  const errors = /* @__PURE__ */ new Map();
3292
3439
  for (const keepout of keepouts) {
@@ -3463,11 +3610,11 @@ function checkDifferentNetViaSpacing(circuitJson, {
3463
3610
  }
3464
3611
 
3465
3612
  // lib/check-source-traces-match-pcb-trace-thickness.ts
3466
- import { cju as cju4 } from "@tscircuit/circuit-json-util";
3613
+ import { cju as cju5 } from "@tscircuit/circuit-json-util";
3467
3614
  import { getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson6 } from "circuit-json-to-connectivity-map";
3468
3615
  function checkSourceTracesMatchPcbTraceThickness(circuitJson) {
3469
3616
  const warnings = [];
3470
- const db = cju4(circuitJson);
3617
+ const db = cju5(circuitJson);
3471
3618
  const sourceTraces = db.source_trace.list();
3472
3619
  const pcbTraces = db.pcb_trace.list();
3473
3620
  const pcbPorts = db.pcb_port.list();
@@ -3606,7 +3753,7 @@ import {
3606
3753
 
3607
3754
  // lib/check-traces-are-contiguous/via-contact-index.ts
3608
3755
  import {
3609
- all_layers as all_layers3
3756
+ all_layers as all_layers4
3610
3757
  } from "circuit-json";
3611
3758
  import { getPrimaryId as getPrimaryId4 } from "@tscircuit/circuit-json-util";
3612
3759
  import { pointToSegmentDistance as pointToSegmentDistance2 } from "@tscircuit/math-utils";
@@ -3618,7 +3765,7 @@ function getViaContactIndex(circuitJson, connectivity) {
3618
3765
  const vias = circuitJson.filter((element) => element.type === "pcb_via");
3619
3766
  const board = circuitJson.find((element) => element.type === "pcb_board");
3620
3767
  const layerCount = board?.num_layers;
3621
- const innerLayers = all_layers3.filter((layer) => layer.startsWith("inner"));
3768
+ const innerLayers = all_layers4.filter((layer) => layer.startsWith("inner"));
3622
3769
  const stack = [
3623
3770
  "top",
3624
3771
  ...innerLayers.slice(
@@ -4144,8 +4291,8 @@ function checkTracesAreContiguous(circuitJson, {
4144
4291
  }
4145
4292
 
4146
4293
  // 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";
4294
+ import { cju as cju6 } from "@tscircuit/circuit-json-util";
4295
+ import { segmentToSegmentMinDistance as segmentToSegmentMinDistance5 } from "@tscircuit/math-utils";
4149
4296
  function getBoardPolygonPoints(board) {
4150
4297
  if (board.outline && board.outline.length > 0) {
4151
4298
  return board.outline.map((p) => ({ x: p.x, y: p.y }));
@@ -4175,7 +4322,7 @@ function checkPcbTracesOutOfBoard(circuitJson, config = {}) {
4175
4322
  const margin = config.margin ?? getBoardDrcValue(board, "min_board_edge_clearance") ?? jlcMinTolerances.min_board_edge_clearance;
4176
4323
  const boardPoints = getBoardPolygonPoints(board);
4177
4324
  if (!boardPoints) return errors;
4178
- const pcbTraces = cju5(circuitJson).pcb_trace.list();
4325
+ const pcbTraces = cju6(circuitJson).pcb_trace.list();
4179
4326
  for (const trace of pcbTraces) {
4180
4327
  if (trace.route.length < 2) continue;
4181
4328
  for (let i = 0; i < trace.route.length - 1; i++) {
@@ -4189,7 +4336,7 @@ function checkPcbTracesOutOfBoard(circuitJson, config = {}) {
4189
4336
  for (let j = 0; j < boardPoints.length; j++) {
4190
4337
  const edgeStart = boardPoints[j];
4191
4338
  const edgeEnd = boardPoints[(j + 1) % boardPoints.length];
4192
- const distance3 = segmentToSegmentMinDistance4(
4339
+ const distance3 = segmentToSegmentMinDistance5(
4193
4340
  segmentStart,
4194
4341
  segmentEnd,
4195
4342
  edgeStart,
@@ -4224,7 +4371,7 @@ function checkPcbTracesOutOfBoard(circuitJson, config = {}) {
4224
4371
 
4225
4372
  // lib/check-pcb-components-overlap/checkPcbComponentOverlap.ts
4226
4373
  import {
4227
- cju as cju6,
4374
+ cju as cju7,
4228
4375
  getBoundsOfPcbElements as getBoundsOfPcbElements5,
4229
4376
  getPrimaryId as getPrimaryId5
4230
4377
  } from "@tscircuit/circuit-json-util";
@@ -4322,9 +4469,9 @@ function checkPcbComponentOverlap(circuitJson) {
4322
4469
  )
4323
4470
  );
4324
4471
  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();
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();
4328
4475
  const courtyards = circuitJson.filter(isCourtyardElement2);
4329
4476
  const componentMap = /* @__PURE__ */ new Map();
4330
4477
  for (const pad of smtPads) {
@@ -5066,7 +5213,7 @@ function checkTwoTerminalSwitchContactsOnDifferentNets(circuitJson) {
5066
5213
  }
5067
5214
 
5068
5215
  // lib/check-all-pins-in-component-are-underspecified.ts
5069
- import { cju as cju7 } from "@tscircuit/circuit-json-util";
5216
+ import { cju as cju8 } from "@tscircuit/circuit-json-util";
5070
5217
  var PIN_ATTRIBUTE_KEYS = [
5071
5218
  "must_be_connected",
5072
5219
  "provides_power",
@@ -5111,7 +5258,7 @@ function hasAnyPinAttribute(port) {
5111
5258
  }
5112
5259
  function checkAllPinsInComponentAreUnderspecified(circuitJson) {
5113
5260
  const warnings = [];
5114
- const db = cju7(circuitJson);
5261
+ const db = cju8(circuitJson);
5115
5262
  const sourceComponents = db.source_component.list();
5116
5263
  const sourcePorts = db.source_port.list();
5117
5264
  const portsByComponent = /* @__PURE__ */ new Map();
@@ -5143,7 +5290,7 @@ function checkAllPinsInComponentAreUnderspecified(circuitJson) {
5143
5290
  }
5144
5291
 
5145
5292
  // lib/check-no-power-pin-defined.ts
5146
- import { cju as cju8 } from "@tscircuit/circuit-json-util";
5293
+ import { cju as cju9 } from "@tscircuit/circuit-json-util";
5147
5294
 
5148
5295
  // lib/util/should-check-chip-power-ground-pins.ts
5149
5296
  var shouldCheckChipPowerGroundPins = (component, ports) => component.ftype === "simple_chip" && ports.filter((port) => port.do_not_connect !== true).length >= 2;
@@ -5151,7 +5298,7 @@ var shouldCheckChipPowerGroundPins = (component, ports) => component.ftype === "
5151
5298
  // lib/check-no-power-pin-defined.ts
5152
5299
  function checkNoPowerPinDefined(circuitJson) {
5153
5300
  const warnings = [];
5154
- const db = cju8(circuitJson);
5301
+ const db = cju9(circuitJson);
5155
5302
  const sourceComponents = db.source_component.list();
5156
5303
  const sourcePorts = db.source_port.list();
5157
5304
  const portsByComponent = /* @__PURE__ */ new Map();
@@ -5182,10 +5329,10 @@ function checkNoPowerPinDefined(circuitJson) {
5182
5329
  }
5183
5330
 
5184
5331
  // lib/check-no-ground-pin-defined.ts
5185
- import { cju as cju9 } from "@tscircuit/circuit-json-util";
5332
+ import { cju as cju10 } from "@tscircuit/circuit-json-util";
5186
5333
  function checkNoGroundPinDefined(circuitJson) {
5187
5334
  const warnings = [];
5188
- const db = cju9(circuitJson);
5335
+ const db = cju10(circuitJson);
5189
5336
  const sourceComponents = db.source_component.list();
5190
5337
  const sourcePorts = db.source_port.list();
5191
5338
  const portsByComponent = /* @__PURE__ */ new Map();
@@ -6046,6 +6193,7 @@ export {
6046
6193
  checkPcbComponentsOutOfBoard,
6047
6194
  checkPcbCopperOverKeepout,
6048
6195
  checkPcbTraceLengths,
6196
+ checkPcbTraceSelfShorts,
6049
6197
  checkPcbTraceViaCounts,
6050
6198
  checkPcbTracesOutOfBoard,
6051
6199
  checkPinMustBeConnected,