@tscircuit/checks 0.0.119 → 0.0.120
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 +12 -12
- package/dist/index.js +31 -29
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.d.ts
CHANGED
|
@@ -4,9 +4,9 @@ import { ConnectivityMap } from 'circuit-json-to-connectivity-map';
|
|
|
4
4
|
|
|
5
5
|
declare function checkEachPcbPortConnectedToPcbTraces(circuitJson: AnyCircuitElement[]): PcbPortNotConnectedError[];
|
|
6
6
|
|
|
7
|
-
declare function checkEachPcbTraceNonOverlapping(circuitJson: AnyCircuitElement[], { connMap,
|
|
7
|
+
declare function checkEachPcbTraceNonOverlapping(circuitJson: AnyCircuitElement[], { connMap, minClearance, }?: {
|
|
8
8
|
connMap?: ConnectivityMap;
|
|
9
|
-
|
|
9
|
+
minClearance?: number;
|
|
10
10
|
}): PcbTraceError[];
|
|
11
11
|
|
|
12
12
|
declare class NetManager {
|
|
@@ -23,14 +23,14 @@ declare function checkViasOffBoard(circuitJson: AnyCircuitElement[]): PcbPlaceme
|
|
|
23
23
|
*/
|
|
24
24
|
declare function checkPcbComponentsOutOfBoard(circuitJson: AnyCircuitElement[]): PcbComponentOutsideBoardError[];
|
|
25
25
|
|
|
26
|
-
declare function checkSameNetViaSpacing(circuitJson: AnyCircuitElement[], { connMap,
|
|
26
|
+
declare function checkSameNetViaSpacing(circuitJson: AnyCircuitElement[], { connMap, minClearance, }?: {
|
|
27
27
|
connMap?: ConnectivityMap;
|
|
28
|
-
|
|
28
|
+
minClearance?: number;
|
|
29
29
|
}): PcbViaClearanceError[];
|
|
30
30
|
|
|
31
|
-
declare function checkDifferentNetViaSpacing(circuitJson: AnyCircuitElement[], { connMap,
|
|
31
|
+
declare function checkDifferentNetViaSpacing(circuitJson: AnyCircuitElement[], { connMap, minClearance, }?: {
|
|
32
32
|
connMap?: ConnectivityMap;
|
|
33
|
-
|
|
33
|
+
minClearance?: number;
|
|
34
34
|
}): PcbViaClearanceError[];
|
|
35
35
|
|
|
36
36
|
declare function checkSourceTracesMatchPcbTraceThickness(circuitJson: AnyCircuitElement[]): PcbTraceWarning[];
|
|
@@ -61,19 +61,19 @@ declare function checkPcbTracesOutOfBoard(circuitJson: AnyCircuitElement[], conf
|
|
|
61
61
|
*/
|
|
62
62
|
declare function checkPcbComponentOverlap(circuitJson: AnyCircuitElement[]): PcbFootprintOverlapError[];
|
|
63
63
|
|
|
64
|
-
declare function checkPadPadClearance(circuitJson: AnyCircuitElement[], { connMap,
|
|
64
|
+
declare function checkPadPadClearance(circuitJson: AnyCircuitElement[], { connMap, minClearance, }?: {
|
|
65
65
|
connMap?: ConnectivityMap;
|
|
66
|
-
|
|
66
|
+
minClearance?: number;
|
|
67
67
|
}): PcbPadPadClearanceError[];
|
|
68
68
|
|
|
69
|
-
declare function checkPadTraceClearance(circuitJson: AnyCircuitElement[], { connMap,
|
|
69
|
+
declare function checkPadTraceClearance(circuitJson: AnyCircuitElement[], { connMap, minClearance, }?: {
|
|
70
70
|
connMap?: ConnectivityMap;
|
|
71
|
-
|
|
71
|
+
minClearance?: number;
|
|
72
72
|
}): PcbPadTraceClearanceError[];
|
|
73
73
|
|
|
74
|
-
declare function checkViaTraceClearance(circuitJson: AnyCircuitElement[], { connMap,
|
|
74
|
+
declare function checkViaTraceClearance(circuitJson: AnyCircuitElement[], { connMap, minClearance, }?: {
|
|
75
75
|
connMap?: ConnectivityMap;
|
|
76
|
-
|
|
76
|
+
minClearance?: number;
|
|
77
77
|
}): PcbViaTraceClearanceError[];
|
|
78
78
|
|
|
79
79
|
type SourcePinMustBeConnectedError = {
|
package/dist/index.js
CHANGED
|
@@ -364,13 +364,15 @@ import {
|
|
|
364
364
|
} from "@tscircuit/math-utils";
|
|
365
365
|
|
|
366
366
|
// lib/drc-defaults.ts
|
|
367
|
+
import { jlcMinTolerances } from "@tscircuit/jlcpcb-manufacturing-specs";
|
|
367
368
|
var DEFAULT_TRACE_MARGIN = 0.1;
|
|
368
|
-
var DEFAULT_TRACE_THICKNESS =
|
|
369
|
+
var DEFAULT_TRACE_THICKNESS = jlcMinTolerances.min_trace_width;
|
|
370
|
+
var DEFAULT_VIA_DIAMETER = jlcMinTolerances.min_via_pad_diameter;
|
|
369
371
|
var DEFAULT_VIA_BOARD_MARGIN = 0.3;
|
|
370
|
-
var DEFAULT_SAME_NET_VIA_MARGIN =
|
|
371
|
-
var DEFAULT_DIFFERENT_NET_VIA_MARGIN =
|
|
372
|
-
var DEFAULT_PAD_PAD_CLEARANCE =
|
|
373
|
-
var DEFAULT_PAD_TRACE_CLEARANCE =
|
|
372
|
+
var DEFAULT_SAME_NET_VIA_MARGIN = jlcMinTolerances.min_via_to_via_clearance;
|
|
373
|
+
var DEFAULT_DIFFERENT_NET_VIA_MARGIN = jlcMinTolerances.min_via_to_via_clearance;
|
|
374
|
+
var DEFAULT_PAD_PAD_CLEARANCE = jlcMinTolerances.min_pad_to_pad_clearance;
|
|
375
|
+
var DEFAULT_PAD_TRACE_CLEARANCE = jlcMinTolerances.min_trace_to_pad_clearance;
|
|
374
376
|
var EPSILON = 5e-3;
|
|
375
377
|
|
|
376
378
|
// lib/check-each-pcb-trace-non-overlapping/getPcbPortIdsConnectedToTraces.ts
|
|
@@ -684,7 +686,7 @@ function getLayersOfPcbElement(obj) {
|
|
|
684
686
|
// lib/check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping.ts
|
|
685
687
|
function checkEachPcbTraceNonOverlapping(circuitJson, {
|
|
686
688
|
connMap,
|
|
687
|
-
|
|
689
|
+
minClearance = DEFAULT_TRACE_MARGIN
|
|
688
690
|
} = {}) {
|
|
689
691
|
const errors = [];
|
|
690
692
|
addStartAndEndPortIdsIfMissing(circuitJson);
|
|
@@ -732,7 +734,7 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
|
|
|
732
734
|
const getReadableName = (id) => getReadableNameForElement2(circuitJson, id);
|
|
733
735
|
const errorIds = /* @__PURE__ */ new Set();
|
|
734
736
|
for (const segmentA of pcbTraceSegments) {
|
|
735
|
-
const requiredMargin =
|
|
737
|
+
const requiredMargin = minClearance;
|
|
736
738
|
const bounds = getCollidableBounds(segmentA);
|
|
737
739
|
const nearbyObjects = spatialIndex.getObjectsInBounds(
|
|
738
740
|
bounds,
|
|
@@ -754,7 +756,7 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
|
|
|
754
756
|
{ x: segmentB.x1, y: segmentB.y1 },
|
|
755
757
|
{ x: segmentB.x2, y: segmentB.y2 }
|
|
756
758
|
) - segmentA.thickness / 2 - segmentB.thickness / 2;
|
|
757
|
-
if (gap2 >
|
|
759
|
+
if (gap2 > minClearance - EPSILON) continue;
|
|
758
760
|
const pcb_trace_error_id = `overlap_${segmentA.pcb_trace_id}_${segmentB.pcb_trace_id}`;
|
|
759
761
|
const pcb_trace_error_id_reverse = `overlap_${segmentB.pcb_trace_id}_${segmentA.pcb_trace_id}`;
|
|
760
762
|
if (errorIds.has(pcb_trace_error_id)) continue;
|
|
@@ -791,7 +793,7 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
|
|
|
791
793
|
{ x: obj.x, y: obj.y, radius }
|
|
792
794
|
);
|
|
793
795
|
const gap2 = distance3 - segmentA.thickness / 2;
|
|
794
|
-
if (gap2 >
|
|
796
|
+
if (gap2 > minClearance - EPSILON) continue;
|
|
795
797
|
const pcb_trace_error_id = `overlap_${segmentA.pcb_trace_id}_${primaryObjId}`;
|
|
796
798
|
if (errorIds.has(pcb_trace_error_id)) continue;
|
|
797
799
|
errorIds.add(pcb_trace_error_id);
|
|
@@ -1174,7 +1176,7 @@ function viasAreAtSameLocation(a, b) {
|
|
|
1174
1176
|
// lib/check-same-net-via-spacing.ts
|
|
1175
1177
|
function checkSameNetViaSpacing(circuitJson, {
|
|
1176
1178
|
connMap,
|
|
1177
|
-
|
|
1179
|
+
minClearance = DEFAULT_SAME_NET_VIA_MARGIN
|
|
1178
1180
|
} = {}) {
|
|
1179
1181
|
const vias = circuitJson.filter((el) => el.type === "pcb_via");
|
|
1180
1182
|
if (vias.length < 2) return [];
|
|
@@ -1188,7 +1190,7 @@ function checkSameNetViaSpacing(circuitJson, {
|
|
|
1188
1190
|
if (viasAreAtSameLocation(viaA, viaB)) continue;
|
|
1189
1191
|
if (!connMap.areIdsConnected(viaA.pcb_via_id, viaB.pcb_via_id)) continue;
|
|
1190
1192
|
const gap = distance2(viaA, viaB) - viaA.outer_diameter / 2 - viaB.outer_diameter / 2;
|
|
1191
|
-
if (gap + EPSILON >=
|
|
1193
|
+
if (gap + EPSILON >= minClearance) continue;
|
|
1192
1194
|
const pairId = [viaA.pcb_via_id, viaB.pcb_via_id].sort().join("_");
|
|
1193
1195
|
if (reported.has(pairId)) continue;
|
|
1194
1196
|
reported.add(pairId);
|
|
@@ -1204,7 +1206,7 @@ function checkSameNetViaSpacing(circuitJson, {
|
|
|
1204
1206
|
)} are too close together (gap: ${gap.toFixed(3)}mm)`,
|
|
1205
1207
|
error_type: "pcb_via_clearance_error",
|
|
1206
1208
|
pcb_via_ids: [viaA.pcb_via_id, viaB.pcb_via_id],
|
|
1207
|
-
minimum_clearance:
|
|
1209
|
+
minimum_clearance: minClearance,
|
|
1208
1210
|
actual_clearance: gap,
|
|
1209
1211
|
pcb_center: {
|
|
1210
1212
|
x: (viaA.x + viaB.x) / 2,
|
|
@@ -1223,7 +1225,7 @@ import {
|
|
|
1223
1225
|
} from "circuit-json-to-connectivity-map";
|
|
1224
1226
|
function checkDifferentNetViaSpacing(circuitJson, {
|
|
1225
1227
|
connMap,
|
|
1226
|
-
|
|
1228
|
+
minClearance = DEFAULT_DIFFERENT_NET_VIA_MARGIN
|
|
1227
1229
|
} = {}) {
|
|
1228
1230
|
const vias = circuitJson.filter((el) => el.type === "pcb_via");
|
|
1229
1231
|
if (vias.length < 2) return [];
|
|
@@ -1237,7 +1239,7 @@ function checkDifferentNetViaSpacing(circuitJson, {
|
|
|
1237
1239
|
if (viasAreAtSameLocation(viaA, viaB)) continue;
|
|
1238
1240
|
if (connMap.areIdsConnected(viaA.pcb_via_id, viaB.pcb_via_id)) continue;
|
|
1239
1241
|
const gap = distance2(viaA, viaB) - viaA.outer_diameter / 2 - viaB.outer_diameter / 2;
|
|
1240
|
-
if (gap + EPSILON >=
|
|
1242
|
+
if (gap + EPSILON >= minClearance) continue;
|
|
1241
1243
|
const pairId = [viaA.pcb_via_id, viaB.pcb_via_id].sort().join("_");
|
|
1242
1244
|
if (reported.has(pairId)) continue;
|
|
1243
1245
|
reported.add(pairId);
|
|
@@ -1255,7 +1257,7 @@ function checkDifferentNetViaSpacing(circuitJson, {
|
|
|
1255
1257
|
)}mm)`,
|
|
1256
1258
|
error_type: "pcb_via_clearance_error",
|
|
1257
1259
|
pcb_via_ids: [viaA.pcb_via_id, viaB.pcb_via_id],
|
|
1258
|
-
minimum_clearance:
|
|
1260
|
+
minimum_clearance: minClearance,
|
|
1259
1261
|
actual_clearance: gap,
|
|
1260
1262
|
pcb_center: {
|
|
1261
1263
|
x: (viaA.x + viaB.x) / 2,
|
|
@@ -1716,7 +1718,7 @@ var getTraceSegments = (circuitJson) => {
|
|
|
1716
1718
|
// lib/check-pad-pad-clearance.ts
|
|
1717
1719
|
function checkPadPadClearance(circuitJson, {
|
|
1718
1720
|
connMap,
|
|
1719
|
-
|
|
1721
|
+
minClearance = DEFAULT_PAD_PAD_CLEARANCE
|
|
1720
1722
|
} = {}) {
|
|
1721
1723
|
const pads = getPads(circuitJson);
|
|
1722
1724
|
if (pads.length < 2) return [];
|
|
@@ -1731,7 +1733,7 @@ function checkPadPadClearance(circuitJson, {
|
|
|
1731
1733
|
const padAId = getPrimaryId3(padA);
|
|
1732
1734
|
const nearbyPads = spatialIndex.getObjectsInBounds(
|
|
1733
1735
|
getPadBounds(padA),
|
|
1734
|
-
|
|
1736
|
+
minClearance
|
|
1735
1737
|
);
|
|
1736
1738
|
for (const padB of nearbyPads) {
|
|
1737
1739
|
const padBId = getPrimaryId3(padB);
|
|
@@ -1744,16 +1746,16 @@ function checkPadPadClearance(circuitJson, {
|
|
|
1744
1746
|
if (connMap.areIdsConnected(padAId, padBId)) continue;
|
|
1745
1747
|
const pairId = [padAId, padBId].sort().join("_");
|
|
1746
1748
|
const gap = getPadToPadGap(padA, padB);
|
|
1747
|
-
if (gap + EPSILON >=
|
|
1749
|
+
if (gap + EPSILON >= minClearance) continue;
|
|
1748
1750
|
const centerA = getPadCenter(padA);
|
|
1749
1751
|
const centerB = getPadCenter(padB);
|
|
1750
1752
|
const nextError = {
|
|
1751
1753
|
type: "pcb_pad_pad_clearance_error",
|
|
1752
1754
|
pcb_pad_pad_clearance_error_id: `pad_pad_clearance_${pairId}`,
|
|
1753
1755
|
error_type: "pcb_pad_pad_clearance_error",
|
|
1754
|
-
message: `Pads ${getReadableNameForElement6(circuitJson, padAId)} and ${getReadableNameForElement6(circuitJson, padBId)} are too close (clearance: ${formatMm(gap)}, minimum: ${formatMm(
|
|
1756
|
+
message: `Pads ${getReadableNameForElement6(circuitJson, padAId)} and ${getReadableNameForElement6(circuitJson, padBId)} are too close (clearance: ${formatMm(gap)}, minimum: ${formatMm(minClearance)})`,
|
|
1755
1757
|
pcb_pad_ids: [padAId, padBId],
|
|
1756
|
-
minimum_clearance:
|
|
1758
|
+
minimum_clearance: minClearance,
|
|
1757
1759
|
actual_clearance: gap,
|
|
1758
1760
|
center: {
|
|
1759
1761
|
x: (centerA.x + centerB.x) / 2,
|
|
@@ -1782,7 +1784,7 @@ import {
|
|
|
1782
1784
|
} from "circuit-json-to-connectivity-map";
|
|
1783
1785
|
function checkPadTraceClearance(circuitJson, {
|
|
1784
1786
|
connMap,
|
|
1785
|
-
|
|
1787
|
+
minClearance = DEFAULT_PAD_TRACE_CLEARANCE
|
|
1786
1788
|
} = {}) {
|
|
1787
1789
|
const pads = getPads(circuitJson);
|
|
1788
1790
|
const segments = getTraceSegments(circuitJson);
|
|
@@ -1797,7 +1799,7 @@ function checkPadTraceClearance(circuitJson, {
|
|
|
1797
1799
|
for (const segment of segments) {
|
|
1798
1800
|
const nearbyPads = spatialIndex.getObjectsInBounds(
|
|
1799
1801
|
getCollidableBounds(segment),
|
|
1800
|
-
|
|
1802
|
+
minClearance + segment.thickness / 2
|
|
1801
1803
|
);
|
|
1802
1804
|
for (const pad of nearbyPads) {
|
|
1803
1805
|
const padId = getPrimaryId4(pad);
|
|
@@ -1813,16 +1815,16 @@ function checkPadTraceClearance(circuitJson, {
|
|
|
1813
1815
|
{ x: segment.x2, y: segment.y2 },
|
|
1814
1816
|
getPadBounds(pad)
|
|
1815
1817
|
) - segment.thickness / 2;
|
|
1816
|
-
if (gap + EPSILON >=
|
|
1818
|
+
if (gap + EPSILON >= minClearance) continue;
|
|
1817
1819
|
const pairId = `${padId}_${segment.pcb_trace_id}`;
|
|
1818
1820
|
const nextError = {
|
|
1819
1821
|
type: "pcb_pad_trace_clearance_error",
|
|
1820
1822
|
pcb_pad_trace_clearance_error_id: `pad_trace_clearance_${pairId}`,
|
|
1821
1823
|
error_type: "pcb_pad_trace_clearance_error",
|
|
1822
|
-
message: `Pad ${getReadableNameForElement7(circuitJson, padId)} and trace ${getReadableNameForElement7(circuitJson, segment.pcb_trace_id)} are too close (clearance: ${formatMm(gap)}, minimum: ${formatMm(
|
|
1824
|
+
message: `Pad ${getReadableNameForElement7(circuitJson, padId)} and trace ${getReadableNameForElement7(circuitJson, segment.pcb_trace_id)} are too close (clearance: ${formatMm(gap)}, minimum: ${formatMm(minClearance)})`,
|
|
1823
1825
|
pcb_pad_id: padId,
|
|
1824
1826
|
pcb_trace_id: segment.pcb_trace_id,
|
|
1825
|
-
minimum_clearance:
|
|
1827
|
+
minimum_clearance: minClearance,
|
|
1826
1828
|
actual_clearance: gap,
|
|
1827
1829
|
center: {
|
|
1828
1830
|
x: (center.x + (segment.x1 + segment.x2) / 2) / 2,
|
|
@@ -1863,7 +1865,7 @@ var getClosestPointOnSegment = (point, segmentStart, segmentEnd) => {
|
|
|
1863
1865
|
};
|
|
1864
1866
|
function checkViaTraceClearance(circuitJson, {
|
|
1865
1867
|
connMap,
|
|
1866
|
-
|
|
1868
|
+
minClearance = DEFAULT_PAD_TRACE_CLEARANCE
|
|
1867
1869
|
} = {}) {
|
|
1868
1870
|
const vias = circuitJson.filter((el) => el.type === "pcb_via");
|
|
1869
1871
|
const segments = getTraceSegments(circuitJson);
|
|
@@ -1885,7 +1887,7 @@ function checkViaTraceClearance(circuitJson, {
|
|
|
1885
1887
|
radius: viaRadius
|
|
1886
1888
|
}
|
|
1887
1889
|
) - segment.thickness / 2;
|
|
1888
|
-
if (gap + EPSILON >=
|
|
1890
|
+
if (gap + EPSILON >= minClearance) continue;
|
|
1889
1891
|
const closestPoint = getClosestPointOnSegment(
|
|
1890
1892
|
{ x: via.x, y: via.y },
|
|
1891
1893
|
{ x: segment.x1, y: segment.y1 },
|
|
@@ -1896,10 +1898,10 @@ function checkViaTraceClearance(circuitJson, {
|
|
|
1896
1898
|
type: "pcb_via_trace_clearance_error",
|
|
1897
1899
|
pcb_via_trace_clearance_error_id: `via_trace_clearance_${pairId}`,
|
|
1898
1900
|
error_type: "pcb_via_trace_clearance_error",
|
|
1899
|
-
message: `Via ${getReadableNameForElement8(circuitJson, via.pcb_via_id)} and trace ${getReadableNameForElement8(circuitJson, segment.pcb_trace_id)} are too close (clearance: ${formatMm(gap)}, minimum: ${formatMm(
|
|
1901
|
+
message: `Via ${getReadableNameForElement8(circuitJson, via.pcb_via_id)} and trace ${getReadableNameForElement8(circuitJson, segment.pcb_trace_id)} are too close (clearance: ${formatMm(gap)}, minimum: ${formatMm(minClearance)})`,
|
|
1900
1902
|
pcb_via_id: via.pcb_via_id,
|
|
1901
1903
|
pcb_trace_id: segment.pcb_trace_id,
|
|
1902
|
-
minimum_clearance:
|
|
1904
|
+
minimum_clearance: minClearance,
|
|
1903
1905
|
actual_clearance: gap,
|
|
1904
1906
|
center: {
|
|
1905
1907
|
x: (via.x + closestPoint.x) / 2,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../lib/add-start-and-end-port-ids-if-missing.ts","../lib/check-each-pcb-port-connected-to-pcb-trace.ts","../lib/util/get-readable-names.ts","../lib/check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping.ts","../lib/data-structures/SpatialIndex.ts","../lib/check-each-pcb-trace-non-overlapping/getCollidableBounds.ts","../lib/drc-defaults.ts","../lib/check-each-pcb-trace-non-overlapping/getPcbPortIdsConnectedToTraces.ts","../lib/check-each-pcb-trace-non-overlapping/getClosestPointBetweenSegments.ts","../lib/check-each-pcb-trace-non-overlapping/getRadiusOfCircuitJsonElement.ts","../lib/check-each-pcb-trace-non-overlapping/getClosestPointBetweenSegmentAndBounds.ts","../lib/util/getLayersOfPcbElement.ts","../lib/net-manager.ts","../lib/check-pcb-components-out-of-board/checkViasOffBoard.ts","../lib/check-pcb-components-out-of-board/checkPcbComponentsOutOfBoard.ts","../lib/check-same-net-via-spacing.ts","../lib/util/distance.ts","../lib/util/viasAreAtSameLocation.ts","../lib/check-different-net-via-spacing.ts","../lib/check-source-traces-match-pcb-trace-thickness.ts","../lib/check-source-traces-have-pcb-traces.ts","../lib/check-trace-out-of-board/checkTraceOutOfBoard.ts","../lib/check-pcb-components-overlap/checkPcbComponentOverlap.ts","../lib/check-pcb-components-overlap/doPcbElementsOverlap.ts","../lib/check-pad-pad-clearance.ts","../lib/check-pad-clearance/common.ts","../lib/check-pad-trace-clearance.ts","../lib/check-via-trace-clearance.ts","../lib/check-pin-must-be-connected.ts","../lib/check-all-pins-in-component-are-underspecified.ts","../lib/check-no-power-pin-defined.ts","../lib/check-no-ground-pin-defined.ts","../lib/check-connector-accessible-orientation.ts","../lib/check-courtyard-overlap/checkCourtyardOverlap.ts","../lib/check-traces-are-contiguous/check-traces-are-contiguous.ts","../lib/run-all-checks.ts"],"sourcesContent":["import type {\n PcbPort,\n PcbTrace,\n AnyCircuitElement,\n PcbSmtPad,\n} from \"circuit-json\"\n\nfunction distance(x1: number, y1: number, x2: number, y2: number): number {\n return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)\n}\n\n/**\n * HACK: this whole method and all usage of it is a hack because of this issue:\n * https://github.com/tscircuit/tscircuit/issues/291\n */\nexport const addStartAndEndPortIdsIfMissing = (\n soup: AnyCircuitElement[],\n): void => {\n const pcbPorts: PcbPort[] = soup.filter((item) => item.type === \"pcb_port\")\n const pcbSmtPads: PcbSmtPad[] = soup.filter(\n (item) => item.type === \"pcb_smtpad\",\n )\n const pcbTraces: PcbTrace[] = soup.filter((item) => item.type === \"pcb_trace\")\n\n function findPortIdOverlappingPoint(\n point: {\n x: number\n y: number\n },\n options: { isFirstOrLastPoint?: boolean; traceWidth?: number } = {},\n ): string | null {\n const traceWidth = options.traceWidth || 0\n const directPort = pcbPorts.find(\n (port) => distance(port.x, port.y, point.x, point.y) < 0.01,\n )\n if (directPort) return directPort.pcb_port_id\n\n // If it starts or ends inside an smtpad, we'll connect it to the por\n if (options.isFirstOrLastPoint) {\n const smtPad = pcbSmtPads.find((pad) => {\n if (pad.shape === \"rect\") {\n return (\n Math.abs(point.x - pad.x) < pad.width / 2 + traceWidth / 2 &&\n Math.abs(point.y - pad.y) < pad.height / 2 + traceWidth / 2\n )\n // biome-ignore lint/style/noUselessElse: <explanation>\n } else if (pad.shape === \"circle\") {\n return distance(point.x, point.y, pad.x, pad.y) < pad.radius\n }\n })\n if (smtPad) return smtPad.pcb_port_id ?? null\n }\n\n return null\n }\n\n // Add start_pcb_port_id and end_pcb_port_id if not present\n for (const trace of pcbTraces) {\n for (let index = 0; index < trace.route.length; index++) {\n const segment = trace.route[index]\n const isFirstOrLastPoint = index === 0 || index === trace.route.length - 1\n if (segment.route_type === \"wire\") {\n if (!segment.start_pcb_port_id && index === 0) {\n const startPortId = findPortIdOverlappingPoint(segment, {\n isFirstOrLastPoint,\n traceWidth: segment.width,\n })\n if (startPortId) {\n segment.start_pcb_port_id = startPortId\n }\n }\n if (!segment.end_pcb_port_id && index === trace.route.length - 1) {\n const endPortId = findPortIdOverlappingPoint(segment, {\n isFirstOrLastPoint,\n traceWidth: segment.width,\n })\n if (endPortId) {\n segment.end_pcb_port_id = endPortId\n }\n }\n }\n }\n }\n}\n","import type {\n PcbPort,\n SourceTrace,\n AnyCircuitElement,\n PcbPortNotConnectedError,\n} from \"circuit-json\"\nimport { addStartAndEndPortIdsIfMissing } from \"./add-start-and-end-port-ids-if-missing\"\nimport { getFullConnectivityMapFromCircuitJson } from \"circuit-json-to-connectivity-map\"\nimport { getReadableNameForPort } from \"./util/get-readable-names\"\n\nfunction checkEachPcbPortConnectedToPcbTraces(\n circuitJson: AnyCircuitElement[],\n): PcbPortNotConnectedError[] {\n addStartAndEndPortIdsIfMissing(circuitJson)\n const sourceTraces: SourceTrace[] = circuitJson.filter(\n (item) => item.type === \"source_trace\",\n ) as SourceTrace[]\n\n const pcbPorts: PcbPort[] = circuitJson.filter(\n (item) => item.type === \"pcb_port\",\n ) as PcbPort[]\n\n const errors: PcbPortNotConnectedError[] = []\n\n // Generate the connectivity map from the circuit\n const connectivityMap = getFullConnectivityMapFromCircuitJson(circuitJson)\n\n // Create a map from source_port_id to pcb_port for quick lookup\n const sourcePortToPcbPort = new Map<string, PcbPort>()\n for (const pcbPort of pcbPorts) {\n sourcePortToPcbPort.set(pcbPort.source_port_id, pcbPort)\n }\n\n // Process each source trace\n for (const sourceTrace of sourceTraces) {\n const connectedSourcePortIds = sourceTrace.connected_source_port_ids\n\n // Skip traces with less than 2 ports (nothing to connect)\n if (connectedSourcePortIds.length < 2) {\n continue\n }\n\n // Find corresponding PCB ports for all source ports in this trace\n const pcbPortsInTrace: PcbPort[] = []\n const missingPcbPorts: string[] = []\n\n for (const sourcePortId of connectedSourcePortIds) {\n const pcbPort = sourcePortToPcbPort.get(sourcePortId)\n if (pcbPort) {\n pcbPortsInTrace.push(pcbPort)\n } else {\n missingPcbPorts.push(sourcePortId)\n }\n }\n\n // Skip if we don't have at least 2 PCB ports to connect\n if (pcbPortsInTrace.length < 2) {\n continue\n }\n\n // Get the net ID for the first PCB port as reference\n const firstPcbPort = pcbPortsInTrace[0]\n const referenceNetId = connectivityMap.getNetConnectedToId(\n firstPcbPort.pcb_port_id,\n )\n\n const netElementIds = connectivityMap.getIdsConnectedToNet(referenceNetId!)\n const pcbTraceIds = netElementIds.filter((id) =>\n circuitJson.some(\n (element) =>\n element.type === \"pcb_trace\" &&\n ((\"pcb_trace_id\" in element && element.pcb_trace_id === id) ||\n (\"route_id\" in element && element.route_id === id)),\n ),\n )\n\n if (pcbTraceIds.length === 0) {\n // Check if this is a trivial case (only 2 ports on same component)\n const uniqueComponentIds = new Set(\n pcbPortsInTrace.map((p) => p.pcb_component_id),\n )\n\n if (uniqueComponentIds.size > 1) {\n // Ports are on different components but no PCB traces connect them\n errors.push({\n type: \"pcb_port_not_connected_error\",\n message: `Ports [${pcbPortsInTrace.map((p) => getReadableNameForPort(circuitJson, p.pcb_port_id)).join(\", \")}] are not connected together through the same net.`,\n error_type: \"pcb_port_not_connected_error\",\n pcb_port_ids: pcbPortsInTrace.map((p) => p.pcb_port_id),\n pcb_component_ids: pcbPortsInTrace\n .map((p) => p.pcb_component_id)\n .filter((id): id is string => id !== undefined),\n pcb_port_not_connected_error_id: `pcb_port_not_connected_error_trace_${sourceTrace.source_trace_id}`,\n })\n }\n }\n }\n\n return errors\n}\n\nexport { checkEachPcbPortConnectedToPcbTraces }\n","import type {\n AnyCircuitElement,\n PcbPlatedHole,\n PcbSmtPad,\n SourceTrace,\n} from \"circuit-json\"\nimport {\n getReadableNameForElement,\n getReadableNameForPcbPort,\n getBoundsOfPcbElements,\n} from \"@tscircuit/circuit-json-util\"\n\nconst CIRCUIT_JSON_ID_PATTERN =\n /\\b(?:pcb|source|schematic|subcircuit)_[a-z0-9_]+\\b/i\n\nconst sanitizeReadableName = (\n candidate: string | null | undefined,\n id: string,\n fallbackLabel: string,\n): string => {\n if (\n !candidate ||\n candidate === id ||\n CIRCUIT_JSON_ID_PATTERN.test(candidate)\n ) {\n return fallbackLabel\n }\n return candidate\n}\n\nconst firstReadableName = (\n candidates: Array<string | null | undefined>,\n id: string,\n): string => {\n for (const candidate of candidates) {\n const readableName = sanitizeReadableName(candidate, id, \"\")\n if (readableName) return readableName\n }\n return \"\"\n}\n\nexport const getReadableNameForComponent = (\n circuitJson: AnyCircuitElement[],\n pcbComponentId: string,\n): string =>\n sanitizeReadableName(\n getReadableNameForElement(circuitJson, pcbComponentId),\n pcbComponentId,\n \"component\",\n )\n\nexport const getReadableNameForPort = (\n circuitJson: AnyCircuitElement[],\n pcbPortId: string,\n): string => {\n const pcbPort = circuitJson.find(\n (element) =>\n element.type === \"pcb_port\" && element.pcb_port_id === pcbPortId,\n )\n\n if (pcbPort?.type === \"pcb_port\") {\n const sourcePort = circuitJson.find(\n (element) =>\n element.type === \"source_port\" &&\n element.source_port_id === pcbPort.source_port_id,\n )\n\n const sourceComponent =\n sourcePort?.type === \"source_port\"\n ? circuitJson.find(\n (element) =>\n element.type === \"source_component\" &&\n element.source_component_id === sourcePort.source_component_id,\n )\n : null\n\n const readableSourceComponentName = firstReadableName(\n [\n sourceComponent?.type === \"source_component\"\n ? sourceComponent.name\n : null,\n ],\n sourceComponent?.type === \"source_component\"\n ? sourceComponent.source_component_id\n : \"\",\n )\n\n const readableSourcePortName = firstReadableName(\n [\n sourcePort?.type === \"source_port\" ? sourcePort.name : null,\n sourcePort?.type === \"source_port\"\n ? sourcePort.pin_number?.toString()\n : null,\n sourcePort?.type === \"source_port\" ? sourcePort.port_hints?.[0] : null,\n ],\n sourcePort?.type === \"source_port\" ? sourcePort.source_port_id : \"\",\n )\n\n if (readableSourceComponentName && readableSourcePortName) {\n return `${readableSourceComponentName}.${readableSourcePortName}`\n }\n if (readableSourcePortName) {\n return readableSourcePortName\n }\n }\n\n return sanitizeReadableName(\n getReadableNameForPcbPort(circuitJson, pcbPortId) ??\n getReadableNameForElement(circuitJson, pcbPortId),\n pcbPortId,\n \"port\",\n )\n}\n\nexport const getReadableNameForSourceTrace = (\n circuitJson: AnyCircuitElement[],\n sourceTrace: SourceTrace,\n): string => {\n const displayName = sanitizeReadableName(\n sourceTrace.display_name,\n sourceTrace.source_trace_id,\n \"\",\n )\n if (displayName) return displayName\n\n const connectedPortNames = (sourceTrace.connected_source_port_ids ?? [])\n .map((sourcePortId) => {\n const pcbPort = circuitJson.find(\n (element) =>\n element.type === \"pcb_port\" &&\n element.source_port_id === sourcePortId,\n )\n\n if (pcbPort?.type === \"pcb_port\") {\n return getReadableNameForPort(circuitJson, pcbPort.pcb_port_id)\n }\n\n const sourcePort = circuitJson.find(\n (element) =>\n element.type === \"source_port\" &&\n element.source_port_id === sourcePortId,\n )\n if (sourcePort?.type !== \"source_port\") return null\n\n const sourceComponent = circuitJson.find(\n (element) =>\n element.type === \"source_component\" &&\n element.source_component_id === sourcePort.source_component_id,\n )\n\n const sourceComponentName =\n sourceComponent?.type === \"source_component\"\n ? sanitizeReadableName(\n sourceComponent.name,\n sourceComponent.source_component_id,\n \"\",\n )\n : \"\"\n\n const sourcePortName = firstReadableName(\n [\n sourcePort.name,\n sourcePort.pin_number?.toString(),\n sourcePort.port_hints?.[0],\n ],\n sourcePort.source_port_id,\n )\n\n if (sourceComponentName && sourcePortName) {\n return `${sourceComponentName}.${sourcePortName}`\n }\n\n return sourcePortName || null\n })\n .filter((name): name is string => Boolean(name))\n\n if (connectedPortNames.length >= 2) {\n return `${connectedPortNames[0]} to ${connectedPortNames[1]}`\n }\n\n if (connectedPortNames.length === 1) {\n return `trace connected to ${connectedPortNames[0]}`\n }\n\n return `trace ${sourceTrace.source_trace_id}`\n}\n\nexport const getReadableNameForElementId = (\n circuitJson: AnyCircuitElement[],\n elementId: string,\n): string =>\n sanitizeReadableName(\n getReadableNameForElement(circuitJson, elementId),\n elementId,\n \"element\",\n )\n\nexport const getReadableNameForGroup = (\n circuitJson: AnyCircuitElement[],\n groupId: string,\n): string =>\n sanitizeReadableName(\n getReadableNameForElement(circuitJson, groupId),\n groupId,\n \"group\",\n )\n\nexport const containsCircuitJsonId = (message: string): boolean =>\n CIRCUIT_JSON_ID_PATTERN.test(message)\n\nexport function getReadableNameForFootprintPad(\n circuitJson: AnyCircuitElement[],\n pad: PcbSmtPad | PcbPlatedHole,\n ordinal: number,\n): string {\n const padKind = pad.type === \"pcb_smtpad\" ? \"SMD pad\" : \"through-hole pad\"\n const portRef = pad.pcb_port_id\n ? getReadableNameForPort(circuitJson, pad.pcb_port_id)\n : null\n const bounds = getBoundsOfPcbElements([pad])\n const centerX = (bounds.minX + bounds.maxX) / 2\n const centerY = (bounds.minY + bounds.maxY) / 2\n const location = `(${centerX.toFixed(2)}mm, ${centerY.toFixed(2)}mm)`\n\n if (portRef) return `${padKind} ${portRef} at ${location}`\n return `${padKind} #${ordinal + 1} at ${location}`\n}\n","import type { AnyCircuitElement, PcbTraceError } from \"circuit-json\"\nimport { getReadableNameForElement, cju } from \"@tscircuit/circuit-json-util\"\nimport {\n SpatialObjectIndex,\n type Bounds,\n} from \"lib/data-structures/SpatialIndex\"\nimport {\n getFullConnectivityMapFromCircuitJson,\n type ConnectivityMap,\n} from \"circuit-json-to-connectivity-map\"\nimport {\n getCollidableBounds,\n type Collidable,\n type PcbTraceSegment,\n} from \"./getCollidableBounds\"\nimport {\n segmentToBoundsMinDistance,\n segmentToCircleMinDistance,\n} from \"@tscircuit/math-utils\"\nimport {\n DEFAULT_TRACE_MARGIN,\n DEFAULT_TRACE_THICKNESS,\n EPSILON,\n} from \"lib/drc-defaults\"\nimport { getPcbPortIdsConnectedToTraces } from \"./getPcbPortIdsConnectedToTraces\"\nimport { segmentToSegmentMinDistance } from \"@tscircuit/math-utils\"\nimport { areBoundsOverlapping } from \"./areBoundsOverlapping\"\nimport { getPrimaryId } from \"@tscircuit/circuit-json-util\"\nimport { getCenterOfBoundsPair } from \"./getCenterOfBoundsPair\"\nimport { getClosestPointBetweenSegments } from \"./getClosestPointBetweenSegments\"\nimport { getCenterOfBounds } from \"./getCenterOfBounds\"\nimport { getRadiusOfCircuitJsonElement } from \"./getRadiusOfCircuitJsonElement\"\nimport { getClosestPointBetweenSegmentAndBounds } from \"./getClosestPointBetweenSegmentAndBounds\"\nimport { getLayersOfPcbElement } from \"../util/getLayersOfPcbElement\"\nimport { addStartAndEndPortIdsIfMissing } from \"lib/add-start-and-end-port-ids-if-missing\"\n\nexport function checkEachPcbTraceNonOverlapping(\n circuitJson: AnyCircuitElement[],\n {\n connMap,\n minSpacing = DEFAULT_TRACE_MARGIN,\n }: {\n connMap?: ConnectivityMap\n minSpacing?: number\n } = {},\n): PcbTraceError[] {\n const errors: PcbTraceError[] = []\n addStartAndEndPortIdsIfMissing(circuitJson)\n connMap ??= getFullConnectivityMapFromCircuitJson(circuitJson)\n\n const pcbTraces = cju(circuitJson).pcb_trace.list()\n const pcbTraceSegments = pcbTraces.flatMap((pcbTrace) => {\n const segments: PcbTraceSegment[] = []\n for (let i = 0; i < pcbTrace.route.length - 1; i++) {\n const p1 = pcbTrace.route[i]\n const p2 = pcbTrace.route[i + 1]\n if (p1.route_type !== \"wire\") continue\n if (p2.route_type !== \"wire\") continue\n if (p1.layer !== p2.layer) continue\n segments.push({\n type: \"pcb_trace_segment\",\n pcb_trace_id: pcbTrace.pcb_trace_id,\n _pcbTrace: pcbTrace,\n thickness:\n \"width\" in p1\n ? p1.width\n : \"width\" in p2\n ? p2.width\n : DEFAULT_TRACE_THICKNESS,\n layer: p1.layer,\n x1: p1.x,\n y1: p1.y,\n x2: p2.x,\n y2: p2.y,\n } as PcbTraceSegment)\n }\n return segments\n })\n const pcbSmtPads = cju(circuitJson).pcb_smtpad.list()\n const pcbPlatedHoles = cju(circuitJson).pcb_plated_hole.list()\n const pcbHoles = cju(circuitJson).pcb_hole.list()\n const pcbVias = cju(circuitJson).pcb_via.list()\n const pcbKeepouts = cju(circuitJson).pcb_keepout.list()\n\n const allObjects: Collidable[] = [\n ...pcbTraceSegments,\n ...pcbSmtPads,\n ...pcbPlatedHoles,\n ...pcbHoles,\n ...pcbVias,\n ...pcbKeepouts,\n ]\n\n const spatialIndex = new SpatialObjectIndex<Collidable>({\n objects: allObjects,\n getBounds: getCollidableBounds,\n })\n\n const getReadableName = (id: string) =>\n getReadableNameForElement(circuitJson, id)\n\n const errorIds = new Set<string>()\n\n // For each segment, check it if overlaps with anything collidable\n for (const segmentA of pcbTraceSegments) {\n const requiredMargin = minSpacing\n const bounds = getCollidableBounds(segmentA)\n const nearbyObjects = spatialIndex.getObjectsInBounds(\n bounds,\n requiredMargin + segmentA.thickness / 2,\n )\n if (segmentA.x1 === segmentA.x2 && segmentA.y1 === segmentA.y2) continue\n\n for (const obj of nearbyObjects) {\n // ignore obstacles not on the trace's layer (except vias)\n if (!getLayersOfPcbElement(obj).includes(segmentA.layer)) {\n continue\n }\n if (obj.type === \"pcb_trace_segment\") {\n const segmentB = obj\n\n if (segmentA.layer !== segmentB.layer) continue\n\n // Check if the segments are overlapping\n if (\n connMap.areIdsConnected(segmentA.pcb_trace_id, segmentB.pcb_trace_id)\n )\n continue\n\n const gap =\n segmentToSegmentMinDistance(\n { x: segmentA.x1, y: segmentA.y1 },\n { x: segmentA.x2, y: segmentA.y2 },\n { x: segmentB.x1, y: segmentB.y1 },\n { x: segmentB.x2, y: segmentB.y2 },\n ) -\n segmentA.thickness / 2 -\n segmentB.thickness / 2\n if (gap > minSpacing - EPSILON) continue\n\n const pcb_trace_error_id = `overlap_${segmentA.pcb_trace_id}_${segmentB.pcb_trace_id}`\n const pcb_trace_error_id_reverse = `overlap_${segmentB.pcb_trace_id}_${segmentA.pcb_trace_id}`\n if (errorIds.has(pcb_trace_error_id)) continue\n if (errorIds.has(pcb_trace_error_id_reverse)) continue\n\n errorIds.add(pcb_trace_error_id)\n errors.push({\n type: \"pcb_trace_error\",\n error_type: \"pcb_trace_error\",\n message: `PCB trace ${getReadableName(segmentA.pcb_trace_id)} overlaps with ${getReadableName(segmentB.pcb_trace_id)} ${gap < 0 ? \"(accidental contact)\" : `(gap: ${gap.toFixed(3)}mm)`}`,\n pcb_trace_id: segmentA.pcb_trace_id,\n source_trace_id: \"\",\n pcb_trace_error_id,\n pcb_component_ids: [],\n center: getClosestPointBetweenSegments(segmentA, segmentB),\n pcb_port_ids: getPcbPortIdsConnectedToTraces([\n segmentA._pcbTrace,\n segmentB._pcbTrace,\n ]),\n })\n continue\n }\n\n const primaryObjId = getPrimaryId(obj as any)\n if (\n connMap.areIdsConnected(\n segmentA.pcb_trace_id,\n \"pcb_trace_id\" in obj ? (obj.pcb_trace_id as string) : primaryObjId,\n )\n )\n continue\n\n const isCircular =\n obj.type === \"pcb_via\" ||\n (obj.type === \"pcb_plated_hole\" && obj.shape === \"circle\") ||\n obj.type === \"pcb_hole\" ||\n (obj.type === \"pcb_smtpad\" && obj.shape === \"circle\")\n\n if (isCircular) {\n const radius = getRadiusOfCircuitJsonElement(obj)\n const distance = segmentToCircleMinDistance(\n { x: segmentA.x1, y: segmentA.y1 },\n { x: segmentA.x2, y: segmentA.y2 },\n { x: obj.x, y: obj.y, radius },\n )\n const gap = distance - segmentA.thickness / 2\n if (gap > minSpacing - EPSILON) continue\n\n const pcb_trace_error_id = `overlap_${segmentA.pcb_trace_id}_${primaryObjId}`\n if (errorIds.has(pcb_trace_error_id)) continue\n errorIds.add(pcb_trace_error_id)\n errors.push({\n type: \"pcb_trace_error\",\n error_type: \"pcb_trace_error\",\n message: `PCB trace ${getReadableName(segmentA.pcb_trace_id)} overlaps with ${obj.type} \"${getReadableName(getPrimaryId(obj as any))}\" ${gap < 0 ? \"(accidental contact)\" : `(gap: ${gap.toFixed(3)}mm)`}`,\n pcb_trace_id: segmentA.pcb_trace_id,\n center: getClosestPointBetweenSegmentAndBounds(\n segmentA,\n getCollidableBounds(obj),\n ),\n source_trace_id: \"\",\n pcb_trace_error_id,\n pcb_component_ids: [\n \"pcb_component_id\" in obj\n ? (obj.pcb_component_id as string)\n : undefined,\n ].filter(Boolean) as string[],\n pcb_port_ids: [\n ...getPcbPortIdsConnectedToTraces([segmentA._pcbTrace]),\n \"pcb_port_id\" in obj ? obj.pcb_port_id : undefined,\n ].filter(Boolean) as string[],\n })\n }\n\n // Handle generic case of hitting the bounds of any collidable obstacle\n // using the bounds of the collidable obstacle\n const gap =\n segmentToBoundsMinDistance(\n { x: segmentA.x1, y: segmentA.y1 },\n { x: segmentA.x2, y: segmentA.y2 },\n getCollidableBounds(obj),\n ) -\n segmentA.thickness / 2\n if (gap + EPSILON < requiredMargin) {\n const pcb_trace_error_id = `overlap_${segmentA.pcb_trace_id}_${primaryObjId}`\n if (errorIds.has(pcb_trace_error_id)) continue\n errorIds.add(pcb_trace_error_id)\n errors.push({\n type: \"pcb_trace_error\",\n error_type: \"pcb_trace_error\",\n message: `PCB trace ${getReadableName(segmentA.pcb_trace_id)} overlaps with ${obj.type} \"${getReadableName(getPrimaryId(obj as any))}\" ${gap < 0 ? \"(accidental contact)\" : `(gap: ${gap.toFixed(3)}mm)`}`,\n pcb_trace_id: segmentA.pcb_trace_id,\n source_trace_id: \"\",\n pcb_trace_error_id,\n pcb_component_ids: [\n \"pcb_component_id\" in obj ? obj.pcb_component_id : undefined,\n ].filter(Boolean) as string[],\n center: getClosestPointBetweenSegmentAndBounds(\n segmentA,\n getCollidableBounds(obj),\n ),\n pcb_port_ids: [\n ...getPcbPortIdsConnectedToTraces([segmentA._pcbTrace]),\n \"pcb_port_id\" in obj ? obj.pcb_port_id : undefined,\n ].filter(Boolean) as string[],\n })\n }\n }\n }\n return errors\n}\n","export type BucketCoordinate = `${number}x${number}`\n\nexport interface Bounds {\n minX: number\n minY: number\n maxX: number\n maxY: number\n}\n\nexport type GetBoundsFn<T> = (obj: T) => Bounds\nexport type GetIdFn<T> = (obj: T) => string\n\nexport class SpatialObjectIndex<T> {\n buckets: Map<BucketCoordinate, Array<T & { spatialIndexId: string }>>\n objectsById: Map<string, T & { spatialIndexId: string }>\n getBounds: GetBoundsFn<T>\n getId: GetIdFn<T>\n CELL_SIZE = 0.4\n\n constructor({\n objects,\n getBounds,\n getId,\n CELL_SIZE,\n }: {\n objects: T[]\n getBounds: GetBoundsFn<T>\n getId?: GetIdFn<T>\n CELL_SIZE?: number\n }) {\n this.buckets = new Map()\n this.objectsById = new Map()\n this.getBounds = getBounds\n this.getId = getId ?? (() => this._getNextId())\n this.CELL_SIZE = CELL_SIZE ?? this.CELL_SIZE\n\n for (const obj of objects) {\n this.addObject(obj)\n }\n }\n\n _idCounter = 0\n _getNextId(): string {\n return `${this._idCounter++}`\n }\n\n addObject(obj: T): void {\n const bounds = this.getBounds(obj)\n const spatialIndexId = this.getId(obj)\n const objWithId = { ...obj, spatialIndexId } as T & {\n spatialIndexId: string\n }\n\n // Store in objectsById for quick lookup\n this.objectsById.set(spatialIndexId, objWithId)\n\n // Calculate the bucket coordinates that cover the object's bounds\n const minBucketX = Math.floor(bounds.minX / this.CELL_SIZE)\n const minBucketY = Math.floor(bounds.minY / this.CELL_SIZE)\n const maxBucketX = Math.floor(bounds.maxX / this.CELL_SIZE)\n const maxBucketY = Math.floor(bounds.maxY / this.CELL_SIZE)\n\n // Add the object to all buckets it intersects with\n for (let bx = minBucketX; bx <= maxBucketX; bx++) {\n for (let by = minBucketY; by <= maxBucketY; by++) {\n const bucketKey = `${bx}x${by}` as BucketCoordinate\n const bucket = this.buckets.get(bucketKey)\n if (!bucket) {\n this.buckets.set(bucketKey, [objWithId])\n } else {\n bucket.push(objWithId)\n }\n }\n }\n }\n\n removeObject(id: string): boolean {\n const obj = this.objectsById.get(id)\n if (!obj) return false\n\n // Remove from objectsById\n this.objectsById.delete(id)\n\n // Calculate the bucket coordinates that cover the object's bounds\n const bounds = this.getBounds(obj)\n const minBucketX = Math.floor(bounds.minX / this.CELL_SIZE)\n const minBucketY = Math.floor(bounds.minY / this.CELL_SIZE)\n const maxBucketX = Math.floor(bounds.maxX / this.CELL_SIZE)\n const maxBucketY = Math.floor(bounds.maxY / this.CELL_SIZE)\n\n // Remove the object from all buckets it might be in\n for (let bx = minBucketX; bx <= maxBucketX; bx++) {\n for (let by = minBucketY; by <= maxBucketY; by++) {\n const bucketKey = `${bx}x${by}` as BucketCoordinate\n const bucket = this.buckets.get(bucketKey)\n if (bucket) {\n const index = bucket.findIndex((item) => item.spatialIndexId === id)\n if (index !== -1) {\n bucket.splice(index, 1)\n if (bucket.length === 0) {\n this.buckets.delete(bucketKey)\n }\n }\n }\n }\n }\n\n return true\n }\n\n getBucketKey(x: number, y: number): BucketCoordinate {\n return `${Math.floor(x / this.CELL_SIZE)}x${Math.floor(y / this.CELL_SIZE)}`\n }\n\n getObjectsInBounds(bounds: Bounds, margin = 0): T[] {\n const objects: T[] = []\n const addedIds = new Set<string>()\n\n // Calculate the bucket coordinates that cover the requested bounds with margin\n const minBucketX = Math.floor((bounds.minX - margin) / this.CELL_SIZE)\n const minBucketY = Math.floor((bounds.minY - margin) / this.CELL_SIZE)\n const maxBucketX = Math.floor((bounds.maxX + margin) / this.CELL_SIZE)\n const maxBucketY = Math.floor((bounds.maxY + margin) / this.CELL_SIZE)\n\n // Collect objects from all buckets that intersect the bounds\n for (let bx = minBucketX; bx <= maxBucketX; bx++) {\n for (let by = minBucketY; by <= maxBucketY; by++) {\n const bucketKey = `${bx}x${by}` as BucketCoordinate\n const bucket = this.buckets.get(bucketKey) || []\n\n for (const obj of bucket) {\n const id = obj.spatialIndexId\n if (addedIds.has(id)) continue\n\n addedIds.add(id)\n objects.push(obj)\n }\n }\n }\n\n return objects\n }\n}\n","import { getBoundsOfPcbElements } from \"@tscircuit/circuit-json-util\"\nimport type {\n AnyCircuitElement,\n PcbHole,\n PCBKeepout,\n PcbPlatedHole,\n PcbSmtPad,\n PcbTrace,\n PcbTraceError,\n PcbVia,\n} from \"circuit-json\"\n\ninterface Bounds {\n minX: number\n minY: number\n maxX: number\n maxY: number\n}\n\nexport type PcbTraceSegment = {\n type: \"pcb_trace_segment\"\n _pcbTrace: PcbTrace\n pcb_trace_id: string\n thickness: number\n layer: string\n x1: number\n y1: number\n x2: number\n y2: number\n}\n\nexport type Collidable =\n | PcbTraceSegment\n | PcbSmtPad\n | PcbPlatedHole\n | PcbHole\n | PcbVia\n | PCBKeepout\n\nexport const getCollidableBounds = (collidable: Collidable): Bounds => {\n if (collidable.type === \"pcb_trace_segment\") {\n return {\n minX: Math.min(collidable.x1, collidable.x2),\n minY: Math.min(collidable.y1, collidable.y2),\n maxX: Math.max(collidable.x1, collidable.x2),\n maxY: Math.max(collidable.y1, collidable.y2),\n }\n }\n return getBoundsOfPcbElements([collidable as any])\n}\n","export const DEFAULT_TRACE_MARGIN = 0.1\nexport const DEFAULT_TRACE_THICKNESS = 0.15\nexport const DEFAULT_VIA_DIAMETER = 0.6\nexport const DEFAULT_VIA_BOARD_MARGIN = 0.3\n\nexport const DEFAULT_SAME_NET_VIA_MARGIN = 0.2\nexport const DEFAULT_DIFFERENT_NET_VIA_MARGIN = 0.3\n\n// jlcpcb default values (Might not pass kicad drc so change the project settings in kicad)\nexport const DEFAULT_PAD_PAD_CLEARANCE = 0.1\nexport const DEFAULT_PAD_TRACE_CLEARANCE = 0.1\n\nexport const EPSILON = 0.005\n","import type { PcbTrace } from \"circuit-json\"\n\nexport function getPcbPortIdsConnectedToTrace(trace: PcbTrace) {\n const connectedPcbPorts = new Set<string>()\n for (const segment of trace.route) {\n if (segment.route_type === \"wire\") {\n if (segment.start_pcb_port_id)\n connectedPcbPorts.add(segment.start_pcb_port_id)\n if (segment.end_pcb_port_id)\n connectedPcbPorts.add(segment.end_pcb_port_id)\n }\n }\n\n return Array.from(connectedPcbPorts)\n}\n\nexport function getPcbPortIdsConnectedToTraces(traces: PcbTrace[]) {\n const connectedPorts = new Set<string>()\n for (const trace of traces) {\n for (const portId of getPcbPortIdsConnectedToTrace(trace)) {\n connectedPorts.add(portId)\n }\n }\n return Array.from(connectedPorts)\n}\n","import type { PcbTraceSegment } from \"./getCollidableBounds\"\n\nexport const getClosestPointBetweenSegments = (\n segmentA: PcbTraceSegment,\n segmentB: PcbTraceSegment,\n): { x: number; y: number } => {\n // Define points for each segment\n const a1 = { x: segmentA.x1, y: segmentA.y1 }\n const a2 = { x: segmentA.x2, y: segmentA.y2 }\n const b1 = { x: segmentB.x1, y: segmentB.y1 }\n const b2 = { x: segmentB.x2, y: segmentB.y2 }\n\n // Calculate direction vectors for each segment\n const va = { x: a2.x - a1.x, y: a2.y - a1.y }\n const vb = { x: b2.x - b1.x, y: b2.y - b1.y }\n\n // Calculate squared lengths of segments\n const lenSqrA = va.x * va.x + va.y * va.y\n const lenSqrB = vb.x * vb.x + vb.y * vb.y\n\n // If either segment is a point (zero length), handle as special case\n if (lenSqrA === 0 || lenSqrB === 0) {\n if (lenSqrA === 0 && lenSqrB === 0) {\n // Both segments are points, return the distance between them\n // Calculate the average point\n return {\n x: (a1.x + b1.x) / 2,\n y: (a1.y + b1.y) / 2,\n }\n }\n if (lenSqrA === 0) {\n // First segment is a point, find closest point on second segment\n const t = clamp(\n ((a1.x - b1.x) * vb.x + (a1.y - b1.y) * vb.y) / lenSqrB,\n 0,\n 1,\n )\n const closestOnB = {\n x: b1.x + t * vb.x,\n y: b1.y + t * vb.y,\n }\n // Calculate the average point\n return {\n x: (a1.x + closestOnB.x) / 2,\n y: (a1.y + closestOnB.y) / 2,\n }\n }\n // Second segment is a point, find closest point on first segment\n const t = clamp(\n ((b1.x - a1.x) * va.x + (b1.y - a1.y) * va.y) / lenSqrA,\n 0,\n 1,\n )\n const closestOnA = {\n x: a1.x + t * va.x,\n y: a1.y + t * va.y,\n }\n // Calculate the average point\n return {\n x: (closestOnA.x + b1.x) / 2,\n y: (closestOnA.y + b1.y) / 2,\n }\n }\n\n // Vector between segment starting points\n const w = { x: a1.x - b1.x, y: a1.y - b1.y }\n\n // Calculate dot products\n const dotAA = va.x * va.x + va.y * va.y\n const dotAB = va.x * vb.x + va.y * vb.y\n const dotAW = va.x * w.x + va.y * w.y\n const dotBB = vb.x * vb.x + vb.y * vb.y\n const dotBW = vb.x * w.x + vb.y * w.y\n\n // Calculate parametric positions (t values) along each segment\n const denominator = dotAA * dotBB - dotAB * dotAB\n\n // If segments are parallel, handle separately\n if (denominator < 1e-10) {\n return closestPointsParallelSegments(\n a1,\n a2,\n b1,\n b2,\n va,\n vb,\n lenSqrA,\n lenSqrB,\n )\n }\n\n // Calculate parameters for closest points\n let tA = (dotAB * dotBW - dotBB * dotAW) / denominator\n let tB = (dotAA * dotBW - dotAB * dotAW) / denominator\n\n // Clamp parameters to segment bounds\n tA = clamp(tA, 0, 1)\n tB = clamp(tB, 0, 1)\n\n // Recalculate tB if tA was clamped\n tB = (tA * dotAB + dotBW) / dotBB\n tB = clamp(tB, 0, 1)\n\n // Recalculate tA if tB was clamped\n tA = (tB * dotAB - dotAW) / dotAA\n tA = clamp(tA, 0, 1)\n\n // Calculate closest points on each segment\n const closestOnA = {\n x: a1.x + tA * va.x,\n y: a1.y + tA * va.y,\n }\n\n const closestOnB = {\n x: b1.x + tB * vb.x,\n y: b1.y + tB * vb.y,\n }\n\n // Calculate distance between closest points\n const dx = closestOnA.x - closestOnB.x\n const dy = closestOnA.y - closestOnB.y\n const distance = Math.sqrt(dx * dx + dy * dy)\n\n // Calculate the average of the closest points\n const averagePoint = {\n x: (closestOnA.x + closestOnB.x) / 2,\n y: (closestOnA.y + closestOnB.y) / 2,\n }\n\n return averagePoint\n}\n\n// Helper function for handling parallel segments\nconst closestPointsParallelSegments = (\n a1: { x: number; y: number },\n a2: { x: number; y: number },\n b1: { x: number; y: number },\n b2: { x: number; y: number },\n va: { x: number; y: number },\n vb: { x: number; y: number },\n lenSqrA: number,\n lenSqrB: number,\n) => {\n // Project b1 onto segment A\n let tA = ((b1.x - a1.x) * va.x + (b1.y - a1.y) * va.y) / lenSqrA\n tA = clamp(tA, 0, 1)\n const pointOnA1 = { x: a1.x + tA * va.x, y: a1.y + tA * va.y }\n\n // Project b2 onto segment A\n let tA2 = ((b2.x - a1.x) * va.x + (b2.y - a1.y) * va.y) / lenSqrA\n tA2 = clamp(tA2, 0, 1)\n const pointOnA2 = { x: a1.x + tA2 * va.x, y: a1.y + tA2 * va.y }\n\n // Project a1 onto segment B\n let tB = ((a1.x - b1.x) * vb.x + (a1.y - b1.y) * vb.y) / lenSqrB\n tB = clamp(tB, 0, 1)\n const pointOnB1 = { x: b1.x + tB * vb.x, y: b1.y + tB * vb.y }\n\n // Project a2 onto segment B\n let tB2 = ((a2.x - b1.x) * vb.x + (a2.y - b1.y) * vb.y) / lenSqrB\n tB2 = clamp(tB2, 0, 1)\n const pointOnB2 = { x: b1.x + tB2 * vb.x, y: b1.y + tB2 * vb.y }\n\n // Calculate all possible distances between end points and their projections\n const distances = [\n {\n pointA: pointOnA1,\n pointB: b1,\n distance: Math.sqrt(\n (pointOnA1.x - b1.x) ** 2 + (pointOnA1.y - b1.y) ** 2,\n ),\n },\n {\n pointA: pointOnA2,\n pointB: b2,\n distance: Math.sqrt(\n (pointOnA2.x - b2.x) ** 2 + (pointOnA2.y - b2.y) ** 2,\n ),\n },\n {\n pointA: a1,\n pointB: pointOnB1,\n distance: Math.sqrt(\n (a1.x - pointOnB1.x) ** 2 + (a1.y - pointOnB1.y) ** 2,\n ),\n },\n {\n pointA: a2,\n pointB: pointOnB2,\n distance: Math.sqrt(\n (a2.x - pointOnB2.x) ** 2 + (a2.y - pointOnB2.y) ** 2,\n ),\n },\n ]\n\n // Find closest pair\n const closestPair = distances.reduce((closest, current) =>\n current.distance < closest.distance ? current : closest,\n )\n\n // Calculate the average of the closest points\n return {\n x: (closestPair.pointA.x + closestPair.pointB.x) / 2,\n y: (closestPair.pointA.y + closestPair.pointB.y) / 2,\n }\n}\n\n// Helper function to clamp a value between min and max\nconst clamp = (value: number, min: number, max: number): number => {\n return Math.max(min, Math.min(max, value))\n}\n","import type {\n AnyCircuitElement,\n PcbHole,\n PcbPlatedHole,\n PcbVia,\n PcbSmtPad,\n} from \"circuit-json\"\n\nexport const getRadiusOfCircuitJsonElement = (\n obj: PcbVia | PcbPlatedHole | PcbHole | PcbSmtPad,\n) => {\n if (obj.type === \"pcb_via\") {\n return obj.outer_diameter / 2\n }\n if (obj.type === \"pcb_plated_hole\" && obj.shape === \"circle\") {\n return obj.outer_diameter / 2\n }\n if (obj.type === \"pcb_hole\" && obj.hole_shape === \"circle\") {\n return obj.hole_diameter / 2\n }\n if (obj.type === \"pcb_smtpad\" && obj.shape === \"circle\") {\n return obj.radius\n }\n throw new Error(\n `Could not determine radius of element: ${JSON.stringify(obj)}`,\n )\n}\n","import type { Bounds } from \"lib/data-structures/SpatialIndex\"\nimport type { PcbTraceSegment } from \"./getCollidableBounds\"\n\nexport const getClosestPointBetweenSegmentAndBounds = (\n segment: PcbTraceSegment,\n bounds: Bounds,\n): { x: number; y: number } => {\n // Define segment points\n const p1 = { x: segment.x1, y: segment.y1 }\n const p2 = { x: segment.x2, y: segment.y2 }\n\n // Define bounds corners\n const minX = bounds.minX\n const minY = bounds.minY\n const maxX = bounds.maxX\n const maxY = bounds.maxY\n\n // Check if segment is a point\n if (p1.x === p2.x && p1.y === p2.y) {\n // For a point, find the closest point on the bounds\n const closestX = Math.max(minX, Math.min(maxX, p1.x))\n const closestY = Math.max(minY, Math.min(maxY, p1.y))\n\n // If the point is inside the bounds, return the point itself\n if (closestX === p1.x && closestY === p1.y) {\n return { x: p1.x, y: p1.y }\n }\n\n // Otherwise, return the closest point on the bounds\n return { x: closestX, y: closestY }\n }\n\n // Calculate direction vector of the segment\n const dx = p2.x - p1.x\n const dy = p2.y - p1.y\n\n // Calculate parameter values for intersection with each boundary\n const tMinX = dx !== 0 ? (minX - p1.x) / dx : Number.NEGATIVE_INFINITY\n const tMaxX = dx !== 0 ? (maxX - p1.x) / dx : Number.POSITIVE_INFINITY\n const tMinY = dy !== 0 ? (minY - p1.y) / dy : Number.NEGATIVE_INFINITY\n const tMaxY = dy !== 0 ? (maxY - p1.y) / dy : Number.POSITIVE_INFINITY\n\n // Find the entering and exiting parameters\n const tEnter = Math.max(Math.min(tMinX, tMaxX), Math.min(tMinY, tMaxY))\n const tExit = Math.min(Math.max(tMinX, tMaxX), Math.max(tMinY, tMaxY))\n\n // Check if segment intersects the bounds\n if (tEnter <= tExit && tExit >= 0 && tEnter <= 1) {\n // Segment intersects bounds, clamp parameter to segment\n const t = Math.max(0, Math.min(1, tEnter))\n return {\n x: p1.x + t * dx,\n y: p1.y + t * dy,\n }\n }\n\n // Segment doesn't intersect bounds, find closest point\n // Check each endpoint of the segment against the bounds\n const closestToP1 = {\n x: Math.max(minX, Math.min(maxX, p1.x)),\n y: Math.max(minY, Math.min(maxY, p1.y)),\n }\n\n const closestToP2 = {\n x: Math.max(minX, Math.min(maxX, p2.x)),\n y: Math.max(minY, Math.min(maxY, p2.y)),\n }\n\n // Calculate distances\n const distToP1Squared =\n (closestToP1.x - p1.x) ** 2 + (closestToP1.y - p1.y) ** 2\n const distToP2Squared =\n (closestToP2.x - p2.x) ** 2 + (closestToP2.y - p2.y) ** 2\n\n // Check each edge of the bounds against the segment\n const edges = [\n { start: { x: minX, y: minY }, end: { x: maxX, y: minY } }, // Bottom edge\n { start: { x: maxX, y: minY }, end: { x: maxX, y: maxY } }, // Right edge\n { start: { x: maxX, y: maxY }, end: { x: minX, y: maxY } }, // Top edge\n { start: { x: minX, y: maxY }, end: { x: minX, y: minY } }, // Left edge\n ]\n\n let minDistance = Math.min(distToP1Squared, distToP2Squared)\n let closestPoint =\n distToP1Squared <= distToP2Squared ? closestToP1 : closestToP2\n\n // Helper function to clamp a value between min and max\n const clamp = (value: number, min: number, max: number): number => {\n return Math.max(min, Math.min(max, value))\n }\n\n // Check each edge\n for (const edge of edges) {\n // Calculate direction vectors\n const va = { x: p2.x - p1.x, y: p2.y - p1.y }\n const vb = { x: edge.end.x - edge.start.x, y: edge.end.y - edge.start.y }\n const w = { x: p1.x - edge.start.x, y: p1.y - edge.start.y }\n\n // Calculate dot products\n const dotAA = va.x * va.x + va.y * va.y\n const dotAB = va.x * vb.x + va.y * vb.y\n const dotAW = va.x * w.x + va.y * w.y\n const dotBB = vb.x * vb.x + vb.y * vb.y\n const dotBW = vb.x * w.x + vb.y * w.y\n\n // Calculate parameters for closest points\n const denominator = dotAA * dotBB - dotAB * dotAB\n\n // Skip if lines are parallel\n if (Math.abs(denominator) < 1e-10) continue\n\n let tA = (dotAB * dotBW - dotBB * dotAW) / denominator\n let tB = (dotAA * dotBW - dotAB * dotAW) / denominator\n\n // Clamp parameters to segment bounds\n tA = clamp(tA, 0, 1)\n tB = clamp(tB, 0, 1)\n\n // Calculate closest points\n const closestOnSegment = {\n x: p1.x + tA * va.x,\n y: p1.y + tA * va.y,\n }\n\n const closestOnEdge = {\n x: edge.start.x + tB * vb.x,\n y: edge.start.y + tB * vb.y,\n }\n\n // Calculate distance\n const dx = closestOnSegment.x - closestOnEdge.x\n const dy = closestOnSegment.y - closestOnEdge.y\n const distSquared = dx * dx + dy * dy\n\n // Update if this is closer\n if (distSquared < minDistance) {\n minDistance = distSquared\n closestPoint = {\n x: (closestOnSegment.x + closestOnEdge.x) / 2,\n y: (closestOnSegment.y + closestOnEdge.y) / 2,\n }\n }\n }\n\n return closestPoint\n}\n","import type { Collidable } from \"lib/check-each-pcb-trace-non-overlapping/getCollidableBounds\"\nimport { all_layers } from \"circuit-json\"\n\nexport function getLayersOfPcbElement(obj: Collidable): string[] {\n if (obj.type === \"pcb_trace_segment\") {\n return [obj.layer]\n }\n if (obj.type === \"pcb_smtpad\") {\n return [obj.layer]\n }\n if (obj.type === \"pcb_plated_hole\") {\n return Array.isArray(obj.layers) ? obj.layers : [...all_layers]\n }\n if (obj.type === \"pcb_hole\") {\n return [...all_layers]\n }\n if (obj.type === \"pcb_via\") {\n return Array.isArray(obj.layers) ? obj.layers : [...all_layers]\n }\n if (obj.type === \"pcb_keepout\") {\n return Array.isArray(obj.layers) ? obj.layers : []\n }\n return []\n}\n","export class NetManager {\n private networks: Set<Set<string>> = new Set()\n\n setConnected(nodes: string[]): void {\n if (nodes.length < 2) return\n\n let targetNetwork: Set<string> | null = null\n\n // Check if any of the nodes are already in a network\n for (const network of this.networks) {\n for (const node of nodes) {\n if (network.has(node)) {\n if (targetNetwork === null) {\n targetNetwork = network\n } else if (targetNetwork !== network) {\n // Merge networks\n for (const mergeNode of network) {\n targetNetwork.add(mergeNode)\n }\n this.networks.delete(network)\n }\n break\n }\n }\n if (targetNetwork !== null && targetNetwork !== network) break\n }\n\n // If no existing network found, create a new one\n if (targetNetwork === null) {\n targetNetwork = new Set(nodes)\n this.networks.add(targetNetwork)\n } else {\n // Add all nodes to the target network\n for (const node of nodes) {\n targetNetwork.add(node)\n }\n }\n }\n\n isConnected(nodes: string[]): boolean {\n if (nodes.length < 2) return true\n\n for (const network of this.networks) {\n if (nodes.every((node) => network.has(node))) {\n return true\n }\n }\n\n return false\n }\n}\n","import type {\n AnyCircuitElement,\n PcbBoard,\n PcbVia,\n PcbPlacementError,\n} from \"circuit-json\"\nimport { getReadableNameForElement } from \"@tscircuit/circuit-json-util\"\nimport { DEFAULT_VIA_BOARD_MARGIN } from \"lib/drc-defaults\"\n\nexport function checkViasOffBoard(\n circuitJson: AnyCircuitElement[],\n): PcbPlacementError[] {\n const board = circuitJson.find((el) => el.type === \"pcb_board\") as PcbBoard\n\n if (!board) return []\n\n const vias = circuitJson.filter((el) => el.type === \"pcb_via\") as PcbVia[]\n\n if (vias.length === 0) return []\n\n if (board.width === undefined || board.height === undefined) return []\n\n const boardMinX = board.center.x - board.width / 2\n const boardMaxX = board.center.x + board.width / 2\n const boardMinY = board.center.y - board.height / 2\n const boardMaxY = board.center.y + board.height / 2\n\n const errors: PcbPlacementError[] = []\n\n for (const via of vias) {\n const viaRadius = via.outer_diameter / 2\n const viaMinX = via.x - viaRadius\n const viaMaxX = via.x + viaRadius\n const viaMinY = via.y - viaRadius\n const viaMaxY = via.y + viaRadius\n\n if (\n viaMinX < boardMinX + DEFAULT_VIA_BOARD_MARGIN ||\n viaMaxX > boardMaxX - DEFAULT_VIA_BOARD_MARGIN ||\n viaMinY < boardMinY + DEFAULT_VIA_BOARD_MARGIN ||\n viaMaxY > boardMaxY - DEFAULT_VIA_BOARD_MARGIN\n ) {\n const viaName = getReadableNameForElement(circuitJson, via.pcb_via_id)\n errors.push({\n type: \"pcb_placement_error\",\n pcb_placement_error_id: `out_of_board_${via.pcb_via_id}`,\n message: `Via ${viaName} is outside or crossing the board boundary`,\n error_type: \"pcb_placement_error\",\n })\n }\n }\n\n return errors\n}\n","import type {\n AnyCircuitElement,\n PcbBoard,\n PcbComponent,\n AnySourceComponent,\n PcbComponentOutsideBoardError,\n} from \"circuit-json\"\nimport { getReadableNameForComponent } from \"lib/util/get-readable-names\"\nimport type { Point } from \"@tscircuit/math-utils\"\nimport * as Flatten from \"@flatten-js/core\"\nimport { rotateDEG, applyToPoint } from \"transformation-matrix\"\n\n/**\n * Create a rectangle polygon centered at (cx,cy) with given width/height and rotation (degrees).\n * Uses transformation-matrix to rotate around the center and returns a Flatten.Polygon.\n */\n\nfunction isPolygonCCW(poly: Flatten.Polygon): boolean {\n // @ts-ignore\n return poly.area() >= 0\n}\n\nfunction rectanglePolygon({\n center,\n size,\n rotationDeg = 0,\n}: {\n center: Point\n size: { width: number; height: number }\n rotationDeg?: number\n}): Flatten.Polygon {\n const cx = center.x\n const cy = center.y\n const hw = size.width / 2\n const hh = size.height / 2\n\n const corners: Flatten.Point[] = [\n new Flatten.Point(cx - hw, cy - hh),\n new Flatten.Point(cx + hw, cy - hh),\n new Flatten.Point(cx + hw, cy + hh),\n new Flatten.Point(cx - hw, cy + hh),\n ]\n\n let poly = new Flatten.Polygon(corners)\n\n if (rotationDeg) {\n const matrix = rotateDEG(rotationDeg, cx, cy)\n const rotatedCorners = corners.map((pt) => {\n const p = applyToPoint(matrix, { x: pt.x, y: pt.y })\n return new Flatten.Point(p.x, p.y)\n })\n poly = new Flatten.Polygon(rotatedCorners)\n }\n\n // Ensure CCW order\n if (!isPolygonCCW(poly)) poly.reverse()\n\n return poly\n}\n\nfunction boardToPolygon({\n board,\n}: {\n board: PcbBoard\n}): Flatten.Polygon | null {\n if (board.outline && board.outline.length > 0) {\n const points = board.outline.map((p) => new Flatten.Point(p.x, p.y))\n const poly = new Flatten.Polygon(points)\n\n if (!isPolygonCCW(poly)) {\n poly.reverse()\n }\n\n return poly\n }\n\n if (\n board.center &&\n typeof board.width === \"number\" &&\n typeof board.height === \"number\"\n ) {\n return rectanglePolygon({\n center: board.center,\n size: { width: board.width, height: board.height },\n rotationDeg: 0,\n })\n }\n\n return null\n}\n\n/** Get human-readable component name from circuit JSON */\nfunction getComponentName({\n circuitJson,\n component,\n}: {\n circuitJson: AnyCircuitElement[]\n component: PcbComponent\n}): string {\n if (component.source_component_id) {\n const sourceComponent = circuitJson.find(\n (el): el is AnySourceComponent =>\n el.type === \"source_component\" &&\n el.source_component_id === component.source_component_id,\n )\n if (sourceComponent && \"name\" in sourceComponent && sourceComponent.name) {\n return sourceComponent.name\n }\n }\n\n return getReadableNameForComponent(circuitJson, component.pcb_component_id)\n}\n\n/**\n * Compute overlap distance based on polygon containment and distances.\n * - If component center is outside board, distance is from center to board polygon.\n * - Samples rotated rectangle corners and edge midpoints; for any outside points,\n * computes distance to board polygon and returns the maximum.\n * - Falls back to intersection area ratio heuristic if needed.\n * - Returns small epsilon if fully inside.\n */\nfunction computeOverlapDistance(\n compPoly: Flatten.Polygon,\n boardPoly: Flatten.Polygon,\n componentCenter: Point,\n componentWidth: number,\n componentHeight: number,\n rotationDeg: number,\n): number {\n const centerPoint = new Flatten.Point(componentCenter.x, componentCenter.y)\n // If center is outside board polygon, return distance to board polygon\n if (!boardPoly.contains(centerPoint)) {\n const dist = boardPoly.distanceTo(centerPoint)\n return Array.isArray(dist) ? dist[0] : Number(dist) || 0\n }\n\n // Sample corners and edge midpoints of rotated rectangle\n const hw = componentWidth / 2\n const hh = componentHeight / 2\n\n // Original corners\n const corners: Point[] = [\n { x: componentCenter.x - hw, y: componentCenter.y - hh },\n { x: componentCenter.x + hw, y: componentCenter.y - hh },\n { x: componentCenter.x + hw, y: componentCenter.y + hh },\n { x: componentCenter.x - hw, y: componentCenter.y + hh },\n ]\n\n // Edge midpoints\n const midpoints: Point[] = []\n for (let i = 0; i < 4; i++) {\n const next = (i + 1) % 4\n midpoints.push({\n x: (corners[i].x + corners[next].x) / 2,\n y: (corners[i].y + corners[next].y) / 2,\n })\n }\n\n // Rotate points around center by rotationDeg\n const matrix = rotateDEG(rotationDeg, componentCenter.x, componentCenter.y)\n const rotatePoint = (pt: Point) => {\n const p = applyToPoint(matrix, pt)\n return new Flatten.Point(p.x, p.y)\n }\n\n const rotatedPoints = corners.concat(midpoints).map(rotatePoint)\n\n // For any point outside board polygon, compute distance to board polygon\n let maxDistance = 0\n for (const pt of rotatedPoints) {\n if (!boardPoly.contains(pt)) {\n const dist = boardPoly.distanceTo(pt)\n const d = Array.isArray(dist) ? dist[0] : Number(dist) || 0\n if (d > maxDistance) maxDistance = d\n }\n }\n\n if (maxDistance > 0) {\n return maxDistance\n }\n\n // Fallback: use intersection area ratio heuristic\n try {\n const intersection = Flatten.BooleanOperations.intersect(\n compPoly,\n boardPoly,\n )\n\n let intersectionArea = 0\n if (!intersection) {\n intersectionArea = 0\n } else if (Array.isArray(intersection)) {\n intersectionArea = intersection.reduce(\n (sum, p) => sum + (typeof p.area === \"function\" ? p.area() : 0),\n 0,\n )\n } else if (typeof (intersection as any).area === \"function\") {\n intersectionArea = (intersection as any).area()\n } else {\n intersectionArea = 0\n }\n\n const compArea = compPoly.area()\n\n if (intersectionArea > 0 && intersectionArea < compArea) {\n const overlapRatio = 1 - intersectionArea / compArea\n const compWidth = Math.abs(componentWidth)\n const compHeight = Math.abs(componentHeight)\n return Math.min(compWidth, compHeight) * overlapRatio\n } else if (intersectionArea === 0) {\n // completely outside (should not happen here), return small epsilon\n return 0.1\n } else {\n return 0.1\n }\n } catch {\n // If boolean ops fail (unlikely), return small epsilon\n return 0.1\n }\n}\n\nfunction getRepositionSuggestion({\n componentPoly,\n boardPoly,\n}: {\n componentPoly: Flatten.Polygon\n boardPoly: Flatten.Polygon\n}): string | null {\n const boardBox = boardPoly.box\n const componentBox = componentPoly.box\n\n let deltaX = 0\n let deltaY = 0\n\n if (componentBox.xmin < boardBox.xmin) {\n deltaX = boardBox.xmin - componentBox.xmin\n } else if (componentBox.xmax > boardBox.xmax) {\n deltaX = boardBox.xmax - componentBox.xmax\n }\n\n if (componentBox.ymin < boardBox.ymin) {\n deltaY = boardBox.ymin - componentBox.ymin\n } else if (componentBox.ymax > boardBox.ymax) {\n deltaY = boardBox.ymax - componentBox.ymax\n }\n\n if (deltaX === 0 && deltaY === 0) {\n return null\n }\n\n const xDir = deltaX >= 0 ? \"right\" : \"left\"\n const yDir = deltaY >= 0 ? \"up\" : \"down\"\n const absDx = Math.abs(Math.round(deltaX * 100) / 100)\n const absDy = Math.abs(Math.round(deltaY * 100) / 100)\n\n if (absDx > 0 && absDy > 0) {\n return `Try moving it ${absDx}mm ${xDir} and ${absDy}mm ${yDir} to fit within the board edge.`\n }\n\n if (absDx > 0) {\n return `Try moving it ${absDx}mm ${xDir} to fit within the board edge.`\n }\n\n return `Try moving it ${absDy}mm ${yDir} to fit within the board edge.`\n}\n\n/**\n * Main function — polygon-first: construct polygons, test containment / intersection,\n * compute overlap distance using boolean intersection area or geometric distance.\n */\nexport function checkPcbComponentsOutOfBoard(\n circuitJson: AnyCircuitElement[],\n): PcbComponentOutsideBoardError[] {\n const board = circuitJson.find(\n (el): el is PcbBoard => el.type === \"pcb_board\",\n )\n if (!board) return []\n\n const boardPoly = boardToPolygon({ board })\n if (!boardPoly) return []\n\n const components = circuitJson.filter(\n (el): el is PcbComponent => el.type === \"pcb_component\",\n )\n if (components.length === 0) return []\n\n const errors: PcbComponentOutsideBoardError[] = []\n\n for (const c of components) {\n if (c.is_allowed_to_be_off_board) continue\n\n // need center, width, height\n if (\n !c.center ||\n typeof c.width !== \"number\" ||\n typeof c.height !== \"number\"\n )\n continue\n\n if (c.width <= 0 || c.height <= 0) continue\n\n // c.width and c.height store the world-coordinate AABB dimensions (post-rotation),\n // so we must NOT re-apply rotation here.\n const compPoly = rectanglePolygon({\n center: c.center,\n size: { width: c.width, height: c.height },\n rotationDeg: 0,\n })\n\n if (compPoly.area() === 0) continue\n\n // If component is entirely inside board polygon -> OK\n // Flatten.Polygon.contains accepts shapes; this is the correct polygon containment check.\n const isInside = boardPoly.contains(compPoly)\n if (isInside) continue\n\n // Component is at least partially outside. Compute overlapDistance:\n const overlapDistance = computeOverlapDistance(\n compPoly,\n boardPoly,\n c.center,\n c.width,\n c.height,\n 0,\n )\n\n const compName = getComponentName({ circuitJson, component: c })\n const overlapDistanceMm = Math.round(overlapDistance * 100) / 100\n const repositionSuggestion = getRepositionSuggestion({\n componentPoly: compPoly,\n boardPoly,\n })\n\n errors.push({\n type: \"pcb_component_outside_board_error\",\n error_type: \"pcb_component_outside_board_error\",\n pcb_component_outside_board_error_id: `pcb_component_outside_board_${c.pcb_component_id}`,\n message: `Component ${compName} extends outside board boundaries by ${overlapDistanceMm}mm.${repositionSuggestion ? ` ${repositionSuggestion}` : \"\"}`,\n pcb_component_id: c.pcb_component_id,\n pcb_board_id: board.pcb_board_id,\n component_center: c.center,\n component_bounds: {\n min_x: compPoly.box.xmin,\n max_x: compPoly.box.xmax,\n min_y: compPoly.box.ymin,\n max_y: compPoly.box.ymax,\n },\n subcircuit_id: c.subcircuit_id,\n source_component_id: c.source_component_id,\n })\n }\n\n return errors\n}\n","import type {\n AnyCircuitElement,\n PcbVia,\n PcbViaClearanceError,\n} from \"circuit-json\"\nimport { getReadableNameForElement } from \"@tscircuit/circuit-json-util\"\nimport {\n getFullConnectivityMapFromCircuitJson,\n type ConnectivityMap,\n} from \"circuit-json-to-connectivity-map\"\nimport { DEFAULT_SAME_NET_VIA_MARGIN, EPSILON } from \"lib/drc-defaults\"\nimport { distance } from \"lib/util/distance\"\nimport { viasAreAtSameLocation } from \"lib/util/viasAreAtSameLocation\"\n\nexport function checkSameNetViaSpacing(\n circuitJson: AnyCircuitElement[],\n {\n connMap,\n minSpacing = DEFAULT_SAME_NET_VIA_MARGIN,\n }: { connMap?: ConnectivityMap; minSpacing?: number } = {},\n): PcbViaClearanceError[] {\n const vias = circuitJson.filter((el) => el.type === \"pcb_via\") as PcbVia[]\n if (vias.length < 2) return []\n connMap ??= getFullConnectivityMapFromCircuitJson(circuitJson)\n const errors: PcbViaClearanceError[] = []\n const reported = new Set<string>()\n\n for (let i = 0; i < vias.length; i++) {\n for (let j = i + 1; j < vias.length; j++) {\n const viaA = vias[i]\n const viaB = vias[j]\n // TODO: It is a very inefficient piece of code, the way to fix it is to use flatbush.\n if (viasAreAtSameLocation(viaA, viaB)) continue\n if (!connMap.areIdsConnected(viaA.pcb_via_id, viaB.pcb_via_id)) continue\n const gap =\n distance(viaA, viaB) - viaA.outer_diameter / 2 - viaB.outer_diameter / 2\n if (gap + EPSILON >= minSpacing) continue\n const pairId = [viaA.pcb_via_id, viaB.pcb_via_id].sort().join(\"_\")\n if (reported.has(pairId)) continue\n reported.add(pairId)\n errors.push({\n type: \"pcb_via_clearance_error\",\n pcb_error_id: `same_net_vias_close_${pairId}`,\n message: `Vias ${getReadableNameForElement(\n circuitJson,\n viaA.pcb_via_id,\n )} and ${getReadableNameForElement(\n circuitJson,\n viaB.pcb_via_id,\n )} are too close together (gap: ${gap.toFixed(3)}mm)`,\n error_type: \"pcb_via_clearance_error\",\n pcb_via_ids: [viaA.pcb_via_id, viaB.pcb_via_id],\n minimum_clearance: minSpacing,\n actual_clearance: gap,\n pcb_center: {\n x: (viaA.x + viaB.x) / 2,\n y: (viaA.y + viaB.y) / 2,\n },\n })\n }\n }\n\n return errors\n}\n","import type { PcbVia } from \"circuit-json\"\n\nexport function distance(a: PcbVia, b: PcbVia): number {\n return Math.hypot(a.x - b.x, a.y - b.y)\n}\n","import type { PcbVia } from \"circuit-json\"\nimport { distance } from \"./distance\"\nimport { EPSILON } from \"lib/drc-defaults\"\n\nexport function viasAreAtSameLocation(a: PcbVia, b: PcbVia): boolean {\n return distance(a, b) <= EPSILON\n}\n","import type {\n AnyCircuitElement,\n PcbVia,\n PcbViaClearanceError,\n} from \"circuit-json\"\nimport { getReadableNameForElement } from \"@tscircuit/circuit-json-util\"\nimport {\n getFullConnectivityMapFromCircuitJson,\n type ConnectivityMap,\n} from \"circuit-json-to-connectivity-map\"\nimport { DEFAULT_DIFFERENT_NET_VIA_MARGIN, EPSILON } from \"lib/drc-defaults\"\nimport { viasAreAtSameLocation } from \"lib/util/viasAreAtSameLocation\"\nimport { distance } from \"lib/util/distance\"\n\nexport function checkDifferentNetViaSpacing(\n circuitJson: AnyCircuitElement[],\n {\n connMap,\n minSpacing = DEFAULT_DIFFERENT_NET_VIA_MARGIN,\n }: { connMap?: ConnectivityMap; minSpacing?: number } = {},\n): PcbViaClearanceError[] {\n const vias = circuitJson.filter((el) => el.type === \"pcb_via\") as PcbVia[]\n if (vias.length < 2) return []\n connMap ??= getFullConnectivityMapFromCircuitJson(circuitJson)\n const errors: PcbViaClearanceError[] = []\n const reported = new Set<string>()\n\n for (let i = 0; i < vias.length; i++) {\n for (let j = i + 1; j < vias.length; j++) {\n const viaA = vias[i]\n const viaB = vias[j]\n // TODO: It is a very inefficient piece of code, the way to fix it is to use flatbush.\n if (viasAreAtSameLocation(viaA, viaB)) continue\n if (connMap.areIdsConnected(viaA.pcb_via_id, viaB.pcb_via_id)) continue\n const gap =\n distance(viaA, viaB) - viaA.outer_diameter / 2 - viaB.outer_diameter / 2\n if (gap + EPSILON >= minSpacing) continue\n const pairId = [viaA.pcb_via_id, viaB.pcb_via_id].sort().join(\"_\")\n if (reported.has(pairId)) continue\n reported.add(pairId)\n errors.push({\n type: \"pcb_via_clearance_error\",\n pcb_error_id: `different_net_vias_close_${pairId}`,\n message: `Vias ${getReadableNameForElement(\n circuitJson,\n viaA.pcb_via_id,\n )} and ${getReadableNameForElement(\n circuitJson,\n viaB.pcb_via_id,\n )} from different nets are too close together (gap: ${gap.toFixed(\n 3,\n )}mm)`,\n error_type: \"pcb_via_clearance_error\",\n pcb_via_ids: [viaA.pcb_via_id, viaB.pcb_via_id],\n minimum_clearance: minSpacing,\n actual_clearance: gap,\n pcb_center: {\n x: (viaA.x + viaB.x) / 2,\n y: (viaA.y + viaB.y) / 2,\n },\n })\n }\n }\n\n return errors\n}\n","import { cju } from \"@tscircuit/circuit-json-util\"\nimport type {\n AnyCircuitElement,\n PcbPort,\n PcbTrace,\n PcbTraceWarning,\n SourceTrace,\n} from \"circuit-json\"\nimport { getFullConnectivityMapFromCircuitJson } from \"circuit-json-to-connectivity-map\"\nimport { getReadableNameForSourceTrace } from \"lib/util/get-readable-names\"\n\nexport function checkSourceTracesMatchPcbTraceThickness(\n circuitJson: AnyCircuitElement[],\n): PcbTraceWarning[] {\n const warnings: PcbTraceWarning[] = []\n const db = cju(circuitJson)\n\n const sourceTraces = db.source_trace.list() as SourceTrace[]\n const pcbTraces = db.pcb_trace.list() as PcbTrace[]\n const pcbPorts = db.pcb_port.list() as PcbPort[]\n const connectivityMap = getFullConnectivityMapFromCircuitJson(circuitJson)\n\n for (const sourceTrace of sourceTraces) {\n const requestedThickness = sourceTrace.min_trace_thickness\n if (requestedThickness === undefined) continue\n\n const connectedPcbPorts = pcbPorts.filter((pcbPort) =>\n sourceTrace.connected_source_port_ids?.includes(pcbPort.source_port_id),\n )\n if (connectedPcbPorts.length < 2) continue\n\n const referenceNetId = connectivityMap.getNetConnectedToId(\n connectedPcbPorts[0].pcb_port_id,\n )\n if (!referenceNetId) continue\n\n const netElementIds = connectivityMap.getIdsConnectedToNet(referenceNetId)\n const relatedPcbTraces = pcbTraces.filter((pcbTrace) =>\n netElementIds.includes(pcbTrace.pcb_trace_id),\n )\n if (relatedPcbTraces.length === 0) continue\n\n const actualWireWidths = relatedPcbTraces.flatMap((pcbTrace) =>\n pcbTrace.route\n .filter((point) => point.route_type === \"wire\")\n .map((point) => point.width),\n )\n if (actualWireWidths.length === 0) continue\n\n const actualThickness = Math.min(...actualWireWidths)\n if (actualThickness >= requestedThickness) continue\n\n let undersizedSegment:\n | { pcb_trace_id: string; center: { x: number; y: number } }\n | undefined\n\n for (const relatedPcbTrace of relatedPcbTraces) {\n for (let i = 0; i < relatedPcbTrace.route.length - 1; i++) {\n const point = relatedPcbTrace.route[i]\n const nextPoint = relatedPcbTrace.route[i + 1]\n if (!point || !nextPoint) continue\n if (point.route_type !== \"wire\" || nextPoint.route_type !== \"wire\") {\n continue\n }\n if (point.width !== actualThickness) continue\n\n undersizedSegment = {\n pcb_trace_id: relatedPcbTrace.pcb_trace_id,\n center: {\n x: (point.x + nextPoint.x) / 2,\n y: (point.y + nextPoint.y) / 2,\n },\n }\n break\n }\n\n if (undersizedSegment) break\n }\n\n if (!undersizedSegment) continue\n\n warnings.push({\n type: \"pcb_trace_warning\",\n pcb_trace_warning_id: `pcb_trace_warning_${sourceTrace.source_trace_id}`,\n warning_type: \"pcb_trace_warning\",\n message: `Trace [${getReadableNameForSourceTrace(circuitJson, sourceTrace)}] is routed thinner than requested (requested: ${requestedThickness}mm, actual: ${actualThickness}mm).`,\n center: undersizedSegment.center,\n source_trace_id: sourceTrace.source_trace_id,\n pcb_trace_id: undersizedSegment.pcb_trace_id,\n pcb_component_ids: Array.from(\n new Set(\n connectedPcbPorts\n .map((pcbPort) => pcbPort.pcb_component_id)\n .filter((id): id is string => id !== undefined),\n ),\n ),\n pcb_port_ids: connectedPcbPorts.map((pcbPort) => pcbPort.pcb_port_id),\n })\n }\n\n return warnings\n}\n","import type {\n AnyCircuitElement,\n PcbPort,\n PcbTrace,\n PcbTraceMissingError,\n SourceTrace,\n} from \"circuit-json\"\nimport { containsCircuitJsonId } from \"lib/util/get-readable-names\"\n\nconst getSourceTraceIdsFromPcbTrace = (\n pcbTraceSourceTraceId?: string,\n): string[] => {\n if (!pcbTraceSourceTraceId) return []\n if (!pcbTraceSourceTraceId.includes(\"__\")) return [pcbTraceSourceTraceId]\n\n return pcbTraceSourceTraceId\n .split(\"__\")\n .filter((part) => part.startsWith(\"source_trace_\"))\n}\n\n/**\n * Check that each source_trace which connects source ports has at least one\n * pcb_trace associated with it. If a source_trace has no corresponding\n * pcb_trace, return an error for that source_trace.\n */\nfunction checkSourceTracesHavePcbTraces(\n circuitJson: AnyCircuitElement[],\n): PcbTraceMissingError[] {\n const errors: PcbTraceMissingError[] = []\n const sourceTraces = circuitJson.filter(\n (el) => el.type === \"source_trace\",\n ) as SourceTrace[]\n const pcbTraces = circuitJson.filter(\n (el) => el.type === \"pcb_trace\",\n ) as PcbTrace[]\n\n for (const sourceTrace of sourceTraces) {\n if (!sourceTrace.connected_source_port_ids?.length) continue\n if ((sourceTrace.connected_source_net_ids?.length ?? 0) > 0) continue\n if (sourceTrace.connected_source_port_ids.length < 2) continue\n\n const hasPcbTrace = pcbTraces.some((pcbTrace) =>\n getSourceTraceIdsFromPcbTrace(pcbTrace.source_trace_id).includes(\n sourceTrace.source_trace_id,\n ),\n )\n if (!hasPcbTrace) {\n // Get PCB ports connected to this source trace\n const connectedPcbPorts = circuitJson.filter(\n (el) =>\n el.type === \"pcb_port\" &&\n sourceTrace.connected_source_port_ids.includes(el.source_port_id),\n ) as PcbPort[]\n\n // Find PCB components that these ports belong to\n const connectedPcbComponentIds = Array.from(\n new Set(\n connectedPcbPorts\n .map((port) => port.pcb_component_id)\n .filter((id): id is string => id !== undefined),\n ),\n )\n\n errors.push({\n type: \"pcb_trace_missing_error\",\n pcb_trace_missing_error_id: `pcb_trace_missing_${sourceTrace.source_trace_id}`,\n error_type: \"pcb_trace_missing_error\",\n message: `Trace [${sourceTrace.display_name && !containsCircuitJsonId(sourceTrace.display_name) ? sourceTrace.display_name : \"trace\"}] is not connected (it has no PCB trace)`,\n source_trace_id: sourceTrace.source_trace_id,\n pcb_component_ids: connectedPcbComponentIds,\n pcb_port_ids: connectedPcbPorts.map((port) => port.pcb_port_id),\n })\n }\n }\n\n return errors\n}\n\nexport { checkSourceTracesHavePcbTraces }\n","import type {\n AnyCircuitElement,\n PcbBoard,\n PcbTrace,\n PcbTraceError,\n} from \"circuit-json\"\nimport { cju } from \"@tscircuit/circuit-json-util\"\nimport type { Point, Polygon } from \"@tscircuit/math-utils\"\nimport { segmentToSegmentMinDistance } from \"@tscircuit/math-utils\"\n\n/**\n * Default margin for trace clearance from board edge (in mm)\n */\nconst DEFAULT_BOARD_MARGIN = 0.2\n\n/**\n * Configuration for trace board boundary checking\n */\nexport interface TraceBoardCheckConfig {\n /** Minimum distance from trace center to board edge (in mm) */\n margin?: number\n}\n\n/**\n * Create a board polygon representation using math-utils Polygon type\n */\nfunction getBoardPolygonPoints(board: PcbBoard): Polygon | null {\n if (board.outline && board.outline.length > 0) {\n // Use custom board outline\n return board.outline.map((p) => ({ x: p.x, y: p.y }))\n }\n\n if (\n board.center &&\n typeof board.width === \"number\" &&\n typeof board.height === \"number\"\n ) {\n // Create rectangular board outline\n const cx = board.center.x\n const cy = board.center.y\n const hw = board.width / 2\n const hh = board.height / 2\n\n return [\n { x: cx - hw, y: cy - hh }, // bottom-left\n { x: cx + hw, y: cy - hh }, // bottom-right\n { x: cx + hw, y: cy + hh }, // top-right\n { x: cx - hw, y: cy + hh }, // top-left\n ]\n }\n\n return null\n}\n\n/**\n * Check if any trace segment is too close to or outside the board outline\n * Uses segment-to-polygon distance with configurable margin\n */\nexport function checkPcbTracesOutOfBoard(\n circuitJson: AnyCircuitElement[],\n config: TraceBoardCheckConfig = {},\n): PcbTraceError[] {\n const errors: PcbTraceError[] = []\n const margin = config.margin ?? DEFAULT_BOARD_MARGIN\n\n // Find the board\n const board = circuitJson.find(\n (el): el is PcbBoard => el.type === \"pcb_board\",\n )\n if (!board) return errors\n\n // Create board polygon using math-utils Point type\n const boardPoints = getBoardPolygonPoints(board)\n if (!boardPoints) return errors\n\n // Get all PCB traces\n const pcbTraces = cju(circuitJson).pcb_trace.list()\n\n for (const trace of pcbTraces) {\n if (trace.route.length < 2) continue\n\n // Check each segment of the trace\n for (let i = 0; i < trace.route.length - 1; i++) {\n const p1 = trace.route[i]\n const p2 = trace.route[i + 1]\n\n // Only check wire segments\n if (p1.route_type !== \"wire\" || p2.route_type !== \"wire\") continue\n\n const traceWidth =\n \"width\" in p1 ? p1.width : \"width\" in p2 ? p2.width : 0.1\n const segmentStart: Point = { x: p1.x, y: p1.y }\n const segmentEnd: Point = { x: p2.x, y: p2.y }\n\n // Calculate minimum distance from trace segment to board polygon\n let minDistance = Infinity\n for (let j = 0; j < boardPoints.length; j++) {\n const edgeStart = boardPoints[j]\n const edgeEnd = boardPoints[(j + 1) % boardPoints.length]\n const distance = segmentToSegmentMinDistance(\n segmentStart,\n segmentEnd,\n edgeStart,\n edgeEnd,\n )\n if (distance < minDistance) {\n minDistance = distance\n }\n }\n\n const minimumDistance = traceWidth / 2 + margin\n\n if (minDistance < minimumDistance) {\n const error: PcbTraceError = {\n type: \"pcb_trace_error\",\n error_type: \"pcb_trace_error\",\n pcb_trace_error_id: `trace_too_close_to_board_${trace.pcb_trace_id}_segment_${i}`,\n message: `Trace too close to board edge (${minDistance.toFixed(3)}mm < ${minimumDistance.toFixed(3)}mm required, margin: ${margin}mm)`,\n pcb_trace_id: trace.pcb_trace_id,\n source_trace_id: trace.source_trace_id || \"\",\n center: {\n x: (segmentStart.x + segmentEnd.x) / 2,\n y: (segmentStart.y + segmentEnd.y) / 2,\n },\n pcb_component_ids: [],\n pcb_port_ids: [],\n }\n errors.push(error)\n }\n }\n }\n\n return errors\n}\n","import {\n cju,\n getBoundsOfPcbElements,\n getPrimaryId,\n} from \"@tscircuit/circuit-json-util\"\nimport { doBoundsOverlap } from \"@tscircuit/math-utils\"\nimport type { AnyCircuitElement, PcbFootprintOverlapError } from \"circuit-json\"\nimport { getFullConnectivityMapFromCircuitJson } from \"circuit-json-to-connectivity-map\"\nimport {\n getReadableNameForElementId,\n getReadableNameForPort,\n} from \"lib/util/get-readable-names\"\nimport {\n type OverlappableElement,\n doPcbElementsOverlap,\n} from \"./doPcbElementsOverlap\"\n\ninterface ComponentWithElements {\n component_id: string\n elements: OverlappableElement[]\n bounds: {\n minX: number\n minY: number\n maxX: number\n maxY: number\n }\n}\n\nconst formatOverlapElementDescription = (\n circuitJson: AnyCircuitElement[],\n element: OverlappableElement,\n): string => {\n if (\"pcb_port_id\" in element && element.pcb_port_id) {\n return getReadableNameForPort(circuitJson, element.pcb_port_id)\n }\n\n const id = getPrimaryId(element)\n const readableName = getReadableNameForElementId(circuitJson, id)\n return readableName === \"element\" ? `[${id}]` : readableName\n}\n\n/**\n * Check for overlapping PCB components\n * Returns errors for components that overlap inappropriately\n */\nexport function checkPcbComponentOverlap(\n circuitJson: AnyCircuitElement[],\n): PcbFootprintOverlapError[] {\n const errors: PcbFootprintOverlapError[] = []\n\n // Build connectivity map to check if components are electrically connected\n const connMap = getFullConnectivityMapFromCircuitJson(circuitJson)\n\n // Get all overlappable elements\n const smtPads = cju(circuitJson).pcb_smtpad.list()\n const platedHoles = cju(circuitJson).pcb_plated_hole.list()\n const holes = cju(circuitJson).pcb_hole.list()\n\n // Group elements by component (or treat standalone elements as their own \"component\")\n const componentMap = new Map<string, ComponentWithElements>()\n\n // Group SMT pads by component (or treat as standalone)\n for (const pad of smtPads) {\n const componentId =\n pad.pcb_component_id || `standalone_pad_${getPrimaryId(pad)}`\n if (!componentMap.has(componentId)) {\n componentMap.set(componentId, {\n component_id: componentId,\n elements: [],\n bounds: { minX: 0, minY: 0, maxX: 0, maxY: 0 },\n })\n }\n componentMap.get(componentId)!.elements.push(pad)\n }\n\n // Group plated holes by component (or treat as standalone)\n for (const hole of platedHoles) {\n const componentId =\n hole.pcb_component_id || `standalone_plated_hole_${getPrimaryId(hole)}`\n if (!componentMap.has(componentId)) {\n componentMap.set(componentId, {\n component_id: componentId,\n elements: [],\n bounds: { minX: 0, minY: 0, maxX: 0, maxY: 0 },\n })\n }\n componentMap.get(componentId)!.elements.push(hole)\n }\n\n // Holes typically don't have pcb_component_id, treat each as standalone\n for (const hole of holes) {\n const componentId = `standalone_hole_${getPrimaryId(hole)}`\n componentMap.set(componentId, {\n component_id: componentId,\n elements: [hole],\n bounds: { minX: 0, minY: 0, maxX: 0, maxY: 0 },\n })\n }\n\n // Compute bounds for each component\n for (const [componentId, componentData] of componentMap) {\n if (componentData.elements.length > 0) {\n componentData.bounds = getBoundsOfPcbElements(componentData.elements)\n }\n }\n\n // Convert map to array for pairwise iteration\n const componentsWithElements = Array.from(componentMap.values())\n\n // Pairwise check: only check elements if component bounds overlap\n for (let i = 0; i < componentsWithElements.length; i++) {\n for (let j = i + 1; j < componentsWithElements.length; j++) {\n const comp1 = componentsWithElements[i]\n const comp2 = componentsWithElements[j]\n\n // First check if component bounds overlap\n if (!doBoundsOverlap(comp1.bounds, comp2.bounds)) {\n continue\n }\n\n // Component bounds overlap, now check individual elements\n for (const elem1 of comp1.elements) {\n for (const elem2 of comp2.elements) {\n const id1 = getPrimaryId(elem1)\n const id2 = getPrimaryId(elem2)\n\n // Check if both are SMT pads and are electrically connected (same net) - if so, skip\n // This allows pads with the same subcircuit connectivity to be in contact\n if (\n elem1.type === \"pcb_smtpad\" &&\n elem2.type === \"pcb_smtpad\" &&\n connMap.areIdsConnected(id1, id2)\n ) {\n continue\n }\n\n // Check if element bounds overlap\n if (doPcbElementsOverlap(elem1, elem2)) {\n const elem1Description = formatOverlapElementDescription(\n circuitJson,\n elem1,\n )\n const elem2Description = formatOverlapElementDescription(\n circuitJson,\n elem2,\n )\n\n // Create error object\n const error: PcbFootprintOverlapError = {\n type: \"pcb_footprint_overlap_error\",\n pcb_error_id: `pcb_footprint_overlap_${id1}_${id2}`,\n error_type: \"pcb_footprint_overlap_error\",\n message: `${elem1.type} ${elem1Description} overlaps with ${elem2.type} ${elem2Description}`,\n }\n\n // Add relevant IDs based on element types\n if (elem1.type === \"pcb_smtpad\" || elem2.type === \"pcb_smtpad\") {\n error.pcb_smtpad_ids = []\n if (elem1.type === \"pcb_smtpad\") error.pcb_smtpad_ids.push(id1)\n if (elem2.type === \"pcb_smtpad\") error.pcb_smtpad_ids.push(id2)\n }\n\n if (\n elem1.type === \"pcb_plated_hole\" ||\n elem2.type === \"pcb_plated_hole\"\n ) {\n error.pcb_plated_hole_ids = []\n if (elem1.type === \"pcb_plated_hole\")\n error.pcb_plated_hole_ids.push(id1)\n if (elem2.type === \"pcb_plated_hole\")\n error.pcb_plated_hole_ids.push(id2)\n }\n\n if (elem1.type === \"pcb_hole\" || elem2.type === \"pcb_hole\") {\n error.pcb_hole_ids = []\n if (elem1.type === \"pcb_hole\") error.pcb_hole_ids.push(id1)\n if (elem2.type === \"pcb_hole\") error.pcb_hole_ids.push(id2)\n }\n\n errors.push(error)\n }\n }\n }\n }\n }\n\n return errors\n}\n","import { getBoundsOfPcbElements } from \"@tscircuit/circuit-json-util\"\nimport { doBoundsOverlap } from \"@tscircuit/math-utils\"\nimport type { PcbHole, PcbPlatedHole, PcbSmtPad } from \"circuit-json\"\nimport type { Collidable } from \"lib/check-each-pcb-trace-non-overlapping/getCollidableBounds\"\nimport { getLayersOfPcbElement } from \"lib/util/getLayersOfPcbElement\"\n\nexport type OverlappableElement = PcbSmtPad | PcbPlatedHole | PcbHole\n\nfunction getElementLayers(elem: OverlappableElement): string[] {\n return getLayersOfPcbElement(elem as Collidable)\n}\n\nfunction doLayersOverlap(layers1: string[], layers2: string[]): boolean {\n if (layers1.length === 0 || layers2.length === 0) return true\n return layers1.some((l) => layers2.includes(l))\n}\n\nexport function doPcbElementsOverlap(\n elem1: OverlappableElement,\n elem2: OverlappableElement,\n): boolean {\n // If the elements are on completely different layers they cannot overlap\n const layers1 = getElementLayers(elem1)\n const layers2 = getElementLayers(elem2)\n if (!doLayersOverlap(layers1, layers2)) return false\n\n const bounds1 = getBoundsOfPcbElements([elem1])\n const bounds2 = getBoundsOfPcbElements([elem2])\n return doBoundsOverlap(bounds1, bounds2)\n}\n","import {\n getPrimaryId,\n getReadableNameForElement,\n} from \"@tscircuit/circuit-json-util\"\nimport type { AnyCircuitElement, PcbPadPadClearanceError } from \"circuit-json\"\nimport {\n type ConnectivityMap,\n getFullConnectivityMapFromCircuitJson,\n} from \"circuit-json-to-connectivity-map\"\nimport { SpatialObjectIndex } from \"lib/data-structures/SpatialIndex\"\nimport { DEFAULT_PAD_PAD_CLEARANCE, EPSILON } from \"lib/drc-defaults\"\nimport { getLayersOfPcbElement } from \"lib/util/getLayersOfPcbElement\"\nimport {\n type PadElement,\n formatMm,\n getPadBounds,\n getPadCenter,\n getPadToPadGap,\n getPads,\n} from \"./check-pad-clearance/common\"\n\nexport function checkPadPadClearance(\n circuitJson: AnyCircuitElement[],\n {\n connMap,\n minSpacing = DEFAULT_PAD_PAD_CLEARANCE,\n }: { connMap?: ConnectivityMap; minSpacing?: number } = {},\n): PcbPadPadClearanceError[] {\n const pads = getPads(circuitJson)\n if (pads.length < 2) return []\n\n connMap ??= getFullConnectivityMapFromCircuitJson(circuitJson)\n const spatialIndex = new SpatialObjectIndex<PadElement>({\n objects: pads,\n getBounds: getPadBounds,\n getId: (pad) => getPrimaryId(pad),\n })\n\n const errors = new Map<string, PcbPadPadClearanceError>()\n\n for (const padA of pads) {\n const padAId = getPrimaryId(padA)\n const nearbyPads = spatialIndex.getObjectsInBounds(\n getPadBounds(padA),\n minSpacing,\n )\n\n for (const padB of nearbyPads) {\n const padBId = getPrimaryId(padB)\n if (padAId === padBId) continue\n if (\n !getLayersOfPcbElement(padA as any).some((layer) =>\n getLayersOfPcbElement(padB as any).includes(layer),\n )\n ) {\n continue\n }\n if (connMap.areIdsConnected(padAId, padBId)) continue\n\n const pairId = [padAId, padBId].sort().join(\"_\")\n const gap = getPadToPadGap(padA, padB)\n if (gap + EPSILON >= minSpacing) continue\n const centerA = getPadCenter(padA)\n const centerB = getPadCenter(padB)\n\n const nextError: PcbPadPadClearanceError = {\n type: \"pcb_pad_pad_clearance_error\",\n pcb_pad_pad_clearance_error_id: `pad_pad_clearance_${pairId}`,\n error_type: \"pcb_pad_pad_clearance_error\",\n message: `Pads ${getReadableNameForElement(circuitJson, padAId)} and ${getReadableNameForElement(circuitJson, padBId)} are too close (clearance: ${formatMm(gap)}, minimum: ${formatMm(minSpacing)})`,\n pcb_pad_ids: [padAId, padBId] as [string, string],\n minimum_clearance: minSpacing,\n actual_clearance: gap,\n center: {\n x: (centerA.x + centerB.x) / 2,\n y: (centerA.y + centerB.y) / 2,\n },\n }\n\n if (!errors.has(pairId)) {\n errors.set(pairId, nextError)\n }\n }\n }\n\n return Array.from(errors.values())\n}\n","import {\n cju,\n distanceBetweenCircleAndCircle,\n distanceBetweenCircleAndPolygon,\n distanceBetweenPolygonAndPolygon,\n getBoundsOfPcbElements,\n} from \"@tscircuit/circuit-json-util\"\nimport { midpoint } from \"@tscircuit/math-utils\"\nimport type {\n AnyCircuitElement,\n PcbPlatedHole,\n PcbSmtPad,\n PcbTrace,\n} from \"circuit-json\"\nimport type { PcbTraceSegment } from \"lib/check-each-pcb-trace-non-overlapping/getCollidableBounds\"\nimport type { Bounds } from \"lib/data-structures/SpatialIndex\"\nimport { DEFAULT_TRACE_THICKNESS } from \"lib/drc-defaults\"\n\nexport type PadElement = PcbSmtPad | PcbPlatedHole\n\nexport const formatMm = (value: number) => {\n const rounded = Math.round(value * 1000) / 1000\n return `${Number(rounded.toFixed(3))}mm`\n}\n\nexport const getPadBounds = (pad: PadElement): Bounds =>\n getBoundsOfPcbElements([pad])\n\nexport const getPadCenter = (pad: PadElement) => {\n const bounds = getPadBounds(pad)\n return midpoint(\n { x: bounds.minX, y: bounds.minY },\n { x: bounds.maxX, y: bounds.maxY },\n )\n}\n\nexport const getPadRadius = (pad: PadElement) => {\n const bounds = getPadBounds(pad)\n return Math.min(bounds.maxX - bounds.minX, bounds.maxY - bounds.minY) / 2\n}\n\nexport const isCircularPad = (pad: PadElement) => pad.shape === \"circle\"\n\nconst getCircleShape = (pad: PadElement) => {\n const center = getPadCenter(pad)\n return {\n kind: \"circle\" as const,\n x: center.x,\n y: center.y,\n radius: getPadRadius(pad),\n }\n}\n\nconst getPolygonShape = (pad: PadElement) => {\n const bounds = getPadBounds(pad)\n return {\n kind: \"polygon\" as const,\n points: [\n { x: bounds.minX, y: bounds.minY },\n { x: bounds.maxX, y: bounds.minY },\n { x: bounds.maxX, y: bounds.maxY },\n { x: bounds.minX, y: bounds.maxY },\n ],\n }\n}\n\nexport const getPadToPadGap = (padA: PadElement, padB: PadElement) => {\n if (isCircularPad(padA) && isCircularPad(padB)) {\n return distanceBetweenCircleAndCircle(\n getCircleShape(padA),\n getCircleShape(padB),\n )\n }\n\n if (isCircularPad(padA)) {\n return distanceBetweenCircleAndPolygon(\n getCircleShape(padA),\n getPolygonShape(padB),\n )\n }\n\n if (isCircularPad(padB)) {\n return distanceBetweenCircleAndPolygon(\n getCircleShape(padB),\n getPolygonShape(padA),\n )\n }\n\n return distanceBetweenPolygonAndPolygon(\n getPolygonShape(padA),\n getPolygonShape(padB),\n )\n}\n\nexport const getPads = (circuitJson: AnyCircuitElement[]) =>\n [\n ...cju(circuitJson).pcb_smtpad.list(),\n ...cju(circuitJson).pcb_plated_hole.list(),\n ] as PadElement[]\n\nexport const getTraceSegments = (\n circuitJson: AnyCircuitElement[],\n): PcbTraceSegment[] => {\n const pcbTraces = cju(circuitJson).pcb_trace.list()\n\n return pcbTraces.flatMap((pcbTrace) => {\n const segments: PcbTraceSegment[] = []\n\n for (let i = 0; i < pcbTrace.route.length - 1; i++) {\n const p1 = pcbTrace.route[i]\n const p2 = pcbTrace.route[i + 1]\n if (p1.route_type !== \"wire\") continue\n if (p2.route_type !== \"wire\") continue\n if (p1.layer !== p2.layer) continue\n\n segments.push({\n type: \"pcb_trace_segment\",\n pcb_trace_id: pcbTrace.pcb_trace_id,\n _pcbTrace: pcbTrace as PcbTrace,\n thickness:\n \"width\" in p1\n ? p1.width\n : \"width\" in p2\n ? p2.width\n : DEFAULT_TRACE_THICKNESS,\n layer: p1.layer,\n x1: p1.x,\n y1: p1.y,\n x2: p2.x,\n y2: p2.y,\n })\n }\n\n return segments\n })\n}\n","import {\n getPrimaryId,\n getReadableNameForElement,\n} from \"@tscircuit/circuit-json-util\"\nimport {\n segmentToBoundsMinDistance,\n segmentToCircleMinDistance,\n} from \"@tscircuit/math-utils\"\nimport type { AnyCircuitElement, PcbPadTraceClearanceError } from \"circuit-json\"\nimport {\n type ConnectivityMap,\n getFullConnectivityMapFromCircuitJson,\n} from \"circuit-json-to-connectivity-map\"\nimport { getCollidableBounds } from \"lib/check-each-pcb-trace-non-overlapping/getCollidableBounds\"\nimport { SpatialObjectIndex } from \"lib/data-structures/SpatialIndex\"\nimport { DEFAULT_PAD_TRACE_CLEARANCE, EPSILON } from \"lib/drc-defaults\"\nimport { getLayersOfPcbElement } from \"lib/util/getLayersOfPcbElement\"\nimport {\n type PadElement,\n formatMm,\n getPadBounds,\n getPadCenter,\n getPadRadius,\n getPads,\n getTraceSegments,\n isCircularPad,\n} from \"./check-pad-clearance/common\"\n\nexport function checkPadTraceClearance(\n circuitJson: AnyCircuitElement[],\n {\n connMap,\n minSpacing = DEFAULT_PAD_TRACE_CLEARANCE,\n }: { connMap?: ConnectivityMap; minSpacing?: number } = {},\n): PcbPadTraceClearanceError[] {\n const pads = getPads(circuitJson)\n const segments = getTraceSegments(circuitJson)\n if (pads.length === 0 || segments.length === 0) return []\n\n connMap ??= getFullConnectivityMapFromCircuitJson(circuitJson)\n const spatialIndex = new SpatialObjectIndex<PadElement>({\n objects: pads,\n getBounds: getPadBounds,\n getId: (pad) => getPrimaryId(pad),\n })\n\n const errors = new Map<\n string,\n { error: PcbPadTraceClearanceError; gap: number }\n >()\n\n for (const segment of segments) {\n const nearbyPads = spatialIndex.getObjectsInBounds(\n getCollidableBounds(segment),\n minSpacing + segment.thickness / 2,\n )\n\n for (const pad of nearbyPads) {\n const padId = getPrimaryId(pad)\n if (!getLayersOfPcbElement(pad as any).includes(segment.layer)) continue\n if (connMap.areIdsConnected(segment.pcb_trace_id, padId)) continue\n const center = getPadCenter(pad)\n\n const gap = isCircularPad(pad)\n ? segmentToCircleMinDistance(\n { x: segment.x1, y: segment.y1 },\n { x: segment.x2, y: segment.y2 },\n { x: center.x, y: center.y, radius: getPadRadius(pad) },\n ) -\n segment.thickness / 2\n : segmentToBoundsMinDistance(\n { x: segment.x1, y: segment.y1 },\n { x: segment.x2, y: segment.y2 },\n getPadBounds(pad),\n ) -\n segment.thickness / 2\n if (gap + EPSILON >= minSpacing) continue\n\n const pairId = `${padId}_${segment.pcb_trace_id}`\n const nextError: PcbPadTraceClearanceError = {\n type: \"pcb_pad_trace_clearance_error\" as const,\n pcb_pad_trace_clearance_error_id: `pad_trace_clearance_${pairId}`,\n error_type: \"pcb_pad_trace_clearance_error\",\n message: `Pad ${getReadableNameForElement(circuitJson, padId)} and trace ${getReadableNameForElement(circuitJson, segment.pcb_trace_id)} are too close (clearance: ${formatMm(gap)}, minimum: ${formatMm(minSpacing)})`,\n pcb_pad_id: padId,\n pcb_trace_id: segment.pcb_trace_id,\n minimum_clearance: minSpacing,\n actual_clearance: gap,\n center: {\n x: (center.x + (segment.x1 + segment.x2) / 2) / 2,\n y: (center.y + (segment.y1 + segment.y2) / 2) / 2,\n },\n }\n\n const current = errors.get(pairId)\n if (!current || gap < current.gap) {\n errors.set(pairId, { error: nextError, gap })\n }\n }\n }\n\n return Array.from(errors.values()).map(({ error }) => error)\n}\n","import { getReadableNameForElement } from \"@tscircuit/circuit-json-util\"\nimport { segmentToCircleMinDistance } from \"@tscircuit/math-utils\"\nimport type {\n AnyCircuitElement,\n PcbVia,\n PcbViaTraceClearanceError,\n} from \"circuit-json\"\nimport {\n type ConnectivityMap,\n getFullConnectivityMapFromCircuitJson,\n} from \"circuit-json-to-connectivity-map\"\nimport { DEFAULT_PAD_TRACE_CLEARANCE, EPSILON } from \"lib/drc-defaults\"\nimport { getLayersOfPcbElement } from \"lib/util/getLayersOfPcbElement\"\nimport { formatMm, getTraceSegments } from \"./check-pad-clearance/common\"\n\nconst getClosestPointOnSegment = (\n point: { x: number; y: number },\n segmentStart: { x: number; y: number },\n segmentEnd: { x: number; y: number },\n) => {\n const dx = segmentEnd.x - segmentStart.x\n const dy = segmentEnd.y - segmentStart.y\n const lengthSquared = dx * dx + dy * dy\n\n if (lengthSquared === 0) return segmentStart\n\n const t = Math.max(\n 0,\n Math.min(\n 1,\n ((point.x - segmentStart.x) * dx + (point.y - segmentStart.y) * dy) /\n lengthSquared,\n ),\n )\n\n return {\n x: segmentStart.x + t * dx,\n y: segmentStart.y + t * dy,\n }\n}\n\nexport function checkViaTraceClearance(\n circuitJson: AnyCircuitElement[],\n {\n connMap,\n minSpacing = DEFAULT_PAD_TRACE_CLEARANCE,\n }: { connMap?: ConnectivityMap; minSpacing?: number } = {},\n): PcbViaTraceClearanceError[] {\n const vias = circuitJson.filter((el) => el.type === \"pcb_via\") as PcbVia[]\n const segments = getTraceSegments(circuitJson)\n if (vias.length === 0 || segments.length === 0) return []\n\n connMap ??= getFullConnectivityMapFromCircuitJson(circuitJson)\n const errors = new Map<\n string,\n { error: PcbViaTraceClearanceError; gap: number }\n >()\n\n for (const via of vias) {\n const viaRadius = via.outer_diameter / 2\n for (const segment of segments) {\n if (!getLayersOfPcbElement(via).includes(segment.layer)) continue\n if (connMap.areIdsConnected(segment.pcb_trace_id, via.pcb_via_id))\n continue\n\n const gap =\n segmentToCircleMinDistance(\n { x: segment.x1, y: segment.y1 },\n { x: segment.x2, y: segment.y2 },\n {\n x: via.x,\n y: via.y,\n radius: viaRadius,\n },\n ) -\n segment.thickness / 2\n if (gap + EPSILON >= minSpacing) continue\n\n const closestPoint = getClosestPointOnSegment(\n { x: via.x, y: via.y },\n { x: segment.x1, y: segment.y1 },\n { x: segment.x2, y: segment.y2 },\n )\n\n const pairId = `${via.pcb_via_id}_${segment.pcb_trace_id}`\n const nextError: PcbViaTraceClearanceError = {\n type: \"pcb_via_trace_clearance_error\",\n pcb_via_trace_clearance_error_id: `via_trace_clearance_${pairId}`,\n error_type: \"pcb_via_trace_clearance_error\",\n message: `Via ${getReadableNameForElement(circuitJson, via.pcb_via_id)} and trace ${getReadableNameForElement(circuitJson, segment.pcb_trace_id)} are too close (clearance: ${formatMm(gap)}, minimum: ${formatMm(minSpacing)})`,\n pcb_via_id: via.pcb_via_id,\n pcb_trace_id: segment.pcb_trace_id,\n minimum_clearance: minSpacing,\n actual_clearance: gap,\n center: {\n x: (via.x + closestPoint.x) / 2,\n y: (via.y + closestPoint.y) / 2,\n },\n }\n\n const current = errors.get(pairId)\n if (!current || gap < current.gap) {\n errors.set(pairId, { error: nextError, gap })\n }\n }\n }\n\n return Array.from(errors.values()).map(({ error }) => error)\n}\n","import type { AnyCircuitElement } from \"circuit-json\"\n\ntype SourceComponent = Extract<\n AnyCircuitElement,\n { source_component_id: string; name: string }\n>\n\ntype SourcePort = Extract<AnyCircuitElement, { type: \"source_port\" }>\ntype SourceTrace = Extract<AnyCircuitElement, { type: \"source_trace\" }>\n\ntype SourcePinMustBeConnectedError = {\n type: \"source_pin_must_be_connected_error\"\n source_pin_must_be_connected_error_id: string\n error_type: \"source_pin_must_be_connected_error\"\n message: string\n source_component_id: string\n source_port_id: string\n subcircuit_id?: string\n}\n\n/**\n * Check that each source port with must_be_connected attribute is actually\n * connected to a trace. Returns errors for any pins that are marked as\n * must_be_connected but are floating (not connected to any trace).\n */\nexport function checkPinMustBeConnected(\n circuitJson: AnyCircuitElement[],\n): SourcePinMustBeConnectedError[] {\n const errors: SourcePinMustBeConnectedError[] = []\n\n // Get all source components, ports, and traces\n const sourceComponents = circuitJson.filter(\n (el): el is SourceComponent =>\n \"source_component_id\" in el &&\n (el.type === \"source_component\" || el.type.startsWith(\"source_simple_\")),\n )\n const sourcePorts = circuitJson.filter(\n (el): el is SourcePort => el.type === \"source_port\",\n )\n const sourceTraces = circuitJson.filter(\n (el): el is SourceTrace => el.type === \"source_trace\",\n )\n\n // Build a set of connected source port IDs\n const connectedPortIds = new Set<string>()\n for (const trace of sourceTraces) {\n for (const portId of trace.connected_source_port_ids ?? []) {\n connectedPortIds.add(portId)\n }\n }\n\n // Build a map of internally connected port groups for each component\n const componentInternalConnections = new Map<string, string[][]>()\n for (const component of sourceComponents) {\n if (\n \"internally_connected_source_port_ids\" in component &&\n component.internally_connected_source_port_ids\n ) {\n componentInternalConnections.set(\n component.source_component_id,\n component.internally_connected_source_port_ids,\n )\n }\n }\n\n // For each internal group, if any port is connected, mark all ports in the group as connected\n for (const internalGroups of componentInternalConnections.values()) {\n for (const group of internalGroups) {\n if (group.some((portId) => connectedPortIds.has(portId))) {\n for (const portId of group) {\n connectedPortIds.add(portId)\n }\n }\n }\n }\n\n // Check each port for must_be_connected attribute\n for (const port of sourcePorts) {\n if (port.must_be_connected === true) {\n if (!connectedPortIds.has(port.source_port_id)) {\n const component = sourceComponents.find(\n (c) => c.source_component_id === port.source_component_id,\n )\n const componentName = component?.name ?? \"Unknown\"\n\n errors.push({\n type: \"source_pin_must_be_connected_error\",\n source_pin_must_be_connected_error_id: `source_pin_must_be_connected_error_${port.source_port_id}`,\n error_type: \"source_pin_must_be_connected_error\",\n message: `Port ${port.name} on ${componentName} must be connected but is floating`,\n source_component_id: port.source_component_id ?? \"\",\n source_port_id: port.source_port_id,\n subcircuit_id: port.subcircuit_id,\n })\n }\n }\n }\n\n return errors\n}\n","import { cju } from \"@tscircuit/circuit-json-util\"\nimport type {\n AnyCircuitElement,\n SourceComponentPinsUnderspecifiedWarning,\n SourcePinAttributes,\n SourcePort,\n SourceComponentBase,\n} from \"circuit-json\"\n\nconst PIN_ATTRIBUTE_KEYS = [\n \"must_be_connected\",\n \"provides_power\",\n \"requires_power\",\n \"provides_ground\",\n \"requires_ground\",\n \"provides_voltage\",\n \"requires_voltage\",\n \"do_not_connect\",\n \"include_in_board_pinout\",\n \"can_use_internal_pullup\",\n \"is_using_internal_pullup\",\n \"needs_external_pullup\",\n \"can_use_internal_pulldown\",\n \"is_using_internal_pulldown\",\n \"needs_external_pulldown\",\n \"can_use_open_drain\",\n \"is_using_open_drain\",\n \"can_use_push_pull\",\n \"is_using_push_pull\",\n \"should_have_decoupling_capacitor\",\n \"recommended_decoupling_capacitor_capacitance\",\n \"is_configured_for_i2c_sda\",\n \"is_configured_for_i2c_scl\",\n \"is_configured_for_spi_mosi\",\n \"is_configured_for_spi_miso\",\n \"is_configured_for_spi_sck\",\n \"is_configured_for_spi_cs\",\n \"is_configured_for_uart_tx\",\n \"is_configured_for_uart_rx\",\n \"supports_i2c_sda\",\n \"supports_i2c_scl\",\n \"supports_spi_mosi\",\n \"supports_spi_miso\",\n \"supports_spi_sck\",\n \"supports_spi_cs\",\n \"supports_uart_tx\",\n \"supports_uart_rx\",\n] as const satisfies readonly (keyof SourcePinAttributes)[]\n\nfunction hasAnyPinAttribute(port: SourcePort): boolean {\n return PIN_ATTRIBUTE_KEYS.some((key) => port[key] !== undefined)\n}\n\n/**\n * Check that each component with ports has at least one pin attribute\n * specified across its ports. Returns a warning when all pins are\n * underspecified (no SourcePinAttributes fields set on any port).\n */\nexport function checkAllPinsInComponentAreUnderspecified(\n circuitJson: AnyCircuitElement[],\n): SourceComponentPinsUnderspecifiedWarning[] {\n const warnings: SourceComponentPinsUnderspecifiedWarning[] = []\n const db = cju(circuitJson)\n\n const sourceComponents = db.source_component.list() as SourceComponentBase[]\n const sourcePorts = db.source_port.list() as SourcePort[]\n\n const portsByComponent = new Map<string, SourcePort[]>()\n for (const port of sourcePorts) {\n if (!port.source_component_id) continue\n const existing = portsByComponent.get(port.source_component_id) ?? []\n existing.push(port)\n portsByComponent.set(port.source_component_id, existing)\n }\n\n for (const component of sourceComponents) {\n if (component.ftype !== \"simple_chip\") continue\n\n const componentPorts =\n portsByComponent.get(component.source_component_id) ?? []\n if (componentPorts.length === 0) continue\n\n const hasAnySpecifiedAttributes = componentPorts.some((port) =>\n hasAnyPinAttribute(port),\n )\n\n if (hasAnySpecifiedAttributes) continue\n\n warnings.push({\n type: \"source_component_pins_underspecified_warning\",\n source_component_pins_underspecified_warning_id: `source_component_pins_underspecified_warning_${component.source_component_id}`,\n warning_type: \"source_component_pins_underspecified_warning\",\n message: `All pins on ${component.name} are underspecified (no pinAttributes set)`,\n source_component_id: component.source_component_id,\n source_port_ids: componentPorts.map((port) => port.source_port_id),\n subcircuit_id: componentPorts[0]?.subcircuit_id,\n })\n }\n\n return warnings\n}\n","import { cju } from \"@tscircuit/circuit-json-util\"\nimport type {\n AnyCircuitElement,\n SourceComponentBase,\n SourceNoPowerPinDefinedWarning,\n SourcePort,\n} from \"circuit-json\"\n\n/**\n * Check that each chip has at least one pin marked as requires_power=true.\n * Returns warnings for chips where no pin declares requires_power.\n */\nexport function checkNoPowerPinDefined(\n circuitJson: AnyCircuitElement[],\n): SourceNoPowerPinDefinedWarning[] {\n const warnings: SourceNoPowerPinDefinedWarning[] = []\n const db = cju(circuitJson)\n\n const sourceComponents = db.source_component.list() as SourceComponentBase[]\n const sourcePorts = db.source_port.list() as SourcePort[]\n\n const portsByComponent = new Map<string, SourcePort[]>()\n for (const port of sourcePorts) {\n if (!port.source_component_id) continue\n const existing = portsByComponent.get(port.source_component_id) ?? []\n existing.push(port)\n portsByComponent.set(port.source_component_id, existing)\n }\n\n for (const component of sourceComponents) {\n if (component.ftype !== \"simple_chip\") continue\n\n const componentPorts =\n portsByComponent.get(component.source_component_id) ?? []\n if (componentPorts.length === 0) continue\n\n const hasRequiredPowerPin = componentPorts.some(\n (port) => port.requires_power === true,\n )\n if (hasRequiredPowerPin) continue\n\n warnings.push({\n type: \"source_no_power_pin_defined_warning\",\n source_no_power_pin_defined_warning_id: `source_no_power_pin_defined_warning_${component.source_component_id}`,\n warning_type: \"source_no_power_pin_defined_warning\",\n message: `${component.name} has no pin with requires_power=true`,\n source_component_id: component.source_component_id,\n source_port_ids: componentPorts.map((port) => port.source_port_id),\n subcircuit_id: componentPorts[0]?.subcircuit_id,\n })\n }\n\n return warnings\n}\n","import { cju } from \"@tscircuit/circuit-json-util\"\nimport type {\n AnyCircuitElement,\n SourceComponentBase,\n SourceNoGroundPinDefinedWarning,\n SourcePort,\n} from \"circuit-json\"\n\n/**\n * Check that each chip has at least one pin marked as requires_ground=true.\n * Returns warnings for chips where no pin declares requires_ground.\n */\nexport function checkNoGroundPinDefined(\n circuitJson: AnyCircuitElement[],\n): SourceNoGroundPinDefinedWarning[] {\n const warnings: SourceNoGroundPinDefinedWarning[] = []\n const db = cju(circuitJson)\n\n const sourceComponents = db.source_component.list() as SourceComponentBase[]\n const sourcePorts = db.source_port.list() as SourcePort[]\n\n const portsByComponent = new Map<string, SourcePort[]>()\n for (const port of sourcePorts) {\n if (!port.source_component_id) continue\n const existing = portsByComponent.get(port.source_component_id) ?? []\n existing.push(port)\n portsByComponent.set(port.source_component_id, existing)\n }\n\n for (const component of sourceComponents) {\n if (component.ftype !== \"simple_chip\") continue\n\n const componentPorts =\n portsByComponent.get(component.source_component_id) ?? []\n if (componentPorts.length === 0) continue\n\n const hasRequiredGroundPin = componentPorts.some(\n (port) => port.requires_ground === true,\n )\n if (hasRequiredGroundPin) continue\n\n warnings.push({\n type: \"source_no_ground_pin_defined_warning\",\n source_no_ground_pin_defined_warning_id: `source_no_ground_pin_defined_warning_${component.source_component_id}`,\n warning_type: \"source_no_ground_pin_defined_warning\",\n message: `${component.name} has no pin with requires_ground=true`,\n source_component_id: component.source_component_id,\n source_port_ids: componentPorts.map((port) => port.source_port_id),\n subcircuit_id: componentPorts[0]?.subcircuit_id,\n })\n }\n\n return warnings\n}\n","import { getBoardBounds } from \"@tscircuit/circuit-json-util\"\nimport type {\n AnyCircuitElement,\n PcbBoard,\n PcbComponent,\n PcbConnectorNotInAccessibleOrientationWarning,\n} from \"circuit-json\"\nimport { getReadableNameForComponent } from \"./util/get-readable-names\"\n\ntype FacingDirection = \"x-\" | \"x+\" | \"y+\" | \"y-\"\n\nfunction getFacingDirection(component: PcbComponent): FacingDirection | null {\n if (!component.center || !component.cable_insertion_center) return null\n\n const dx = component.cable_insertion_center.x - component.center.x\n const dy = component.cable_insertion_center.y - component.center.y\n\n if (Math.abs(dx) < 1e-6 && Math.abs(dy) < 1e-6) return null\n\n if (Math.abs(dx) >= Math.abs(dy)) {\n return dx >= 0 ? \"x+\" : \"x-\"\n }\n\n return dy >= 0 ? \"y+\" : \"y-\"\n}\n\nfunction getRecommendedFacingDirection(\n component: PcbComponent,\n bounds: { minX: number; maxX: number; minY: number; maxY: number },\n): FacingDirection | null {\n if (!component.center) return null\n\n const distances = [\n { direction: \"x-\" as const, distance: component.center.x - bounds.minX },\n { direction: \"x+\" as const, distance: bounds.maxX - component.center.x },\n { direction: \"y-\" as const, distance: component.center.y - bounds.minY },\n { direction: \"y+\" as const, distance: bounds.maxY - component.center.y },\n ]\n\n distances.sort((a, b) => a.distance - b.distance)\n return distances[0]?.direction ?? null\n}\n\nexport function checkConnectorAccessibleOrientation(\n circuitJson: AnyCircuitElement[],\n): PcbConnectorNotInAccessibleOrientationWarning[] {\n const board = circuitJson.find(\n (el): el is PcbBoard => el.type === \"pcb_board\",\n )\n if (!board) return []\n\n const bounds = (() => {\n try {\n return getBoardBounds(board)\n } catch {\n return null\n }\n })()\n if (!bounds) return []\n\n const warnings: PcbConnectorNotInAccessibleOrientationWarning[] = []\n\n const components = circuitJson.filter(\n (el): el is PcbComponent => el.type === \"pcb_component\",\n )\n\n for (const component of components) {\n if (!component.cable_insertion_center) continue\n\n const facingDirection = getFacingDirection(component)\n const recommendedFacingDirection = getRecommendedFacingDirection(\n component,\n bounds,\n )\n\n if (!facingDirection || !recommendedFacingDirection) continue\n if (facingDirection === recommendedFacingDirection) continue\n\n const componentName = getReadableNameForComponent(\n circuitJson,\n component.pcb_component_id,\n )\n\n warnings.push({\n type: \"pcb_connector_not_in_accessible_orientation_warning\",\n warning_type: \"pcb_connector_not_in_accessible_orientation_warning\",\n pcb_connector_not_in_accessible_orientation_warning_id: `pcb_connector_not_in_accessible_orientation_warning_${component.pcb_component_id}`,\n message: `${componentName} is facing ${facingDirection} but should face ${recommendedFacingDirection} so the connector is accessible from the board edge`,\n pcb_component_id: component.pcb_component_id,\n source_component_id: component.source_component_id,\n pcb_board_id: board.pcb_board_id,\n facing_direction: facingDirection,\n recommended_facing_direction: recommendedFacingDirection,\n subcircuit_id: component.subcircuit_id,\n })\n }\n\n return warnings\n}\n","import {\n doSegmentsIntersect,\n isPointInsidePolygon,\n} from \"@tscircuit/math-utils\"\nimport type {\n AnyCircuitElement,\n PcbCourtyardOverlapError,\n PcbCourtyardRect,\n PcbCourtyardCircle,\n PcbCourtyardOutline,\n} from \"circuit-json\"\n\ntype CourtyardElement =\n | PcbCourtyardRect\n | PcbCourtyardCircle\n | PcbCourtyardOutline\n\nfunction getCourtyardPolygon(el: CourtyardElement): { x: number; y: number }[] {\n if (el.type === \"pcb_courtyard_rect\") {\n const hw = el.width / 2\n const hh = el.height / 2\n const corners = [\n { x: -hw, y: -hh },\n { x: +hw, y: -hh },\n { x: +hw, y: +hh },\n { x: -hw, y: +hh },\n ]\n const angle = ((el.ccw_rotation ?? 0) * Math.PI) / 180\n const cos = Math.cos(angle)\n const sin = Math.sin(angle)\n return corners.map(({ x, y }) => ({\n x: el.center.x + x * cos - y * sin,\n y: el.center.y + x * sin + y * cos,\n }))\n }\n if (el.type === \"pcb_courtyard_circle\") {\n const N = 32\n return Array.from({ length: N }, (_, i) => {\n const a = (2 * Math.PI * i) / N\n return {\n x: el.center.x + el.radius * Math.cos(a),\n y: el.center.y + el.radius * Math.sin(a),\n }\n })\n }\n return el.outline\n}\n\nfunction getComponentName(\n circuitJson: AnyCircuitElement[],\n pcbComponentId: string,\n): string {\n const pcbComponent = circuitJson.find(\n (el) =>\n el.type === \"pcb_component\" && el.pcb_component_id === pcbComponentId,\n )\n if (pcbComponent?.type !== \"pcb_component\") return pcbComponentId\n const sourceComponent = circuitJson.find(\n (el) =>\n el.type === \"source_component\" &&\n el.source_component_id === pcbComponent.source_component_id,\n )\n if (sourceComponent?.type === \"source_component\" && sourceComponent.name) {\n return sourceComponent.name\n }\n return pcbComponentId\n}\n\ntype Point = { x: number; y: number }\n\nfunction polygonsOverlap(polyA: Point[], polyB: Point[]): boolean {\n // Check if any vertex of A is inside B or vice versa\n if (polyA.some((p) => isPointInsidePolygon(p, polyB))) return true\n if (polyB.some((p) => isPointInsidePolygon(p, polyA))) return true\n // Check if any edge of A intersects any edge of B\n for (let i = 0; i < polyA.length; i++) {\n const a1 = polyA[i]\n const a2 = polyA[(i + 1) % polyA.length]\n for (let j = 0; j < polyB.length; j++) {\n const b1 = polyB[j]\n const b2 = polyB[(j + 1) % polyB.length]\n if (doSegmentsIntersect(a1, a2, b1, b2)) return true\n }\n }\n return false\n}\n\n/**\n * Check for overlapping PCB component courtyards.\n * Returns one error per pair of components whose courtyard elements overlap.\n */\nexport function checkCourtyardOverlap(\n circuitJson: AnyCircuitElement[],\n): PcbCourtyardOverlapError[] {\n const courtyards = circuitJson.filter(\n (el): el is CourtyardElement =>\n el.type === \"pcb_courtyard_rect\" ||\n el.type === \"pcb_courtyard_circle\" ||\n el.type === \"pcb_courtyard_outline\",\n )\n\n // Group by component\n const byComponent = new Map<string, CourtyardElement[]>()\n for (const el of courtyards) {\n const id = el.pcb_component_id\n if (!byComponent.has(id)) byComponent.set(id, [])\n byComponent.get(id)!.push(el)\n }\n\n const componentIds = Array.from(byComponent.keys())\n const errors: PcbCourtyardOverlapError[] = []\n\n for (let i = 0; i < componentIds.length; i++) {\n for (let j = i + 1; j < componentIds.length; j++) {\n const idA = componentIds[i]\n const idB = componentIds[j]\n\n let overlapping = false\n outer: for (const a of byComponent.get(idA)!) {\n for (const b of byComponent.get(idB)!) {\n // Skip overlap check if courtyards are on different layers\n if (\"layer\" in a && \"layer\" in b && a.layer !== b.layer) {\n continue\n }\n const polyA = getCourtyardPolygon(a)\n const polyB = getCourtyardPolygon(b)\n if (polygonsOverlap(polyA, polyB)) {\n overlapping = true\n break outer\n }\n }\n }\n\n if (overlapping) {\n errors.push({\n type: \"pcb_courtyard_overlap_error\",\n pcb_error_id: `pcb_courtyard_overlap_${idA}_${idB}`,\n error_type: \"pcb_courtyard_overlap_error\",\n message: `Courtyard of ${getComponentName(circuitJson, idA)} overlaps with courtyard of ${getComponentName(circuitJson, idB)}`,\n pcb_component_ids: [idA, idB],\n })\n }\n }\n }\n\n return errors\n}\n","import type {\n AnyCircuitElement,\n PcbTraceError,\n PcbPort,\n PcbTrace,\n SourceTrace,\n PcbSmtPad,\n PcbPlatedHole,\n} from \"circuit-json\"\nimport { isPointInPad } from \"./is-point-in-pad\"\nimport { getReadableNameForPcbPort } from \"@tscircuit/circuit-json-util\"\n\nfunction checkTracesAreContiguous(\n circuitJson: AnyCircuitElement[],\n): PcbTraceError[] {\n const errors: PcbTraceError[] = []\n\n const pcbPorts = circuitJson.filter(\n (el) => el.type === \"pcb_port\",\n ) as PcbPort[]\n const pcbTraces = circuitJson.filter(\n (el) => el.type === \"pcb_trace\",\n ) as PcbTrace[]\n const sourceTraces = circuitJson.filter(\n (el) => el.type === \"source_trace\",\n ) as SourceTrace[]\n const pcbSmtPads = circuitJson.filter(\n (el) => el.type === \"pcb_smtpad\",\n ) as PcbSmtPad[]\n const pcbPlatedHoles = circuitJson.filter(\n (el) => el.type === \"pcb_plated_hole\",\n ) as PcbPlatedHole[]\n\n const padMap = new Map<string, PcbSmtPad | PcbPlatedHole>()\n\n for (const pad of pcbSmtPads) {\n if (pad.pcb_port_id) {\n padMap.set(pad.pcb_port_id, pad)\n }\n }\n\n for (const hole of pcbPlatedHoles) {\n if (hole.pcb_port_id) {\n padMap.set(hole.pcb_port_id, hole)\n }\n }\n\n for (const trace of pcbTraces) {\n if (trace.route.length === 0) continue\n\n const firstPoint = trace.route[0]\n const lastPoint = trace.route[trace.route.length - 1]\n\n const sourceTrace = sourceTraces.find(\n (st) => st.source_trace_id === trace.source_trace_id,\n )\n\n const expectedPorts = sourceTrace\n ? pcbPorts.filter((port) =>\n sourceTrace.connected_source_port_ids?.includes(port.source_port_id),\n )\n : []\n\n for (let i = 1; i < trace.route.length - 1; i++) {\n const prevPoint = trace.route[i - 1]\n const currentPoint = trace.route[i]\n const nextPoint = trace.route[i + 1]\n\n if (currentPoint.route_type === \"via\") {\n const prevIsWire = prevPoint.route_type === \"wire\"\n const nextIsWire = nextPoint.route_type === \"wire\"\n\n if (prevIsWire && nextIsWire) {\n const prevAligned =\n Math.abs(prevPoint.x - currentPoint.x) < 0.01 &&\n Math.abs(prevPoint.y - currentPoint.y) < 0.01\n\n const nextAligned =\n Math.abs(nextPoint.x - currentPoint.x) < 0.01 &&\n Math.abs(nextPoint.y - currentPoint.y) < 0.01\n\n if (!prevAligned || !nextAligned) {\n const traceName =\n sourceTrace?.display_name || trace.source_trace_id || \"unknown\"\n errors.push({\n type: \"pcb_trace_error\",\n message: `Via in trace [${traceName}] is misaligned at position {x: ${currentPoint.x}, y: ${currentPoint.y}}.`,\n source_trace_id:\n sourceTrace?.source_trace_id ||\n trace.source_trace_id ||\n `!${trace.pcb_trace_id}`,\n error_type: \"pcb_trace_error\",\n pcb_trace_id: trace.pcb_trace_id,\n pcb_trace_error_id: \"\",\n pcb_component_ids: [],\n pcb_port_ids: [],\n })\n }\n }\n }\n }\n\n const traceName =\n sourceTrace?.display_name || trace.source_trace_id || \"unknown\"\n\n // For traces with known expected ports, check specific connections\n for (const port of expectedPorts) {\n if (!port.pcb_port_id) continue\n\n const pad = padMap.get(port.pcb_port_id)\n\n if (!pad) continue\n\n const isFirstPointConnected =\n firstPoint.route_type === \"wire\" &&\n isPointInPad({ x: firstPoint.x, y: firstPoint.y }, pad)\n\n const isLastPointConnected =\n lastPoint.route_type === \"wire\" &&\n isPointInPad({ x: lastPoint.x, y: lastPoint.y }, pad)\n\n if (!isFirstPointConnected && !isLastPointConnected) {\n const portName = getReadableNameForPcbPort(\n circuitJson,\n port.pcb_port_id,\n ).replace(\"pcb_port\", \"\")\n const padType = pad.type.replace(/pcb_/, \"\")\n // Use the midpoint between trace endpoints as error location\n const errorCenter = {\n x: (firstPoint.x + lastPoint.x) / 2,\n y: (firstPoint.y + lastPoint.y) / 2,\n }\n errors.push({\n type: \"pcb_trace_error\",\n message: `Trace [${traceName}] is missing a connection to ${padType}${portName}`,\n source_trace_id:\n sourceTrace?.source_trace_id ||\n trace.source_trace_id ||\n `!${trace.pcb_trace_id}`,\n error_type: \"pcb_trace_error\",\n pcb_trace_id: trace.pcb_trace_id,\n pcb_trace_error_id: \"\",\n center: errorCenter,\n pcb_component_ids: [],\n pcb_port_ids: [port.pcb_port_id],\n })\n }\n }\n\n // For net-level traces (no expected ports), check if endpoints are floating\n if (expectedPorts.length === 0) {\n let firstConnectsToAnyPad = false\n let lastConnectsToAnyPad = false\n\n for (const [portId, pad] of padMap) {\n if (\n firstPoint.route_type === \"wire\" &&\n isPointInPad({ x: firstPoint.x, y: firstPoint.y }, pad)\n ) {\n firstConnectsToAnyPad = true\n }\n if (\n lastPoint.route_type === \"wire\" &&\n isPointInPad({ x: lastPoint.x, y: lastPoint.y }, pad)\n ) {\n lastConnectsToAnyPad = true\n }\n }\n\n if (!firstConnectsToAnyPad && firstPoint.route_type === \"wire\") {\n errors.push({\n type: \"pcb_trace_error\",\n message: `Trace [${traceName}] has disconnected endpoint at (${firstPoint.x.toFixed(2)}, ${firstPoint.y.toFixed(2)})`,\n source_trace_id:\n sourceTrace?.source_trace_id ||\n trace.source_trace_id ||\n `!${trace.pcb_trace_id}`,\n error_type: \"pcb_trace_error\",\n pcb_trace_id: trace.pcb_trace_id,\n pcb_trace_error_id: \"\",\n center: { x: firstPoint.x, y: firstPoint.y },\n pcb_component_ids: [],\n pcb_port_ids: [],\n })\n }\n if (!lastConnectsToAnyPad && lastPoint.route_type === \"wire\") {\n errors.push({\n type: \"pcb_trace_error\",\n message: `Trace [${traceName}] has disconnected endpoint at (${lastPoint.x.toFixed(2)}, ${lastPoint.y.toFixed(2)})`,\n source_trace_id:\n sourceTrace?.source_trace_id ||\n trace.source_trace_id ||\n `!${trace.pcb_trace_id}`,\n error_type: \"pcb_trace_error\",\n pcb_trace_id: trace.pcb_trace_id,\n pcb_trace_error_id: \"\",\n center: { x: lastPoint.x, y: lastPoint.y },\n pcb_component_ids: [],\n pcb_port_ids: [],\n })\n }\n }\n }\n\n return errors\n}\n\nexport { checkTracesAreContiguous }\n","import type { AnyCircuitElement } from \"circuit-json\"\nimport { checkAllPinsInComponentAreUnderspecified } from \"./check-all-pins-in-component-are-underspecified\"\nimport { checkConnectorAccessibleOrientation } from \"./check-connector-accessible-orientation\"\nimport { checkCourtyardOverlap } from \"./check-courtyard-overlap/checkCourtyardOverlap\"\nimport { checkDifferentNetViaSpacing } from \"./check-different-net-via-spacing\"\nimport { checkEachPcbPortConnectedToPcbTraces } from \"./check-each-pcb-port-connected-to-pcb-trace\"\nimport { checkEachPcbTraceNonOverlapping } from \"./check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping\"\nimport { checkNoGroundPinDefined } from \"./check-no-ground-pin-defined\"\nimport { checkNoPowerPinDefined } from \"./check-no-power-pin-defined\"\nimport { checkPadPadClearance } from \"./check-pad-pad-clearance\"\nimport { checkPadTraceClearance } from \"./check-pad-trace-clearance\"\nimport { checkPcbComponentsOutOfBoard } from \"./check-pcb-components-out-of-board/checkPcbComponentsOutOfBoard\"\nimport { checkViasOffBoard } from \"./check-pcb-components-out-of-board/checkViasOffBoard\"\nimport { checkPcbComponentOverlap } from \"./check-pcb-components-overlap/checkPcbComponentOverlap\"\nimport { checkPinMustBeConnected } from \"./check-pin-must-be-connected\"\nimport { checkSameNetViaSpacing } from \"./check-same-net-via-spacing\"\nimport { checkSourceTracesHavePcbTraces } from \"./check-source-traces-have-pcb-traces\"\nimport { checkPcbTracesOutOfBoard } from \"./check-trace-out-of-board/checkTraceOutOfBoard\"\nimport { checkTracesAreContiguous } from \"./check-traces-are-contiguous/check-traces-are-contiguous\"\nimport { checkViaTraceClearance } from \"./check-via-trace-clearance\"\n\nexport async function runAllPlacementChecks(circuitJson: AnyCircuitElement[]) {\n return [\n ...checkViasOffBoard(circuitJson),\n ...checkPcbComponentsOutOfBoard(circuitJson),\n ...checkPcbComponentOverlap(circuitJson),\n ...checkPadPadClearance(circuitJson),\n ...checkPadTraceClearance(circuitJson),\n ...checkCourtyardOverlap(circuitJson),\n ...checkConnectorAccessibleOrientation(circuitJson),\n ]\n}\n\nexport async function runAllNetlistChecks(circuitJson: AnyCircuitElement[]) {\n return [...checkPinMustBeConnected(circuitJson)]\n}\n\nexport async function runAllPinSpecificationChecks(\n circuitJson: AnyCircuitElement[],\n) {\n return [\n ...checkAllPinsInComponentAreUnderspecified(circuitJson),\n ...checkNoPowerPinDefined(circuitJson),\n ...checkNoGroundPinDefined(circuitJson),\n ]\n}\n\nexport async function runAllRoutingChecks(circuitJson: AnyCircuitElement[]) {\n return [\n ...checkEachPcbPortConnectedToPcbTraces(circuitJson),\n ...checkSourceTracesHavePcbTraces(circuitJson),\n ...checkEachPcbTraceNonOverlapping(circuitJson),\n ...checkViaTraceClearance(circuitJson),\n ...checkSameNetViaSpacing(circuitJson),\n ...checkDifferentNetViaSpacing(circuitJson),\n // ...checkTracesAreContiguous(circuitJson),\n ...checkPcbTracesOutOfBoard(circuitJson),\n ]\n}\n\nexport async function runAllChecks(circuitJson: AnyCircuitElement[]) {\n return [\n ...(await runAllPlacementChecks(circuitJson)),\n ...(await runAllNetlistChecks(circuitJson)),\n ...(await runAllPinSpecificationChecks(circuitJson)),\n ...(await runAllRoutingChecks(circuitJson)),\n ]\n}\n"],"mappings":"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","names":["getReadableNameForElement","getFullConnectivityMapFromCircuitJson","getBoundsOfPcbElements","t","closestOnB","closestOnA","distance","clamp","dx","dy","getFullConnectivityMapFromCircuitJson","getReadableNameForElement","gap","distance","getReadableNameForElement","getReadableNameForElement","getReadableNameForElement","getFullConnectivityMapFromCircuitJson","distance","distance","getFullConnectivityMapFromCircuitJson","distance","getReadableNameForElement","getReadableNameForElement","getFullConnectivityMapFromCircuitJson","getFullConnectivityMapFromCircuitJson","distance","getReadableNameForElement","cju","getFullConnectivityMapFromCircuitJson","cju","getFullConnectivityMapFromCircuitJson","cju","segmentToSegmentMinDistance","distance","cju","getBoundsOfPcbElements","getPrimaryId","doBoundsOverlap","getFullConnectivityMapFromCircuitJson","getBoundsOfPcbElements","getBoundsOfPcbElements","getPrimaryId","getFullConnectivityMapFromCircuitJson","cju","getBoundsOfPcbElements","doBoundsOverlap","getPrimaryId","getReadableNameForElement","getFullConnectivityMapFromCircuitJson","cju","getBoundsOfPcbElements","getBoundsOfPcbElements","cju","getFullConnectivityMapFromCircuitJson","getPrimaryId","getReadableNameForElement","getPrimaryId","getReadableNameForElement","segmentToBoundsMinDistance","segmentToCircleMinDistance","getFullConnectivityMapFromCircuitJson","getFullConnectivityMapFromCircuitJson","getPrimaryId","segmentToCircleMinDistance","segmentToBoundsMinDistance","getReadableNameForElement","getReadableNameForElement","segmentToCircleMinDistance","getFullConnectivityMapFromCircuitJson","getFullConnectivityMapFromCircuitJson","segmentToCircleMinDistance","getReadableNameForElement","cju","cju","cju","getComponentName","getReadableNameForPcbPort"]}
|
|
1
|
+
{"version":3,"sources":["../lib/add-start-and-end-port-ids-if-missing.ts","../lib/check-each-pcb-port-connected-to-pcb-trace.ts","../lib/util/get-readable-names.ts","../lib/check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping.ts","../lib/data-structures/SpatialIndex.ts","../lib/check-each-pcb-trace-non-overlapping/getCollidableBounds.ts","../lib/drc-defaults.ts","../lib/check-each-pcb-trace-non-overlapping/getPcbPortIdsConnectedToTraces.ts","../lib/check-each-pcb-trace-non-overlapping/getClosestPointBetweenSegments.ts","../lib/check-each-pcb-trace-non-overlapping/getRadiusOfCircuitJsonElement.ts","../lib/check-each-pcb-trace-non-overlapping/getClosestPointBetweenSegmentAndBounds.ts","../lib/util/getLayersOfPcbElement.ts","../lib/net-manager.ts","../lib/check-pcb-components-out-of-board/checkViasOffBoard.ts","../lib/check-pcb-components-out-of-board/checkPcbComponentsOutOfBoard.ts","../lib/check-same-net-via-spacing.ts","../lib/util/distance.ts","../lib/util/viasAreAtSameLocation.ts","../lib/check-different-net-via-spacing.ts","../lib/check-source-traces-match-pcb-trace-thickness.ts","../lib/check-source-traces-have-pcb-traces.ts","../lib/check-trace-out-of-board/checkTraceOutOfBoard.ts","../lib/check-pcb-components-overlap/checkPcbComponentOverlap.ts","../lib/check-pcb-components-overlap/doPcbElementsOverlap.ts","../lib/check-pad-pad-clearance.ts","../lib/check-pad-clearance/common.ts","../lib/check-pad-trace-clearance.ts","../lib/check-via-trace-clearance.ts","../lib/check-pin-must-be-connected.ts","../lib/check-all-pins-in-component-are-underspecified.ts","../lib/check-no-power-pin-defined.ts","../lib/check-no-ground-pin-defined.ts","../lib/check-connector-accessible-orientation.ts","../lib/check-courtyard-overlap/checkCourtyardOverlap.ts","../lib/check-traces-are-contiguous/check-traces-are-contiguous.ts","../lib/run-all-checks.ts"],"sourcesContent":["import type {\n PcbPort,\n PcbTrace,\n AnyCircuitElement,\n PcbSmtPad,\n} from \"circuit-json\"\n\nfunction distance(x1: number, y1: number, x2: number, y2: number): number {\n return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)\n}\n\n/**\n * HACK: this whole method and all usage of it is a hack because of this issue:\n * https://github.com/tscircuit/tscircuit/issues/291\n */\nexport const addStartAndEndPortIdsIfMissing = (\n soup: AnyCircuitElement[],\n): void => {\n const pcbPorts: PcbPort[] = soup.filter((item) => item.type === \"pcb_port\")\n const pcbSmtPads: PcbSmtPad[] = soup.filter(\n (item) => item.type === \"pcb_smtpad\",\n )\n const pcbTraces: PcbTrace[] = soup.filter((item) => item.type === \"pcb_trace\")\n\n function findPortIdOverlappingPoint(\n point: {\n x: number\n y: number\n },\n options: { isFirstOrLastPoint?: boolean; traceWidth?: number } = {},\n ): string | null {\n const traceWidth = options.traceWidth || 0\n const directPort = pcbPorts.find(\n (port) => distance(port.x, port.y, point.x, point.y) < 0.01,\n )\n if (directPort) return directPort.pcb_port_id\n\n // If it starts or ends inside an smtpad, we'll connect it to the por\n if (options.isFirstOrLastPoint) {\n const smtPad = pcbSmtPads.find((pad) => {\n if (pad.shape === \"rect\") {\n return (\n Math.abs(point.x - pad.x) < pad.width / 2 + traceWidth / 2 &&\n Math.abs(point.y - pad.y) < pad.height / 2 + traceWidth / 2\n )\n // biome-ignore lint/style/noUselessElse: <explanation>\n } else if (pad.shape === \"circle\") {\n return distance(point.x, point.y, pad.x, pad.y) < pad.radius\n }\n })\n if (smtPad) return smtPad.pcb_port_id ?? null\n }\n\n return null\n }\n\n // Add start_pcb_port_id and end_pcb_port_id if not present\n for (const trace of pcbTraces) {\n for (let index = 0; index < trace.route.length; index++) {\n const segment = trace.route[index]\n const isFirstOrLastPoint = index === 0 || index === trace.route.length - 1\n if (segment.route_type === \"wire\") {\n if (!segment.start_pcb_port_id && index === 0) {\n const startPortId = findPortIdOverlappingPoint(segment, {\n isFirstOrLastPoint,\n traceWidth: segment.width,\n })\n if (startPortId) {\n segment.start_pcb_port_id = startPortId\n }\n }\n if (!segment.end_pcb_port_id && index === trace.route.length - 1) {\n const endPortId = findPortIdOverlappingPoint(segment, {\n isFirstOrLastPoint,\n traceWidth: segment.width,\n })\n if (endPortId) {\n segment.end_pcb_port_id = endPortId\n }\n }\n }\n }\n }\n}\n","import type {\n PcbPort,\n SourceTrace,\n AnyCircuitElement,\n PcbPortNotConnectedError,\n} from \"circuit-json\"\nimport { addStartAndEndPortIdsIfMissing } from \"./add-start-and-end-port-ids-if-missing\"\nimport { getFullConnectivityMapFromCircuitJson } from \"circuit-json-to-connectivity-map\"\nimport { getReadableNameForPort } from \"./util/get-readable-names\"\n\nfunction checkEachPcbPortConnectedToPcbTraces(\n circuitJson: AnyCircuitElement[],\n): PcbPortNotConnectedError[] {\n addStartAndEndPortIdsIfMissing(circuitJson)\n const sourceTraces: SourceTrace[] = circuitJson.filter(\n (item) => item.type === \"source_trace\",\n ) as SourceTrace[]\n\n const pcbPorts: PcbPort[] = circuitJson.filter(\n (item) => item.type === \"pcb_port\",\n ) as PcbPort[]\n\n const errors: PcbPortNotConnectedError[] = []\n\n // Generate the connectivity map from the circuit\n const connectivityMap = getFullConnectivityMapFromCircuitJson(circuitJson)\n\n // Create a map from source_port_id to pcb_port for quick lookup\n const sourcePortToPcbPort = new Map<string, PcbPort>()\n for (const pcbPort of pcbPorts) {\n sourcePortToPcbPort.set(pcbPort.source_port_id, pcbPort)\n }\n\n // Process each source trace\n for (const sourceTrace of sourceTraces) {\n const connectedSourcePortIds = sourceTrace.connected_source_port_ids\n\n // Skip traces with less than 2 ports (nothing to connect)\n if (connectedSourcePortIds.length < 2) {\n continue\n }\n\n // Find corresponding PCB ports for all source ports in this trace\n const pcbPortsInTrace: PcbPort[] = []\n const missingPcbPorts: string[] = []\n\n for (const sourcePortId of connectedSourcePortIds) {\n const pcbPort = sourcePortToPcbPort.get(sourcePortId)\n if (pcbPort) {\n pcbPortsInTrace.push(pcbPort)\n } else {\n missingPcbPorts.push(sourcePortId)\n }\n }\n\n // Skip if we don't have at least 2 PCB ports to connect\n if (pcbPortsInTrace.length < 2) {\n continue\n }\n\n // Get the net ID for the first PCB port as reference\n const firstPcbPort = pcbPortsInTrace[0]\n const referenceNetId = connectivityMap.getNetConnectedToId(\n firstPcbPort.pcb_port_id,\n )\n\n const netElementIds = connectivityMap.getIdsConnectedToNet(referenceNetId!)\n const pcbTraceIds = netElementIds.filter((id) =>\n circuitJson.some(\n (element) =>\n element.type === \"pcb_trace\" &&\n ((\"pcb_trace_id\" in element && element.pcb_trace_id === id) ||\n (\"route_id\" in element && element.route_id === id)),\n ),\n )\n\n if (pcbTraceIds.length === 0) {\n // Check if this is a trivial case (only 2 ports on same component)\n const uniqueComponentIds = new Set(\n pcbPortsInTrace.map((p) => p.pcb_component_id),\n )\n\n if (uniqueComponentIds.size > 1) {\n // Ports are on different components but no PCB traces connect them\n errors.push({\n type: \"pcb_port_not_connected_error\",\n message: `Ports [${pcbPortsInTrace.map((p) => getReadableNameForPort(circuitJson, p.pcb_port_id)).join(\", \")}] are not connected together through the same net.`,\n error_type: \"pcb_port_not_connected_error\",\n pcb_port_ids: pcbPortsInTrace.map((p) => p.pcb_port_id),\n pcb_component_ids: pcbPortsInTrace\n .map((p) => p.pcb_component_id)\n .filter((id): id is string => id !== undefined),\n pcb_port_not_connected_error_id: `pcb_port_not_connected_error_trace_${sourceTrace.source_trace_id}`,\n })\n }\n }\n }\n\n return errors\n}\n\nexport { checkEachPcbPortConnectedToPcbTraces }\n","import type {\n AnyCircuitElement,\n PcbPlatedHole,\n PcbSmtPad,\n SourceTrace,\n} from \"circuit-json\"\nimport {\n getReadableNameForElement,\n getReadableNameForPcbPort,\n getBoundsOfPcbElements,\n} from \"@tscircuit/circuit-json-util\"\n\nconst CIRCUIT_JSON_ID_PATTERN =\n /\\b(?:pcb|source|schematic|subcircuit)_[a-z0-9_]+\\b/i\n\nconst sanitizeReadableName = (\n candidate: string | null | undefined,\n id: string,\n fallbackLabel: string,\n): string => {\n if (\n !candidate ||\n candidate === id ||\n CIRCUIT_JSON_ID_PATTERN.test(candidate)\n ) {\n return fallbackLabel\n }\n return candidate\n}\n\nconst firstReadableName = (\n candidates: Array<string | null | undefined>,\n id: string,\n): string => {\n for (const candidate of candidates) {\n const readableName = sanitizeReadableName(candidate, id, \"\")\n if (readableName) return readableName\n }\n return \"\"\n}\n\nexport const getReadableNameForComponent = (\n circuitJson: AnyCircuitElement[],\n pcbComponentId: string,\n): string =>\n sanitizeReadableName(\n getReadableNameForElement(circuitJson, pcbComponentId),\n pcbComponentId,\n \"component\",\n )\n\nexport const getReadableNameForPort = (\n circuitJson: AnyCircuitElement[],\n pcbPortId: string,\n): string => {\n const pcbPort = circuitJson.find(\n (element) =>\n element.type === \"pcb_port\" && element.pcb_port_id === pcbPortId,\n )\n\n if (pcbPort?.type === \"pcb_port\") {\n const sourcePort = circuitJson.find(\n (element) =>\n element.type === \"source_port\" &&\n element.source_port_id === pcbPort.source_port_id,\n )\n\n const sourceComponent =\n sourcePort?.type === \"source_port\"\n ? circuitJson.find(\n (element) =>\n element.type === \"source_component\" &&\n element.source_component_id === sourcePort.source_component_id,\n )\n : null\n\n const readableSourceComponentName = firstReadableName(\n [\n sourceComponent?.type === \"source_component\"\n ? sourceComponent.name\n : null,\n ],\n sourceComponent?.type === \"source_component\"\n ? sourceComponent.source_component_id\n : \"\",\n )\n\n const readableSourcePortName = firstReadableName(\n [\n sourcePort?.type === \"source_port\" ? sourcePort.name : null,\n sourcePort?.type === \"source_port\"\n ? sourcePort.pin_number?.toString()\n : null,\n sourcePort?.type === \"source_port\" ? sourcePort.port_hints?.[0] : null,\n ],\n sourcePort?.type === \"source_port\" ? sourcePort.source_port_id : \"\",\n )\n\n if (readableSourceComponentName && readableSourcePortName) {\n return `${readableSourceComponentName}.${readableSourcePortName}`\n }\n if (readableSourcePortName) {\n return readableSourcePortName\n }\n }\n\n return sanitizeReadableName(\n getReadableNameForPcbPort(circuitJson, pcbPortId) ??\n getReadableNameForElement(circuitJson, pcbPortId),\n pcbPortId,\n \"port\",\n )\n}\n\nexport const getReadableNameForSourceTrace = (\n circuitJson: AnyCircuitElement[],\n sourceTrace: SourceTrace,\n): string => {\n const displayName = sanitizeReadableName(\n sourceTrace.display_name,\n sourceTrace.source_trace_id,\n \"\",\n )\n if (displayName) return displayName\n\n const connectedPortNames = (sourceTrace.connected_source_port_ids ?? [])\n .map((sourcePortId) => {\n const pcbPort = circuitJson.find(\n (element) =>\n element.type === \"pcb_port\" &&\n element.source_port_id === sourcePortId,\n )\n\n if (pcbPort?.type === \"pcb_port\") {\n return getReadableNameForPort(circuitJson, pcbPort.pcb_port_id)\n }\n\n const sourcePort = circuitJson.find(\n (element) =>\n element.type === \"source_port\" &&\n element.source_port_id === sourcePortId,\n )\n if (sourcePort?.type !== \"source_port\") return null\n\n const sourceComponent = circuitJson.find(\n (element) =>\n element.type === \"source_component\" &&\n element.source_component_id === sourcePort.source_component_id,\n )\n\n const sourceComponentName =\n sourceComponent?.type === \"source_component\"\n ? sanitizeReadableName(\n sourceComponent.name,\n sourceComponent.source_component_id,\n \"\",\n )\n : \"\"\n\n const sourcePortName = firstReadableName(\n [\n sourcePort.name,\n sourcePort.pin_number?.toString(),\n sourcePort.port_hints?.[0],\n ],\n sourcePort.source_port_id,\n )\n\n if (sourceComponentName && sourcePortName) {\n return `${sourceComponentName}.${sourcePortName}`\n }\n\n return sourcePortName || null\n })\n .filter((name): name is string => Boolean(name))\n\n if (connectedPortNames.length >= 2) {\n return `${connectedPortNames[0]} to ${connectedPortNames[1]}`\n }\n\n if (connectedPortNames.length === 1) {\n return `trace connected to ${connectedPortNames[0]}`\n }\n\n return `trace ${sourceTrace.source_trace_id}`\n}\n\nexport const getReadableNameForElementId = (\n circuitJson: AnyCircuitElement[],\n elementId: string,\n): string =>\n sanitizeReadableName(\n getReadableNameForElement(circuitJson, elementId),\n elementId,\n \"element\",\n )\n\nexport const getReadableNameForGroup = (\n circuitJson: AnyCircuitElement[],\n groupId: string,\n): string =>\n sanitizeReadableName(\n getReadableNameForElement(circuitJson, groupId),\n groupId,\n \"group\",\n )\n\nexport const containsCircuitJsonId = (message: string): boolean =>\n CIRCUIT_JSON_ID_PATTERN.test(message)\n\nexport function getReadableNameForFootprintPad(\n circuitJson: AnyCircuitElement[],\n pad: PcbSmtPad | PcbPlatedHole,\n ordinal: number,\n): string {\n const padKind = pad.type === \"pcb_smtpad\" ? \"SMD pad\" : \"through-hole pad\"\n const portRef = pad.pcb_port_id\n ? getReadableNameForPort(circuitJson, pad.pcb_port_id)\n : null\n const bounds = getBoundsOfPcbElements([pad])\n const centerX = (bounds.minX + bounds.maxX) / 2\n const centerY = (bounds.minY + bounds.maxY) / 2\n const location = `(${centerX.toFixed(2)}mm, ${centerY.toFixed(2)}mm)`\n\n if (portRef) return `${padKind} ${portRef} at ${location}`\n return `${padKind} #${ordinal + 1} at ${location}`\n}\n","import type { AnyCircuitElement, PcbTraceError } from \"circuit-json\"\nimport { getReadableNameForElement, cju } from \"@tscircuit/circuit-json-util\"\nimport {\n SpatialObjectIndex,\n type Bounds,\n} from \"lib/data-structures/SpatialIndex\"\nimport {\n getFullConnectivityMapFromCircuitJson,\n type ConnectivityMap,\n} from \"circuit-json-to-connectivity-map\"\nimport {\n getCollidableBounds,\n type Collidable,\n type PcbTraceSegment,\n} from \"./getCollidableBounds\"\nimport {\n segmentToBoundsMinDistance,\n segmentToCircleMinDistance,\n} from \"@tscircuit/math-utils\"\nimport {\n DEFAULT_TRACE_MARGIN,\n DEFAULT_TRACE_THICKNESS,\n EPSILON,\n} from \"lib/drc-defaults\"\nimport { getPcbPortIdsConnectedToTraces } from \"./getPcbPortIdsConnectedToTraces\"\nimport { segmentToSegmentMinDistance } from \"@tscircuit/math-utils\"\nimport { areBoundsOverlapping } from \"./areBoundsOverlapping\"\nimport { getPrimaryId } from \"@tscircuit/circuit-json-util\"\nimport { getCenterOfBoundsPair } from \"./getCenterOfBoundsPair\"\nimport { getClosestPointBetweenSegments } from \"./getClosestPointBetweenSegments\"\nimport { getCenterOfBounds } from \"./getCenterOfBounds\"\nimport { getRadiusOfCircuitJsonElement } from \"./getRadiusOfCircuitJsonElement\"\nimport { getClosestPointBetweenSegmentAndBounds } from \"./getClosestPointBetweenSegmentAndBounds\"\nimport { getLayersOfPcbElement } from \"../util/getLayersOfPcbElement\"\nimport { addStartAndEndPortIdsIfMissing } from \"lib/add-start-and-end-port-ids-if-missing\"\n\nexport function checkEachPcbTraceNonOverlapping(\n circuitJson: AnyCircuitElement[],\n {\n connMap,\n minClearance = DEFAULT_TRACE_MARGIN,\n }: {\n connMap?: ConnectivityMap\n minClearance?: number\n } = {},\n): PcbTraceError[] {\n const errors: PcbTraceError[] = []\n addStartAndEndPortIdsIfMissing(circuitJson)\n connMap ??= getFullConnectivityMapFromCircuitJson(circuitJson)\n\n const pcbTraces = cju(circuitJson).pcb_trace.list()\n const pcbTraceSegments = pcbTraces.flatMap((pcbTrace) => {\n const segments: PcbTraceSegment[] = []\n for (let i = 0; i < pcbTrace.route.length - 1; i++) {\n const p1 = pcbTrace.route[i]\n const p2 = pcbTrace.route[i + 1]\n if (p1.route_type !== \"wire\") continue\n if (p2.route_type !== \"wire\") continue\n if (p1.layer !== p2.layer) continue\n segments.push({\n type: \"pcb_trace_segment\",\n pcb_trace_id: pcbTrace.pcb_trace_id,\n _pcbTrace: pcbTrace,\n thickness:\n \"width\" in p1\n ? p1.width\n : \"width\" in p2\n ? p2.width\n : DEFAULT_TRACE_THICKNESS,\n layer: p1.layer,\n x1: p1.x,\n y1: p1.y,\n x2: p2.x,\n y2: p2.y,\n } as PcbTraceSegment)\n }\n return segments\n })\n const pcbSmtPads = cju(circuitJson).pcb_smtpad.list()\n const pcbPlatedHoles = cju(circuitJson).pcb_plated_hole.list()\n const pcbHoles = cju(circuitJson).pcb_hole.list()\n const pcbVias = cju(circuitJson).pcb_via.list()\n const pcbKeepouts = cju(circuitJson).pcb_keepout.list()\n\n const allObjects: Collidable[] = [\n ...pcbTraceSegments,\n ...pcbSmtPads,\n ...pcbPlatedHoles,\n ...pcbHoles,\n ...pcbVias,\n ...pcbKeepouts,\n ]\n\n const spatialIndex = new SpatialObjectIndex<Collidable>({\n objects: allObjects,\n getBounds: getCollidableBounds,\n })\n\n const getReadableName = (id: string) =>\n getReadableNameForElement(circuitJson, id)\n\n const errorIds = new Set<string>()\n\n // For each segment, check it if overlaps with anything collidable\n for (const segmentA of pcbTraceSegments) {\n const requiredMargin = minClearance\n const bounds = getCollidableBounds(segmentA)\n const nearbyObjects = spatialIndex.getObjectsInBounds(\n bounds,\n requiredMargin + segmentA.thickness / 2,\n )\n if (segmentA.x1 === segmentA.x2 && segmentA.y1 === segmentA.y2) continue\n\n for (const obj of nearbyObjects) {\n // ignore obstacles not on the trace's layer (except vias)\n if (!getLayersOfPcbElement(obj).includes(segmentA.layer)) {\n continue\n }\n if (obj.type === \"pcb_trace_segment\") {\n const segmentB = obj\n\n if (segmentA.layer !== segmentB.layer) continue\n\n // Check if the segments are overlapping\n if (\n connMap.areIdsConnected(segmentA.pcb_trace_id, segmentB.pcb_trace_id)\n )\n continue\n\n const gap =\n segmentToSegmentMinDistance(\n { x: segmentA.x1, y: segmentA.y1 },\n { x: segmentA.x2, y: segmentA.y2 },\n { x: segmentB.x1, y: segmentB.y1 },\n { x: segmentB.x2, y: segmentB.y2 },\n ) -\n segmentA.thickness / 2 -\n segmentB.thickness / 2\n if (gap > minClearance - EPSILON) continue\n\n const pcb_trace_error_id = `overlap_${segmentA.pcb_trace_id}_${segmentB.pcb_trace_id}`\n const pcb_trace_error_id_reverse = `overlap_${segmentB.pcb_trace_id}_${segmentA.pcb_trace_id}`\n if (errorIds.has(pcb_trace_error_id)) continue\n if (errorIds.has(pcb_trace_error_id_reverse)) continue\n\n errorIds.add(pcb_trace_error_id)\n errors.push({\n type: \"pcb_trace_error\",\n error_type: \"pcb_trace_error\",\n message: `PCB trace ${getReadableName(segmentA.pcb_trace_id)} overlaps with ${getReadableName(segmentB.pcb_trace_id)} ${gap < 0 ? \"(accidental contact)\" : `(gap: ${gap.toFixed(3)}mm)`}`,\n pcb_trace_id: segmentA.pcb_trace_id,\n source_trace_id: \"\",\n pcb_trace_error_id,\n pcb_component_ids: [],\n center: getClosestPointBetweenSegments(segmentA, segmentB),\n pcb_port_ids: getPcbPortIdsConnectedToTraces([\n segmentA._pcbTrace,\n segmentB._pcbTrace,\n ]),\n })\n continue\n }\n\n const primaryObjId = getPrimaryId(obj as any)\n if (\n connMap.areIdsConnected(\n segmentA.pcb_trace_id,\n \"pcb_trace_id\" in obj ? (obj.pcb_trace_id as string) : primaryObjId,\n )\n )\n continue\n\n const isCircular =\n obj.type === \"pcb_via\" ||\n (obj.type === \"pcb_plated_hole\" && obj.shape === \"circle\") ||\n obj.type === \"pcb_hole\" ||\n (obj.type === \"pcb_smtpad\" && obj.shape === \"circle\")\n\n if (isCircular) {\n const radius = getRadiusOfCircuitJsonElement(obj)\n const distance = segmentToCircleMinDistance(\n { x: segmentA.x1, y: segmentA.y1 },\n { x: segmentA.x2, y: segmentA.y2 },\n { x: obj.x, y: obj.y, radius },\n )\n const gap = distance - segmentA.thickness / 2\n if (gap > minClearance - EPSILON) continue\n\n const pcb_trace_error_id = `overlap_${segmentA.pcb_trace_id}_${primaryObjId}`\n if (errorIds.has(pcb_trace_error_id)) continue\n errorIds.add(pcb_trace_error_id)\n errors.push({\n type: \"pcb_trace_error\",\n error_type: \"pcb_trace_error\",\n message: `PCB trace ${getReadableName(segmentA.pcb_trace_id)} overlaps with ${obj.type} \"${getReadableName(getPrimaryId(obj as any))}\" ${gap < 0 ? \"(accidental contact)\" : `(gap: ${gap.toFixed(3)}mm)`}`,\n pcb_trace_id: segmentA.pcb_trace_id,\n center: getClosestPointBetweenSegmentAndBounds(\n segmentA,\n getCollidableBounds(obj),\n ),\n source_trace_id: \"\",\n pcb_trace_error_id,\n pcb_component_ids: [\n \"pcb_component_id\" in obj\n ? (obj.pcb_component_id as string)\n : undefined,\n ].filter(Boolean) as string[],\n pcb_port_ids: [\n ...getPcbPortIdsConnectedToTraces([segmentA._pcbTrace]),\n \"pcb_port_id\" in obj ? obj.pcb_port_id : undefined,\n ].filter(Boolean) as string[],\n })\n }\n\n // Handle generic case of hitting the bounds of any collidable obstacle\n // using the bounds of the collidable obstacle\n const gap =\n segmentToBoundsMinDistance(\n { x: segmentA.x1, y: segmentA.y1 },\n { x: segmentA.x2, y: segmentA.y2 },\n getCollidableBounds(obj),\n ) -\n segmentA.thickness / 2\n if (gap + EPSILON < requiredMargin) {\n const pcb_trace_error_id = `overlap_${segmentA.pcb_trace_id}_${primaryObjId}`\n if (errorIds.has(pcb_trace_error_id)) continue\n errorIds.add(pcb_trace_error_id)\n errors.push({\n type: \"pcb_trace_error\",\n error_type: \"pcb_trace_error\",\n message: `PCB trace ${getReadableName(segmentA.pcb_trace_id)} overlaps with ${obj.type} \"${getReadableName(getPrimaryId(obj as any))}\" ${gap < 0 ? \"(accidental contact)\" : `(gap: ${gap.toFixed(3)}mm)`}`,\n pcb_trace_id: segmentA.pcb_trace_id,\n source_trace_id: \"\",\n pcb_trace_error_id,\n pcb_component_ids: [\n \"pcb_component_id\" in obj ? obj.pcb_component_id : undefined,\n ].filter(Boolean) as string[],\n center: getClosestPointBetweenSegmentAndBounds(\n segmentA,\n getCollidableBounds(obj),\n ),\n pcb_port_ids: [\n ...getPcbPortIdsConnectedToTraces([segmentA._pcbTrace]),\n \"pcb_port_id\" in obj ? obj.pcb_port_id : undefined,\n ].filter(Boolean) as string[],\n })\n }\n }\n }\n return errors\n}\n","export type BucketCoordinate = `${number}x${number}`\n\nexport interface Bounds {\n minX: number\n minY: number\n maxX: number\n maxY: number\n}\n\nexport type GetBoundsFn<T> = (obj: T) => Bounds\nexport type GetIdFn<T> = (obj: T) => string\n\nexport class SpatialObjectIndex<T> {\n buckets: Map<BucketCoordinate, Array<T & { spatialIndexId: string }>>\n objectsById: Map<string, T & { spatialIndexId: string }>\n getBounds: GetBoundsFn<T>\n getId: GetIdFn<T>\n CELL_SIZE = 0.4\n\n constructor({\n objects,\n getBounds,\n getId,\n CELL_SIZE,\n }: {\n objects: T[]\n getBounds: GetBoundsFn<T>\n getId?: GetIdFn<T>\n CELL_SIZE?: number\n }) {\n this.buckets = new Map()\n this.objectsById = new Map()\n this.getBounds = getBounds\n this.getId = getId ?? (() => this._getNextId())\n this.CELL_SIZE = CELL_SIZE ?? this.CELL_SIZE\n\n for (const obj of objects) {\n this.addObject(obj)\n }\n }\n\n _idCounter = 0\n _getNextId(): string {\n return `${this._idCounter++}`\n }\n\n addObject(obj: T): void {\n const bounds = this.getBounds(obj)\n const spatialIndexId = this.getId(obj)\n const objWithId = { ...obj, spatialIndexId } as T & {\n spatialIndexId: string\n }\n\n // Store in objectsById for quick lookup\n this.objectsById.set(spatialIndexId, objWithId)\n\n // Calculate the bucket coordinates that cover the object's bounds\n const minBucketX = Math.floor(bounds.minX / this.CELL_SIZE)\n const minBucketY = Math.floor(bounds.minY / this.CELL_SIZE)\n const maxBucketX = Math.floor(bounds.maxX / this.CELL_SIZE)\n const maxBucketY = Math.floor(bounds.maxY / this.CELL_SIZE)\n\n // Add the object to all buckets it intersects with\n for (let bx = minBucketX; bx <= maxBucketX; bx++) {\n for (let by = minBucketY; by <= maxBucketY; by++) {\n const bucketKey = `${bx}x${by}` as BucketCoordinate\n const bucket = this.buckets.get(bucketKey)\n if (!bucket) {\n this.buckets.set(bucketKey, [objWithId])\n } else {\n bucket.push(objWithId)\n }\n }\n }\n }\n\n removeObject(id: string): boolean {\n const obj = this.objectsById.get(id)\n if (!obj) return false\n\n // Remove from objectsById\n this.objectsById.delete(id)\n\n // Calculate the bucket coordinates that cover the object's bounds\n const bounds = this.getBounds(obj)\n const minBucketX = Math.floor(bounds.minX / this.CELL_SIZE)\n const minBucketY = Math.floor(bounds.minY / this.CELL_SIZE)\n const maxBucketX = Math.floor(bounds.maxX / this.CELL_SIZE)\n const maxBucketY = Math.floor(bounds.maxY / this.CELL_SIZE)\n\n // Remove the object from all buckets it might be in\n for (let bx = minBucketX; bx <= maxBucketX; bx++) {\n for (let by = minBucketY; by <= maxBucketY; by++) {\n const bucketKey = `${bx}x${by}` as BucketCoordinate\n const bucket = this.buckets.get(bucketKey)\n if (bucket) {\n const index = bucket.findIndex((item) => item.spatialIndexId === id)\n if (index !== -1) {\n bucket.splice(index, 1)\n if (bucket.length === 0) {\n this.buckets.delete(bucketKey)\n }\n }\n }\n }\n }\n\n return true\n }\n\n getBucketKey(x: number, y: number): BucketCoordinate {\n return `${Math.floor(x / this.CELL_SIZE)}x${Math.floor(y / this.CELL_SIZE)}`\n }\n\n getObjectsInBounds(bounds: Bounds, margin = 0): T[] {\n const objects: T[] = []\n const addedIds = new Set<string>()\n\n // Calculate the bucket coordinates that cover the requested bounds with margin\n const minBucketX = Math.floor((bounds.minX - margin) / this.CELL_SIZE)\n const minBucketY = Math.floor((bounds.minY - margin) / this.CELL_SIZE)\n const maxBucketX = Math.floor((bounds.maxX + margin) / this.CELL_SIZE)\n const maxBucketY = Math.floor((bounds.maxY + margin) / this.CELL_SIZE)\n\n // Collect objects from all buckets that intersect the bounds\n for (let bx = minBucketX; bx <= maxBucketX; bx++) {\n for (let by = minBucketY; by <= maxBucketY; by++) {\n const bucketKey = `${bx}x${by}` as BucketCoordinate\n const bucket = this.buckets.get(bucketKey) || []\n\n for (const obj of bucket) {\n const id = obj.spatialIndexId\n if (addedIds.has(id)) continue\n\n addedIds.add(id)\n objects.push(obj)\n }\n }\n }\n\n return objects\n }\n}\n","import { getBoundsOfPcbElements } from \"@tscircuit/circuit-json-util\"\nimport type {\n AnyCircuitElement,\n PcbHole,\n PCBKeepout,\n PcbPlatedHole,\n PcbSmtPad,\n PcbTrace,\n PcbTraceError,\n PcbVia,\n} from \"circuit-json\"\n\ninterface Bounds {\n minX: number\n minY: number\n maxX: number\n maxY: number\n}\n\nexport type PcbTraceSegment = {\n type: \"pcb_trace_segment\"\n _pcbTrace: PcbTrace\n pcb_trace_id: string\n thickness: number\n layer: string\n x1: number\n y1: number\n x2: number\n y2: number\n}\n\nexport type Collidable =\n | PcbTraceSegment\n | PcbSmtPad\n | PcbPlatedHole\n | PcbHole\n | PcbVia\n | PCBKeepout\n\nexport const getCollidableBounds = (collidable: Collidable): Bounds => {\n if (collidable.type === \"pcb_trace_segment\") {\n return {\n minX: Math.min(collidable.x1, collidable.x2),\n minY: Math.min(collidable.y1, collidable.y2),\n maxX: Math.max(collidable.x1, collidable.x2),\n maxY: Math.max(collidable.y1, collidable.y2),\n }\n }\n return getBoundsOfPcbElements([collidable as any])\n}\n","import { jlcMinTolerances } from \"@tscircuit/jlcpcb-manufacturing-specs\"\n\nexport const DEFAULT_TRACE_MARGIN = 0.1\nexport const DEFAULT_TRACE_THICKNESS = jlcMinTolerances.min_trace_width\nexport const DEFAULT_VIA_DIAMETER = jlcMinTolerances.min_via_pad_diameter\nexport const DEFAULT_VIA_BOARD_MARGIN = 0.3\n\nexport const DEFAULT_SAME_NET_VIA_MARGIN =\n jlcMinTolerances.min_via_to_via_clearance\nexport const DEFAULT_DIFFERENT_NET_VIA_MARGIN =\n jlcMinTolerances.min_via_to_via_clearance\n\nexport const DEFAULT_PAD_PAD_CLEARANCE =\n jlcMinTolerances.min_pad_to_pad_clearance\nexport const DEFAULT_PAD_TRACE_CLEARANCE =\n jlcMinTolerances.min_trace_to_pad_clearance\n\nexport const EPSILON = 0.005\n","import type { PcbTrace } from \"circuit-json\"\n\nexport function getPcbPortIdsConnectedToTrace(trace: PcbTrace) {\n const connectedPcbPorts = new Set<string>()\n for (const segment of trace.route) {\n if (segment.route_type === \"wire\") {\n if (segment.start_pcb_port_id)\n connectedPcbPorts.add(segment.start_pcb_port_id)\n if (segment.end_pcb_port_id)\n connectedPcbPorts.add(segment.end_pcb_port_id)\n }\n }\n\n return Array.from(connectedPcbPorts)\n}\n\nexport function getPcbPortIdsConnectedToTraces(traces: PcbTrace[]) {\n const connectedPorts = new Set<string>()\n for (const trace of traces) {\n for (const portId of getPcbPortIdsConnectedToTrace(trace)) {\n connectedPorts.add(portId)\n }\n }\n return Array.from(connectedPorts)\n}\n","import type { PcbTraceSegment } from \"./getCollidableBounds\"\n\nexport const getClosestPointBetweenSegments = (\n segmentA: PcbTraceSegment,\n segmentB: PcbTraceSegment,\n): { x: number; y: number } => {\n // Define points for each segment\n const a1 = { x: segmentA.x1, y: segmentA.y1 }\n const a2 = { x: segmentA.x2, y: segmentA.y2 }\n const b1 = { x: segmentB.x1, y: segmentB.y1 }\n const b2 = { x: segmentB.x2, y: segmentB.y2 }\n\n // Calculate direction vectors for each segment\n const va = { x: a2.x - a1.x, y: a2.y - a1.y }\n const vb = { x: b2.x - b1.x, y: b2.y - b1.y }\n\n // Calculate squared lengths of segments\n const lenSqrA = va.x * va.x + va.y * va.y\n const lenSqrB = vb.x * vb.x + vb.y * vb.y\n\n // If either segment is a point (zero length), handle as special case\n if (lenSqrA === 0 || lenSqrB === 0) {\n if (lenSqrA === 0 && lenSqrB === 0) {\n // Both segments are points, return the distance between them\n // Calculate the average point\n return {\n x: (a1.x + b1.x) / 2,\n y: (a1.y + b1.y) / 2,\n }\n }\n if (lenSqrA === 0) {\n // First segment is a point, find closest point on second segment\n const t = clamp(\n ((a1.x - b1.x) * vb.x + (a1.y - b1.y) * vb.y) / lenSqrB,\n 0,\n 1,\n )\n const closestOnB = {\n x: b1.x + t * vb.x,\n y: b1.y + t * vb.y,\n }\n // Calculate the average point\n return {\n x: (a1.x + closestOnB.x) / 2,\n y: (a1.y + closestOnB.y) / 2,\n }\n }\n // Second segment is a point, find closest point on first segment\n const t = clamp(\n ((b1.x - a1.x) * va.x + (b1.y - a1.y) * va.y) / lenSqrA,\n 0,\n 1,\n )\n const closestOnA = {\n x: a1.x + t * va.x,\n y: a1.y + t * va.y,\n }\n // Calculate the average point\n return {\n x: (closestOnA.x + b1.x) / 2,\n y: (closestOnA.y + b1.y) / 2,\n }\n }\n\n // Vector between segment starting points\n const w = { x: a1.x - b1.x, y: a1.y - b1.y }\n\n // Calculate dot products\n const dotAA = va.x * va.x + va.y * va.y\n const dotAB = va.x * vb.x + va.y * vb.y\n const dotAW = va.x * w.x + va.y * w.y\n const dotBB = vb.x * vb.x + vb.y * vb.y\n const dotBW = vb.x * w.x + vb.y * w.y\n\n // Calculate parametric positions (t values) along each segment\n const denominator = dotAA * dotBB - dotAB * dotAB\n\n // If segments are parallel, handle separately\n if (denominator < 1e-10) {\n return closestPointsParallelSegments(\n a1,\n a2,\n b1,\n b2,\n va,\n vb,\n lenSqrA,\n lenSqrB,\n )\n }\n\n // Calculate parameters for closest points\n let tA = (dotAB * dotBW - dotBB * dotAW) / denominator\n let tB = (dotAA * dotBW - dotAB * dotAW) / denominator\n\n // Clamp parameters to segment bounds\n tA = clamp(tA, 0, 1)\n tB = clamp(tB, 0, 1)\n\n // Recalculate tB if tA was clamped\n tB = (tA * dotAB + dotBW) / dotBB\n tB = clamp(tB, 0, 1)\n\n // Recalculate tA if tB was clamped\n tA = (tB * dotAB - dotAW) / dotAA\n tA = clamp(tA, 0, 1)\n\n // Calculate closest points on each segment\n const closestOnA = {\n x: a1.x + tA * va.x,\n y: a1.y + tA * va.y,\n }\n\n const closestOnB = {\n x: b1.x + tB * vb.x,\n y: b1.y + tB * vb.y,\n }\n\n // Calculate distance between closest points\n const dx = closestOnA.x - closestOnB.x\n const dy = closestOnA.y - closestOnB.y\n const distance = Math.sqrt(dx * dx + dy * dy)\n\n // Calculate the average of the closest points\n const averagePoint = {\n x: (closestOnA.x + closestOnB.x) / 2,\n y: (closestOnA.y + closestOnB.y) / 2,\n }\n\n return averagePoint\n}\n\n// Helper function for handling parallel segments\nconst closestPointsParallelSegments = (\n a1: { x: number; y: number },\n a2: { x: number; y: number },\n b1: { x: number; y: number },\n b2: { x: number; y: number },\n va: { x: number; y: number },\n vb: { x: number; y: number },\n lenSqrA: number,\n lenSqrB: number,\n) => {\n // Project b1 onto segment A\n let tA = ((b1.x - a1.x) * va.x + (b1.y - a1.y) * va.y) / lenSqrA\n tA = clamp(tA, 0, 1)\n const pointOnA1 = { x: a1.x + tA * va.x, y: a1.y + tA * va.y }\n\n // Project b2 onto segment A\n let tA2 = ((b2.x - a1.x) * va.x + (b2.y - a1.y) * va.y) / lenSqrA\n tA2 = clamp(tA2, 0, 1)\n const pointOnA2 = { x: a1.x + tA2 * va.x, y: a1.y + tA2 * va.y }\n\n // Project a1 onto segment B\n let tB = ((a1.x - b1.x) * vb.x + (a1.y - b1.y) * vb.y) / lenSqrB\n tB = clamp(tB, 0, 1)\n const pointOnB1 = { x: b1.x + tB * vb.x, y: b1.y + tB * vb.y }\n\n // Project a2 onto segment B\n let tB2 = ((a2.x - b1.x) * vb.x + (a2.y - b1.y) * vb.y) / lenSqrB\n tB2 = clamp(tB2, 0, 1)\n const pointOnB2 = { x: b1.x + tB2 * vb.x, y: b1.y + tB2 * vb.y }\n\n // Calculate all possible distances between end points and their projections\n const distances = [\n {\n pointA: pointOnA1,\n pointB: b1,\n distance: Math.sqrt(\n (pointOnA1.x - b1.x) ** 2 + (pointOnA1.y - b1.y) ** 2,\n ),\n },\n {\n pointA: pointOnA2,\n pointB: b2,\n distance: Math.sqrt(\n (pointOnA2.x - b2.x) ** 2 + (pointOnA2.y - b2.y) ** 2,\n ),\n },\n {\n pointA: a1,\n pointB: pointOnB1,\n distance: Math.sqrt(\n (a1.x - pointOnB1.x) ** 2 + (a1.y - pointOnB1.y) ** 2,\n ),\n },\n {\n pointA: a2,\n pointB: pointOnB2,\n distance: Math.sqrt(\n (a2.x - pointOnB2.x) ** 2 + (a2.y - pointOnB2.y) ** 2,\n ),\n },\n ]\n\n // Find closest pair\n const closestPair = distances.reduce((closest, current) =>\n current.distance < closest.distance ? current : closest,\n )\n\n // Calculate the average of the closest points\n return {\n x: (closestPair.pointA.x + closestPair.pointB.x) / 2,\n y: (closestPair.pointA.y + closestPair.pointB.y) / 2,\n }\n}\n\n// Helper function to clamp a value between min and max\nconst clamp = (value: number, min: number, max: number): number => {\n return Math.max(min, Math.min(max, value))\n}\n","import type {\n AnyCircuitElement,\n PcbHole,\n PcbPlatedHole,\n PcbVia,\n PcbSmtPad,\n} from \"circuit-json\"\n\nexport const getRadiusOfCircuitJsonElement = (\n obj: PcbVia | PcbPlatedHole | PcbHole | PcbSmtPad,\n) => {\n if (obj.type === \"pcb_via\") {\n return obj.outer_diameter / 2\n }\n if (obj.type === \"pcb_plated_hole\" && obj.shape === \"circle\") {\n return obj.outer_diameter / 2\n }\n if (obj.type === \"pcb_hole\" && obj.hole_shape === \"circle\") {\n return obj.hole_diameter / 2\n }\n if (obj.type === \"pcb_smtpad\" && obj.shape === \"circle\") {\n return obj.radius\n }\n throw new Error(\n `Could not determine radius of element: ${JSON.stringify(obj)}`,\n )\n}\n","import type { Bounds } from \"lib/data-structures/SpatialIndex\"\nimport type { PcbTraceSegment } from \"./getCollidableBounds\"\n\nexport const getClosestPointBetweenSegmentAndBounds = (\n segment: PcbTraceSegment,\n bounds: Bounds,\n): { x: number; y: number } => {\n // Define segment points\n const p1 = { x: segment.x1, y: segment.y1 }\n const p2 = { x: segment.x2, y: segment.y2 }\n\n // Define bounds corners\n const minX = bounds.minX\n const minY = bounds.minY\n const maxX = bounds.maxX\n const maxY = bounds.maxY\n\n // Check if segment is a point\n if (p1.x === p2.x && p1.y === p2.y) {\n // For a point, find the closest point on the bounds\n const closestX = Math.max(minX, Math.min(maxX, p1.x))\n const closestY = Math.max(minY, Math.min(maxY, p1.y))\n\n // If the point is inside the bounds, return the point itself\n if (closestX === p1.x && closestY === p1.y) {\n return { x: p1.x, y: p1.y }\n }\n\n // Otherwise, return the closest point on the bounds\n return { x: closestX, y: closestY }\n }\n\n // Calculate direction vector of the segment\n const dx = p2.x - p1.x\n const dy = p2.y - p1.y\n\n // Calculate parameter values for intersection with each boundary\n const tMinX = dx !== 0 ? (minX - p1.x) / dx : Number.NEGATIVE_INFINITY\n const tMaxX = dx !== 0 ? (maxX - p1.x) / dx : Number.POSITIVE_INFINITY\n const tMinY = dy !== 0 ? (minY - p1.y) / dy : Number.NEGATIVE_INFINITY\n const tMaxY = dy !== 0 ? (maxY - p1.y) / dy : Number.POSITIVE_INFINITY\n\n // Find the entering and exiting parameters\n const tEnter = Math.max(Math.min(tMinX, tMaxX), Math.min(tMinY, tMaxY))\n const tExit = Math.min(Math.max(tMinX, tMaxX), Math.max(tMinY, tMaxY))\n\n // Check if segment intersects the bounds\n if (tEnter <= tExit && tExit >= 0 && tEnter <= 1) {\n // Segment intersects bounds, clamp parameter to segment\n const t = Math.max(0, Math.min(1, tEnter))\n return {\n x: p1.x + t * dx,\n y: p1.y + t * dy,\n }\n }\n\n // Segment doesn't intersect bounds, find closest point\n // Check each endpoint of the segment against the bounds\n const closestToP1 = {\n x: Math.max(minX, Math.min(maxX, p1.x)),\n y: Math.max(minY, Math.min(maxY, p1.y)),\n }\n\n const closestToP2 = {\n x: Math.max(minX, Math.min(maxX, p2.x)),\n y: Math.max(minY, Math.min(maxY, p2.y)),\n }\n\n // Calculate distances\n const distToP1Squared =\n (closestToP1.x - p1.x) ** 2 + (closestToP1.y - p1.y) ** 2\n const distToP2Squared =\n (closestToP2.x - p2.x) ** 2 + (closestToP2.y - p2.y) ** 2\n\n // Check each edge of the bounds against the segment\n const edges = [\n { start: { x: minX, y: minY }, end: { x: maxX, y: minY } }, // Bottom edge\n { start: { x: maxX, y: minY }, end: { x: maxX, y: maxY } }, // Right edge\n { start: { x: maxX, y: maxY }, end: { x: minX, y: maxY } }, // Top edge\n { start: { x: minX, y: maxY }, end: { x: minX, y: minY } }, // Left edge\n ]\n\n let minDistance = Math.min(distToP1Squared, distToP2Squared)\n let closestPoint =\n distToP1Squared <= distToP2Squared ? closestToP1 : closestToP2\n\n // Helper function to clamp a value between min and max\n const clamp = (value: number, min: number, max: number): number => {\n return Math.max(min, Math.min(max, value))\n }\n\n // Check each edge\n for (const edge of edges) {\n // Calculate direction vectors\n const va = { x: p2.x - p1.x, y: p2.y - p1.y }\n const vb = { x: edge.end.x - edge.start.x, y: edge.end.y - edge.start.y }\n const w = { x: p1.x - edge.start.x, y: p1.y - edge.start.y }\n\n // Calculate dot products\n const dotAA = va.x * va.x + va.y * va.y\n const dotAB = va.x * vb.x + va.y * vb.y\n const dotAW = va.x * w.x + va.y * w.y\n const dotBB = vb.x * vb.x + vb.y * vb.y\n const dotBW = vb.x * w.x + vb.y * w.y\n\n // Calculate parameters for closest points\n const denominator = dotAA * dotBB - dotAB * dotAB\n\n // Skip if lines are parallel\n if (Math.abs(denominator) < 1e-10) continue\n\n let tA = (dotAB * dotBW - dotBB * dotAW) / denominator\n let tB = (dotAA * dotBW - dotAB * dotAW) / denominator\n\n // Clamp parameters to segment bounds\n tA = clamp(tA, 0, 1)\n tB = clamp(tB, 0, 1)\n\n // Calculate closest points\n const closestOnSegment = {\n x: p1.x + tA * va.x,\n y: p1.y + tA * va.y,\n }\n\n const closestOnEdge = {\n x: edge.start.x + tB * vb.x,\n y: edge.start.y + tB * vb.y,\n }\n\n // Calculate distance\n const dx = closestOnSegment.x - closestOnEdge.x\n const dy = closestOnSegment.y - closestOnEdge.y\n const distSquared = dx * dx + dy * dy\n\n // Update if this is closer\n if (distSquared < minDistance) {\n minDistance = distSquared\n closestPoint = {\n x: (closestOnSegment.x + closestOnEdge.x) / 2,\n y: (closestOnSegment.y + closestOnEdge.y) / 2,\n }\n }\n }\n\n return closestPoint\n}\n","import type { Collidable } from \"lib/check-each-pcb-trace-non-overlapping/getCollidableBounds\"\nimport { all_layers } from \"circuit-json\"\n\nexport function getLayersOfPcbElement(obj: Collidable): string[] {\n if (obj.type === \"pcb_trace_segment\") {\n return [obj.layer]\n }\n if (obj.type === \"pcb_smtpad\") {\n return [obj.layer]\n }\n if (obj.type === \"pcb_plated_hole\") {\n return Array.isArray(obj.layers) ? obj.layers : [...all_layers]\n }\n if (obj.type === \"pcb_hole\") {\n return [...all_layers]\n }\n if (obj.type === \"pcb_via\") {\n return Array.isArray(obj.layers) ? obj.layers : [...all_layers]\n }\n if (obj.type === \"pcb_keepout\") {\n return Array.isArray(obj.layers) ? obj.layers : []\n }\n return []\n}\n","export class NetManager {\n private networks: Set<Set<string>> = new Set()\n\n setConnected(nodes: string[]): void {\n if (nodes.length < 2) return\n\n let targetNetwork: Set<string> | null = null\n\n // Check if any of the nodes are already in a network\n for (const network of this.networks) {\n for (const node of nodes) {\n if (network.has(node)) {\n if (targetNetwork === null) {\n targetNetwork = network\n } else if (targetNetwork !== network) {\n // Merge networks\n for (const mergeNode of network) {\n targetNetwork.add(mergeNode)\n }\n this.networks.delete(network)\n }\n break\n }\n }\n if (targetNetwork !== null && targetNetwork !== network) break\n }\n\n // If no existing network found, create a new one\n if (targetNetwork === null) {\n targetNetwork = new Set(nodes)\n this.networks.add(targetNetwork)\n } else {\n // Add all nodes to the target network\n for (const node of nodes) {\n targetNetwork.add(node)\n }\n }\n }\n\n isConnected(nodes: string[]): boolean {\n if (nodes.length < 2) return true\n\n for (const network of this.networks) {\n if (nodes.every((node) => network.has(node))) {\n return true\n }\n }\n\n return false\n }\n}\n","import type {\n AnyCircuitElement,\n PcbBoard,\n PcbVia,\n PcbPlacementError,\n} from \"circuit-json\"\nimport { getReadableNameForElement } from \"@tscircuit/circuit-json-util\"\nimport { DEFAULT_VIA_BOARD_MARGIN } from \"lib/drc-defaults\"\n\nexport function checkViasOffBoard(\n circuitJson: AnyCircuitElement[],\n): PcbPlacementError[] {\n const board = circuitJson.find((el) => el.type === \"pcb_board\") as PcbBoard\n\n if (!board) return []\n\n const vias = circuitJson.filter((el) => el.type === \"pcb_via\") as PcbVia[]\n\n if (vias.length === 0) return []\n\n if (board.width === undefined || board.height === undefined) return []\n\n const boardMinX = board.center.x - board.width / 2\n const boardMaxX = board.center.x + board.width / 2\n const boardMinY = board.center.y - board.height / 2\n const boardMaxY = board.center.y + board.height / 2\n\n const errors: PcbPlacementError[] = []\n\n for (const via of vias) {\n const viaRadius = via.outer_diameter / 2\n const viaMinX = via.x - viaRadius\n const viaMaxX = via.x + viaRadius\n const viaMinY = via.y - viaRadius\n const viaMaxY = via.y + viaRadius\n\n if (\n viaMinX < boardMinX + DEFAULT_VIA_BOARD_MARGIN ||\n viaMaxX > boardMaxX - DEFAULT_VIA_BOARD_MARGIN ||\n viaMinY < boardMinY + DEFAULT_VIA_BOARD_MARGIN ||\n viaMaxY > boardMaxY - DEFAULT_VIA_BOARD_MARGIN\n ) {\n const viaName = getReadableNameForElement(circuitJson, via.pcb_via_id)\n errors.push({\n type: \"pcb_placement_error\",\n pcb_placement_error_id: `out_of_board_${via.pcb_via_id}`,\n message: `Via ${viaName} is outside or crossing the board boundary`,\n error_type: \"pcb_placement_error\",\n })\n }\n }\n\n return errors\n}\n","import type {\n AnyCircuitElement,\n PcbBoard,\n PcbComponent,\n AnySourceComponent,\n PcbComponentOutsideBoardError,\n} from \"circuit-json\"\nimport { getReadableNameForComponent } from \"lib/util/get-readable-names\"\nimport type { Point } from \"@tscircuit/math-utils\"\nimport * as Flatten from \"@flatten-js/core\"\nimport { rotateDEG, applyToPoint } from \"transformation-matrix\"\n\n/**\n * Create a rectangle polygon centered at (cx,cy) with given width/height and rotation (degrees).\n * Uses transformation-matrix to rotate around the center and returns a Flatten.Polygon.\n */\n\nfunction isPolygonCCW(poly: Flatten.Polygon): boolean {\n // @ts-ignore\n return poly.area() >= 0\n}\n\nfunction rectanglePolygon({\n center,\n size,\n rotationDeg = 0,\n}: {\n center: Point\n size: { width: number; height: number }\n rotationDeg?: number\n}): Flatten.Polygon {\n const cx = center.x\n const cy = center.y\n const hw = size.width / 2\n const hh = size.height / 2\n\n const corners: Flatten.Point[] = [\n new Flatten.Point(cx - hw, cy - hh),\n new Flatten.Point(cx + hw, cy - hh),\n new Flatten.Point(cx + hw, cy + hh),\n new Flatten.Point(cx - hw, cy + hh),\n ]\n\n let poly = new Flatten.Polygon(corners)\n\n if (rotationDeg) {\n const matrix = rotateDEG(rotationDeg, cx, cy)\n const rotatedCorners = corners.map((pt) => {\n const p = applyToPoint(matrix, { x: pt.x, y: pt.y })\n return new Flatten.Point(p.x, p.y)\n })\n poly = new Flatten.Polygon(rotatedCorners)\n }\n\n // Ensure CCW order\n if (!isPolygonCCW(poly)) poly.reverse()\n\n return poly\n}\n\nfunction boardToPolygon({\n board,\n}: {\n board: PcbBoard\n}): Flatten.Polygon | null {\n if (board.outline && board.outline.length > 0) {\n const points = board.outline.map((p) => new Flatten.Point(p.x, p.y))\n const poly = new Flatten.Polygon(points)\n\n if (!isPolygonCCW(poly)) {\n poly.reverse()\n }\n\n return poly\n }\n\n if (\n board.center &&\n typeof board.width === \"number\" &&\n typeof board.height === \"number\"\n ) {\n return rectanglePolygon({\n center: board.center,\n size: { width: board.width, height: board.height },\n rotationDeg: 0,\n })\n }\n\n return null\n}\n\n/** Get human-readable component name from circuit JSON */\nfunction getComponentName({\n circuitJson,\n component,\n}: {\n circuitJson: AnyCircuitElement[]\n component: PcbComponent\n}): string {\n if (component.source_component_id) {\n const sourceComponent = circuitJson.find(\n (el): el is AnySourceComponent =>\n el.type === \"source_component\" &&\n el.source_component_id === component.source_component_id,\n )\n if (sourceComponent && \"name\" in sourceComponent && sourceComponent.name) {\n return sourceComponent.name\n }\n }\n\n return getReadableNameForComponent(circuitJson, component.pcb_component_id)\n}\n\n/**\n * Compute overlap distance based on polygon containment and distances.\n * - If component center is outside board, distance is from center to board polygon.\n * - Samples rotated rectangle corners and edge midpoints; for any outside points,\n * computes distance to board polygon and returns the maximum.\n * - Falls back to intersection area ratio heuristic if needed.\n * - Returns small epsilon if fully inside.\n */\nfunction computeOverlapDistance(\n compPoly: Flatten.Polygon,\n boardPoly: Flatten.Polygon,\n componentCenter: Point,\n componentWidth: number,\n componentHeight: number,\n rotationDeg: number,\n): number {\n const centerPoint = new Flatten.Point(componentCenter.x, componentCenter.y)\n // If center is outside board polygon, return distance to board polygon\n if (!boardPoly.contains(centerPoint)) {\n const dist = boardPoly.distanceTo(centerPoint)\n return Array.isArray(dist) ? dist[0] : Number(dist) || 0\n }\n\n // Sample corners and edge midpoints of rotated rectangle\n const hw = componentWidth / 2\n const hh = componentHeight / 2\n\n // Original corners\n const corners: Point[] = [\n { x: componentCenter.x - hw, y: componentCenter.y - hh },\n { x: componentCenter.x + hw, y: componentCenter.y - hh },\n { x: componentCenter.x + hw, y: componentCenter.y + hh },\n { x: componentCenter.x - hw, y: componentCenter.y + hh },\n ]\n\n // Edge midpoints\n const midpoints: Point[] = []\n for (let i = 0; i < 4; i++) {\n const next = (i + 1) % 4\n midpoints.push({\n x: (corners[i].x + corners[next].x) / 2,\n y: (corners[i].y + corners[next].y) / 2,\n })\n }\n\n // Rotate points around center by rotationDeg\n const matrix = rotateDEG(rotationDeg, componentCenter.x, componentCenter.y)\n const rotatePoint = (pt: Point) => {\n const p = applyToPoint(matrix, pt)\n return new Flatten.Point(p.x, p.y)\n }\n\n const rotatedPoints = corners.concat(midpoints).map(rotatePoint)\n\n // For any point outside board polygon, compute distance to board polygon\n let maxDistance = 0\n for (const pt of rotatedPoints) {\n if (!boardPoly.contains(pt)) {\n const dist = boardPoly.distanceTo(pt)\n const d = Array.isArray(dist) ? dist[0] : Number(dist) || 0\n if (d > maxDistance) maxDistance = d\n }\n }\n\n if (maxDistance > 0) {\n return maxDistance\n }\n\n // Fallback: use intersection area ratio heuristic\n try {\n const intersection = Flatten.BooleanOperations.intersect(\n compPoly,\n boardPoly,\n )\n\n let intersectionArea = 0\n if (!intersection) {\n intersectionArea = 0\n } else if (Array.isArray(intersection)) {\n intersectionArea = intersection.reduce(\n (sum, p) => sum + (typeof p.area === \"function\" ? p.area() : 0),\n 0,\n )\n } else if (typeof (intersection as any).area === \"function\") {\n intersectionArea = (intersection as any).area()\n } else {\n intersectionArea = 0\n }\n\n const compArea = compPoly.area()\n\n if (intersectionArea > 0 && intersectionArea < compArea) {\n const overlapRatio = 1 - intersectionArea / compArea\n const compWidth = Math.abs(componentWidth)\n const compHeight = Math.abs(componentHeight)\n return Math.min(compWidth, compHeight) * overlapRatio\n } else if (intersectionArea === 0) {\n // completely outside (should not happen here), return small epsilon\n return 0.1\n } else {\n return 0.1\n }\n } catch {\n // If boolean ops fail (unlikely), return small epsilon\n return 0.1\n }\n}\n\nfunction getRepositionSuggestion({\n componentPoly,\n boardPoly,\n}: {\n componentPoly: Flatten.Polygon\n boardPoly: Flatten.Polygon\n}): string | null {\n const boardBox = boardPoly.box\n const componentBox = componentPoly.box\n\n let deltaX = 0\n let deltaY = 0\n\n if (componentBox.xmin < boardBox.xmin) {\n deltaX = boardBox.xmin - componentBox.xmin\n } else if (componentBox.xmax > boardBox.xmax) {\n deltaX = boardBox.xmax - componentBox.xmax\n }\n\n if (componentBox.ymin < boardBox.ymin) {\n deltaY = boardBox.ymin - componentBox.ymin\n } else if (componentBox.ymax > boardBox.ymax) {\n deltaY = boardBox.ymax - componentBox.ymax\n }\n\n if (deltaX === 0 && deltaY === 0) {\n return null\n }\n\n const xDir = deltaX >= 0 ? \"right\" : \"left\"\n const yDir = deltaY >= 0 ? \"up\" : \"down\"\n const absDx = Math.abs(Math.round(deltaX * 100) / 100)\n const absDy = Math.abs(Math.round(deltaY * 100) / 100)\n\n if (absDx > 0 && absDy > 0) {\n return `Try moving it ${absDx}mm ${xDir} and ${absDy}mm ${yDir} to fit within the board edge.`\n }\n\n if (absDx > 0) {\n return `Try moving it ${absDx}mm ${xDir} to fit within the board edge.`\n }\n\n return `Try moving it ${absDy}mm ${yDir} to fit within the board edge.`\n}\n\n/**\n * Main function — polygon-first: construct polygons, test containment / intersection,\n * compute overlap distance using boolean intersection area or geometric distance.\n */\nexport function checkPcbComponentsOutOfBoard(\n circuitJson: AnyCircuitElement[],\n): PcbComponentOutsideBoardError[] {\n const board = circuitJson.find(\n (el): el is PcbBoard => el.type === \"pcb_board\",\n )\n if (!board) return []\n\n const boardPoly = boardToPolygon({ board })\n if (!boardPoly) return []\n\n const components = circuitJson.filter(\n (el): el is PcbComponent => el.type === \"pcb_component\",\n )\n if (components.length === 0) return []\n\n const errors: PcbComponentOutsideBoardError[] = []\n\n for (const c of components) {\n if (c.is_allowed_to_be_off_board) continue\n\n // need center, width, height\n if (\n !c.center ||\n typeof c.width !== \"number\" ||\n typeof c.height !== \"number\"\n )\n continue\n\n if (c.width <= 0 || c.height <= 0) continue\n\n // c.width and c.height store the world-coordinate AABB dimensions (post-rotation),\n // so we must NOT re-apply rotation here.\n const compPoly = rectanglePolygon({\n center: c.center,\n size: { width: c.width, height: c.height },\n rotationDeg: 0,\n })\n\n if (compPoly.area() === 0) continue\n\n // If component is entirely inside board polygon -> OK\n // Flatten.Polygon.contains accepts shapes; this is the correct polygon containment check.\n const isInside = boardPoly.contains(compPoly)\n if (isInside) continue\n\n // Component is at least partially outside. Compute overlapDistance:\n const overlapDistance = computeOverlapDistance(\n compPoly,\n boardPoly,\n c.center,\n c.width,\n c.height,\n 0,\n )\n\n const compName = getComponentName({ circuitJson, component: c })\n const overlapDistanceMm = Math.round(overlapDistance * 100) / 100\n const repositionSuggestion = getRepositionSuggestion({\n componentPoly: compPoly,\n boardPoly,\n })\n\n errors.push({\n type: \"pcb_component_outside_board_error\",\n error_type: \"pcb_component_outside_board_error\",\n pcb_component_outside_board_error_id: `pcb_component_outside_board_${c.pcb_component_id}`,\n message: `Component ${compName} extends outside board boundaries by ${overlapDistanceMm}mm.${repositionSuggestion ? ` ${repositionSuggestion}` : \"\"}`,\n pcb_component_id: c.pcb_component_id,\n pcb_board_id: board.pcb_board_id,\n component_center: c.center,\n component_bounds: {\n min_x: compPoly.box.xmin,\n max_x: compPoly.box.xmax,\n min_y: compPoly.box.ymin,\n max_y: compPoly.box.ymax,\n },\n subcircuit_id: c.subcircuit_id,\n source_component_id: c.source_component_id,\n })\n }\n\n return errors\n}\n","import type {\n AnyCircuitElement,\n PcbVia,\n PcbViaClearanceError,\n} from \"circuit-json\"\nimport { getReadableNameForElement } from \"@tscircuit/circuit-json-util\"\nimport {\n getFullConnectivityMapFromCircuitJson,\n type ConnectivityMap,\n} from \"circuit-json-to-connectivity-map\"\nimport { DEFAULT_SAME_NET_VIA_MARGIN, EPSILON } from \"lib/drc-defaults\"\nimport { distance } from \"lib/util/distance\"\nimport { viasAreAtSameLocation } from \"lib/util/viasAreAtSameLocation\"\n\nexport function checkSameNetViaSpacing(\n circuitJson: AnyCircuitElement[],\n {\n connMap,\n minClearance = DEFAULT_SAME_NET_VIA_MARGIN,\n }: { connMap?: ConnectivityMap; minClearance?: number } = {},\n): PcbViaClearanceError[] {\n const vias = circuitJson.filter((el) => el.type === \"pcb_via\") as PcbVia[]\n if (vias.length < 2) return []\n connMap ??= getFullConnectivityMapFromCircuitJson(circuitJson)\n const errors: PcbViaClearanceError[] = []\n const reported = new Set<string>()\n\n for (let i = 0; i < vias.length; i++) {\n for (let j = i + 1; j < vias.length; j++) {\n const viaA = vias[i]\n const viaB = vias[j]\n // TODO: It is a very inefficient piece of code, the way to fix it is to use flatbush.\n if (viasAreAtSameLocation(viaA, viaB)) continue\n if (!connMap.areIdsConnected(viaA.pcb_via_id, viaB.pcb_via_id)) continue\n const gap =\n distance(viaA, viaB) - viaA.outer_diameter / 2 - viaB.outer_diameter / 2\n if (gap + EPSILON >= minClearance!) continue\n const pairId = [viaA.pcb_via_id, viaB.pcb_via_id].sort().join(\"_\")\n if (reported.has(pairId)) continue\n reported.add(pairId)\n errors.push({\n type: \"pcb_via_clearance_error\",\n pcb_error_id: `same_net_vias_close_${pairId}`,\n message: `Vias ${getReadableNameForElement(\n circuitJson,\n viaA.pcb_via_id,\n )} and ${getReadableNameForElement(\n circuitJson,\n viaB.pcb_via_id,\n )} are too close together (gap: ${gap.toFixed(3)}mm)`,\n error_type: \"pcb_via_clearance_error\",\n pcb_via_ids: [viaA.pcb_via_id, viaB.pcb_via_id],\n minimum_clearance: minClearance,\n actual_clearance: gap,\n pcb_center: {\n x: (viaA.x + viaB.x) / 2,\n y: (viaA.y + viaB.y) / 2,\n },\n })\n }\n }\n\n return errors\n}\n","import type { PcbVia } from \"circuit-json\"\n\nexport function distance(a: PcbVia, b: PcbVia): number {\n return Math.hypot(a.x - b.x, a.y - b.y)\n}\n","import type { PcbVia } from \"circuit-json\"\nimport { distance } from \"./distance\"\nimport { EPSILON } from \"lib/drc-defaults\"\n\nexport function viasAreAtSameLocation(a: PcbVia, b: PcbVia): boolean {\n return distance(a, b) <= EPSILON\n}\n","import type {\n AnyCircuitElement,\n PcbVia,\n PcbViaClearanceError,\n} from \"circuit-json\"\nimport { getReadableNameForElement } from \"@tscircuit/circuit-json-util\"\nimport {\n getFullConnectivityMapFromCircuitJson,\n type ConnectivityMap,\n} from \"circuit-json-to-connectivity-map\"\nimport { DEFAULT_DIFFERENT_NET_VIA_MARGIN, EPSILON } from \"lib/drc-defaults\"\nimport { viasAreAtSameLocation } from \"lib/util/viasAreAtSameLocation\"\nimport { distance } from \"lib/util/distance\"\n\nexport function checkDifferentNetViaSpacing(\n circuitJson: AnyCircuitElement[],\n {\n connMap,\n minClearance = DEFAULT_DIFFERENT_NET_VIA_MARGIN,\n }: { connMap?: ConnectivityMap; minClearance?: number } = {},\n): PcbViaClearanceError[] {\n const vias = circuitJson.filter((el) => el.type === \"pcb_via\") as PcbVia[]\n if (vias.length < 2) return []\n connMap ??= getFullConnectivityMapFromCircuitJson(circuitJson)\n const errors: PcbViaClearanceError[] = []\n const reported = new Set<string>()\n\n for (let i = 0; i < vias.length; i++) {\n for (let j = i + 1; j < vias.length; j++) {\n const viaA = vias[i]\n const viaB = vias[j]\n // TODO: It is a very inefficient piece of code, the way to fix it is to use flatbush.\n if (viasAreAtSameLocation(viaA, viaB)) continue\n if (connMap.areIdsConnected(viaA.pcb_via_id, viaB.pcb_via_id)) continue\n const gap =\n distance(viaA, viaB) - viaA.outer_diameter / 2 - viaB.outer_diameter / 2\n if (gap + EPSILON >= minClearance!) continue\n const pairId = [viaA.pcb_via_id, viaB.pcb_via_id].sort().join(\"_\")\n if (reported.has(pairId)) continue\n reported.add(pairId)\n errors.push({\n type: \"pcb_via_clearance_error\",\n pcb_error_id: `different_net_vias_close_${pairId}`,\n message: `Vias ${getReadableNameForElement(\n circuitJson,\n viaA.pcb_via_id,\n )} and ${getReadableNameForElement(\n circuitJson,\n viaB.pcb_via_id,\n )} from different nets are too close together (gap: ${gap.toFixed(\n 3,\n )}mm)`,\n error_type: \"pcb_via_clearance_error\",\n pcb_via_ids: [viaA.pcb_via_id, viaB.pcb_via_id],\n minimum_clearance: minClearance,\n actual_clearance: gap,\n pcb_center: {\n x: (viaA.x + viaB.x) / 2,\n y: (viaA.y + viaB.y) / 2,\n },\n })\n }\n }\n\n return errors\n}\n","import { cju } from \"@tscircuit/circuit-json-util\"\nimport type {\n AnyCircuitElement,\n PcbPort,\n PcbTrace,\n PcbTraceWarning,\n SourceTrace,\n} from \"circuit-json\"\nimport { getFullConnectivityMapFromCircuitJson } from \"circuit-json-to-connectivity-map\"\nimport { getReadableNameForSourceTrace } from \"lib/util/get-readable-names\"\n\nexport function checkSourceTracesMatchPcbTraceThickness(\n circuitJson: AnyCircuitElement[],\n): PcbTraceWarning[] {\n const warnings: PcbTraceWarning[] = []\n const db = cju(circuitJson)\n\n const sourceTraces = db.source_trace.list() as SourceTrace[]\n const pcbTraces = db.pcb_trace.list() as PcbTrace[]\n const pcbPorts = db.pcb_port.list() as PcbPort[]\n const connectivityMap = getFullConnectivityMapFromCircuitJson(circuitJson)\n\n for (const sourceTrace of sourceTraces) {\n const requestedThickness = sourceTrace.min_trace_thickness\n if (requestedThickness === undefined) continue\n\n const connectedPcbPorts = pcbPorts.filter((pcbPort) =>\n sourceTrace.connected_source_port_ids?.includes(pcbPort.source_port_id),\n )\n if (connectedPcbPorts.length < 2) continue\n\n const referenceNetId = connectivityMap.getNetConnectedToId(\n connectedPcbPorts[0].pcb_port_id,\n )\n if (!referenceNetId) continue\n\n const netElementIds = connectivityMap.getIdsConnectedToNet(referenceNetId)\n const relatedPcbTraces = pcbTraces.filter((pcbTrace) =>\n netElementIds.includes(pcbTrace.pcb_trace_id),\n )\n if (relatedPcbTraces.length === 0) continue\n\n const actualWireWidths = relatedPcbTraces.flatMap((pcbTrace) =>\n pcbTrace.route\n .filter((point) => point.route_type === \"wire\")\n .map((point) => point.width),\n )\n if (actualWireWidths.length === 0) continue\n\n const actualThickness = Math.min(...actualWireWidths)\n if (actualThickness >= requestedThickness) continue\n\n let undersizedSegment:\n | { pcb_trace_id: string; center: { x: number; y: number } }\n | undefined\n\n for (const relatedPcbTrace of relatedPcbTraces) {\n for (let i = 0; i < relatedPcbTrace.route.length - 1; i++) {\n const point = relatedPcbTrace.route[i]\n const nextPoint = relatedPcbTrace.route[i + 1]\n if (!point || !nextPoint) continue\n if (point.route_type !== \"wire\" || nextPoint.route_type !== \"wire\") {\n continue\n }\n if (point.width !== actualThickness) continue\n\n undersizedSegment = {\n pcb_trace_id: relatedPcbTrace.pcb_trace_id,\n center: {\n x: (point.x + nextPoint.x) / 2,\n y: (point.y + nextPoint.y) / 2,\n },\n }\n break\n }\n\n if (undersizedSegment) break\n }\n\n if (!undersizedSegment) continue\n\n warnings.push({\n type: \"pcb_trace_warning\",\n pcb_trace_warning_id: `pcb_trace_warning_${sourceTrace.source_trace_id}`,\n warning_type: \"pcb_trace_warning\",\n message: `Trace [${getReadableNameForSourceTrace(circuitJson, sourceTrace)}] is routed thinner than requested (requested: ${requestedThickness}mm, actual: ${actualThickness}mm).`,\n center: undersizedSegment.center,\n source_trace_id: sourceTrace.source_trace_id,\n pcb_trace_id: undersizedSegment.pcb_trace_id,\n pcb_component_ids: Array.from(\n new Set(\n connectedPcbPorts\n .map((pcbPort) => pcbPort.pcb_component_id)\n .filter((id): id is string => id !== undefined),\n ),\n ),\n pcb_port_ids: connectedPcbPorts.map((pcbPort) => pcbPort.pcb_port_id),\n })\n }\n\n return warnings\n}\n","import type {\n AnyCircuitElement,\n PcbPort,\n PcbTrace,\n PcbTraceMissingError,\n SourceTrace,\n} from \"circuit-json\"\nimport { containsCircuitJsonId } from \"lib/util/get-readable-names\"\n\nconst getSourceTraceIdsFromPcbTrace = (\n pcbTraceSourceTraceId?: string,\n): string[] => {\n if (!pcbTraceSourceTraceId) return []\n if (!pcbTraceSourceTraceId.includes(\"__\")) return [pcbTraceSourceTraceId]\n\n return pcbTraceSourceTraceId\n .split(\"__\")\n .filter((part) => part.startsWith(\"source_trace_\"))\n}\n\n/**\n * Check that each source_trace which connects source ports has at least one\n * pcb_trace associated with it. If a source_trace has no corresponding\n * pcb_trace, return an error for that source_trace.\n */\nfunction checkSourceTracesHavePcbTraces(\n circuitJson: AnyCircuitElement[],\n): PcbTraceMissingError[] {\n const errors: PcbTraceMissingError[] = []\n const sourceTraces = circuitJson.filter(\n (el) => el.type === \"source_trace\",\n ) as SourceTrace[]\n const pcbTraces = circuitJson.filter(\n (el) => el.type === \"pcb_trace\",\n ) as PcbTrace[]\n\n for (const sourceTrace of sourceTraces) {\n if (!sourceTrace.connected_source_port_ids?.length) continue\n if ((sourceTrace.connected_source_net_ids?.length ?? 0) > 0) continue\n if (sourceTrace.connected_source_port_ids.length < 2) continue\n\n const hasPcbTrace = pcbTraces.some((pcbTrace) =>\n getSourceTraceIdsFromPcbTrace(pcbTrace.source_trace_id).includes(\n sourceTrace.source_trace_id,\n ),\n )\n if (!hasPcbTrace) {\n // Get PCB ports connected to this source trace\n const connectedPcbPorts = circuitJson.filter(\n (el) =>\n el.type === \"pcb_port\" &&\n sourceTrace.connected_source_port_ids.includes(el.source_port_id),\n ) as PcbPort[]\n\n // Find PCB components that these ports belong to\n const connectedPcbComponentIds = Array.from(\n new Set(\n connectedPcbPorts\n .map((port) => port.pcb_component_id)\n .filter((id): id is string => id !== undefined),\n ),\n )\n\n errors.push({\n type: \"pcb_trace_missing_error\",\n pcb_trace_missing_error_id: `pcb_trace_missing_${sourceTrace.source_trace_id}`,\n error_type: \"pcb_trace_missing_error\",\n message: `Trace [${sourceTrace.display_name && !containsCircuitJsonId(sourceTrace.display_name) ? sourceTrace.display_name : \"trace\"}] is not connected (it has no PCB trace)`,\n source_trace_id: sourceTrace.source_trace_id,\n pcb_component_ids: connectedPcbComponentIds,\n pcb_port_ids: connectedPcbPorts.map((port) => port.pcb_port_id),\n })\n }\n }\n\n return errors\n}\n\nexport { checkSourceTracesHavePcbTraces }\n","import type {\n AnyCircuitElement,\n PcbBoard,\n PcbTrace,\n PcbTraceError,\n} from \"circuit-json\"\nimport { cju } from \"@tscircuit/circuit-json-util\"\nimport type { Point, Polygon } from \"@tscircuit/math-utils\"\nimport { segmentToSegmentMinDistance } from \"@tscircuit/math-utils\"\n\n/**\n * Default margin for trace clearance from board edge (in mm)\n */\nconst DEFAULT_BOARD_MARGIN = 0.2\n\n/**\n * Configuration for trace board boundary checking\n */\nexport interface TraceBoardCheckConfig {\n /** Minimum distance from trace center to board edge (in mm) */\n margin?: number\n}\n\n/**\n * Create a board polygon representation using math-utils Polygon type\n */\nfunction getBoardPolygonPoints(board: PcbBoard): Polygon | null {\n if (board.outline && board.outline.length > 0) {\n // Use custom board outline\n return board.outline.map((p) => ({ x: p.x, y: p.y }))\n }\n\n if (\n board.center &&\n typeof board.width === \"number\" &&\n typeof board.height === \"number\"\n ) {\n // Create rectangular board outline\n const cx = board.center.x\n const cy = board.center.y\n const hw = board.width / 2\n const hh = board.height / 2\n\n return [\n { x: cx - hw, y: cy - hh }, // bottom-left\n { x: cx + hw, y: cy - hh }, // bottom-right\n { x: cx + hw, y: cy + hh }, // top-right\n { x: cx - hw, y: cy + hh }, // top-left\n ]\n }\n\n return null\n}\n\n/**\n * Check if any trace segment is too close to or outside the board outline\n * Uses segment-to-polygon distance with configurable margin\n */\nexport function checkPcbTracesOutOfBoard(\n circuitJson: AnyCircuitElement[],\n config: TraceBoardCheckConfig = {},\n): PcbTraceError[] {\n const errors: PcbTraceError[] = []\n const margin = config.margin ?? DEFAULT_BOARD_MARGIN\n\n // Find the board\n const board = circuitJson.find(\n (el): el is PcbBoard => el.type === \"pcb_board\",\n )\n if (!board) return errors\n\n // Create board polygon using math-utils Point type\n const boardPoints = getBoardPolygonPoints(board)\n if (!boardPoints) return errors\n\n // Get all PCB traces\n const pcbTraces = cju(circuitJson).pcb_trace.list()\n\n for (const trace of pcbTraces) {\n if (trace.route.length < 2) continue\n\n // Check each segment of the trace\n for (let i = 0; i < trace.route.length - 1; i++) {\n const p1 = trace.route[i]\n const p2 = trace.route[i + 1]\n\n // Only check wire segments\n if (p1.route_type !== \"wire\" || p2.route_type !== \"wire\") continue\n\n const traceWidth =\n \"width\" in p1 ? p1.width : \"width\" in p2 ? p2.width : 0.1\n const segmentStart: Point = { x: p1.x, y: p1.y }\n const segmentEnd: Point = { x: p2.x, y: p2.y }\n\n // Calculate minimum distance from trace segment to board polygon\n let minDistance = Infinity\n for (let j = 0; j < boardPoints.length; j++) {\n const edgeStart = boardPoints[j]\n const edgeEnd = boardPoints[(j + 1) % boardPoints.length]\n const distance = segmentToSegmentMinDistance(\n segmentStart,\n segmentEnd,\n edgeStart,\n edgeEnd,\n )\n if (distance < minDistance) {\n minDistance = distance\n }\n }\n\n const minimumDistance = traceWidth / 2 + margin\n\n if (minDistance < minimumDistance) {\n const error: PcbTraceError = {\n type: \"pcb_trace_error\",\n error_type: \"pcb_trace_error\",\n pcb_trace_error_id: `trace_too_close_to_board_${trace.pcb_trace_id}_segment_${i}`,\n message: `Trace too close to board edge (${minDistance.toFixed(3)}mm < ${minimumDistance.toFixed(3)}mm required, margin: ${margin}mm)`,\n pcb_trace_id: trace.pcb_trace_id,\n source_trace_id: trace.source_trace_id || \"\",\n center: {\n x: (segmentStart.x + segmentEnd.x) / 2,\n y: (segmentStart.y + segmentEnd.y) / 2,\n },\n pcb_component_ids: [],\n pcb_port_ids: [],\n }\n errors.push(error)\n }\n }\n }\n\n return errors\n}\n","import {\n cju,\n getBoundsOfPcbElements,\n getPrimaryId,\n} from \"@tscircuit/circuit-json-util\"\nimport { doBoundsOverlap } from \"@tscircuit/math-utils\"\nimport type { AnyCircuitElement, PcbFootprintOverlapError } from \"circuit-json\"\nimport { getFullConnectivityMapFromCircuitJson } from \"circuit-json-to-connectivity-map\"\nimport {\n getReadableNameForElementId,\n getReadableNameForPort,\n} from \"lib/util/get-readable-names\"\nimport {\n type OverlappableElement,\n doPcbElementsOverlap,\n} from \"./doPcbElementsOverlap\"\n\ninterface ComponentWithElements {\n component_id: string\n elements: OverlappableElement[]\n bounds: {\n minX: number\n minY: number\n maxX: number\n maxY: number\n }\n}\n\nconst formatOverlapElementDescription = (\n circuitJson: AnyCircuitElement[],\n element: OverlappableElement,\n): string => {\n if (\"pcb_port_id\" in element && element.pcb_port_id) {\n return getReadableNameForPort(circuitJson, element.pcb_port_id)\n }\n\n const id = getPrimaryId(element)\n const readableName = getReadableNameForElementId(circuitJson, id)\n return readableName === \"element\" ? `[${id}]` : readableName\n}\n\n/**\n * Check for overlapping PCB components\n * Returns errors for components that overlap inappropriately\n */\nexport function checkPcbComponentOverlap(\n circuitJson: AnyCircuitElement[],\n): PcbFootprintOverlapError[] {\n const errors: PcbFootprintOverlapError[] = []\n\n // Build connectivity map to check if components are electrically connected\n const connMap = getFullConnectivityMapFromCircuitJson(circuitJson)\n\n // Get all overlappable elements\n const smtPads = cju(circuitJson).pcb_smtpad.list()\n const platedHoles = cju(circuitJson).pcb_plated_hole.list()\n const holes = cju(circuitJson).pcb_hole.list()\n\n // Group elements by component (or treat standalone elements as their own \"component\")\n const componentMap = new Map<string, ComponentWithElements>()\n\n // Group SMT pads by component (or treat as standalone)\n for (const pad of smtPads) {\n const componentId =\n pad.pcb_component_id || `standalone_pad_${getPrimaryId(pad)}`\n if (!componentMap.has(componentId)) {\n componentMap.set(componentId, {\n component_id: componentId,\n elements: [],\n bounds: { minX: 0, minY: 0, maxX: 0, maxY: 0 },\n })\n }\n componentMap.get(componentId)!.elements.push(pad)\n }\n\n // Group plated holes by component (or treat as standalone)\n for (const hole of platedHoles) {\n const componentId =\n hole.pcb_component_id || `standalone_plated_hole_${getPrimaryId(hole)}`\n if (!componentMap.has(componentId)) {\n componentMap.set(componentId, {\n component_id: componentId,\n elements: [],\n bounds: { minX: 0, minY: 0, maxX: 0, maxY: 0 },\n })\n }\n componentMap.get(componentId)!.elements.push(hole)\n }\n\n // Holes typically don't have pcb_component_id, treat each as standalone\n for (const hole of holes) {\n const componentId = `standalone_hole_${getPrimaryId(hole)}`\n componentMap.set(componentId, {\n component_id: componentId,\n elements: [hole],\n bounds: { minX: 0, minY: 0, maxX: 0, maxY: 0 },\n })\n }\n\n // Compute bounds for each component\n for (const [componentId, componentData] of componentMap) {\n if (componentData.elements.length > 0) {\n componentData.bounds = getBoundsOfPcbElements(componentData.elements)\n }\n }\n\n // Convert map to array for pairwise iteration\n const componentsWithElements = Array.from(componentMap.values())\n\n // Pairwise check: only check elements if component bounds overlap\n for (let i = 0; i < componentsWithElements.length; i++) {\n for (let j = i + 1; j < componentsWithElements.length; j++) {\n const comp1 = componentsWithElements[i]\n const comp2 = componentsWithElements[j]\n\n // First check if component bounds overlap\n if (!doBoundsOverlap(comp1.bounds, comp2.bounds)) {\n continue\n }\n\n // Component bounds overlap, now check individual elements\n for (const elem1 of comp1.elements) {\n for (const elem2 of comp2.elements) {\n const id1 = getPrimaryId(elem1)\n const id2 = getPrimaryId(elem2)\n\n // Check if both are SMT pads and are electrically connected (same net) - if so, skip\n // This allows pads with the same subcircuit connectivity to be in contact\n if (\n elem1.type === \"pcb_smtpad\" &&\n elem2.type === \"pcb_smtpad\" &&\n connMap.areIdsConnected(id1, id2)\n ) {\n continue\n }\n\n // Check if element bounds overlap\n if (doPcbElementsOverlap(elem1, elem2)) {\n const elem1Description = formatOverlapElementDescription(\n circuitJson,\n elem1,\n )\n const elem2Description = formatOverlapElementDescription(\n circuitJson,\n elem2,\n )\n\n // Create error object\n const error: PcbFootprintOverlapError = {\n type: \"pcb_footprint_overlap_error\",\n pcb_error_id: `pcb_footprint_overlap_${id1}_${id2}`,\n error_type: \"pcb_footprint_overlap_error\",\n message: `${elem1.type} ${elem1Description} overlaps with ${elem2.type} ${elem2Description}`,\n }\n\n // Add relevant IDs based on element types\n if (elem1.type === \"pcb_smtpad\" || elem2.type === \"pcb_smtpad\") {\n error.pcb_smtpad_ids = []\n if (elem1.type === \"pcb_smtpad\") error.pcb_smtpad_ids.push(id1)\n if (elem2.type === \"pcb_smtpad\") error.pcb_smtpad_ids.push(id2)\n }\n\n if (\n elem1.type === \"pcb_plated_hole\" ||\n elem2.type === \"pcb_plated_hole\"\n ) {\n error.pcb_plated_hole_ids = []\n if (elem1.type === \"pcb_plated_hole\")\n error.pcb_plated_hole_ids.push(id1)\n if (elem2.type === \"pcb_plated_hole\")\n error.pcb_plated_hole_ids.push(id2)\n }\n\n if (elem1.type === \"pcb_hole\" || elem2.type === \"pcb_hole\") {\n error.pcb_hole_ids = []\n if (elem1.type === \"pcb_hole\") error.pcb_hole_ids.push(id1)\n if (elem2.type === \"pcb_hole\") error.pcb_hole_ids.push(id2)\n }\n\n errors.push(error)\n }\n }\n }\n }\n }\n\n return errors\n}\n","import { getBoundsOfPcbElements } from \"@tscircuit/circuit-json-util\"\nimport { doBoundsOverlap } from \"@tscircuit/math-utils\"\nimport type { PcbHole, PcbPlatedHole, PcbSmtPad } from \"circuit-json\"\nimport type { Collidable } from \"lib/check-each-pcb-trace-non-overlapping/getCollidableBounds\"\nimport { getLayersOfPcbElement } from \"lib/util/getLayersOfPcbElement\"\n\nexport type OverlappableElement = PcbSmtPad | PcbPlatedHole | PcbHole\n\nfunction getElementLayers(elem: OverlappableElement): string[] {\n return getLayersOfPcbElement(elem as Collidable)\n}\n\nfunction doLayersOverlap(layers1: string[], layers2: string[]): boolean {\n if (layers1.length === 0 || layers2.length === 0) return true\n return layers1.some((l) => layers2.includes(l))\n}\n\nexport function doPcbElementsOverlap(\n elem1: OverlappableElement,\n elem2: OverlappableElement,\n): boolean {\n // If the elements are on completely different layers they cannot overlap\n const layers1 = getElementLayers(elem1)\n const layers2 = getElementLayers(elem2)\n if (!doLayersOverlap(layers1, layers2)) return false\n\n const bounds1 = getBoundsOfPcbElements([elem1])\n const bounds2 = getBoundsOfPcbElements([elem2])\n return doBoundsOverlap(bounds1, bounds2)\n}\n","import {\n getPrimaryId,\n getReadableNameForElement,\n} from \"@tscircuit/circuit-json-util\"\nimport type { AnyCircuitElement, PcbPadPadClearanceError } from \"circuit-json\"\nimport {\n type ConnectivityMap,\n getFullConnectivityMapFromCircuitJson,\n} from \"circuit-json-to-connectivity-map\"\nimport { SpatialObjectIndex } from \"lib/data-structures/SpatialIndex\"\nimport { DEFAULT_PAD_PAD_CLEARANCE, EPSILON } from \"lib/drc-defaults\"\nimport { getLayersOfPcbElement } from \"lib/util/getLayersOfPcbElement\"\nimport {\n type PadElement,\n formatMm,\n getPadBounds,\n getPadCenter,\n getPadToPadGap,\n getPads,\n} from \"./check-pad-clearance/common\"\n\nexport function checkPadPadClearance(\n circuitJson: AnyCircuitElement[],\n {\n connMap,\n minClearance = DEFAULT_PAD_PAD_CLEARANCE,\n }: { connMap?: ConnectivityMap; minClearance?: number } = {},\n): PcbPadPadClearanceError[] {\n const pads = getPads(circuitJson)\n if (pads.length < 2) return []\n\n connMap ??= getFullConnectivityMapFromCircuitJson(circuitJson)\n const spatialIndex = new SpatialObjectIndex<PadElement>({\n objects: pads,\n getBounds: getPadBounds,\n getId: (pad) => getPrimaryId(pad),\n })\n\n const errors = new Map<string, PcbPadPadClearanceError>()\n\n for (const padA of pads) {\n const padAId = getPrimaryId(padA)\n const nearbyPads = spatialIndex.getObjectsInBounds(\n getPadBounds(padA),\n minClearance,\n )\n\n for (const padB of nearbyPads) {\n const padBId = getPrimaryId(padB)\n if (padAId === padBId) continue\n if (\n !getLayersOfPcbElement(padA as any).some((layer) =>\n getLayersOfPcbElement(padB as any).includes(layer),\n )\n ) {\n continue\n }\n if (connMap.areIdsConnected(padAId, padBId)) continue\n\n const pairId = [padAId, padBId].sort().join(\"_\")\n const gap = getPadToPadGap(padA, padB)\n if (gap + EPSILON >= minClearance!) continue\n const centerA = getPadCenter(padA)\n const centerB = getPadCenter(padB)\n\n const nextError: PcbPadPadClearanceError = {\n type: \"pcb_pad_pad_clearance_error\",\n pcb_pad_pad_clearance_error_id: `pad_pad_clearance_${pairId}`,\n error_type: \"pcb_pad_pad_clearance_error\",\n message: `Pads ${getReadableNameForElement(circuitJson, padAId)} and ${getReadableNameForElement(circuitJson, padBId)} are too close (clearance: ${formatMm(gap)}, minimum: ${formatMm(minClearance!)})`,\n pcb_pad_ids: [padAId, padBId] as [string, string],\n minimum_clearance: minClearance,\n actual_clearance: gap,\n center: {\n x: (centerA.x + centerB.x) / 2,\n y: (centerA.y + centerB.y) / 2,\n },\n }\n\n if (!errors.has(pairId)) {\n errors.set(pairId, nextError)\n }\n }\n }\n\n return Array.from(errors.values())\n}\n","import {\n cju,\n distanceBetweenCircleAndCircle,\n distanceBetweenCircleAndPolygon,\n distanceBetweenPolygonAndPolygon,\n getBoundsOfPcbElements,\n} from \"@tscircuit/circuit-json-util\"\nimport { midpoint } from \"@tscircuit/math-utils\"\nimport type {\n AnyCircuitElement,\n PcbPlatedHole,\n PcbSmtPad,\n PcbTrace,\n} from \"circuit-json\"\nimport type { PcbTraceSegment } from \"lib/check-each-pcb-trace-non-overlapping/getCollidableBounds\"\nimport type { Bounds } from \"lib/data-structures/SpatialIndex\"\nimport { DEFAULT_TRACE_THICKNESS } from \"lib/drc-defaults\"\n\nexport type PadElement = PcbSmtPad | PcbPlatedHole\n\nexport const formatMm = (value: number) => {\n const rounded = Math.round(value * 1000) / 1000\n return `${Number(rounded.toFixed(3))}mm`\n}\n\nexport const getPadBounds = (pad: PadElement): Bounds =>\n getBoundsOfPcbElements([pad])\n\nexport const getPadCenter = (pad: PadElement) => {\n const bounds = getPadBounds(pad)\n return midpoint(\n { x: bounds.minX, y: bounds.minY },\n { x: bounds.maxX, y: bounds.maxY },\n )\n}\n\nexport const getPadRadius = (pad: PadElement) => {\n const bounds = getPadBounds(pad)\n return Math.min(bounds.maxX - bounds.minX, bounds.maxY - bounds.minY) / 2\n}\n\nexport const isCircularPad = (pad: PadElement) => pad.shape === \"circle\"\n\nconst getCircleShape = (pad: PadElement) => {\n const center = getPadCenter(pad)\n return {\n kind: \"circle\" as const,\n x: center.x,\n y: center.y,\n radius: getPadRadius(pad),\n }\n}\n\nconst getPolygonShape = (pad: PadElement) => {\n const bounds = getPadBounds(pad)\n return {\n kind: \"polygon\" as const,\n points: [\n { x: bounds.minX, y: bounds.minY },\n { x: bounds.maxX, y: bounds.minY },\n { x: bounds.maxX, y: bounds.maxY },\n { x: bounds.minX, y: bounds.maxY },\n ],\n }\n}\n\nexport const getPadToPadGap = (padA: PadElement, padB: PadElement) => {\n if (isCircularPad(padA) && isCircularPad(padB)) {\n return distanceBetweenCircleAndCircle(\n getCircleShape(padA),\n getCircleShape(padB),\n )\n }\n\n if (isCircularPad(padA)) {\n return distanceBetweenCircleAndPolygon(\n getCircleShape(padA),\n getPolygonShape(padB),\n )\n }\n\n if (isCircularPad(padB)) {\n return distanceBetweenCircleAndPolygon(\n getCircleShape(padB),\n getPolygonShape(padA),\n )\n }\n\n return distanceBetweenPolygonAndPolygon(\n getPolygonShape(padA),\n getPolygonShape(padB),\n )\n}\n\nexport const getPads = (circuitJson: AnyCircuitElement[]) =>\n [\n ...cju(circuitJson).pcb_smtpad.list(),\n ...cju(circuitJson).pcb_plated_hole.list(),\n ] as PadElement[]\n\nexport const getTraceSegments = (\n circuitJson: AnyCircuitElement[],\n): PcbTraceSegment[] => {\n const pcbTraces = cju(circuitJson).pcb_trace.list()\n\n return pcbTraces.flatMap((pcbTrace) => {\n const segments: PcbTraceSegment[] = []\n\n for (let i = 0; i < pcbTrace.route.length - 1; i++) {\n const p1 = pcbTrace.route[i]\n const p2 = pcbTrace.route[i + 1]\n if (p1.route_type !== \"wire\") continue\n if (p2.route_type !== \"wire\") continue\n if (p1.layer !== p2.layer) continue\n\n segments.push({\n type: \"pcb_trace_segment\",\n pcb_trace_id: pcbTrace.pcb_trace_id,\n _pcbTrace: pcbTrace as PcbTrace,\n thickness:\n \"width\" in p1\n ? p1.width\n : \"width\" in p2\n ? p2.width\n : DEFAULT_TRACE_THICKNESS!,\n layer: p1.layer,\n x1: p1.x,\n y1: p1.y,\n x2: p2.x,\n y2: p2.y,\n })\n }\n\n return segments\n })\n}\n","import {\n getPrimaryId,\n getReadableNameForElement,\n} from \"@tscircuit/circuit-json-util\"\nimport {\n segmentToBoundsMinDistance,\n segmentToCircleMinDistance,\n} from \"@tscircuit/math-utils\"\nimport type { AnyCircuitElement, PcbPadTraceClearanceError } from \"circuit-json\"\nimport {\n type ConnectivityMap,\n getFullConnectivityMapFromCircuitJson,\n} from \"circuit-json-to-connectivity-map\"\nimport { getCollidableBounds } from \"lib/check-each-pcb-trace-non-overlapping/getCollidableBounds\"\nimport { SpatialObjectIndex } from \"lib/data-structures/SpatialIndex\"\nimport { DEFAULT_PAD_TRACE_CLEARANCE, EPSILON } from \"lib/drc-defaults\"\nimport { getLayersOfPcbElement } from \"lib/util/getLayersOfPcbElement\"\nimport {\n type PadElement,\n formatMm,\n getPadBounds,\n getPadCenter,\n getPadRadius,\n getPads,\n getTraceSegments,\n isCircularPad,\n} from \"./check-pad-clearance/common\"\n\nexport function checkPadTraceClearance(\n circuitJson: AnyCircuitElement[],\n {\n connMap,\n minClearance = DEFAULT_PAD_TRACE_CLEARANCE,\n }: { connMap?: ConnectivityMap; minClearance?: number } = {},\n): PcbPadTraceClearanceError[] {\n const pads = getPads(circuitJson)\n const segments = getTraceSegments(circuitJson)\n if (pads.length === 0 || segments.length === 0) return []\n\n connMap ??= getFullConnectivityMapFromCircuitJson(circuitJson)\n const spatialIndex = new SpatialObjectIndex<PadElement>({\n objects: pads,\n getBounds: getPadBounds,\n getId: (pad) => getPrimaryId(pad),\n })\n\n const errors = new Map<\n string,\n { error: PcbPadTraceClearanceError; gap: number }\n >()\n\n for (const segment of segments) {\n const nearbyPads = spatialIndex.getObjectsInBounds(\n getCollidableBounds(segment),\n minClearance! + segment.thickness / 2,\n )\n\n for (const pad of nearbyPads) {\n const padId = getPrimaryId(pad)\n if (!getLayersOfPcbElement(pad as any).includes(segment.layer)) continue\n if (connMap.areIdsConnected(segment.pcb_trace_id, padId)) continue\n const center = getPadCenter(pad)\n\n const gap = isCircularPad(pad)\n ? segmentToCircleMinDistance(\n { x: segment.x1, y: segment.y1 },\n { x: segment.x2, y: segment.y2 },\n { x: center.x, y: center.y, radius: getPadRadius(pad) },\n ) -\n segment.thickness / 2\n : segmentToBoundsMinDistance(\n { x: segment.x1, y: segment.y1 },\n { x: segment.x2, y: segment.y2 },\n getPadBounds(pad),\n ) -\n segment.thickness / 2\n if (gap + EPSILON >= minClearance!) continue\n\n const pairId = `${padId}_${segment.pcb_trace_id}`\n const nextError: PcbPadTraceClearanceError = {\n type: \"pcb_pad_trace_clearance_error\" as const,\n pcb_pad_trace_clearance_error_id: `pad_trace_clearance_${pairId}`,\n error_type: \"pcb_pad_trace_clearance_error\",\n message: `Pad ${getReadableNameForElement(circuitJson, padId)} and trace ${getReadableNameForElement(circuitJson, segment.pcb_trace_id)} are too close (clearance: ${formatMm(gap)}, minimum: ${formatMm(minClearance!)})`,\n pcb_pad_id: padId,\n pcb_trace_id: segment.pcb_trace_id,\n minimum_clearance: minClearance,\n actual_clearance: gap,\n center: {\n x: (center.x + (segment.x1 + segment.x2) / 2) / 2,\n y: (center.y + (segment.y1 + segment.y2) / 2) / 2,\n },\n }\n\n const current = errors.get(pairId)\n if (!current || gap < current.gap) {\n errors.set(pairId, { error: nextError, gap })\n }\n }\n }\n\n return Array.from(errors.values()).map(({ error }) => error)\n}\n","import { getReadableNameForElement } from \"@tscircuit/circuit-json-util\"\nimport { segmentToCircleMinDistance } from \"@tscircuit/math-utils\"\nimport type {\n AnyCircuitElement,\n PcbVia,\n PcbViaTraceClearanceError,\n} from \"circuit-json\"\nimport {\n type ConnectivityMap,\n getFullConnectivityMapFromCircuitJson,\n} from \"circuit-json-to-connectivity-map\"\nimport { DEFAULT_PAD_TRACE_CLEARANCE, EPSILON } from \"lib/drc-defaults\"\nimport { getLayersOfPcbElement } from \"lib/util/getLayersOfPcbElement\"\nimport { formatMm, getTraceSegments } from \"./check-pad-clearance/common\"\n\nconst getClosestPointOnSegment = (\n point: { x: number; y: number },\n segmentStart: { x: number; y: number },\n segmentEnd: { x: number; y: number },\n) => {\n const dx = segmentEnd.x - segmentStart.x\n const dy = segmentEnd.y - segmentStart.y\n const lengthSquared = dx * dx + dy * dy\n\n if (lengthSquared === 0) return segmentStart\n\n const t = Math.max(\n 0,\n Math.min(\n 1,\n ((point.x - segmentStart.x) * dx + (point.y - segmentStart.y) * dy) /\n lengthSquared,\n ),\n )\n\n return {\n x: segmentStart.x + t * dx,\n y: segmentStart.y + t * dy,\n }\n}\n\nexport function checkViaTraceClearance(\n circuitJson: AnyCircuitElement[],\n {\n connMap,\n minClearance = DEFAULT_PAD_TRACE_CLEARANCE,\n }: { connMap?: ConnectivityMap; minClearance?: number } = {},\n): PcbViaTraceClearanceError[] {\n const vias = circuitJson.filter((el) => el.type === \"pcb_via\") as PcbVia[]\n const segments = getTraceSegments(circuitJson)\n if (vias.length === 0 || segments.length === 0) return []\n\n connMap ??= getFullConnectivityMapFromCircuitJson(circuitJson)\n const errors = new Map<\n string,\n { error: PcbViaTraceClearanceError; gap: number }\n >()\n\n for (const via of vias) {\n const viaRadius = via.outer_diameter / 2\n for (const segment of segments) {\n if (!getLayersOfPcbElement(via).includes(segment.layer)) continue\n if (connMap.areIdsConnected(segment.pcb_trace_id, via.pcb_via_id))\n continue\n\n const gap =\n segmentToCircleMinDistance(\n { x: segment.x1, y: segment.y1 },\n { x: segment.x2, y: segment.y2 },\n {\n x: via.x,\n y: via.y,\n radius: viaRadius,\n },\n ) -\n segment.thickness / 2\n if (gap + EPSILON >= minClearance!) continue\n\n const closestPoint = getClosestPointOnSegment(\n { x: via.x, y: via.y },\n { x: segment.x1, y: segment.y1 },\n { x: segment.x2, y: segment.y2 },\n )\n\n const pairId = `${via.pcb_via_id}_${segment.pcb_trace_id}`\n const nextError: PcbViaTraceClearanceError = {\n type: \"pcb_via_trace_clearance_error\",\n pcb_via_trace_clearance_error_id: `via_trace_clearance_${pairId}`,\n error_type: \"pcb_via_trace_clearance_error\",\n message: `Via ${getReadableNameForElement(circuitJson, via.pcb_via_id)} and trace ${getReadableNameForElement(circuitJson, segment.pcb_trace_id)} are too close (clearance: ${formatMm(gap)}, minimum: ${formatMm(minClearance!)})`,\n pcb_via_id: via.pcb_via_id,\n pcb_trace_id: segment.pcb_trace_id,\n minimum_clearance: minClearance,\n actual_clearance: gap,\n center: {\n x: (via.x + closestPoint.x) / 2,\n y: (via.y + closestPoint.y) / 2,\n },\n }\n\n const current = errors.get(pairId)\n if (!current || gap < current.gap) {\n errors.set(pairId, { error: nextError, gap })\n }\n }\n }\n\n return Array.from(errors.values()).map(({ error }) => error)\n}\n","import type { AnyCircuitElement } from \"circuit-json\"\n\ntype SourceComponent = Extract<\n AnyCircuitElement,\n { source_component_id: string; name: string }\n>\n\ntype SourcePort = Extract<AnyCircuitElement, { type: \"source_port\" }>\ntype SourceTrace = Extract<AnyCircuitElement, { type: \"source_trace\" }>\n\ntype SourcePinMustBeConnectedError = {\n type: \"source_pin_must_be_connected_error\"\n source_pin_must_be_connected_error_id: string\n error_type: \"source_pin_must_be_connected_error\"\n message: string\n source_component_id: string\n source_port_id: string\n subcircuit_id?: string\n}\n\n/**\n * Check that each source port with must_be_connected attribute is actually\n * connected to a trace. Returns errors for any pins that are marked as\n * must_be_connected but are floating (not connected to any trace).\n */\nexport function checkPinMustBeConnected(\n circuitJson: AnyCircuitElement[],\n): SourcePinMustBeConnectedError[] {\n const errors: SourcePinMustBeConnectedError[] = []\n\n // Get all source components, ports, and traces\n const sourceComponents = circuitJson.filter(\n (el): el is SourceComponent =>\n \"source_component_id\" in el &&\n (el.type === \"source_component\" || el.type.startsWith(\"source_simple_\")),\n )\n const sourcePorts = circuitJson.filter(\n (el): el is SourcePort => el.type === \"source_port\",\n )\n const sourceTraces = circuitJson.filter(\n (el): el is SourceTrace => el.type === \"source_trace\",\n )\n\n // Build a set of connected source port IDs\n const connectedPortIds = new Set<string>()\n for (const trace of sourceTraces) {\n for (const portId of trace.connected_source_port_ids ?? []) {\n connectedPortIds.add(portId)\n }\n }\n\n // Build a map of internally connected port groups for each component\n const componentInternalConnections = new Map<string, string[][]>()\n for (const component of sourceComponents) {\n if (\n \"internally_connected_source_port_ids\" in component &&\n component.internally_connected_source_port_ids\n ) {\n componentInternalConnections.set(\n component.source_component_id,\n component.internally_connected_source_port_ids,\n )\n }\n }\n\n // For each internal group, if any port is connected, mark all ports in the group as connected\n for (const internalGroups of componentInternalConnections.values()) {\n for (const group of internalGroups) {\n if (group.some((portId) => connectedPortIds.has(portId))) {\n for (const portId of group) {\n connectedPortIds.add(portId)\n }\n }\n }\n }\n\n // Check each port for must_be_connected attribute\n for (const port of sourcePorts) {\n if (port.must_be_connected === true) {\n if (!connectedPortIds.has(port.source_port_id)) {\n const component = sourceComponents.find(\n (c) => c.source_component_id === port.source_component_id,\n )\n const componentName = component?.name ?? \"Unknown\"\n\n errors.push({\n type: \"source_pin_must_be_connected_error\",\n source_pin_must_be_connected_error_id: `source_pin_must_be_connected_error_${port.source_port_id}`,\n error_type: \"source_pin_must_be_connected_error\",\n message: `Port ${port.name} on ${componentName} must be connected but is floating`,\n source_component_id: port.source_component_id ?? \"\",\n source_port_id: port.source_port_id,\n subcircuit_id: port.subcircuit_id,\n })\n }\n }\n }\n\n return errors\n}\n","import { cju } from \"@tscircuit/circuit-json-util\"\nimport type {\n AnyCircuitElement,\n SourceComponentPinsUnderspecifiedWarning,\n SourcePinAttributes,\n SourcePort,\n SourceComponentBase,\n} from \"circuit-json\"\n\nconst PIN_ATTRIBUTE_KEYS = [\n \"must_be_connected\",\n \"provides_power\",\n \"requires_power\",\n \"provides_ground\",\n \"requires_ground\",\n \"provides_voltage\",\n \"requires_voltage\",\n \"do_not_connect\",\n \"include_in_board_pinout\",\n \"can_use_internal_pullup\",\n \"is_using_internal_pullup\",\n \"needs_external_pullup\",\n \"can_use_internal_pulldown\",\n \"is_using_internal_pulldown\",\n \"needs_external_pulldown\",\n \"can_use_open_drain\",\n \"is_using_open_drain\",\n \"can_use_push_pull\",\n \"is_using_push_pull\",\n \"should_have_decoupling_capacitor\",\n \"recommended_decoupling_capacitor_capacitance\",\n \"is_configured_for_i2c_sda\",\n \"is_configured_for_i2c_scl\",\n \"is_configured_for_spi_mosi\",\n \"is_configured_for_spi_miso\",\n \"is_configured_for_spi_sck\",\n \"is_configured_for_spi_cs\",\n \"is_configured_for_uart_tx\",\n \"is_configured_for_uart_rx\",\n \"supports_i2c_sda\",\n \"supports_i2c_scl\",\n \"supports_spi_mosi\",\n \"supports_spi_miso\",\n \"supports_spi_sck\",\n \"supports_spi_cs\",\n \"supports_uart_tx\",\n \"supports_uart_rx\",\n] as const satisfies readonly (keyof SourcePinAttributes)[]\n\nfunction hasAnyPinAttribute(port: SourcePort): boolean {\n return PIN_ATTRIBUTE_KEYS.some((key) => port[key] !== undefined)\n}\n\n/**\n * Check that each component with ports has at least one pin attribute\n * specified across its ports. Returns a warning when all pins are\n * underspecified (no SourcePinAttributes fields set on any port).\n */\nexport function checkAllPinsInComponentAreUnderspecified(\n circuitJson: AnyCircuitElement[],\n): SourceComponentPinsUnderspecifiedWarning[] {\n const warnings: SourceComponentPinsUnderspecifiedWarning[] = []\n const db = cju(circuitJson)\n\n const sourceComponents = db.source_component.list() as SourceComponentBase[]\n const sourcePorts = db.source_port.list() as SourcePort[]\n\n const portsByComponent = new Map<string, SourcePort[]>()\n for (const port of sourcePorts) {\n if (!port.source_component_id) continue\n const existing = portsByComponent.get(port.source_component_id) ?? []\n existing.push(port)\n portsByComponent.set(port.source_component_id, existing)\n }\n\n for (const component of sourceComponents) {\n if (component.ftype !== \"simple_chip\") continue\n\n const componentPorts =\n portsByComponent.get(component.source_component_id) ?? []\n if (componentPorts.length === 0) continue\n\n const hasAnySpecifiedAttributes = componentPorts.some((port) =>\n hasAnyPinAttribute(port),\n )\n\n if (hasAnySpecifiedAttributes) continue\n\n warnings.push({\n type: \"source_component_pins_underspecified_warning\",\n source_component_pins_underspecified_warning_id: `source_component_pins_underspecified_warning_${component.source_component_id}`,\n warning_type: \"source_component_pins_underspecified_warning\",\n message: `All pins on ${component.name} are underspecified (no pinAttributes set)`,\n source_component_id: component.source_component_id,\n source_port_ids: componentPorts.map((port) => port.source_port_id),\n subcircuit_id: componentPorts[0]?.subcircuit_id,\n })\n }\n\n return warnings\n}\n","import { cju } from \"@tscircuit/circuit-json-util\"\nimport type {\n AnyCircuitElement,\n SourceComponentBase,\n SourceNoPowerPinDefinedWarning,\n SourcePort,\n} from \"circuit-json\"\n\n/**\n * Check that each chip has at least one pin marked as requires_power=true.\n * Returns warnings for chips where no pin declares requires_power.\n */\nexport function checkNoPowerPinDefined(\n circuitJson: AnyCircuitElement[],\n): SourceNoPowerPinDefinedWarning[] {\n const warnings: SourceNoPowerPinDefinedWarning[] = []\n const db = cju(circuitJson)\n\n const sourceComponents = db.source_component.list() as SourceComponentBase[]\n const sourcePorts = db.source_port.list() as SourcePort[]\n\n const portsByComponent = new Map<string, SourcePort[]>()\n for (const port of sourcePorts) {\n if (!port.source_component_id) continue\n const existing = portsByComponent.get(port.source_component_id) ?? []\n existing.push(port)\n portsByComponent.set(port.source_component_id, existing)\n }\n\n for (const component of sourceComponents) {\n if (component.ftype !== \"simple_chip\") continue\n\n const componentPorts =\n portsByComponent.get(component.source_component_id) ?? []\n if (componentPorts.length === 0) continue\n\n const hasRequiredPowerPin = componentPorts.some(\n (port) => port.requires_power === true,\n )\n if (hasRequiredPowerPin) continue\n\n warnings.push({\n type: \"source_no_power_pin_defined_warning\",\n source_no_power_pin_defined_warning_id: `source_no_power_pin_defined_warning_${component.source_component_id}`,\n warning_type: \"source_no_power_pin_defined_warning\",\n message: `${component.name} has no pin with requires_power=true`,\n source_component_id: component.source_component_id,\n source_port_ids: componentPorts.map((port) => port.source_port_id),\n subcircuit_id: componentPorts[0]?.subcircuit_id,\n })\n }\n\n return warnings\n}\n","import { cju } from \"@tscircuit/circuit-json-util\"\nimport type {\n AnyCircuitElement,\n SourceComponentBase,\n SourceNoGroundPinDefinedWarning,\n SourcePort,\n} from \"circuit-json\"\n\n/**\n * Check that each chip has at least one pin marked as requires_ground=true.\n * Returns warnings for chips where no pin declares requires_ground.\n */\nexport function checkNoGroundPinDefined(\n circuitJson: AnyCircuitElement[],\n): SourceNoGroundPinDefinedWarning[] {\n const warnings: SourceNoGroundPinDefinedWarning[] = []\n const db = cju(circuitJson)\n\n const sourceComponents = db.source_component.list() as SourceComponentBase[]\n const sourcePorts = db.source_port.list() as SourcePort[]\n\n const portsByComponent = new Map<string, SourcePort[]>()\n for (const port of sourcePorts) {\n if (!port.source_component_id) continue\n const existing = portsByComponent.get(port.source_component_id) ?? []\n existing.push(port)\n portsByComponent.set(port.source_component_id, existing)\n }\n\n for (const component of sourceComponents) {\n if (component.ftype !== \"simple_chip\") continue\n\n const componentPorts =\n portsByComponent.get(component.source_component_id) ?? []\n if (componentPorts.length === 0) continue\n\n const hasRequiredGroundPin = componentPorts.some(\n (port) => port.requires_ground === true,\n )\n if (hasRequiredGroundPin) continue\n\n warnings.push({\n type: \"source_no_ground_pin_defined_warning\",\n source_no_ground_pin_defined_warning_id: `source_no_ground_pin_defined_warning_${component.source_component_id}`,\n warning_type: \"source_no_ground_pin_defined_warning\",\n message: `${component.name} has no pin with requires_ground=true`,\n source_component_id: component.source_component_id,\n source_port_ids: componentPorts.map((port) => port.source_port_id),\n subcircuit_id: componentPorts[0]?.subcircuit_id,\n })\n }\n\n return warnings\n}\n","import { getBoardBounds } from \"@tscircuit/circuit-json-util\"\nimport type {\n AnyCircuitElement,\n PcbBoard,\n PcbComponent,\n PcbConnectorNotInAccessibleOrientationWarning,\n} from \"circuit-json\"\nimport { getReadableNameForComponent } from \"./util/get-readable-names\"\n\ntype FacingDirection = \"x-\" | \"x+\" | \"y+\" | \"y-\"\n\nfunction getFacingDirection(component: PcbComponent): FacingDirection | null {\n if (!component.center || !component.cable_insertion_center) return null\n\n const dx = component.cable_insertion_center.x - component.center.x\n const dy = component.cable_insertion_center.y - component.center.y\n\n if (Math.abs(dx) < 1e-6 && Math.abs(dy) < 1e-6) return null\n\n if (Math.abs(dx) >= Math.abs(dy)) {\n return dx >= 0 ? \"x+\" : \"x-\"\n }\n\n return dy >= 0 ? \"y+\" : \"y-\"\n}\n\nfunction getRecommendedFacingDirection(\n component: PcbComponent,\n bounds: { minX: number; maxX: number; minY: number; maxY: number },\n): FacingDirection | null {\n if (!component.center) return null\n\n const distances = [\n { direction: \"x-\" as const, distance: component.center.x - bounds.minX },\n { direction: \"x+\" as const, distance: bounds.maxX - component.center.x },\n { direction: \"y-\" as const, distance: component.center.y - bounds.minY },\n { direction: \"y+\" as const, distance: bounds.maxY - component.center.y },\n ]\n\n distances.sort((a, b) => a.distance - b.distance)\n return distances[0]?.direction ?? null\n}\n\nexport function checkConnectorAccessibleOrientation(\n circuitJson: AnyCircuitElement[],\n): PcbConnectorNotInAccessibleOrientationWarning[] {\n const board = circuitJson.find(\n (el): el is PcbBoard => el.type === \"pcb_board\",\n )\n if (!board) return []\n\n const bounds = (() => {\n try {\n return getBoardBounds(board)\n } catch {\n return null\n }\n })()\n if (!bounds) return []\n\n const warnings: PcbConnectorNotInAccessibleOrientationWarning[] = []\n\n const components = circuitJson.filter(\n (el): el is PcbComponent => el.type === \"pcb_component\",\n )\n\n for (const component of components) {\n if (!component.cable_insertion_center) continue\n\n const facingDirection = getFacingDirection(component)\n const recommendedFacingDirection = getRecommendedFacingDirection(\n component,\n bounds,\n )\n\n if (!facingDirection || !recommendedFacingDirection) continue\n if (facingDirection === recommendedFacingDirection) continue\n\n const componentName = getReadableNameForComponent(\n circuitJson,\n component.pcb_component_id,\n )\n\n warnings.push({\n type: \"pcb_connector_not_in_accessible_orientation_warning\",\n warning_type: \"pcb_connector_not_in_accessible_orientation_warning\",\n pcb_connector_not_in_accessible_orientation_warning_id: `pcb_connector_not_in_accessible_orientation_warning_${component.pcb_component_id}`,\n message: `${componentName} is facing ${facingDirection} but should face ${recommendedFacingDirection} so the connector is accessible from the board edge`,\n pcb_component_id: component.pcb_component_id,\n source_component_id: component.source_component_id,\n pcb_board_id: board.pcb_board_id,\n facing_direction: facingDirection,\n recommended_facing_direction: recommendedFacingDirection,\n subcircuit_id: component.subcircuit_id,\n })\n }\n\n return warnings\n}\n","import {\n doSegmentsIntersect,\n isPointInsidePolygon,\n} from \"@tscircuit/math-utils\"\nimport type {\n AnyCircuitElement,\n PcbCourtyardOverlapError,\n PcbCourtyardRect,\n PcbCourtyardCircle,\n PcbCourtyardOutline,\n} from \"circuit-json\"\n\ntype CourtyardElement =\n | PcbCourtyardRect\n | PcbCourtyardCircle\n | PcbCourtyardOutline\n\nfunction getCourtyardPolygon(el: CourtyardElement): { x: number; y: number }[] {\n if (el.type === \"pcb_courtyard_rect\") {\n const hw = el.width / 2\n const hh = el.height / 2\n const corners = [\n { x: -hw, y: -hh },\n { x: +hw, y: -hh },\n { x: +hw, y: +hh },\n { x: -hw, y: +hh },\n ]\n const angle = ((el.ccw_rotation ?? 0) * Math.PI) / 180\n const cos = Math.cos(angle)\n const sin = Math.sin(angle)\n return corners.map(({ x, y }) => ({\n x: el.center.x + x * cos - y * sin,\n y: el.center.y + x * sin + y * cos,\n }))\n }\n if (el.type === \"pcb_courtyard_circle\") {\n const N = 32\n return Array.from({ length: N }, (_, i) => {\n const a = (2 * Math.PI * i) / N\n return {\n x: el.center.x + el.radius * Math.cos(a),\n y: el.center.y + el.radius * Math.sin(a),\n }\n })\n }\n return el.outline\n}\n\nfunction getComponentName(\n circuitJson: AnyCircuitElement[],\n pcbComponentId: string,\n): string {\n const pcbComponent = circuitJson.find(\n (el) =>\n el.type === \"pcb_component\" && el.pcb_component_id === pcbComponentId,\n )\n if (pcbComponent?.type !== \"pcb_component\") return pcbComponentId\n const sourceComponent = circuitJson.find(\n (el) =>\n el.type === \"source_component\" &&\n el.source_component_id === pcbComponent.source_component_id,\n )\n if (sourceComponent?.type === \"source_component\" && sourceComponent.name) {\n return sourceComponent.name\n }\n return pcbComponentId\n}\n\ntype Point = { x: number; y: number }\n\nfunction polygonsOverlap(polyA: Point[], polyB: Point[]): boolean {\n // Check if any vertex of A is inside B or vice versa\n if (polyA.some((p) => isPointInsidePolygon(p, polyB))) return true\n if (polyB.some((p) => isPointInsidePolygon(p, polyA))) return true\n // Check if any edge of A intersects any edge of B\n for (let i = 0; i < polyA.length; i++) {\n const a1 = polyA[i]\n const a2 = polyA[(i + 1) % polyA.length]\n for (let j = 0; j < polyB.length; j++) {\n const b1 = polyB[j]\n const b2 = polyB[(j + 1) % polyB.length]\n if (doSegmentsIntersect(a1, a2, b1, b2)) return true\n }\n }\n return false\n}\n\n/**\n * Check for overlapping PCB component courtyards.\n * Returns one error per pair of components whose courtyard elements overlap.\n */\nexport function checkCourtyardOverlap(\n circuitJson: AnyCircuitElement[],\n): PcbCourtyardOverlapError[] {\n const courtyards = circuitJson.filter(\n (el): el is CourtyardElement =>\n el.type === \"pcb_courtyard_rect\" ||\n el.type === \"pcb_courtyard_circle\" ||\n el.type === \"pcb_courtyard_outline\",\n )\n\n // Group by component\n const byComponent = new Map<string, CourtyardElement[]>()\n for (const el of courtyards) {\n const id = el.pcb_component_id\n if (!byComponent.has(id)) byComponent.set(id, [])\n byComponent.get(id)!.push(el)\n }\n\n const componentIds = Array.from(byComponent.keys())\n const errors: PcbCourtyardOverlapError[] = []\n\n for (let i = 0; i < componentIds.length; i++) {\n for (let j = i + 1; j < componentIds.length; j++) {\n const idA = componentIds[i]\n const idB = componentIds[j]\n\n let overlapping = false\n outer: for (const a of byComponent.get(idA)!) {\n for (const b of byComponent.get(idB)!) {\n // Skip overlap check if courtyards are on different layers\n if (\"layer\" in a && \"layer\" in b && a.layer !== b.layer) {\n continue\n }\n const polyA = getCourtyardPolygon(a)\n const polyB = getCourtyardPolygon(b)\n if (polygonsOverlap(polyA, polyB)) {\n overlapping = true\n break outer\n }\n }\n }\n\n if (overlapping) {\n errors.push({\n type: \"pcb_courtyard_overlap_error\",\n pcb_error_id: `pcb_courtyard_overlap_${idA}_${idB}`,\n error_type: \"pcb_courtyard_overlap_error\",\n message: `Courtyard of ${getComponentName(circuitJson, idA)} overlaps with courtyard of ${getComponentName(circuitJson, idB)}`,\n pcb_component_ids: [idA, idB],\n })\n }\n }\n }\n\n return errors\n}\n","import type {\n AnyCircuitElement,\n PcbTraceError,\n PcbPort,\n PcbTrace,\n SourceTrace,\n PcbSmtPad,\n PcbPlatedHole,\n} from \"circuit-json\"\nimport { isPointInPad } from \"./is-point-in-pad\"\nimport { getReadableNameForPcbPort } from \"@tscircuit/circuit-json-util\"\n\nfunction checkTracesAreContiguous(\n circuitJson: AnyCircuitElement[],\n): PcbTraceError[] {\n const errors: PcbTraceError[] = []\n\n const pcbPorts = circuitJson.filter(\n (el) => el.type === \"pcb_port\",\n ) as PcbPort[]\n const pcbTraces = circuitJson.filter(\n (el) => el.type === \"pcb_trace\",\n ) as PcbTrace[]\n const sourceTraces = circuitJson.filter(\n (el) => el.type === \"source_trace\",\n ) as SourceTrace[]\n const pcbSmtPads = circuitJson.filter(\n (el) => el.type === \"pcb_smtpad\",\n ) as PcbSmtPad[]\n const pcbPlatedHoles = circuitJson.filter(\n (el) => el.type === \"pcb_plated_hole\",\n ) as PcbPlatedHole[]\n\n const padMap = new Map<string, PcbSmtPad | PcbPlatedHole>()\n\n for (const pad of pcbSmtPads) {\n if (pad.pcb_port_id) {\n padMap.set(pad.pcb_port_id, pad)\n }\n }\n\n for (const hole of pcbPlatedHoles) {\n if (hole.pcb_port_id) {\n padMap.set(hole.pcb_port_id, hole)\n }\n }\n\n for (const trace of pcbTraces) {\n if (trace.route.length === 0) continue\n\n const firstPoint = trace.route[0]\n const lastPoint = trace.route[trace.route.length - 1]\n\n const sourceTrace = sourceTraces.find(\n (st) => st.source_trace_id === trace.source_trace_id,\n )\n\n const expectedPorts = sourceTrace\n ? pcbPorts.filter((port) =>\n sourceTrace.connected_source_port_ids?.includes(port.source_port_id),\n )\n : []\n\n for (let i = 1; i < trace.route.length - 1; i++) {\n const prevPoint = trace.route[i - 1]\n const currentPoint = trace.route[i]\n const nextPoint = trace.route[i + 1]\n\n if (currentPoint.route_type === \"via\") {\n const prevIsWire = prevPoint.route_type === \"wire\"\n const nextIsWire = nextPoint.route_type === \"wire\"\n\n if (prevIsWire && nextIsWire) {\n const prevAligned =\n Math.abs(prevPoint.x - currentPoint.x) < 0.01 &&\n Math.abs(prevPoint.y - currentPoint.y) < 0.01\n\n const nextAligned =\n Math.abs(nextPoint.x - currentPoint.x) < 0.01 &&\n Math.abs(nextPoint.y - currentPoint.y) < 0.01\n\n if (!prevAligned || !nextAligned) {\n const traceName =\n sourceTrace?.display_name || trace.source_trace_id || \"unknown\"\n errors.push({\n type: \"pcb_trace_error\",\n message: `Via in trace [${traceName}] is misaligned at position {x: ${currentPoint.x}, y: ${currentPoint.y}}.`,\n source_trace_id:\n sourceTrace?.source_trace_id ||\n trace.source_trace_id ||\n `!${trace.pcb_trace_id}`,\n error_type: \"pcb_trace_error\",\n pcb_trace_id: trace.pcb_trace_id,\n pcb_trace_error_id: \"\",\n pcb_component_ids: [],\n pcb_port_ids: [],\n })\n }\n }\n }\n }\n\n const traceName =\n sourceTrace?.display_name || trace.source_trace_id || \"unknown\"\n\n // For traces with known expected ports, check specific connections\n for (const port of expectedPorts) {\n if (!port.pcb_port_id) continue\n\n const pad = padMap.get(port.pcb_port_id)\n\n if (!pad) continue\n\n const isFirstPointConnected =\n firstPoint.route_type === \"wire\" &&\n isPointInPad({ x: firstPoint.x, y: firstPoint.y }, pad)\n\n const isLastPointConnected =\n lastPoint.route_type === \"wire\" &&\n isPointInPad({ x: lastPoint.x, y: lastPoint.y }, pad)\n\n if (!isFirstPointConnected && !isLastPointConnected) {\n const portName = getReadableNameForPcbPort(\n circuitJson,\n port.pcb_port_id,\n ).replace(\"pcb_port\", \"\")\n const padType = pad.type.replace(/pcb_/, \"\")\n // Use the midpoint between trace endpoints as error location\n const errorCenter = {\n x: (firstPoint.x + lastPoint.x) / 2,\n y: (firstPoint.y + lastPoint.y) / 2,\n }\n errors.push({\n type: \"pcb_trace_error\",\n message: `Trace [${traceName}] is missing a connection to ${padType}${portName}`,\n source_trace_id:\n sourceTrace?.source_trace_id ||\n trace.source_trace_id ||\n `!${trace.pcb_trace_id}`,\n error_type: \"pcb_trace_error\",\n pcb_trace_id: trace.pcb_trace_id,\n pcb_trace_error_id: \"\",\n center: errorCenter,\n pcb_component_ids: [],\n pcb_port_ids: [port.pcb_port_id],\n })\n }\n }\n\n // For net-level traces (no expected ports), check if endpoints are floating\n if (expectedPorts.length === 0) {\n let firstConnectsToAnyPad = false\n let lastConnectsToAnyPad = false\n\n for (const [portId, pad] of padMap) {\n if (\n firstPoint.route_type === \"wire\" &&\n isPointInPad({ x: firstPoint.x, y: firstPoint.y }, pad)\n ) {\n firstConnectsToAnyPad = true\n }\n if (\n lastPoint.route_type === \"wire\" &&\n isPointInPad({ x: lastPoint.x, y: lastPoint.y }, pad)\n ) {\n lastConnectsToAnyPad = true\n }\n }\n\n if (!firstConnectsToAnyPad && firstPoint.route_type === \"wire\") {\n errors.push({\n type: \"pcb_trace_error\",\n message: `Trace [${traceName}] has disconnected endpoint at (${firstPoint.x.toFixed(2)}, ${firstPoint.y.toFixed(2)})`,\n source_trace_id:\n sourceTrace?.source_trace_id ||\n trace.source_trace_id ||\n `!${trace.pcb_trace_id}`,\n error_type: \"pcb_trace_error\",\n pcb_trace_id: trace.pcb_trace_id,\n pcb_trace_error_id: \"\",\n center: { x: firstPoint.x, y: firstPoint.y },\n pcb_component_ids: [],\n pcb_port_ids: [],\n })\n }\n if (!lastConnectsToAnyPad && lastPoint.route_type === \"wire\") {\n errors.push({\n type: \"pcb_trace_error\",\n message: `Trace [${traceName}] has disconnected endpoint at (${lastPoint.x.toFixed(2)}, ${lastPoint.y.toFixed(2)})`,\n source_trace_id:\n sourceTrace?.source_trace_id ||\n trace.source_trace_id ||\n `!${trace.pcb_trace_id}`,\n error_type: \"pcb_trace_error\",\n pcb_trace_id: trace.pcb_trace_id,\n pcb_trace_error_id: \"\",\n center: { x: lastPoint.x, y: lastPoint.y },\n pcb_component_ids: [],\n pcb_port_ids: [],\n })\n }\n }\n }\n\n return errors\n}\n\nexport { checkTracesAreContiguous }\n","import type { AnyCircuitElement } from \"circuit-json\"\nimport { checkAllPinsInComponentAreUnderspecified } from \"./check-all-pins-in-component-are-underspecified\"\nimport { checkConnectorAccessibleOrientation } from \"./check-connector-accessible-orientation\"\nimport { checkCourtyardOverlap } from \"./check-courtyard-overlap/checkCourtyardOverlap\"\nimport { checkDifferentNetViaSpacing } from \"./check-different-net-via-spacing\"\nimport { checkEachPcbPortConnectedToPcbTraces } from \"./check-each-pcb-port-connected-to-pcb-trace\"\nimport { checkEachPcbTraceNonOverlapping } from \"./check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping\"\nimport { checkNoGroundPinDefined } from \"./check-no-ground-pin-defined\"\nimport { checkNoPowerPinDefined } from \"./check-no-power-pin-defined\"\nimport { checkPadPadClearance } from \"./check-pad-pad-clearance\"\nimport { checkPadTraceClearance } from \"./check-pad-trace-clearance\"\nimport { checkPcbComponentsOutOfBoard } from \"./check-pcb-components-out-of-board/checkPcbComponentsOutOfBoard\"\nimport { checkViasOffBoard } from \"./check-pcb-components-out-of-board/checkViasOffBoard\"\nimport { checkPcbComponentOverlap } from \"./check-pcb-components-overlap/checkPcbComponentOverlap\"\nimport { checkPinMustBeConnected } from \"./check-pin-must-be-connected\"\nimport { checkSameNetViaSpacing } from \"./check-same-net-via-spacing\"\nimport { checkSourceTracesHavePcbTraces } from \"./check-source-traces-have-pcb-traces\"\nimport { checkPcbTracesOutOfBoard } from \"./check-trace-out-of-board/checkTraceOutOfBoard\"\nimport { checkTracesAreContiguous } from \"./check-traces-are-contiguous/check-traces-are-contiguous\"\nimport { checkViaTraceClearance } from \"./check-via-trace-clearance\"\n\nexport async function runAllPlacementChecks(circuitJson: AnyCircuitElement[]) {\n return [\n ...checkViasOffBoard(circuitJson),\n ...checkPcbComponentsOutOfBoard(circuitJson),\n ...checkPcbComponentOverlap(circuitJson),\n ...checkPadPadClearance(circuitJson),\n ...checkPadTraceClearance(circuitJson),\n ...checkCourtyardOverlap(circuitJson),\n ...checkConnectorAccessibleOrientation(circuitJson),\n ]\n}\n\nexport async function runAllNetlistChecks(circuitJson: AnyCircuitElement[]) {\n return [...checkPinMustBeConnected(circuitJson)]\n}\n\nexport async function runAllPinSpecificationChecks(\n circuitJson: AnyCircuitElement[],\n) {\n return [\n ...checkAllPinsInComponentAreUnderspecified(circuitJson),\n ...checkNoPowerPinDefined(circuitJson),\n ...checkNoGroundPinDefined(circuitJson),\n ]\n}\n\nexport async function runAllRoutingChecks(circuitJson: AnyCircuitElement[]) {\n return [\n ...checkEachPcbPortConnectedToPcbTraces(circuitJson),\n ...checkSourceTracesHavePcbTraces(circuitJson),\n ...checkEachPcbTraceNonOverlapping(circuitJson),\n ...checkViaTraceClearance(circuitJson),\n ...checkSameNetViaSpacing(circuitJson),\n ...checkDifferentNetViaSpacing(circuitJson),\n // ...checkTracesAreContiguous(circuitJson),\n ...checkPcbTracesOutOfBoard(circuitJson),\n ]\n}\n\nexport async function runAllChecks(circuitJson: AnyCircuitElement[]) {\n return [\n ...(await runAllPlacementChecks(circuitJson)),\n ...(await runAllNetlistChecks(circuitJson)),\n ...(await runAllPinSpecificationChecks(circuitJson)),\n ...(await runAllRoutingChecks(circuitJson)),\n ]\n}\n"],"mappings":"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","names":["getReadableNameForElement","getFullConnectivityMapFromCircuitJson","getBoundsOfPcbElements","t","closestOnB","closestOnA","distance","clamp","dx","dy","getFullConnectivityMapFromCircuitJson","getReadableNameForElement","gap","distance","getReadableNameForElement","getReadableNameForElement","getReadableNameForElement","getFullConnectivityMapFromCircuitJson","distance","distance","getFullConnectivityMapFromCircuitJson","distance","getReadableNameForElement","getReadableNameForElement","getFullConnectivityMapFromCircuitJson","getFullConnectivityMapFromCircuitJson","distance","getReadableNameForElement","cju","getFullConnectivityMapFromCircuitJson","cju","getFullConnectivityMapFromCircuitJson","cju","segmentToSegmentMinDistance","distance","cju","getBoundsOfPcbElements","getPrimaryId","doBoundsOverlap","getFullConnectivityMapFromCircuitJson","getBoundsOfPcbElements","getBoundsOfPcbElements","getPrimaryId","getFullConnectivityMapFromCircuitJson","cju","getBoundsOfPcbElements","doBoundsOverlap","getPrimaryId","getReadableNameForElement","getFullConnectivityMapFromCircuitJson","cju","getBoundsOfPcbElements","getBoundsOfPcbElements","cju","getFullConnectivityMapFromCircuitJson","getPrimaryId","getReadableNameForElement","getPrimaryId","getReadableNameForElement","segmentToBoundsMinDistance","segmentToCircleMinDistance","getFullConnectivityMapFromCircuitJson","getFullConnectivityMapFromCircuitJson","getPrimaryId","segmentToCircleMinDistance","segmentToBoundsMinDistance","getReadableNameForElement","getReadableNameForElement","segmentToCircleMinDistance","getFullConnectivityMapFromCircuitJson","getFullConnectivityMapFromCircuitJson","segmentToCircleMinDistance","getReadableNameForElement","cju","cju","cju","getComponentName","getReadableNameForPcbPort"]}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@tscircuit/checks",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
|
-
"version": "0.0.
|
|
5
|
+
"version": "0.0.120",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist"
|
|
8
8
|
],
|
|
@@ -14,11 +14,12 @@
|
|
|
14
14
|
"devDependencies": {
|
|
15
15
|
"@biomejs/biome": "^1.9.0",
|
|
16
16
|
"@tscircuit/circuit-json-util": "^0.0.93",
|
|
17
|
+
"@tscircuit/jlcpcb-manufacturing-specs": "git+https://github.com/tscircuit/jlcpcb-manufacturing-specs#5644e294acfdd545ceb468c3b95c9ed82cb9a00b",
|
|
17
18
|
"@tscircuit/log-soup": "^1.0.2",
|
|
18
19
|
"@types/bun": "^1.2.8",
|
|
19
20
|
"@types/debug": "^4.1.12",
|
|
20
21
|
"bun-match-svg": "^0.0.11",
|
|
21
|
-
"circuit-json": "^0.0.
|
|
22
|
+
"circuit-json": "^0.0.418",
|
|
22
23
|
"circuit-to-svg": "^0.0.344",
|
|
23
24
|
"debug": "^4.3.5",
|
|
24
25
|
"tscircuit": "^0.0.1643",
|