@tscircuit/checks 0.0.193 → 0.0.195

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 CHANGED
@@ -57,8 +57,8 @@ declare function checkSourceTracesMatchPcbTraceThickness(circuitJson: AnyCircuit
57
57
 
58
58
  /**
59
59
  * Check that each source_trace which connects source ports has at least one
60
- * pcb_trace associated with it. If a source_trace has no corresponding
61
- * pcb_trace, return an error for that source_trace.
60
+ * pcb_trace associated with it, or its ports physically joined through a
61
+ * same-net copper pour. Otherwise return an error for that source_trace.
62
62
  */
63
63
  declare function checkSourceTracesHavePcbTraces(circuitJson: AnyCircuitElement[]): PcbTraceMissingError[];
64
64
 
package/dist/index.js CHANGED
@@ -464,6 +464,169 @@ function getReadableNameForFootprintPad(circuitJson, pad, ordinal) {
464
464
  return `${padKind} #${ordinal + 1} at ${location}`;
465
465
  }
466
466
 
467
+ // lib/copper-pour-connectivity/get-copper-pour-connectivity.ts
468
+ import {
469
+ getPrimaryId,
470
+ copperPolygonsTouch,
471
+ getPlatedHolePolygon,
472
+ getPourPolygon,
473
+ getSmtPadPolygon,
474
+ getTraceSegmentPolygon,
475
+ getViaPolygon
476
+ } from "@tscircuit/circuit-json-util";
477
+ function getCopperPourConnectivity(circuitJson, connectivity) {
478
+ const scale = 1e6;
479
+ const tolerance = 1e-7 * scale;
480
+ const conductors = [];
481
+ const pouredNets = /* @__PURE__ */ new Set();
482
+ const netForId = (id) => connectivity.getNetConnectedToId(id);
483
+ const add = (conductor) => {
484
+ if (!conductor.polygon.isEmpty()) {
485
+ conductors.push({
486
+ ...conductor,
487
+ polygon: conductor.polygon.scale(scale, scale)
488
+ });
489
+ }
490
+ };
491
+ for (const pour of circuitJson) {
492
+ if (pour.type !== "pcb_copper_pour" || !pour.source_net_id) continue;
493
+ const netId = netForId(pour.source_net_id);
494
+ if (!netId) continue;
495
+ pouredNets.add(netId);
496
+ add({
497
+ polygon: getPourPolygon(pour),
498
+ layers: [pour.layer],
499
+ netId,
500
+ isPour: true
501
+ });
502
+ }
503
+ const components = new Map(
504
+ circuitJson.filter((e) => e.type === "pcb_component").map((e) => [e.pcb_component_id, e])
505
+ );
506
+ if (pouredNets.size > 0) {
507
+ for (const copper of circuitJson) {
508
+ if (copper.type === "pcb_trace") {
509
+ const netId2 = netForId(copper.pcb_trace_id);
510
+ if (!netId2 || !pouredNets.has(netId2) || copper.route_thickness_mode === "interpolated")
511
+ continue;
512
+ for (let i = 0; i < copper.route.length - 1; i++) {
513
+ const start = copper.route[i];
514
+ const end = copper.route[i + 1];
515
+ if (start.route_type !== "wire" || end.route_type !== "wire" || start.layer !== end.layer)
516
+ continue;
517
+ add({
518
+ polygon: getTraceSegmentPolygon(start, end, start.width),
519
+ layers: [start.layer],
520
+ netId: netId2
521
+ });
522
+ }
523
+ continue;
524
+ }
525
+ if (copper.type !== "pcb_smtpad" && copper.type !== "pcb_plated_hole" && copper.type !== "pcb_via")
526
+ continue;
527
+ const netId = netForId(getPrimaryId(copper));
528
+ if (!netId || !pouredNets.has(netId)) continue;
529
+ if (copper.type === "pcb_smtpad") {
530
+ add({
531
+ polygon: getSmtPadPolygon(copper),
532
+ layers: [copper.layer],
533
+ netId,
534
+ portId: copper.pcb_port_id
535
+ });
536
+ } else if (copper.type === "pcb_plated_hole") {
537
+ add({
538
+ polygon: getPlatedHolePolygon(
539
+ copper,
540
+ copper.pcb_component_id ? components.get(copper.pcb_component_id)?.rotation : 0
541
+ ),
542
+ layers: copper.layers,
543
+ netId,
544
+ portId: copper.pcb_port_id
545
+ });
546
+ } else {
547
+ add({
548
+ polygon: getViaPolygon(
549
+ copper,
550
+ copper.outer_diameter,
551
+ copper.hole_diameter
552
+ ),
553
+ layers: copper.layers,
554
+ netId
555
+ });
556
+ }
557
+ }
558
+ }
559
+ const parent = conductors.map((_, i) => i);
560
+ const find = (i) => {
561
+ while (parent[i] !== i) {
562
+ parent[i] = parent[parent[i]];
563
+ i = parent[i];
564
+ }
565
+ return i;
566
+ };
567
+ const bounds = conductors.map((c) => c.polygon.box);
568
+ const order = conductors.map((_, i) => i).sort((a, b) => bounds[a].xmin - bounds[b].xmin);
569
+ for (let a = 0; a < order.length; a++) {
570
+ const i = order[a];
571
+ for (let b = a + 1; b < order.length; b++) {
572
+ const j = order[b];
573
+ if (bounds[j].xmin > bounds[i].xmax + tolerance) break;
574
+ if (find(i) === find(j) || conductors[i].netId !== conductors[j].netId)
575
+ continue;
576
+ if (bounds[j].ymin > bounds[i].ymax + tolerance || bounds[j].ymax < bounds[i].ymin - tolerance)
577
+ continue;
578
+ if (!conductors[i].layers.some(
579
+ (layer) => conductors[j].layers.includes(layer)
580
+ ))
581
+ continue;
582
+ if (copperPolygonsTouch(
583
+ conductors[i].polygon,
584
+ conductors[j].polygon,
585
+ tolerance
586
+ ))
587
+ parent[find(j)] = find(i);
588
+ }
589
+ }
590
+ const pourRoots = new Set(
591
+ conductors.flatMap((c, i) => c.isPour ? [find(i)] : [])
592
+ );
593
+ const rootsByPort = /* @__PURE__ */ new Map();
594
+ for (const [i, conductor] of conductors.entries()) {
595
+ if (!conductor.portId || !pourRoots.has(find(i))) continue;
596
+ const roots = rootsByPort.get(conductor.portId) ?? /* @__PURE__ */ new Set();
597
+ roots.add(find(i));
598
+ rootsByPort.set(conductor.portId, roots);
599
+ }
600
+ const portsByNet = /* @__PURE__ */ new Map();
601
+ for (const port of circuitJson) {
602
+ if (port.type !== "pcb_port") continue;
603
+ const netId = netForId(port.pcb_port_id);
604
+ if (!netId || !pouredNets.has(netId)) continue;
605
+ const ports = portsByNet.get(netId) ?? [];
606
+ ports.push(port.pcb_port_id);
607
+ portsByNet.set(netId, ports);
608
+ }
609
+ return {
610
+ isPortConnectedToNet(portId, sourceNetIds) {
611
+ const roots = rootsByPort.get(portId);
612
+ return sourceNetIds.every(
613
+ (id) => [...roots ?? []].some(
614
+ (root) => conductors[root].netId === netForId(id) && // Distinct same-net islands are not a physical connection.
615
+ (portsByNet.get(conductors[root].netId) ?? []).every(
616
+ (peer) => rootsByPort.get(peer)?.has(root)
617
+ )
618
+ )
619
+ );
620
+ },
621
+ arePortsConnected(portIds) {
622
+ if (portIds.length < 2) return false;
623
+ return [...rootsByPort.get(portIds[0]) ?? []].some(
624
+ (root) => portIds.every((id) => rootsByPort.get(id)?.has(root))
625
+ );
626
+ }
627
+ };
628
+ }
629
+
467
630
  // lib/check-each-pcb-port-connected-to-pcb-trace.ts
468
631
  function checkEachPcbPortConnectedToPcbTraces(circuitJson) {
469
632
  addStartAndEndPortIdsIfMissing(circuitJson);
@@ -479,6 +642,11 @@ function checkEachPcbPortConnectedToPcbTraces(circuitJson) {
479
642
  const errors = [];
480
643
  const connectivityMap = getFullConnectivityMapFromCircuitJson(circuitJson);
481
644
  const pcbConnectivityMap = new PcbConnectivityMap(circuitJson);
645
+ let pourConnectivity;
646
+ const getPourConnectivity = () => pourConnectivity ??= getCopperPourConnectivity(
647
+ circuitJson,
648
+ connectivityMap
649
+ );
482
650
  const sourcePortToPcbPort = /* @__PURE__ */ new Map();
483
651
  for (const pcbPort of pcbPorts) {
484
652
  sourcePortToPcbPort.set(pcbPort.source_port_id, pcbPort);
@@ -494,7 +662,10 @@ function checkEachPcbPortConnectedToPcbTraces(circuitJson) {
494
662
  const connectedPcbTraces = pcbConnectivityMap.getAllTracesConnectedToPort(
495
663
  pcbPort.pcb_port_id
496
664
  );
497
- if (connectedPcbTraces.length === 0) {
665
+ if (connectedPcbTraces.length === 0 && !getPourConnectivity().isPortConnectedToNet(
666
+ pcbPort.pcb_port_id,
667
+ sourceTrace.connected_source_net_ids
668
+ )) {
498
669
  const connectedNetNames = sourceTrace.connected_source_net_ids.map((sourceNetId) => sourceNetNameById.get(sourceNetId)).filter((name) => Boolean(name));
499
670
  const netDescription = connectedNetNames.length > 0 ? `net [${connectedNetNames.join(", ")}]` : "its connected net";
500
671
  errors.push({
@@ -534,7 +705,9 @@ function checkEachPcbPortConnectedToPcbTraces(circuitJson) {
534
705
  (element) => element.type === "pcb_trace" && ("pcb_trace_id" in element && element.pcb_trace_id === id || "route_id" in element && element.route_id === id)
535
706
  )
536
707
  );
537
- if (pcbTraceIds.length === 0) {
708
+ if (pcbTraceIds.length === 0 && !getPourConnectivity().arePortsConnected(
709
+ pcbPortsInTrace.map((port) => port.pcb_port_id)
710
+ )) {
538
711
  const uniqueComponentIds = new Set(
539
712
  pcbPortsInTrace.map((p) => p.pcb_component_id)
540
713
  );
@@ -555,7 +728,7 @@ function checkEachPcbPortConnectedToPcbTraces(circuitJson) {
555
728
 
556
729
  // lib/check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping.ts
557
730
  import { cju as cju2, getReadableNameForElement as getReadableNameForElement2 } from "@tscircuit/circuit-json-util";
558
- import { getPrimaryId } from "@tscircuit/circuit-json-util";
731
+ import { getPrimaryId as getPrimaryId2 } from "@tscircuit/circuit-json-util";
559
732
  import {
560
733
  segmentToBoundsMinDistance,
561
734
  segmentToCircleMinDistance as segmentToCircleMinDistance2
@@ -1467,7 +1640,7 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
1467
1640
  });
1468
1641
  continue;
1469
1642
  }
1470
- const primaryObjId = getPrimaryId(obj);
1643
+ const primaryObjId = getPrimaryId2(obj);
1471
1644
  if (connMap.areIdsConnected(
1472
1645
  segmentA.pcb_trace_id,
1473
1646
  "pcb_trace_id" in obj ? obj.pcb_trace_id : primaryObjId
@@ -1519,7 +1692,7 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
1519
1692
  error_type: "pcb_trace_error",
1520
1693
  message: constructErrorMessage(
1521
1694
  getReadableName(segmentA.pcb_trace_id),
1522
- `${obj.type} "${getReadableName(getPrimaryId(obj))}"`,
1695
+ `${obj.type} "${getReadableName(getPrimaryId2(obj))}"`,
1523
1696
  gap2
1524
1697
  ),
1525
1698
  pcb_trace_id: segmentA.pcb_trace_id,
@@ -1552,7 +1725,7 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
1552
1725
  error_type: "pcb_trace_error",
1553
1726
  message: constructErrorMessage(
1554
1727
  getReadableName(segmentA.pcb_trace_id),
1555
- `${obj.type} "${getReadableName(getPrimaryId(obj))}"`,
1728
+ `${obj.type} "${getReadableName(getPrimaryId2(obj))}"`,
1556
1729
  gap
1557
1730
  ),
1558
1731
  pcb_trace_id: segmentA.pcb_trace_id,
@@ -2414,8 +2587,8 @@ function checkPcbComponentOverCutout(circuitJson) {
2414
2587
  }
2415
2588
 
2416
2589
  // lib/check-pcb-copper-over-keepout.ts
2417
- import { cju as cju3, getPrimaryId as getPrimaryId2 } from "@tscircuit/circuit-json-util";
2418
- var getErrorOwnerId = (copper) => "pcb_component_id" in copper && copper.pcb_component_id ? copper.pcb_component_id : getPrimaryId2(copper);
2590
+ import { cju as cju3, getPrimaryId as getPrimaryId3 } from "@tscircuit/circuit-json-util";
2591
+ var getErrorOwnerId = (copper) => "pcb_component_id" in copper && copper.pcb_component_id ? copper.pcb_component_id : getPrimaryId3(copper);
2419
2592
  var getReadableCopperName = (circuitJson, copper) => {
2420
2593
  if ("pcb_component_id" in copper && copper.pcb_component_id) {
2421
2594
  const pcbComponent = circuitJson.find(
@@ -2427,7 +2600,7 @@ var getReadableCopperName = (circuitJson, copper) => {
2427
2600
  const componentName = sourceComponent?.type === "source_component" && sourceComponent.name ? sourceComponent.name : getReadableNameForComponent(circuitJson, copper.pcb_component_id);
2428
2601
  return `component ${componentName}`;
2429
2602
  }
2430
- return copper.type === "pcb_via" ? `via ${copper.pcb_via_id}` : `${copper.type} ${getPrimaryId2(copper)}`;
2603
+ return copper.type === "pcb_via" ? `via ${copper.pcb_via_id}` : `${copper.type} ${getPrimaryId3(copper)}`;
2431
2604
  };
2432
2605
  function checkPcbCopperOverKeepout(circuitJson) {
2433
2606
  const keepouts = cju3(circuitJson).pcb_keepout.list();
@@ -2675,6 +2848,11 @@ function checkSourceTracesHavePcbTraces(circuitJson) {
2675
2848
  pcbPorts.map((pcbPort) => [pcbPort.source_port_id, pcbPort])
2676
2849
  );
2677
2850
  const connectivityMap = getFullConnectivityMapFromCircuitJson6(circuitJson);
2851
+ let pourConnectivity;
2852
+ const getPourConnectivity = () => pourConnectivity ??= getCopperPourConnectivity(
2853
+ circuitJson,
2854
+ connectivityMap
2855
+ );
2678
2856
  for (const sourceTrace of sourceTraces) {
2679
2857
  if (!sourceTrace.connected_source_port_ids?.length) continue;
2680
2858
  if ((sourceTrace.connected_source_net_ids?.length ?? 0) > 0) continue;
@@ -2687,6 +2865,10 @@ function checkSourceTracesHavePcbTraces(circuitJson) {
2687
2865
  );
2688
2866
  if (!hasPcbTrace) {
2689
2867
  const connectedPcbPorts = sourceTrace.connected_source_port_ids.map((sourcePortId) => sourcePortToPcbPort.get(sourcePortId)).filter((pcbPort) => pcbPort !== void 0);
2868
+ if (connectedPcbPorts.length === sourceTrace.connected_source_port_ids.length && getPourConnectivity().arePortsConnected(
2869
+ connectedPcbPorts.map((port) => port.pcb_port_id)
2870
+ ))
2871
+ continue;
2690
2872
  const connectedPcbComponentIds = Array.from(
2691
2873
  new Set(
2692
2874
  connectedPcbPorts.map((port) => port.pcb_component_id).filter((id) => id !== void 0)
@@ -2721,7 +2903,7 @@ import {
2721
2903
  import {
2722
2904
  all_layers as all_layers2
2723
2905
  } from "circuit-json";
2724
- import { getPrimaryId as getPrimaryId3 } from "@tscircuit/circuit-json-util";
2906
+ import { getPrimaryId as getPrimaryId4 } from "@tscircuit/circuit-json-util";
2725
2907
  import { pointToSegmentDistance as pointToSegmentDistance2 } from "@tscircuit/math-utils";
2726
2908
  var CONTACT_EPSILON = 1e-9;
2727
2909
  function getViaContactIndex(circuitJson, connectivity) {
@@ -2745,7 +2927,7 @@ function getViaContactIndex(circuitJson, connectivity) {
2745
2927
  const net = connectivity.getNetConnectedToId(id);
2746
2928
  if (!net) return;
2747
2929
  const touchesPad = pads.some((pad) => {
2748
- if (connectivity.getNetConnectedToId(getPrimaryId3(pad)) !== net || !getLayersOfPcbElement(pad).some((layer) => layers.includes(layer)))
2930
+ if (connectivity.getNetConnectedToId(getPrimaryId4(pad)) !== net || !getLayersOfPcbElement(pad).some((layer) => layers.includes(layer)))
2749
2931
  return false;
2750
2932
  if (contact.radius === void 0) return isPointInPad(contact, pad);
2751
2933
  const viaGeometry = {
@@ -3336,7 +3518,7 @@ function checkPcbTracesOutOfBoard(circuitJson, config = {}) {
3336
3518
  import {
3337
3519
  cju as cju6,
3338
3520
  getBoundsOfPcbElements as getBoundsOfPcbElements5,
3339
- getPrimaryId as getPrimaryId4
3521
+ getPrimaryId as getPrimaryId5
3340
3522
  } from "@tscircuit/circuit-json-util";
3341
3523
  import { doBoundsOverlap as doBoundsOverlap3 } from "@tscircuit/math-utils";
3342
3524
  import { getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson8 } from "circuit-json-to-connectivity-map";
@@ -3420,7 +3602,7 @@ var formatOverlapElementDescription = (circuitJson, element) => {
3420
3602
  if ("pcb_port_id" in element && element.pcb_port_id) {
3421
3603
  return getReadableNameForPort(circuitJson, element.pcb_port_id);
3422
3604
  }
3423
- const id = getPrimaryId4(element);
3605
+ const id = getPrimaryId5(element);
3424
3606
  const readableName = getReadableNameForElementId(circuitJson, id);
3425
3607
  return readableName === "element" ? `[${id}]` : readableName;
3426
3608
  };
@@ -3433,7 +3615,7 @@ function checkPcbComponentOverlap(circuitJson) {
3433
3615
  const courtyards = circuitJson.filter(isCourtyardElement2);
3434
3616
  const componentMap = /* @__PURE__ */ new Map();
3435
3617
  for (const pad of smtPads) {
3436
- const componentId = pad.pcb_component_id || `standalone_pad_${getPrimaryId4(pad)}`;
3618
+ const componentId = pad.pcb_component_id || `standalone_pad_${getPrimaryId5(pad)}`;
3437
3619
  if (!componentMap.has(componentId)) {
3438
3620
  componentMap.set(componentId, {
3439
3621
  component_id: componentId,
@@ -3443,7 +3625,7 @@ function checkPcbComponentOverlap(circuitJson) {
3443
3625
  componentMap.get(componentId).elements.push(pad);
3444
3626
  }
3445
3627
  for (const hole of platedHoles) {
3446
- const componentId = hole.pcb_component_id || `standalone_plated_hole_${getPrimaryId4(hole)}`;
3628
+ const componentId = hole.pcb_component_id || `standalone_plated_hole_${getPrimaryId5(hole)}`;
3447
3629
  if (!componentMap.has(componentId)) {
3448
3630
  componentMap.set(componentId, {
3449
3631
  component_id: componentId,
@@ -3453,7 +3635,7 @@ function checkPcbComponentOverlap(circuitJson) {
3453
3635
  componentMap.get(componentId).elements.push(hole);
3454
3636
  }
3455
3637
  for (const hole of holes) {
3456
- const componentId = hole.pcb_component_id || `standalone_hole_${getPrimaryId4(hole)}`;
3638
+ const componentId = hole.pcb_component_id || `standalone_hole_${getPrimaryId5(hole)}`;
3457
3639
  if (!componentMap.has(componentId)) {
3458
3640
  componentMap.set(componentId, {
3459
3641
  component_id: componentId,
@@ -3489,8 +3671,8 @@ function checkPcbComponentOverlap(circuitJson) {
3489
3671
  }
3490
3672
  for (const elem1 of comp1.elements) {
3491
3673
  for (const elem2 of comp2.elements) {
3492
- const id1 = getPrimaryId4(elem1);
3493
- const id2 = getPrimaryId4(elem2);
3674
+ const id1 = getPrimaryId5(elem1);
3675
+ const id2 = getPrimaryId5(elem2);
3494
3676
  if ((isCourtyardElement2(elem1) || isCourtyardElement2(elem2)) && !isHoleElement(elem1) && !isHoleElement(elem2)) {
3495
3677
  continue;
3496
3678
  }
@@ -3750,7 +3932,7 @@ var checkPcbTraceViaCounts = (circuitJson) => {
3750
3932
 
3751
3933
  // lib/check-pad-pad-clearance.ts
3752
3934
  import {
3753
- getPrimaryId as getPrimaryId5,
3935
+ getPrimaryId as getPrimaryId6,
3754
3936
  getReadableNameForElement as getReadableNameForElement6
3755
3937
  } from "@tscircuit/circuit-json-util";
3756
3938
  import { formatMm } from "format-si-unit";
@@ -3769,17 +3951,17 @@ function checkPadPadClearance(circuitJson, {
3769
3951
  const spatialIndex = new SpatialObjectIndex({
3770
3952
  objects: pads,
3771
3953
  getBounds: getPadBounds,
3772
- getId: (pad) => getPrimaryId5(pad)
3954
+ getId: (pad) => getPrimaryId6(pad)
3773
3955
  });
3774
3956
  const errors = /* @__PURE__ */ new Map();
3775
3957
  for (const padA of pads) {
3776
- const padAId = getPrimaryId5(padA);
3958
+ const padAId = getPrimaryId6(padA);
3777
3959
  const nearbyPads = spatialIndex.getObjectsInBounds(
3778
3960
  getPadBounds(padA),
3779
3961
  minClearance
3780
3962
  );
3781
3963
  for (const padB of nearbyPads) {
3782
- const padBId = getPrimaryId5(padB);
3964
+ const padBId = getPrimaryId6(padB);
3783
3965
  if (padAId === padBId) continue;
3784
3966
  if (!getLayersOfPcbElement(padA).some(
3785
3967
  (layer) => getLayersOfPcbElement(padB).includes(layer)
@@ -3815,7 +3997,7 @@ function checkPadPadClearance(circuitJson, {
3815
3997
 
3816
3998
  // lib/check-pad-trace-clearance.ts
3817
3999
  import {
3818
- getPrimaryId as getPrimaryId6,
4000
+ getPrimaryId as getPrimaryId7,
3819
4001
  getReadableNameForElement as getReadableNameForElement7
3820
4002
  } from "@tscircuit/circuit-json-util";
3821
4003
  import { formatMm as formatMm2 } from "format-si-unit";
@@ -3835,7 +4017,7 @@ function checkPadTraceClearance(circuitJson, {
3835
4017
  const spatialIndex = new SpatialObjectIndex({
3836
4018
  objects: pads,
3837
4019
  getBounds: getPadBounds,
3838
- getId: (pad) => getPrimaryId6(pad)
4020
+ getId: (pad) => getPrimaryId7(pad)
3839
4021
  });
3840
4022
  const errors = /* @__PURE__ */ new Map();
3841
4023
  const overlappingPairIds = /* @__PURE__ */ new Set();
@@ -3845,7 +4027,7 @@ function checkPadTraceClearance(circuitJson, {
3845
4027
  minClearance + segment.thickness / 2
3846
4028
  );
3847
4029
  for (const pad of nearbyPads) {
3848
- const padId = getPrimaryId6(pad);
4030
+ const padId = getPrimaryId7(pad);
3849
4031
  if (!getLayersOfPcbElement(pad).includes(segment.layer)) continue;
3850
4032
  if (connMap.areIdsConnected(segment.pcb_trace_id, padId)) continue;
3851
4033
  const pairId = `${padId}_${segment.pcb_trace_id}`;
@@ -3931,7 +4113,7 @@ function checkViaTraceClearance(circuitJson, {
3931
4113
 
3932
4114
  // lib/check-via-pad-clearance.ts
3933
4115
  import {
3934
- getPrimaryId as getPrimaryId7,
4116
+ getPrimaryId as getPrimaryId8,
3935
4117
  getReadableNameForElement as getReadableNameForElement9
3936
4118
  } from "@tscircuit/circuit-json-util";
3937
4119
  import { formatMm as formatMm4 } from "format-si-unit";
@@ -3953,7 +4135,7 @@ function checkViaPadClearance(circuitJson, {
3953
4135
  const padIndex = new SpatialObjectIndex({
3954
4136
  objects: pads,
3955
4137
  getBounds: getPadBounds,
3956
- getId: getPrimaryId7
4138
+ getId: getPrimaryId8
3957
4139
  });
3958
4140
  const errors = [];
3959
4141
  for (const via of vias) {
@@ -3962,7 +4144,7 @@ function checkViaPadClearance(circuitJson, {
3962
4144
  requiredClearance
3963
4145
  );
3964
4146
  for (const pad of nearbyPads) {
3965
- const padId = getPrimaryId7(pad);
4147
+ const padId = getPrimaryId8(pad);
3966
4148
  if (!getLayersOfPcbElement(via).some(
3967
4149
  (layer) => getLayersOfPcbElement(pad).includes(layer)
3968
4150
  )) {
@@ -3987,7 +4169,7 @@ function checkViaPadClearance(circuitJson, {
3987
4169
  }
3988
4170
 
3989
4171
  // lib/check-vias-in-pads.ts
3990
- import { getPrimaryId as getPrimaryId8 } from "@tscircuit/circuit-json-util";
4172
+ import { getPrimaryId as getPrimaryId9 } from "@tscircuit/circuit-json-util";
3991
4173
  import { getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson13 } from "circuit-json-to-connectivity-map";
3992
4174
  function checkViasInPads(circuitJson) {
3993
4175
  const board = getPcbBoard(circuitJson);
@@ -4001,18 +4183,18 @@ function checkViasInPads(circuitJson) {
4001
4183
  if (vias.length === 0 || pads.length === 0) return [];
4002
4184
  const connMap = getFullConnectivityMapFromCircuitJson13(circuitJson);
4003
4185
  const padOrdinals = new Map(
4004
- pads.map((pad, index) => [getPrimaryId8(pad), index])
4186
+ pads.map((pad, index) => [getPrimaryId9(pad), index])
4005
4187
  );
4006
4188
  const padIndex = new SpatialObjectIndex({
4007
4189
  objects: pads,
4008
4190
  getBounds: getPadBounds,
4009
- getId: getPrimaryId8
4191
+ getId: getPrimaryId9
4010
4192
  });
4011
4193
  const errors = [];
4012
4194
  for (const via of vias) {
4013
4195
  const nearbyPads = padIndex.getObjectsInBounds(getPadBounds(via));
4014
4196
  for (const pad of nearbyPads) {
4015
- const padId = getPrimaryId8(pad);
4197
+ const padId = getPrimaryId9(pad);
4016
4198
  const viaLayers = getLayersOfPcbElement(via);
4017
4199
  const padLayers = getLayersOfPcbElement(pad);
4018
4200
  if (!viaLayers.some((layer) => padLayers.includes(layer))) continue;
@@ -4563,12 +4745,12 @@ function checkSchematicComponentPortsOutsideBody(circuitJson) {
4563
4745
  }
4564
4746
 
4565
4747
  // lib/consolidate-pcb-overlap-errors.ts
4566
- import { getPrimaryId as getPrimaryId9 } from "@tscircuit/circuit-json-util";
4748
+ import { getPrimaryId as getPrimaryId10 } from "@tscircuit/circuit-json-util";
4567
4749
  function consolidatePcbOverlapErrors(circuitJson, errors) {
4568
4750
  const ownerByElementId = /* @__PURE__ */ new Map();
4569
4751
  const elementById = /* @__PURE__ */ new Map();
4570
4752
  for (const element of circuitJson) {
4571
- const id = getPrimaryId9(element);
4753
+ const id = getPrimaryId10(element);
4572
4754
  elementById.set(id, element);
4573
4755
  if ("pcb_component_id" in element && element.pcb_component_id) {
4574
4756
  ownerByElementId.set(id, element.pcb_component_id);
@@ -4885,9 +5067,14 @@ function polygonsOverlap(polyA, polyB) {
4885
5067
  return false;
4886
5068
  }
4887
5069
  function checkCourtyardOverlap(circuitJson) {
5070
+ const doNotPlaceComponentIds = new Set(
5071
+ circuitJson.flatMap(
5072
+ (el) => el.type === "pcb_component" && el.do_not_place ? [el.pcb_component_id] : []
5073
+ )
5074
+ );
4888
5075
  const courtyards = circuitJson.filter(
4889
5076
  (el) => el.type === "pcb_courtyard_rect" || el.type === "pcb_courtyard_circle" || el.type === "pcb_courtyard_outline"
4890
- );
5077
+ ).filter((el) => !doNotPlaceComponentIds.has(el.pcb_component_id));
4891
5078
  const byComponent = /* @__PURE__ */ new Map();
4892
5079
  for (const el of courtyards) {
4893
5080
  const id = el.pcb_component_id;