@tscircuit/checks 0.0.169 → 0.0.171
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/README.md +2 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +214 -77
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -20,6 +20,7 @@ and output an array of arrays for any issues found.
|
|
|
20
20
|
| [`checkEachPcbTraceNonOverlapping`](./lib/check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping.ts) | Returns `pcb_trace_error` when `pcb_trace` segments physically overlap incompatible geometry on the same layer. Pad/via near-misses are reported by the typed clearance checks instead. |
|
|
21
21
|
| [`checkPcbComponentOverlap`](./lib/check-pcb-components-overlap/checkPcbComponentOverlap.ts) | Returns `pcb_footprint_overlap_error` when footprint elements from different components overlap in disallowed ways. |
|
|
22
22
|
| [`checkPcbComponentsOutOfBoard`](./lib/check-pcb-components-out-of-board/checkPcbComponentsOutOfBoard.ts) | Returns `pcb_placement_error` when PCB components do not fit inside the board area. |
|
|
23
|
+
| [`checkPcbCopperOverKeepout`](./lib/check-pcb-copper-over-keepout.ts) | Returns one `pcb_placement_error` per non-excluded component or via whose copper overlaps a keepout on a shared layer. |
|
|
23
24
|
| [`checkPcbTracesOutOfBoard`](./lib/check-trace-out-of-board/checkTraceOutOfBoard.ts) | Returns `pcb_trace_error` when any trace segment or via extends beyond the board boundary. |
|
|
24
25
|
| [`checkPadTraceClearance`](./lib/check-pad-trace-clearance.ts) | Returns `pcb_pad_trace_clearance_error` when a pad and unrelated trace have a positive gap below the minimum clearance. Physical overlaps are reported by `checkEachPcbTraceNonOverlapping`. |
|
|
25
26
|
| [`checkViaTraceClearance`](./lib/check-via-trace-clearance.ts) | Returns `pcb_via_trace_clearance_error` when a via and unrelated trace have a positive gap below the minimum clearance. Physical overlaps are reported by `checkEachPcbTraceNonOverlapping`. |
|
|
@@ -34,7 +35,7 @@ and output an array of arrays for any issues found.
|
|
|
34
35
|
|
|
35
36
|
| Function | Description |
|
|
36
37
|
| --- | --- |
|
|
37
|
-
| [`runAllPlacementChecks`](./lib/run-all-checks.ts) | Runs placement checks (`checkCopperToBoardEdgeClearance`, `checkPcbComponentsOutOfBoard`, `checkPcbComponentOverlap`, `checkPadPadClearance`, `checkCourtyardOverlap`, `checkConnectorAccessibleOrientation`, and `checkTestPointAccessibility`). |
|
|
38
|
+
| [`runAllPlacementChecks`](./lib/run-all-checks.ts) | Runs placement checks (`checkCopperToBoardEdgeClearance`, `checkPcbComponentsOutOfBoard`, `checkPcbCopperOverKeepout`, `checkPcbComponentOverlap`, `checkPadPadClearance`, `checkCourtyardOverlap`, `checkConnectorAccessibleOrientation`, and `checkTestPointAccessibility`). |
|
|
38
39
|
| [`runAllNetlistChecks`](./lib/run-all-checks.ts) | Runs netlist connectivity checks (currently `checkPinMustBeConnected`). |
|
|
39
40
|
| [`runAllPinSpecificationChecks`](./lib/run-all-checks.ts) | Runs pin specification checks (e.g. `checkAllPinsInComponentAreUnderspecified`, `checkNoPowerPinDefined`, and `checkNoGroundPinDefined`). |
|
|
40
41
|
| [`runAllSchematicChecks`](./lib/run-all-checks.ts) | Runs schematic-layout checks (currently `checkSchematicComponentExcessiveVerticalPadding`). |
|
package/dist/index.d.ts
CHANGED
|
@@ -41,6 +41,8 @@ declare function checkPcbComponentsOutOfBoard(circuitJson: AnyCircuitElement[]):
|
|
|
41
41
|
*/
|
|
42
42
|
declare function checkPcbComponentOverCutout(circuitJson: AnyCircuitElement[]): PcbPlacementError[];
|
|
43
43
|
|
|
44
|
+
declare function checkPcbCopperOverKeepout(circuitJson: AnyCircuitElement[]): PcbPlacementError[];
|
|
45
|
+
|
|
44
46
|
declare function checkSameNetViaSpacing(circuitJson: AnyCircuitElement[], { connMap, minClearance, }?: {
|
|
45
47
|
connMap?: ConnectivityMap;
|
|
46
48
|
minClearance?: number;
|
|
@@ -198,4 +200,4 @@ declare function checkConnectorAccessibleOrientation(circuitJson: AnyCircuitElem
|
|
|
198
200
|
*/
|
|
199
201
|
declare function checkTestPointAccessibility(circuitJson: AnyCircuitElement[]): PcbPlacementError[];
|
|
200
202
|
|
|
201
|
-
export { NetManager, checkAllPinsInComponentAreUnderspecified, checkConnectorAccessibleOrientation, checkCopperToBoardEdgeClearance, checkDifferentNetViaSpacing, checkEachPcbPortConnectedToPcbTraces, checkEachPcbTraceNonOverlapping, checkNoGroundPinDefined, checkNoPowerPinDefined, checkPadPadClearance, checkPadTraceClearance, checkPcbComponentOverCutout, checkPcbComponentOverlap, checkPcbComponentsMissingCourtyard, checkPcbComponentsOutOfBoard, checkPcbTraceLengths, checkPcbTraceViaCounts, checkPcbTracesOutOfBoard, checkPinMustBeConnected, checkSameNetViaSpacing, checkSchematicComponentExcessiveVerticalPadding, checkSourceTracesHavePcbTraces, checkSourceTracesMatchPcbTraceThickness, checkTestPointAccessibility, checkTracesAreContiguous, checkViaPadClearance, checkViaTraceClearance, checkViasInPads, checkViasOffBoard, dedupePcbDrcErrors, runAllChecks, runAllNetlistChecks, runAllPinSpecificationChecks, runAllPlacementChecks, runAllRoutingChecks, runAllSchematicChecks };
|
|
203
|
+
export { NetManager, checkAllPinsInComponentAreUnderspecified, checkConnectorAccessibleOrientation, checkCopperToBoardEdgeClearance, checkDifferentNetViaSpacing, checkEachPcbPortConnectedToPcbTraces, checkEachPcbTraceNonOverlapping, checkNoGroundPinDefined, checkNoPowerPinDefined, checkPadPadClearance, checkPadTraceClearance, checkPcbComponentOverCutout, checkPcbComponentOverlap, checkPcbComponentsMissingCourtyard, checkPcbComponentsOutOfBoard, checkPcbCopperOverKeepout, checkPcbTraceLengths, checkPcbTraceViaCounts, checkPcbTracesOutOfBoard, checkPinMustBeConnected, checkSameNetViaSpacing, checkSchematicComponentExcessiveVerticalPadding, checkSourceTracesHavePcbTraces, checkSourceTracesMatchPcbTraceThickness, checkTestPointAccessibility, checkTracesAreContiguous, checkViaPadClearance, checkViaTraceClearance, checkViasInPads, checkViasOffBoard, dedupePcbDrcErrors, runAllChecks, runAllNetlistChecks, runAllPinSpecificationChecks, runAllPlacementChecks, runAllRoutingChecks, runAllSchematicChecks };
|
package/dist/index.js
CHANGED
|
@@ -605,8 +605,27 @@ var getPcbBoard = (circuitJson) => circuitJson.find((el) => el.type === "pcb_boa
|
|
|
605
605
|
var getBoardDrcValue = (board, key) => board?.[key];
|
|
606
606
|
|
|
607
607
|
// lib/check-pad-clearance/common.ts
|
|
608
|
-
var getPadBounds = (pad) =>
|
|
608
|
+
var getPadBounds = (pad) => {
|
|
609
|
+
if (pad.type === "pcb_keepout") {
|
|
610
|
+
if (pad.shape === "circle") {
|
|
611
|
+
return {
|
|
612
|
+
minX: pad.center.x - pad.radius,
|
|
613
|
+
minY: pad.center.y - pad.radius,
|
|
614
|
+
maxX: pad.center.x + pad.radius,
|
|
615
|
+
maxY: pad.center.y + pad.radius
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
return {
|
|
619
|
+
minX: pad.center.x - pad.width / 2,
|
|
620
|
+
minY: pad.center.y - pad.height / 2,
|
|
621
|
+
maxX: pad.center.x + pad.width / 2,
|
|
622
|
+
maxY: pad.center.y + pad.height / 2
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
return getBoundsOfPcbElements2([pad]);
|
|
626
|
+
};
|
|
609
627
|
var getPadCenter = (pad) => {
|
|
628
|
+
if (pad.type === "pcb_keepout") return pad.center;
|
|
610
629
|
const bounds = getPadBounds(pad);
|
|
611
630
|
return midpoint(
|
|
612
631
|
{ x: bounds.minX, y: bounds.minY },
|
|
@@ -614,6 +633,7 @@ var getPadCenter = (pad) => {
|
|
|
614
633
|
);
|
|
615
634
|
};
|
|
616
635
|
var getPadRadius = (pad) => {
|
|
636
|
+
if (pad.type === "pcb_keepout" && pad.shape === "circle") return pad.radius;
|
|
617
637
|
const bounds = getPadBounds(pad);
|
|
618
638
|
return Math.min(bounds.maxX - bounds.minX, bounds.maxY - bounds.minY) / 2;
|
|
619
639
|
};
|
|
@@ -629,6 +649,32 @@ var getCircleShape = (pad) => {
|
|
|
629
649
|
};
|
|
630
650
|
};
|
|
631
651
|
var getPolygonShape = (pad) => {
|
|
652
|
+
if (pad.type === "pcb_keepout") {
|
|
653
|
+
if (pad.shape !== "rect") {
|
|
654
|
+
throw new Error(`Expected rectangular keepout, got ${pad.shape}`);
|
|
655
|
+
}
|
|
656
|
+
return {
|
|
657
|
+
kind: "polygon",
|
|
658
|
+
points: [
|
|
659
|
+
{
|
|
660
|
+
x: pad.center.x - pad.width / 2,
|
|
661
|
+
y: pad.center.y - pad.height / 2
|
|
662
|
+
},
|
|
663
|
+
{
|
|
664
|
+
x: pad.center.x + pad.width / 2,
|
|
665
|
+
y: pad.center.y - pad.height / 2
|
|
666
|
+
},
|
|
667
|
+
{
|
|
668
|
+
x: pad.center.x + pad.width / 2,
|
|
669
|
+
y: pad.center.y + pad.height / 2
|
|
670
|
+
},
|
|
671
|
+
{
|
|
672
|
+
x: pad.center.x - pad.width / 2,
|
|
673
|
+
y: pad.center.y + pad.height / 2
|
|
674
|
+
}
|
|
675
|
+
]
|
|
676
|
+
};
|
|
677
|
+
}
|
|
632
678
|
if (pad.type === "pcb_smtpad" && (pad.shape === "polygon" || pad.shape === "rotated_rect")) {
|
|
633
679
|
return {
|
|
634
680
|
kind: "polygon",
|
|
@@ -2340,6 +2386,62 @@ function checkPcbComponentOverCutout(circuitJson) {
|
|
|
2340
2386
|
return errors;
|
|
2341
2387
|
}
|
|
2342
2388
|
|
|
2389
|
+
// lib/check-pcb-copper-over-keepout.ts
|
|
2390
|
+
import { cju as cju3, getPrimaryId as getPrimaryId2 } from "@tscircuit/circuit-json-util";
|
|
2391
|
+
var getErrorOwnerId = (copper) => "pcb_component_id" in copper && copper.pcb_component_id ? copper.pcb_component_id : getPrimaryId2(copper);
|
|
2392
|
+
var getReadableCopperName = (circuitJson, copper) => {
|
|
2393
|
+
if ("pcb_component_id" in copper && copper.pcb_component_id) {
|
|
2394
|
+
const pcbComponent = circuitJson.find(
|
|
2395
|
+
(element) => element.type === "pcb_component" && element.pcb_component_id === copper.pcb_component_id
|
|
2396
|
+
);
|
|
2397
|
+
const sourceComponent = pcbComponent?.type === "pcb_component" ? circuitJson.find(
|
|
2398
|
+
(element) => element.type === "source_component" && element.source_component_id === pcbComponent.source_component_id
|
|
2399
|
+
) : void 0;
|
|
2400
|
+
const componentName = sourceComponent?.type === "source_component" && sourceComponent.name ? sourceComponent.name : getReadableNameForComponent(circuitJson, copper.pcb_component_id);
|
|
2401
|
+
return `component ${componentName}`;
|
|
2402
|
+
}
|
|
2403
|
+
return copper.type === "pcb_via" ? `via ${copper.pcb_via_id}` : `${copper.type} ${getPrimaryId2(copper)}`;
|
|
2404
|
+
};
|
|
2405
|
+
function checkPcbCopperOverKeepout(circuitJson) {
|
|
2406
|
+
const keepouts = cju3(circuitJson).pcb_keepout.list();
|
|
2407
|
+
if (keepouts.length === 0) return [];
|
|
2408
|
+
const copper = [
|
|
2409
|
+
...getPads(circuitJson),
|
|
2410
|
+
...cju3(circuitJson).pcb_via.list()
|
|
2411
|
+
];
|
|
2412
|
+
const errors = /* @__PURE__ */ new Map();
|
|
2413
|
+
for (const keepout of keepouts) {
|
|
2414
|
+
const excludedComponentIds = new Set(
|
|
2415
|
+
keepout.excluded_pcb_component_ids ?? []
|
|
2416
|
+
);
|
|
2417
|
+
for (const copperElement of copper) {
|
|
2418
|
+
const copperComponentId = "pcb_component_id" in copperElement ? copperElement.pcb_component_id : void 0;
|
|
2419
|
+
if (copperComponentId && excludedComponentIds.has(copperComponentId)) {
|
|
2420
|
+
continue;
|
|
2421
|
+
}
|
|
2422
|
+
const copperLayers = getLayersOfPcbElement(copperElement);
|
|
2423
|
+
if (!copperLayers.some((layer) => keepout.layers.includes(layer))) {
|
|
2424
|
+
continue;
|
|
2425
|
+
}
|
|
2426
|
+
if (getPadToPadGap(copperElement, keepout) > EPSILON) continue;
|
|
2427
|
+
const ownerId = getErrorOwnerId(copperElement);
|
|
2428
|
+
const errorId = `copper_over_keepout_${ownerId}_${keepout.pcb_keepout_id}`;
|
|
2429
|
+
if (errors.has(errorId)) continue;
|
|
2430
|
+
errors.set(errorId, {
|
|
2431
|
+
type: "pcb_placement_error",
|
|
2432
|
+
pcb_placement_error_id: errorId,
|
|
2433
|
+
error_type: "pcb_placement_error",
|
|
2434
|
+
message: `Copper for ${getReadableCopperName(
|
|
2435
|
+
circuitJson,
|
|
2436
|
+
copperElement
|
|
2437
|
+
)} overlaps ${keepout.description ? `PCB keepout "${keepout.description}"` : "a PCB keepout"}`,
|
|
2438
|
+
subcircuit_id: copperElement.subcircuit_id ?? keepout.subcircuit_id
|
|
2439
|
+
});
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
return [...errors.values()];
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2343
2445
|
// lib/check-same-net-via-spacing.ts
|
|
2344
2446
|
import { getReadableNameForElement as getReadableNameForElement4 } from "@tscircuit/circuit-json-util";
|
|
2345
2447
|
import {
|
|
@@ -2457,11 +2559,11 @@ function checkDifferentNetViaSpacing(circuitJson, {
|
|
|
2457
2559
|
}
|
|
2458
2560
|
|
|
2459
2561
|
// lib/check-source-traces-match-pcb-trace-thickness.ts
|
|
2460
|
-
import { cju as
|
|
2562
|
+
import { cju as cju4 } from "@tscircuit/circuit-json-util";
|
|
2461
2563
|
import { getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson5 } from "circuit-json-to-connectivity-map";
|
|
2462
2564
|
function checkSourceTracesMatchPcbTraceThickness(circuitJson) {
|
|
2463
2565
|
const warnings = [];
|
|
2464
|
-
const db =
|
|
2566
|
+
const db = cju4(circuitJson);
|
|
2465
2567
|
const sourceTraces = db.source_trace.list();
|
|
2466
2568
|
const pcbTraces = db.pcb_trace.list();
|
|
2467
2569
|
const pcbPorts = db.pcb_port.list();
|
|
@@ -2583,6 +2685,9 @@ import {
|
|
|
2583
2685
|
getReadableNameForPcbTrace
|
|
2584
2686
|
} from "@tscircuit/circuit-json-util";
|
|
2585
2687
|
import { PcbConnectivityMap as PcbConnectivityMap2 } from "circuit-json-to-connectivity-map";
|
|
2688
|
+
function routePointTouchesPad(point, pad) {
|
|
2689
|
+
return point.route_type === "wire" && getLayersOfPcbElement(pad).includes(point.layer) && isPointInPad(point, pad);
|
|
2690
|
+
}
|
|
2586
2691
|
function getRoutePointCenter(point) {
|
|
2587
2692
|
if (point.route_type === "through_pad") {
|
|
2588
2693
|
return {
|
|
@@ -2593,15 +2698,12 @@ function getRoutePointCenter(point) {
|
|
|
2593
2698
|
return { x: point.x, y: point.y };
|
|
2594
2699
|
}
|
|
2595
2700
|
function routePointConnectsToAnotherExpectedPort(point, expectedPorts, missingPcbPortId, padMap) {
|
|
2596
|
-
if (point.route_type !== "wire") return false;
|
|
2597
2701
|
return expectedPorts.some((expectedPort) => {
|
|
2598
2702
|
if (!expectedPort.pcb_port_id || expectedPort.pcb_port_id === missingPcbPortId) {
|
|
2599
2703
|
return false;
|
|
2600
2704
|
}
|
|
2601
2705
|
const expectedPads = padMap.get(expectedPort.pcb_port_id);
|
|
2602
|
-
return expectedPads?.some(
|
|
2603
|
-
(pad) => isPointInPad({ x: point.x, y: point.y }, pad)
|
|
2604
|
-
) ?? false;
|
|
2706
|
+
return expectedPads?.some((pad) => routePointTouchesPad(point, pad)) ?? false;
|
|
2605
2707
|
});
|
|
2606
2708
|
}
|
|
2607
2709
|
function getMissingConnectionErrorCenter({
|
|
@@ -2685,6 +2787,65 @@ function checkTracesAreContiguous(circuitJson) {
|
|
|
2685
2787
|
]);
|
|
2686
2788
|
}
|
|
2687
2789
|
}
|
|
2790
|
+
const touchedPortIdsByTraceId = /* @__PURE__ */ new Map();
|
|
2791
|
+
const traceIdsByTouchedPortId = /* @__PURE__ */ new Map();
|
|
2792
|
+
for (const trace of pcbTraces) {
|
|
2793
|
+
const touchedPortIds = /* @__PURE__ */ new Set();
|
|
2794
|
+
const firstPoint = trace.route[0];
|
|
2795
|
+
const lastPoint = trace.route.at(-1);
|
|
2796
|
+
for (const point of [firstPoint, lastPoint]) {
|
|
2797
|
+
if (!point) continue;
|
|
2798
|
+
for (const [pcbPortId, pads] of padMap) {
|
|
2799
|
+
if (pads.some((pad) => routePointTouchesPad(point, pad))) {
|
|
2800
|
+
touchedPortIds.add(pcbPortId);
|
|
2801
|
+
}
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2804
|
+
touchedPortIdsByTraceId.set(trace.pcb_trace_id, touchedPortIds);
|
|
2805
|
+
for (const pcbPortId of touchedPortIds) {
|
|
2806
|
+
const traceIds = traceIdsByTouchedPortId.get(pcbPortId) ?? /* @__PURE__ */ new Set();
|
|
2807
|
+
traceIds.add(trace.pcb_trace_id);
|
|
2808
|
+
traceIdsByTouchedPortId.set(pcbPortId, traceIds);
|
|
2809
|
+
}
|
|
2810
|
+
}
|
|
2811
|
+
const physicallyConnectedTracesByTraceId = /* @__PURE__ */ new Map();
|
|
2812
|
+
const getPhysicallyConnectedTraces = (startTrace) => {
|
|
2813
|
+
const cached = physicallyConnectedTracesByTraceId.get(
|
|
2814
|
+
startTrace.pcb_trace_id
|
|
2815
|
+
);
|
|
2816
|
+
if (cached) return cached;
|
|
2817
|
+
const connectedTraceIds = /* @__PURE__ */ new Set();
|
|
2818
|
+
const pendingTraceIds = [startTrace.pcb_trace_id];
|
|
2819
|
+
while (pendingTraceIds.length > 0) {
|
|
2820
|
+
const traceId = pendingTraceIds.pop();
|
|
2821
|
+
if (connectedTraceIds.has(traceId)) continue;
|
|
2822
|
+
connectedTraceIds.add(traceId);
|
|
2823
|
+
for (const connectedTrace of pcbConnectivityMap.getAllTracesConnectedToTrace(
|
|
2824
|
+
traceId
|
|
2825
|
+
)) {
|
|
2826
|
+
if (!connectedTraceIds.has(connectedTrace.pcb_trace_id)) {
|
|
2827
|
+
pendingTraceIds.push(connectedTrace.pcb_trace_id);
|
|
2828
|
+
}
|
|
2829
|
+
}
|
|
2830
|
+
for (const pcbPortId of touchedPortIdsByTraceId.get(traceId) ?? []) {
|
|
2831
|
+
for (const touchingTraceId of traceIdsByTouchedPortId.get(pcbPortId) ?? []) {
|
|
2832
|
+
if (!connectedTraceIds.has(touchingTraceId)) {
|
|
2833
|
+
pendingTraceIds.push(touchingTraceId);
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2838
|
+
const connectedTraces = pcbTraces.filter(
|
|
2839
|
+
(trace) => connectedTraceIds.has(trace.pcb_trace_id)
|
|
2840
|
+
);
|
|
2841
|
+
for (const trace of connectedTraces) {
|
|
2842
|
+
physicallyConnectedTracesByTraceId.set(
|
|
2843
|
+
trace.pcb_trace_id,
|
|
2844
|
+
connectedTraces
|
|
2845
|
+
);
|
|
2846
|
+
}
|
|
2847
|
+
return connectedTraces;
|
|
2848
|
+
};
|
|
2688
2849
|
for (const trace of pcbTraces) {
|
|
2689
2850
|
if (trace.route.length === 0) continue;
|
|
2690
2851
|
const firstPoint = trace.route[0];
|
|
@@ -2736,39 +2897,17 @@ function checkTracesAreContiguous(circuitJson) {
|
|
|
2736
2897
|
if (!port.pcb_port_id) continue;
|
|
2737
2898
|
const pads = padMap.get(port.pcb_port_id);
|
|
2738
2899
|
if (!pads?.length) continue;
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
)
|
|
2745
|
-
];
|
|
2746
|
-
for (const candidateTrace of candidateTraces) {
|
|
2747
|
-
if (candidateTrace.pcb_trace_id === trace.pcb_trace_id) continue;
|
|
2748
|
-
if (!candidateTrace.source_trace_id) continue;
|
|
2749
|
-
const candidateSourceTrace = sourceTraces.find(
|
|
2750
|
-
(st) => st.source_trace_id === candidateTrace.source_trace_id
|
|
2751
|
-
);
|
|
2752
|
-
if (!sourceTrace || candidateTrace.source_trace_id !== sourceTrace.source_trace_id && !candidateSourceTrace?.connected_source_port_ids.includes(
|
|
2753
|
-
port.source_port_id
|
|
2754
|
-
)) {
|
|
2755
|
-
continue;
|
|
2756
|
-
}
|
|
2757
|
-
const candidateFirstPoint = candidateTrace.route[0];
|
|
2758
|
-
const candidateLastPoint = candidateTrace.route.at(-1);
|
|
2759
|
-
if (getPcbPortIdsConnectedToTrace(candidateTrace).includes(
|
|
2760
|
-
port.pcb_port_id
|
|
2761
|
-
) || candidateFirstPoint?.route_type === "wire" && pads.some((pad) => isPointInPad(candidateFirstPoint, pad)) || candidateLastPoint?.route_type === "wire" && pads.some((pad) => isPointInPad(candidateLastPoint, pad))) {
|
|
2762
|
-
isConnectedByRoutedSourceTrace = true;
|
|
2763
|
-
break;
|
|
2764
|
-
}
|
|
2765
|
-
}
|
|
2900
|
+
const isConnectedByRoutedSourceTrace = getPhysicallyConnectedTraces(
|
|
2901
|
+
trace
|
|
2902
|
+
).some(
|
|
2903
|
+
(candidateTrace) => touchedPortIdsByTraceId.get(candidateTrace.pcb_trace_id)?.has(port.pcb_port_id)
|
|
2904
|
+
);
|
|
2766
2905
|
if (isConnectedByRoutedSourceTrace) continue;
|
|
2767
|
-
const isFirstPointConnected =
|
|
2768
|
-
(pad) =>
|
|
2906
|
+
const isFirstPointConnected = pads.some(
|
|
2907
|
+
(pad) => routePointTouchesPad(firstPoint, pad)
|
|
2769
2908
|
);
|
|
2770
|
-
const isLastPointConnected =
|
|
2771
|
-
(pad) =>
|
|
2909
|
+
const isLastPointConnected = pads.some(
|
|
2910
|
+
(pad) => routePointTouchesPad(lastPoint, pad)
|
|
2772
2911
|
);
|
|
2773
2912
|
if (!isFirstPointConnected && !isLastPointConnected) {
|
|
2774
2913
|
const portName = getReadableNameForPcbPort2(
|
|
@@ -2801,14 +2940,10 @@ function checkTracesAreContiguous(circuitJson) {
|
|
|
2801
2940
|
let lastConnectsToAnyPad = false;
|
|
2802
2941
|
const endpointsAreSame = firstPoint.route_type === "wire" && lastPoint.route_type === "wire" && Math.abs(firstPoint.x - lastPoint.x) < 0.01 && Math.abs(firstPoint.y - lastPoint.y) < 0.01;
|
|
2803
2942
|
for (const pads of padMap.values()) {
|
|
2804
|
-
if (
|
|
2805
|
-
(pad) => isPointInPad({ x: firstPoint.x, y: firstPoint.y }, pad)
|
|
2806
|
-
)) {
|
|
2943
|
+
if (pads.some((pad) => routePointTouchesPad(firstPoint, pad))) {
|
|
2807
2944
|
firstConnectsToAnyPad = true;
|
|
2808
2945
|
}
|
|
2809
|
-
if (
|
|
2810
|
-
(pad) => isPointInPad({ x: lastPoint.x, y: lastPoint.y }, pad)
|
|
2811
|
-
)) {
|
|
2946
|
+
if (pads.some((pad) => routePointTouchesPad(lastPoint, pad))) {
|
|
2812
2947
|
lastConnectsToAnyPad = true;
|
|
2813
2948
|
}
|
|
2814
2949
|
}
|
|
@@ -2844,7 +2979,7 @@ function checkTracesAreContiguous(circuitJson) {
|
|
|
2844
2979
|
}
|
|
2845
2980
|
|
|
2846
2981
|
// lib/check-trace-out-of-board/checkTraceOutOfBoard.ts
|
|
2847
|
-
import { cju as
|
|
2982
|
+
import { cju as cju5 } from "@tscircuit/circuit-json-util";
|
|
2848
2983
|
import { segmentToSegmentMinDistance as segmentToSegmentMinDistance4 } from "@tscircuit/math-utils";
|
|
2849
2984
|
function getBoardPolygonPoints(board) {
|
|
2850
2985
|
if (board.outline && board.outline.length > 0) {
|
|
@@ -2875,7 +3010,7 @@ function checkPcbTracesOutOfBoard(circuitJson, config = {}) {
|
|
|
2875
3010
|
const margin = config.margin ?? getBoardDrcValue(board, "min_board_edge_clearance") ?? jlcMinTolerances.min_board_edge_clearance;
|
|
2876
3011
|
const boardPoints = getBoardPolygonPoints(board);
|
|
2877
3012
|
if (!boardPoints) return errors;
|
|
2878
|
-
const pcbTraces =
|
|
3013
|
+
const pcbTraces = cju5(circuitJson).pcb_trace.list();
|
|
2879
3014
|
for (const trace of pcbTraces) {
|
|
2880
3015
|
if (trace.route.length < 2) continue;
|
|
2881
3016
|
for (let i = 0; i < trace.route.length - 1; i++) {
|
|
@@ -2924,9 +3059,9 @@ function checkPcbTracesOutOfBoard(circuitJson, config = {}) {
|
|
|
2924
3059
|
|
|
2925
3060
|
// lib/check-pcb-components-overlap/checkPcbComponentOverlap.ts
|
|
2926
3061
|
import {
|
|
2927
|
-
cju as
|
|
3062
|
+
cju as cju6,
|
|
2928
3063
|
getBoundsOfPcbElements as getBoundsOfPcbElements5,
|
|
2929
|
-
getPrimaryId as
|
|
3064
|
+
getPrimaryId as getPrimaryId3
|
|
2930
3065
|
} from "@tscircuit/circuit-json-util";
|
|
2931
3066
|
import { doBoundsOverlap as doBoundsOverlap3 } from "@tscircuit/math-utils";
|
|
2932
3067
|
import { getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson7 } from "circuit-json-to-connectivity-map";
|
|
@@ -2962,20 +3097,20 @@ var formatOverlapElementDescription = (circuitJson, element) => {
|
|
|
2962
3097
|
if ("pcb_port_id" in element && element.pcb_port_id) {
|
|
2963
3098
|
return getReadableNameForPort(circuitJson, element.pcb_port_id);
|
|
2964
3099
|
}
|
|
2965
|
-
const id =
|
|
3100
|
+
const id = getPrimaryId3(element);
|
|
2966
3101
|
const readableName = getReadableNameForElementId(circuitJson, id);
|
|
2967
3102
|
return readableName === "element" ? `[${id}]` : readableName;
|
|
2968
3103
|
};
|
|
2969
3104
|
function checkPcbComponentOverlap(circuitJson) {
|
|
2970
3105
|
const errors = [];
|
|
2971
3106
|
const connMap = getFullConnectivityMapFromCircuitJson7(circuitJson);
|
|
2972
|
-
const smtPads =
|
|
2973
|
-
const platedHoles =
|
|
2974
|
-
const holes =
|
|
3107
|
+
const smtPads = cju6(circuitJson).pcb_smtpad.list();
|
|
3108
|
+
const platedHoles = cju6(circuitJson).pcb_plated_hole.list();
|
|
3109
|
+
const holes = cju6(circuitJson).pcb_hole.list();
|
|
2975
3110
|
const courtyards = circuitJson.filter(isCourtyardElement);
|
|
2976
3111
|
const componentMap = /* @__PURE__ */ new Map();
|
|
2977
3112
|
for (const pad of smtPads) {
|
|
2978
|
-
const componentId = pad.pcb_component_id || `standalone_pad_${
|
|
3113
|
+
const componentId = pad.pcb_component_id || `standalone_pad_${getPrimaryId3(pad)}`;
|
|
2979
3114
|
if (!componentMap.has(componentId)) {
|
|
2980
3115
|
componentMap.set(componentId, {
|
|
2981
3116
|
component_id: componentId,
|
|
@@ -2985,7 +3120,7 @@ function checkPcbComponentOverlap(circuitJson) {
|
|
|
2985
3120
|
componentMap.get(componentId).elements.push(pad);
|
|
2986
3121
|
}
|
|
2987
3122
|
for (const hole of platedHoles) {
|
|
2988
|
-
const componentId = hole.pcb_component_id || `standalone_plated_hole_${
|
|
3123
|
+
const componentId = hole.pcb_component_id || `standalone_plated_hole_${getPrimaryId3(hole)}`;
|
|
2989
3124
|
if (!componentMap.has(componentId)) {
|
|
2990
3125
|
componentMap.set(componentId, {
|
|
2991
3126
|
component_id: componentId,
|
|
@@ -2995,7 +3130,7 @@ function checkPcbComponentOverlap(circuitJson) {
|
|
|
2995
3130
|
componentMap.get(componentId).elements.push(hole);
|
|
2996
3131
|
}
|
|
2997
3132
|
for (const hole of holes) {
|
|
2998
|
-
const componentId = hole.pcb_component_id || `standalone_hole_${
|
|
3133
|
+
const componentId = hole.pcb_component_id || `standalone_hole_${getPrimaryId3(hole)}`;
|
|
2999
3134
|
if (!componentMap.has(componentId)) {
|
|
3000
3135
|
componentMap.set(componentId, {
|
|
3001
3136
|
component_id: componentId,
|
|
@@ -3031,8 +3166,8 @@ function checkPcbComponentOverlap(circuitJson) {
|
|
|
3031
3166
|
}
|
|
3032
3167
|
for (const elem1 of comp1.elements) {
|
|
3033
3168
|
for (const elem2 of comp2.elements) {
|
|
3034
|
-
const id1 =
|
|
3035
|
-
const id2 =
|
|
3169
|
+
const id1 = getPrimaryId3(elem1);
|
|
3170
|
+
const id2 = getPrimaryId3(elem2);
|
|
3036
3171
|
if ((isCourtyardElement(elem1) || isCourtyardElement(elem2)) && elem1.type !== "pcb_hole" && elem2.type !== "pcb_hole") {
|
|
3037
3172
|
continue;
|
|
3038
3173
|
}
|
|
@@ -3227,7 +3362,7 @@ var checkPcbTraceViaCounts = (circuitJson) => {
|
|
|
3227
3362
|
|
|
3228
3363
|
// lib/check-pad-pad-clearance.ts
|
|
3229
3364
|
import {
|
|
3230
|
-
getPrimaryId as
|
|
3365
|
+
getPrimaryId as getPrimaryId4,
|
|
3231
3366
|
getReadableNameForElement as getReadableNameForElement6
|
|
3232
3367
|
} from "@tscircuit/circuit-json-util";
|
|
3233
3368
|
import { formatMm } from "format-si-unit";
|
|
@@ -3246,17 +3381,17 @@ function checkPadPadClearance(circuitJson, {
|
|
|
3246
3381
|
const spatialIndex = new SpatialObjectIndex({
|
|
3247
3382
|
objects: pads,
|
|
3248
3383
|
getBounds: getPadBounds,
|
|
3249
|
-
getId: (pad) =>
|
|
3384
|
+
getId: (pad) => getPrimaryId4(pad)
|
|
3250
3385
|
});
|
|
3251
3386
|
const errors = /* @__PURE__ */ new Map();
|
|
3252
3387
|
for (const padA of pads) {
|
|
3253
|
-
const padAId =
|
|
3388
|
+
const padAId = getPrimaryId4(padA);
|
|
3254
3389
|
const nearbyPads = spatialIndex.getObjectsInBounds(
|
|
3255
3390
|
getPadBounds(padA),
|
|
3256
3391
|
minClearance
|
|
3257
3392
|
);
|
|
3258
3393
|
for (const padB of nearbyPads) {
|
|
3259
|
-
const padBId =
|
|
3394
|
+
const padBId = getPrimaryId4(padB);
|
|
3260
3395
|
if (padAId === padBId) continue;
|
|
3261
3396
|
if (!getLayersOfPcbElement(padA).some(
|
|
3262
3397
|
(layer) => getLayersOfPcbElement(padB).includes(layer)
|
|
@@ -3292,7 +3427,7 @@ function checkPadPadClearance(circuitJson, {
|
|
|
3292
3427
|
|
|
3293
3428
|
// lib/check-pad-trace-clearance.ts
|
|
3294
3429
|
import {
|
|
3295
|
-
getPrimaryId as
|
|
3430
|
+
getPrimaryId as getPrimaryId5,
|
|
3296
3431
|
getReadableNameForElement as getReadableNameForElement7
|
|
3297
3432
|
} from "@tscircuit/circuit-json-util";
|
|
3298
3433
|
import { formatMm as formatMm2 } from "format-si-unit";
|
|
@@ -3312,7 +3447,7 @@ function checkPadTraceClearance(circuitJson, {
|
|
|
3312
3447
|
const spatialIndex = new SpatialObjectIndex({
|
|
3313
3448
|
objects: pads,
|
|
3314
3449
|
getBounds: getPadBounds,
|
|
3315
|
-
getId: (pad) =>
|
|
3450
|
+
getId: (pad) => getPrimaryId5(pad)
|
|
3316
3451
|
});
|
|
3317
3452
|
const errors = /* @__PURE__ */ new Map();
|
|
3318
3453
|
const overlappingPairIds = /* @__PURE__ */ new Set();
|
|
@@ -3322,7 +3457,7 @@ function checkPadTraceClearance(circuitJson, {
|
|
|
3322
3457
|
minClearance + segment.thickness / 2
|
|
3323
3458
|
);
|
|
3324
3459
|
for (const pad of nearbyPads) {
|
|
3325
|
-
const padId =
|
|
3460
|
+
const padId = getPrimaryId5(pad);
|
|
3326
3461
|
if (!getLayersOfPcbElement(pad).includes(segment.layer)) continue;
|
|
3327
3462
|
if (connMap.areIdsConnected(segment.pcb_trace_id, padId)) continue;
|
|
3328
3463
|
const pairId = `${padId}_${segment.pcb_trace_id}`;
|
|
@@ -3408,7 +3543,7 @@ function checkViaTraceClearance(circuitJson, {
|
|
|
3408
3543
|
|
|
3409
3544
|
// lib/check-via-pad-clearance.ts
|
|
3410
3545
|
import {
|
|
3411
|
-
getPrimaryId as
|
|
3546
|
+
getPrimaryId as getPrimaryId6,
|
|
3412
3547
|
getReadableNameForElement as getReadableNameForElement9
|
|
3413
3548
|
} from "@tscircuit/circuit-json-util";
|
|
3414
3549
|
import { formatMm as formatMm4 } from "format-si-unit";
|
|
@@ -3430,7 +3565,7 @@ function checkViaPadClearance(circuitJson, {
|
|
|
3430
3565
|
const padIndex = new SpatialObjectIndex({
|
|
3431
3566
|
objects: pads,
|
|
3432
3567
|
getBounds: getPadBounds,
|
|
3433
|
-
getId:
|
|
3568
|
+
getId: getPrimaryId6
|
|
3434
3569
|
});
|
|
3435
3570
|
const errors = [];
|
|
3436
3571
|
for (const via of vias) {
|
|
@@ -3439,7 +3574,7 @@ function checkViaPadClearance(circuitJson, {
|
|
|
3439
3574
|
requiredClearance
|
|
3440
3575
|
);
|
|
3441
3576
|
for (const pad of nearbyPads) {
|
|
3442
|
-
const padId =
|
|
3577
|
+
const padId = getPrimaryId6(pad);
|
|
3443
3578
|
if (!getLayersOfPcbElement(via).some(
|
|
3444
3579
|
(layer) => getLayersOfPcbElement(pad).includes(layer)
|
|
3445
3580
|
)) {
|
|
@@ -3469,7 +3604,7 @@ function checkViaPadClearance(circuitJson, {
|
|
|
3469
3604
|
}
|
|
3470
3605
|
|
|
3471
3606
|
// lib/check-vias-in-pads.ts
|
|
3472
|
-
import { getPrimaryId as
|
|
3607
|
+
import { getPrimaryId as getPrimaryId7 } from "@tscircuit/circuit-json-util";
|
|
3473
3608
|
function checkViasInPads(circuitJson) {
|
|
3474
3609
|
const board = getPcbBoard(circuitJson);
|
|
3475
3610
|
if (board && "is_via_in_pad_allowed" in board && board.is_via_in_pad_allowed === true) {
|
|
@@ -3481,12 +3616,12 @@ function checkViasInPads(circuitJson) {
|
|
|
3481
3616
|
const pads = getPads(circuitJson);
|
|
3482
3617
|
if (vias.length === 0 || pads.length === 0) return [];
|
|
3483
3618
|
const padOrdinals = new Map(
|
|
3484
|
-
pads.map((pad, index) => [
|
|
3619
|
+
pads.map((pad, index) => [getPrimaryId7(pad), index])
|
|
3485
3620
|
);
|
|
3486
3621
|
const padIndex = new SpatialObjectIndex({
|
|
3487
3622
|
objects: pads,
|
|
3488
3623
|
getBounds: getPadBounds,
|
|
3489
|
-
getId:
|
|
3624
|
+
getId: getPrimaryId7
|
|
3490
3625
|
});
|
|
3491
3626
|
const errors = [];
|
|
3492
3627
|
for (const via of vias) {
|
|
@@ -3501,7 +3636,7 @@ function checkViasInPads(circuitJson) {
|
|
|
3501
3636
|
const padLayers = getLayersOfPcbElement(pad);
|
|
3502
3637
|
if (!viaLayers.some((layer) => padLayers.includes(layer))) continue;
|
|
3503
3638
|
if (!isPointInPad(via, pad)) continue;
|
|
3504
|
-
const padId =
|
|
3639
|
+
const padId = getPrimaryId7(pad);
|
|
3505
3640
|
const padOrdinal = padOrdinals.get(padId) ?? 0;
|
|
3506
3641
|
const padName = getReadableNameForFootprintPad(
|
|
3507
3642
|
circuitJson,
|
|
@@ -3600,7 +3735,7 @@ function checkPinMustBeConnected(circuitJson) {
|
|
|
3600
3735
|
}
|
|
3601
3736
|
|
|
3602
3737
|
// lib/check-all-pins-in-component-are-underspecified.ts
|
|
3603
|
-
import { cju as
|
|
3738
|
+
import { cju as cju7 } from "@tscircuit/circuit-json-util";
|
|
3604
3739
|
var PIN_ATTRIBUTE_KEYS = [
|
|
3605
3740
|
"must_be_connected",
|
|
3606
3741
|
"provides_power",
|
|
@@ -3645,7 +3780,7 @@ function hasAnyPinAttribute(port) {
|
|
|
3645
3780
|
}
|
|
3646
3781
|
function checkAllPinsInComponentAreUnderspecified(circuitJson) {
|
|
3647
3782
|
const warnings = [];
|
|
3648
|
-
const db =
|
|
3783
|
+
const db = cju7(circuitJson);
|
|
3649
3784
|
const sourceComponents = db.source_component.list();
|
|
3650
3785
|
const sourcePorts = db.source_port.list();
|
|
3651
3786
|
const portsByComponent = /* @__PURE__ */ new Map();
|
|
@@ -3677,10 +3812,10 @@ function checkAllPinsInComponentAreUnderspecified(circuitJson) {
|
|
|
3677
3812
|
}
|
|
3678
3813
|
|
|
3679
3814
|
// lib/check-no-power-pin-defined.ts
|
|
3680
|
-
import { cju as
|
|
3815
|
+
import { cju as cju8 } from "@tscircuit/circuit-json-util";
|
|
3681
3816
|
function checkNoPowerPinDefined(circuitJson) {
|
|
3682
3817
|
const warnings = [];
|
|
3683
|
-
const db =
|
|
3818
|
+
const db = cju8(circuitJson);
|
|
3684
3819
|
const sourceComponents = db.source_component.list();
|
|
3685
3820
|
const sourcePorts = db.source_port.list();
|
|
3686
3821
|
const portsByComponent = /* @__PURE__ */ new Map();
|
|
@@ -3712,10 +3847,10 @@ function checkNoPowerPinDefined(circuitJson) {
|
|
|
3712
3847
|
}
|
|
3713
3848
|
|
|
3714
3849
|
// lib/check-no-ground-pin-defined.ts
|
|
3715
|
-
import { cju as
|
|
3850
|
+
import { cju as cju9 } from "@tscircuit/circuit-json-util";
|
|
3716
3851
|
function checkNoGroundPinDefined(circuitJson) {
|
|
3717
3852
|
const warnings = [];
|
|
3718
|
-
const db =
|
|
3853
|
+
const db = cju9(circuitJson);
|
|
3719
3854
|
const sourceComponents = db.source_component.list();
|
|
3720
3855
|
const sourcePorts = db.source_port.list();
|
|
3721
3856
|
const portsByComponent = /* @__PURE__ */ new Map();
|
|
@@ -4091,6 +4226,7 @@ async function runAllPlacementChecks(circuitJson) {
|
|
|
4091
4226
|
...checkViasInPads(circuitJson),
|
|
4092
4227
|
...checkPcbComponentsOutOfBoard(circuitJson),
|
|
4093
4228
|
...checkPcbComponentOverCutout(circuitJson),
|
|
4229
|
+
...checkPcbCopperOverKeepout(circuitJson),
|
|
4094
4230
|
...checkPcbComponentOverlap(circuitJson),
|
|
4095
4231
|
...checkPcbComponentsMissingCourtyard(circuitJson),
|
|
4096
4232
|
...checkPadPadClearance(circuitJson),
|
|
@@ -4153,6 +4289,7 @@ export {
|
|
|
4153
4289
|
checkPcbComponentOverlap,
|
|
4154
4290
|
checkPcbComponentsMissingCourtyard,
|
|
4155
4291
|
checkPcbComponentsOutOfBoard,
|
|
4292
|
+
checkPcbCopperOverKeepout,
|
|
4156
4293
|
checkPcbTraceLengths,
|
|
4157
4294
|
checkPcbTraceViaCounts,
|
|
4158
4295
|
checkPcbTracesOutOfBoard,
|