@tscircuit/checks 0.0.175 → 0.0.177
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +122 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2690,14 +2690,82 @@ function checkSourceTracesHavePcbTraces(circuitJson) {
|
|
|
2690
2690
|
}
|
|
2691
2691
|
|
|
2692
2692
|
// lib/check-traces-are-contiguous/check-traces-are-contiguous.ts
|
|
2693
|
+
import { pointToSegmentDistance as pointToSegmentDistance2 } from "@tscircuit/math-utils";
|
|
2693
2694
|
import {
|
|
2694
2695
|
getReadableNameForPcbPort as getReadableNameForPcbPort2,
|
|
2695
2696
|
getReadableNameForPcbTrace
|
|
2696
2697
|
} from "@tscircuit/circuit-json-util";
|
|
2697
|
-
import {
|
|
2698
|
+
import {
|
|
2699
|
+
getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson7,
|
|
2700
|
+
PcbConnectivityMap as PcbConnectivityMap2
|
|
2701
|
+
} from "circuit-json-to-connectivity-map";
|
|
2702
|
+
var ENDPOINT_CONTACT_EPSILON = 1e-9;
|
|
2703
|
+
var TRACE_SEGMENT_GEOMETRY_EPSILON = 1e-9;
|
|
2698
2704
|
function routePointTouchesPad(point, pad) {
|
|
2699
2705
|
return point.route_type === "wire" && getLayersOfPcbElement(pad).includes(point.layer) && isPointInPad(point, pad);
|
|
2700
2706
|
}
|
|
2707
|
+
function getTraceWireSegmentsByNetAndLayer(pcbTraces, fullConnectivityMap) {
|
|
2708
|
+
const segmentsByNetAndLayer = /* @__PURE__ */ new Map();
|
|
2709
|
+
for (const trace of pcbTraces) {
|
|
2710
|
+
if (trace.route_thickness_mode === "interpolated") continue;
|
|
2711
|
+
const netId = fullConnectivityMap.getNetConnectedToId(trace.pcb_trace_id);
|
|
2712
|
+
if (!netId) continue;
|
|
2713
|
+
for (let i = 0; i < trace.route.length - 1; i++) {
|
|
2714
|
+
const start = trace.route[i];
|
|
2715
|
+
const end = trace.route[i + 1];
|
|
2716
|
+
if (start.route_type !== "wire" || end.route_type !== "wire") continue;
|
|
2717
|
+
if (start.layer !== end.layer) continue;
|
|
2718
|
+
if (Math.hypot(start.x - end.x, start.y - end.y) <= TRACE_SEGMENT_GEOMETRY_EPSILON) {
|
|
2719
|
+
continue;
|
|
2720
|
+
}
|
|
2721
|
+
const segmentsByLayer = segmentsByNetAndLayer.get(netId) ?? /* @__PURE__ */ new Map();
|
|
2722
|
+
const segments = segmentsByLayer.get(start.layer) ?? [];
|
|
2723
|
+
segments.push({ trace, start, end });
|
|
2724
|
+
segmentsByLayer.set(start.layer, segments);
|
|
2725
|
+
segmentsByNetAndLayer.set(netId, segmentsByLayer);
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2728
|
+
return segmentsByNetAndLayer;
|
|
2729
|
+
}
|
|
2730
|
+
function getEndpointTraceCopperWidth(trace, endpoint) {
|
|
2731
|
+
if (trace.route_thickness_mode === "interpolated") return void 0;
|
|
2732
|
+
let segmentStartIndex = endpoint === "start" ? 0 : trace.route.length - 2;
|
|
2733
|
+
const indexStep = endpoint === "start" ? 1 : -1;
|
|
2734
|
+
while (segmentStartIndex >= 0 && segmentStartIndex < trace.route.length - 1) {
|
|
2735
|
+
const segmentStart = trace.route[segmentStartIndex];
|
|
2736
|
+
const segmentEnd = trace.route[segmentStartIndex + 1];
|
|
2737
|
+
if (segmentStart?.route_type !== "wire" || segmentEnd?.route_type !== "wire" || segmentStart.layer !== segmentEnd.layer) {
|
|
2738
|
+
return void 0;
|
|
2739
|
+
}
|
|
2740
|
+
if (Math.hypot(segmentStart.x - segmentEnd.x, segmentStart.y - segmentEnd.y) > TRACE_SEGMENT_GEOMETRY_EPSILON) {
|
|
2741
|
+
return segmentStart.width;
|
|
2742
|
+
}
|
|
2743
|
+
segmentStartIndex += indexStep;
|
|
2744
|
+
}
|
|
2745
|
+
return void 0;
|
|
2746
|
+
}
|
|
2747
|
+
function routePointTouchesLogicallyConnectedTraceCopper({
|
|
2748
|
+
point,
|
|
2749
|
+
endpointTraceCopperWidth,
|
|
2750
|
+
ownerTrace,
|
|
2751
|
+
traceWireSegmentsByNetAndLayer,
|
|
2752
|
+
fullConnectivityMap
|
|
2753
|
+
}) {
|
|
2754
|
+
if (point.route_type !== "wire") return false;
|
|
2755
|
+
const ownerNetId = fullConnectivityMap.getNetConnectedToId(
|
|
2756
|
+
ownerTrace.pcb_trace_id
|
|
2757
|
+
);
|
|
2758
|
+
if (!ownerNetId) return false;
|
|
2759
|
+
const candidateSegments = traceWireSegmentsByNetAndLayer.get(ownerNetId)?.get(point.layer) ?? [];
|
|
2760
|
+
for (const segment of candidateSegments) {
|
|
2761
|
+
if (segment.trace.pcb_trace_id === ownerTrace.pcb_trace_id) continue;
|
|
2762
|
+
const maximumContactDistance = endpointTraceCopperWidth / 2 + segment.start.width / 2 + ENDPOINT_CONTACT_EPSILON;
|
|
2763
|
+
if (pointToSegmentDistance2(point, segment.start, segment.end) <= maximumContactDistance) {
|
|
2764
|
+
return true;
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
return false;
|
|
2768
|
+
}
|
|
2701
2769
|
function getRoutePointCenter(point) {
|
|
2702
2770
|
if (point.route_type === "through_pad") {
|
|
2703
2771
|
return {
|
|
@@ -2783,6 +2851,19 @@ function checkTracesAreContiguous(circuitJson) {
|
|
|
2783
2851
|
);
|
|
2784
2852
|
const padMap = /* @__PURE__ */ new Map();
|
|
2785
2853
|
const pcbConnectivityMap = new PcbConnectivityMap2(circuitJson);
|
|
2854
|
+
let fullConnectivityMap;
|
|
2855
|
+
let traceWireSegmentsByNetAndLayer;
|
|
2856
|
+
const getFullConnectivityMap = () => {
|
|
2857
|
+
fullConnectivityMap ??= getFullConnectivityMapFromCircuitJson7(circuitJson);
|
|
2858
|
+
return fullConnectivityMap;
|
|
2859
|
+
};
|
|
2860
|
+
const getTraceWireSegmentIndex = () => {
|
|
2861
|
+
traceWireSegmentsByNetAndLayer ??= getTraceWireSegmentsByNetAndLayer(
|
|
2862
|
+
pcbTraces,
|
|
2863
|
+
getFullConnectivityMap()
|
|
2864
|
+
);
|
|
2865
|
+
return traceWireSegmentsByNetAndLayer;
|
|
2866
|
+
};
|
|
2786
2867
|
const checkedSourceTraceIds = /* @__PURE__ */ new Set();
|
|
2787
2868
|
for (const pad of pcbSmtPads) {
|
|
2788
2869
|
if (pad.pcb_port_id) {
|
|
@@ -2948,7 +3029,6 @@ function checkTracesAreContiguous(circuitJson) {
|
|
|
2948
3029
|
if (expectedPorts.length === 0) {
|
|
2949
3030
|
let firstConnectsToAnyPad = false;
|
|
2950
3031
|
let lastConnectsToAnyPad = false;
|
|
2951
|
-
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;
|
|
2952
3032
|
for (const pads of padMap.values()) {
|
|
2953
3033
|
if (pads.some((pad) => routePointTouchesPad(firstPoint, pad))) {
|
|
2954
3034
|
firstConnectsToAnyPad = true;
|
|
@@ -2957,7 +3037,26 @@ function checkTracesAreContiguous(circuitJson) {
|
|
|
2957
3037
|
lastConnectsToAnyPad = true;
|
|
2958
3038
|
}
|
|
2959
3039
|
}
|
|
2960
|
-
|
|
3040
|
+
const firstEndpointTraceCopperWidth = !firstConnectsToAnyPad ? getEndpointTraceCopperWidth(trace, "start") : void 0;
|
|
3041
|
+
const lastEndpointTraceCopperWidth = !lastConnectsToAnyPad ? getEndpointTraceCopperWidth(trace, "end") : void 0;
|
|
3042
|
+
const firstConnectsToLogicallyConnectedTraceCopper = firstEndpointTraceCopperWidth !== void 0 && routePointTouchesLogicallyConnectedTraceCopper({
|
|
3043
|
+
point: firstPoint,
|
|
3044
|
+
endpointTraceCopperWidth: firstEndpointTraceCopperWidth,
|
|
3045
|
+
ownerTrace: trace,
|
|
3046
|
+
traceWireSegmentsByNetAndLayer: getTraceWireSegmentIndex(),
|
|
3047
|
+
fullConnectivityMap: getFullConnectivityMap()
|
|
3048
|
+
});
|
|
3049
|
+
const lastConnectsToLogicallyConnectedTraceCopper = lastEndpointTraceCopperWidth !== void 0 && routePointTouchesLogicallyConnectedTraceCopper({
|
|
3050
|
+
point: lastPoint,
|
|
3051
|
+
endpointTraceCopperWidth: lastEndpointTraceCopperWidth,
|
|
3052
|
+
ownerTrace: trace,
|
|
3053
|
+
traceWireSegmentsByNetAndLayer: getTraceWireSegmentIndex(),
|
|
3054
|
+
fullConnectivityMap: getFullConnectivityMap()
|
|
3055
|
+
});
|
|
3056
|
+
const firstIsConnected = firstConnectsToAnyPad || firstConnectsToLogicallyConnectedTraceCopper;
|
|
3057
|
+
const lastIsConnected = lastConnectsToAnyPad || lastConnectsToLogicallyConnectedTraceCopper;
|
|
3058
|
+
const endpointsAreSame = firstPoint.route_type === "wire" && lastPoint.route_type === "wire" && firstPoint.layer === lastPoint.layer && Math.hypot(firstPoint.x - lastPoint.x, firstPoint.y - lastPoint.y) <= ENDPOINT_CONTACT_EPSILON;
|
|
3059
|
+
if (!firstIsConnected && firstPoint.route_type === "wire") {
|
|
2961
3060
|
errors.push({
|
|
2962
3061
|
type: "pcb_trace_error",
|
|
2963
3062
|
message: `Trace [${traceName}] has disconnected endpoint at (${firstPoint.x.toFixed(2)}, ${firstPoint.y.toFixed(2)})`,
|
|
@@ -2970,7 +3069,7 @@ function checkTracesAreContiguous(circuitJson) {
|
|
|
2970
3069
|
pcb_port_ids: []
|
|
2971
3070
|
});
|
|
2972
3071
|
}
|
|
2973
|
-
if (!
|
|
3072
|
+
if (!lastIsConnected && lastPoint.route_type === "wire" && !(endpointsAreSame && !firstIsConnected)) {
|
|
2974
3073
|
errors.push({
|
|
2975
3074
|
type: "pcb_trace_error",
|
|
2976
3075
|
message: `Trace [${traceName}] has disconnected endpoint at (${lastPoint.x.toFixed(2)}, ${lastPoint.y.toFixed(2)})`,
|
|
@@ -3074,7 +3173,7 @@ import {
|
|
|
3074
3173
|
getPrimaryId as getPrimaryId3
|
|
3075
3174
|
} from "@tscircuit/circuit-json-util";
|
|
3076
3175
|
import { doBoundsOverlap as doBoundsOverlap3 } from "@tscircuit/math-utils";
|
|
3077
|
-
import { getFullConnectivityMapFromCircuitJson as
|
|
3176
|
+
import { getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson8 } from "circuit-json-to-connectivity-map";
|
|
3078
3177
|
|
|
3079
3178
|
// lib/check-pcb-components-overlap/doPcbElementsOverlap.ts
|
|
3080
3179
|
import { getBoundsOfPcbElements as getBoundsOfPcbElements4 } from "@tscircuit/circuit-json-util";
|
|
@@ -3113,7 +3212,7 @@ var formatOverlapElementDescription = (circuitJson, element) => {
|
|
|
3113
3212
|
};
|
|
3114
3213
|
function checkPcbComponentOverlap(circuitJson) {
|
|
3115
3214
|
const errors = [];
|
|
3116
|
-
const connMap =
|
|
3215
|
+
const connMap = getFullConnectivityMapFromCircuitJson8(circuitJson);
|
|
3117
3216
|
const smtPads = cju6(circuitJson).pcb_smtpad.list();
|
|
3118
3217
|
const platedHoles = cju6(circuitJson).pcb_plated_hole.list();
|
|
3119
3218
|
const holes = cju6(circuitJson).pcb_hole.list();
|
|
@@ -3377,7 +3476,7 @@ import {
|
|
|
3377
3476
|
} from "@tscircuit/circuit-json-util";
|
|
3378
3477
|
import { formatMm } from "format-si-unit";
|
|
3379
3478
|
import {
|
|
3380
|
-
getFullConnectivityMapFromCircuitJson as
|
|
3479
|
+
getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson9
|
|
3381
3480
|
} from "circuit-json-to-connectivity-map";
|
|
3382
3481
|
function checkPadPadClearance(circuitJson, {
|
|
3383
3482
|
connMap,
|
|
@@ -3387,7 +3486,7 @@ function checkPadPadClearance(circuitJson, {
|
|
|
3387
3486
|
if (pads.length < 2) return [];
|
|
3388
3487
|
const board = getPcbBoard(circuitJson);
|
|
3389
3488
|
minClearance ??= getBoardDrcValue(board, "min_pad_edge_to_pad_edge_clearance") ?? jlcMinTolerances.min_pad_edge_to_pad_edge_clearance;
|
|
3390
|
-
connMap ??=
|
|
3489
|
+
connMap ??= getFullConnectivityMapFromCircuitJson9(circuitJson);
|
|
3391
3490
|
const spatialIndex = new SpatialObjectIndex({
|
|
3392
3491
|
objects: pads,
|
|
3393
3492
|
getBounds: getPadBounds,
|
|
@@ -3442,7 +3541,7 @@ import {
|
|
|
3442
3541
|
} from "@tscircuit/circuit-json-util";
|
|
3443
3542
|
import { formatMm as formatMm2 } from "format-si-unit";
|
|
3444
3543
|
import {
|
|
3445
|
-
getFullConnectivityMapFromCircuitJson as
|
|
3544
|
+
getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson10
|
|
3446
3545
|
} from "circuit-json-to-connectivity-map";
|
|
3447
3546
|
function checkPadTraceClearance(circuitJson, {
|
|
3448
3547
|
connMap,
|
|
@@ -3453,7 +3552,7 @@ function checkPadTraceClearance(circuitJson, {
|
|
|
3453
3552
|
if (pads.length === 0 || segments.length === 0) return [];
|
|
3454
3553
|
const board = getPcbBoard(circuitJson);
|
|
3455
3554
|
minClearance ??= getBoardDrcValue(board, "min_trace_to_pad_edge_clearance") ?? jlcMinTolerances.min_trace_to_pad_edge_clearance;
|
|
3456
|
-
connMap ??=
|
|
3555
|
+
connMap ??= getFullConnectivityMapFromCircuitJson10(circuitJson);
|
|
3457
3556
|
const spatialIndex = new SpatialObjectIndex({
|
|
3458
3557
|
objects: pads,
|
|
3459
3558
|
getBounds: getPadBounds,
|
|
@@ -3503,7 +3602,7 @@ function checkPadTraceClearance(circuitJson, {
|
|
|
3503
3602
|
import { getReadableNameForElement as getReadableNameForElement8 } from "@tscircuit/circuit-json-util";
|
|
3504
3603
|
import { formatMm as formatMm3 } from "format-si-unit";
|
|
3505
3604
|
import {
|
|
3506
|
-
getFullConnectivityMapFromCircuitJson as
|
|
3605
|
+
getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson11
|
|
3507
3606
|
} from "circuit-json-to-connectivity-map";
|
|
3508
3607
|
function checkViaTraceClearance(circuitJson, {
|
|
3509
3608
|
connMap,
|
|
@@ -3514,7 +3613,7 @@ function checkViaTraceClearance(circuitJson, {
|
|
|
3514
3613
|
if (vias.length === 0 || segments.length === 0) return [];
|
|
3515
3614
|
const board = getPcbBoard(circuitJson);
|
|
3516
3615
|
minClearance ??= getBoardDrcValue(board, "min_trace_to_pad_edge_clearance") ?? jlcMinTolerances.min_trace_to_pad_edge_clearance;
|
|
3517
|
-
connMap ??=
|
|
3616
|
+
connMap ??= getFullConnectivityMapFromCircuitJson11(circuitJson);
|
|
3518
3617
|
const errors = /* @__PURE__ */ new Map();
|
|
3519
3618
|
const overlappingPairIds = /* @__PURE__ */ new Set();
|
|
3520
3619
|
for (const via of vias) {
|
|
@@ -3558,7 +3657,7 @@ import {
|
|
|
3558
3657
|
} from "@tscircuit/circuit-json-util";
|
|
3559
3658
|
import { formatMm as formatMm4 } from "format-si-unit";
|
|
3560
3659
|
import {
|
|
3561
|
-
getFullConnectivityMapFromCircuitJson as
|
|
3660
|
+
getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson12
|
|
3562
3661
|
} from "circuit-json-to-connectivity-map";
|
|
3563
3662
|
function checkViaPadClearance(circuitJson, {
|
|
3564
3663
|
connMap,
|
|
@@ -3571,7 +3670,7 @@ function checkViaPadClearance(circuitJson, {
|
|
|
3571
3670
|
if (vias.length === 0 || pads.length === 0) return [];
|
|
3572
3671
|
const board = getPcbBoard(circuitJson);
|
|
3573
3672
|
const requiredClearance = minClearance ?? getBoardDrcValue(board, "min_pad_edge_to_pad_edge_clearance") ?? jlcMinTolerances.min_pad_edge_to_pad_edge_clearance;
|
|
3574
|
-
connMap ??=
|
|
3673
|
+
connMap ??= getFullConnectivityMapFromCircuitJson12(circuitJson);
|
|
3575
3674
|
const padIndex = new SpatialObjectIndex({
|
|
3576
3675
|
objects: pads,
|
|
3577
3676
|
getBounds: getPadBounds,
|
|
@@ -3615,7 +3714,7 @@ function checkViaPadClearance(circuitJson, {
|
|
|
3615
3714
|
|
|
3616
3715
|
// lib/check-vias-in-pads.ts
|
|
3617
3716
|
import { getPrimaryId as getPrimaryId7 } from "@tscircuit/circuit-json-util";
|
|
3618
|
-
import { getFullConnectivityMapFromCircuitJson as
|
|
3717
|
+
import { getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson13 } from "circuit-json-to-connectivity-map";
|
|
3619
3718
|
function checkViasInPads(circuitJson) {
|
|
3620
3719
|
const board = getPcbBoard(circuitJson);
|
|
3621
3720
|
if (board && "is_via_in_pad_allowed" in board && board.is_via_in_pad_allowed === true) {
|
|
@@ -3626,7 +3725,7 @@ function checkViasInPads(circuitJson) {
|
|
|
3626
3725
|
);
|
|
3627
3726
|
const pads = getPads(circuitJson);
|
|
3628
3727
|
if (vias.length === 0 || pads.length === 0) return [];
|
|
3629
|
-
const connMap =
|
|
3728
|
+
const connMap = getFullConnectivityMapFromCircuitJson13(circuitJson);
|
|
3630
3729
|
const padOrdinals = new Map(
|
|
3631
3730
|
pads.map((pad, index) => [getPrimaryId7(pad), index])
|
|
3632
3731
|
);
|
|
@@ -3821,6 +3920,11 @@ function checkAllPinsInComponentAreUnderspecified(circuitJson) {
|
|
|
3821
3920
|
|
|
3822
3921
|
// lib/check-no-power-pin-defined.ts
|
|
3823
3922
|
import { cju as cju8 } from "@tscircuit/circuit-json-util";
|
|
3923
|
+
|
|
3924
|
+
// lib/util/should-check-chip-power-ground-pins.ts
|
|
3925
|
+
var shouldCheckChipPowerGroundPins = (component, ports) => component.ftype === "simple_chip" && ports.filter((port) => port.do_not_connect !== true).length >= 2;
|
|
3926
|
+
|
|
3927
|
+
// lib/check-no-power-pin-defined.ts
|
|
3824
3928
|
function checkNoPowerPinDefined(circuitJson) {
|
|
3825
3929
|
const warnings = [];
|
|
3826
3930
|
const db = cju8(circuitJson);
|
|
@@ -3834,9 +3938,8 @@ function checkNoPowerPinDefined(circuitJson) {
|
|
|
3834
3938
|
portsByComponent.set(port.source_component_id, existing);
|
|
3835
3939
|
}
|
|
3836
3940
|
for (const component of sourceComponents) {
|
|
3837
|
-
if (component.ftype !== "simple_chip") continue;
|
|
3838
3941
|
const componentPorts = portsByComponent.get(component.source_component_id) ?? [];
|
|
3839
|
-
if (componentPorts
|
|
3942
|
+
if (!shouldCheckChipPowerGroundPins(component, componentPorts)) continue;
|
|
3840
3943
|
const hasRequiredPowerPin = componentPorts.some(
|
|
3841
3944
|
(port) => port.requires_power === true
|
|
3842
3945
|
);
|
|
@@ -3869,9 +3972,8 @@ function checkNoGroundPinDefined(circuitJson) {
|
|
|
3869
3972
|
portsByComponent.set(port.source_component_id, existing);
|
|
3870
3973
|
}
|
|
3871
3974
|
for (const component of sourceComponents) {
|
|
3872
|
-
if (component.ftype !== "simple_chip") continue;
|
|
3873
3975
|
const componentPorts = portsByComponent.get(component.source_component_id) ?? [];
|
|
3874
|
-
if (componentPorts
|
|
3976
|
+
if (!shouldCheckChipPowerGroundPins(component, componentPorts)) continue;
|
|
3875
3977
|
const hasRequiredGroundPin = componentPorts.some(
|
|
3876
3978
|
(port) => port.requires_ground === true
|
|
3877
3979
|
);
|