@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 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 { PcbConnectivityMap as PcbConnectivityMap2 } from "circuit-json-to-connectivity-map";
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
- if (!firstConnectsToAnyPad && firstPoint.route_type === "wire") {
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 (!lastConnectsToAnyPad && lastPoint.route_type === "wire" && !(endpointsAreSame && !firstConnectsToAnyPad)) {
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 getFullConnectivityMapFromCircuitJson7 } from "circuit-json-to-connectivity-map";
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 = getFullConnectivityMapFromCircuitJson7(circuitJson);
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 getFullConnectivityMapFromCircuitJson8
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 ??= getFullConnectivityMapFromCircuitJson8(circuitJson);
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 getFullConnectivityMapFromCircuitJson9
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 ??= getFullConnectivityMapFromCircuitJson9(circuitJson);
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 getFullConnectivityMapFromCircuitJson10
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 ??= getFullConnectivityMapFromCircuitJson10(circuitJson);
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 getFullConnectivityMapFromCircuitJson11
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 ??= getFullConnectivityMapFromCircuitJson11(circuitJson);
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 getFullConnectivityMapFromCircuitJson12 } from "circuit-json-to-connectivity-map";
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 = getFullConnectivityMapFromCircuitJson12(circuitJson);
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.length === 0) continue;
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.length === 0) continue;
3976
+ if (!shouldCheckChipPowerGroundPins(component, componentPorts)) continue;
3875
3977
  const hasRequiredGroundPin = componentPorts.some(
3876
3978
  (port) => port.requires_ground === true
3877
3979
  );