@tscircuit/checks 0.0.122 → 0.0.123

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
@@ -237,7 +237,16 @@ function checkEachPcbPortConnectedToPcbTraces(circuitJson) {
237
237
  }
238
238
 
239
239
  // lib/check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping.ts
240
- import { getReadableNameForElement as getReadableNameForElement2, cju } from "@tscircuit/circuit-json-util";
240
+ import { cju, getReadableNameForElement as getReadableNameForElement2 } from "@tscircuit/circuit-json-util";
241
+ import { getPrimaryId } from "@tscircuit/circuit-json-util";
242
+ import {
243
+ segmentToBoundsMinDistance,
244
+ segmentToCircleMinDistance
245
+ } from "@tscircuit/math-utils";
246
+ import { segmentToSegmentMinDistance } from "@tscircuit/math-utils";
247
+ import {
248
+ getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson2
249
+ } from "circuit-json-to-connectivity-map";
241
250
 
242
251
  // lib/data-structures/SpatialIndex.ts
243
252
  var SpatialObjectIndex = class {
@@ -338,31 +347,6 @@ var SpatialObjectIndex = class {
338
347
  }
339
348
  };
340
349
 
341
- // lib/check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping.ts
342
- import {
343
- getFullConnectivityMapFromCircuitJson as getFullConnectivityMapFromCircuitJson2
344
- } from "circuit-json-to-connectivity-map";
345
-
346
- // lib/check-each-pcb-trace-non-overlapping/getCollidableBounds.ts
347
- import { getBoundsOfPcbElements as getBoundsOfPcbElements2 } from "@tscircuit/circuit-json-util";
348
- var getCollidableBounds = (collidable) => {
349
- if (collidable.type === "pcb_trace_segment") {
350
- return {
351
- minX: Math.min(collidable.x1, collidable.x2),
352
- minY: Math.min(collidable.y1, collidable.y2),
353
- maxX: Math.max(collidable.x1, collidable.x2),
354
- maxY: Math.max(collidable.y1, collidable.y2)
355
- };
356
- }
357
- return getBoundsOfPcbElements2([collidable]);
358
- };
359
-
360
- // lib/check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping.ts
361
- import {
362
- segmentToBoundsMinDistance,
363
- segmentToCircleMinDistance
364
- } from "@tscircuit/math-utils";
365
-
366
350
  // node_modules/@tscircuit/jlcpcb-manufacturing-specs/lib/jlcpcb-manufacturing-specs.ts
367
351
  var jlcMinTolerances = {
368
352
  min_trace_width: 0.1,
@@ -386,32 +370,122 @@ var DEFAULT_PAD_PAD_CLEARANCE = jlcMinTolerances.min_pad_edge_to_pad_edge_cleara
386
370
  var DEFAULT_PAD_TRACE_CLEARANCE = jlcMinTolerances.min_trace_to_pad_edge_clearance;
387
371
  var EPSILON = 5e-3;
388
372
 
389
- // lib/check-each-pcb-trace-non-overlapping/getPcbPortIdsConnectedToTraces.ts
390
- function getPcbPortIdsConnectedToTrace(trace) {
391
- const connectedPcbPorts = /* @__PURE__ */ new Set();
392
- for (const segment of trace.route) {
393
- if (segment.route_type === "wire") {
394
- if (segment.start_pcb_port_id)
395
- connectedPcbPorts.add(segment.start_pcb_port_id);
396
- if (segment.end_pcb_port_id)
397
- connectedPcbPorts.add(segment.end_pcb_port_id);
398
- }
373
+ // lib/util/getLayersOfPcbElement.ts
374
+ import { all_layers } from "circuit-json";
375
+ function getLayersOfPcbElement(obj) {
376
+ if (obj.type === "pcb_trace_segment") {
377
+ return [obj.layer];
399
378
  }
400
- return Array.from(connectedPcbPorts);
401
- }
402
- function getPcbPortIdsConnectedToTraces(traces) {
403
- const connectedPorts = /* @__PURE__ */ new Set();
404
- for (const trace of traces) {
405
- for (const portId of getPcbPortIdsConnectedToTrace(trace)) {
406
- connectedPorts.add(portId);
407
- }
379
+ if (obj.type === "pcb_smtpad") {
380
+ return [obj.layer];
408
381
  }
409
- return Array.from(connectedPorts);
382
+ if (obj.type === "pcb_plated_hole") {
383
+ return Array.isArray(obj.layers) ? obj.layers : [...all_layers];
384
+ }
385
+ if (obj.type === "pcb_hole") {
386
+ return [...all_layers];
387
+ }
388
+ if (obj.type === "pcb_via") {
389
+ return Array.isArray(obj.layers) ? obj.layers : [...all_layers];
390
+ }
391
+ if (obj.type === "pcb_keepout") {
392
+ return Array.isArray(obj.layers) ? obj.layers : [];
393
+ }
394
+ return [];
410
395
  }
411
396
 
412
- // lib/check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping.ts
413
- import { segmentToSegmentMinDistance } from "@tscircuit/math-utils";
414
- import { getPrimaryId } from "@tscircuit/circuit-json-util";
397
+ // lib/check-each-pcb-trace-non-overlapping/getClosestPointBetweenSegmentAndBounds.ts
398
+ var getClosestPointBetweenSegmentAndBounds = (segment, bounds) => {
399
+ const p1 = { x: segment.x1, y: segment.y1 };
400
+ const p2 = { x: segment.x2, y: segment.y2 };
401
+ const minX = bounds.minX;
402
+ const minY = bounds.minY;
403
+ const maxX = bounds.maxX;
404
+ const maxY = bounds.maxY;
405
+ if (p1.x === p2.x && p1.y === p2.y) {
406
+ const closestX = Math.max(minX, Math.min(maxX, p1.x));
407
+ const closestY = Math.max(minY, Math.min(maxY, p1.y));
408
+ if (closestX === p1.x && closestY === p1.y) {
409
+ return { x: p1.x, y: p1.y };
410
+ }
411
+ return { x: closestX, y: closestY };
412
+ }
413
+ const dx = p2.x - p1.x;
414
+ const dy = p2.y - p1.y;
415
+ const tMinX = dx !== 0 ? (minX - p1.x) / dx : Number.NEGATIVE_INFINITY;
416
+ const tMaxX = dx !== 0 ? (maxX - p1.x) / dx : Number.POSITIVE_INFINITY;
417
+ const tMinY = dy !== 0 ? (minY - p1.y) / dy : Number.NEGATIVE_INFINITY;
418
+ const tMaxY = dy !== 0 ? (maxY - p1.y) / dy : Number.POSITIVE_INFINITY;
419
+ const tEnter = Math.max(Math.min(tMinX, tMaxX), Math.min(tMinY, tMaxY));
420
+ const tExit = Math.min(Math.max(tMinX, tMaxX), Math.max(tMinY, tMaxY));
421
+ if (tEnter <= tExit && tExit >= 0 && tEnter <= 1) {
422
+ const t = Math.max(0, Math.min(1, tEnter));
423
+ return {
424
+ x: p1.x + t * dx,
425
+ y: p1.y + t * dy
426
+ };
427
+ }
428
+ const closestToP1 = {
429
+ x: Math.max(minX, Math.min(maxX, p1.x)),
430
+ y: Math.max(minY, Math.min(maxY, p1.y))
431
+ };
432
+ const closestToP2 = {
433
+ x: Math.max(minX, Math.min(maxX, p2.x)),
434
+ y: Math.max(minY, Math.min(maxY, p2.y))
435
+ };
436
+ const distToP1Squared = (closestToP1.x - p1.x) ** 2 + (closestToP1.y - p1.y) ** 2;
437
+ const distToP2Squared = (closestToP2.x - p2.x) ** 2 + (closestToP2.y - p2.y) ** 2;
438
+ const edges = [
439
+ { start: { x: minX, y: minY }, end: { x: maxX, y: minY } },
440
+ // Bottom edge
441
+ { start: { x: maxX, y: minY }, end: { x: maxX, y: maxY } },
442
+ // Right edge
443
+ { start: { x: maxX, y: maxY }, end: { x: minX, y: maxY } },
444
+ // Top edge
445
+ { start: { x: minX, y: maxY }, end: { x: minX, y: minY } }
446
+ // Left edge
447
+ ];
448
+ let minDistance = Math.min(distToP1Squared, distToP2Squared);
449
+ let closestPoint = distToP1Squared <= distToP2Squared ? closestToP1 : closestToP2;
450
+ const clamp2 = (value, min, max) => {
451
+ return Math.max(min, Math.min(max, value));
452
+ };
453
+ for (const edge of edges) {
454
+ const va = { x: p2.x - p1.x, y: p2.y - p1.y };
455
+ const vb = { x: edge.end.x - edge.start.x, y: edge.end.y - edge.start.y };
456
+ const w = { x: p1.x - edge.start.x, y: p1.y - edge.start.y };
457
+ const dotAA = va.x * va.x + va.y * va.y;
458
+ const dotAB = va.x * vb.x + va.y * vb.y;
459
+ const dotAW = va.x * w.x + va.y * w.y;
460
+ const dotBB = vb.x * vb.x + vb.y * vb.y;
461
+ const dotBW = vb.x * w.x + vb.y * w.y;
462
+ const denominator = dotAA * dotBB - dotAB * dotAB;
463
+ if (Math.abs(denominator) < 1e-10) continue;
464
+ let tA = (dotAB * dotBW - dotBB * dotAW) / denominator;
465
+ let tB = (dotAA * dotBW - dotAB * dotAW) / denominator;
466
+ tA = clamp2(tA, 0, 1);
467
+ tB = clamp2(tB, 0, 1);
468
+ const closestOnSegment = {
469
+ x: p1.x + tA * va.x,
470
+ y: p1.y + tA * va.y
471
+ };
472
+ const closestOnEdge = {
473
+ x: edge.start.x + tB * vb.x,
474
+ y: edge.start.y + tB * vb.y
475
+ };
476
+ const dx2 = closestOnSegment.x - closestOnEdge.x;
477
+ const dy2 = closestOnSegment.y - closestOnEdge.y;
478
+ const distSquared = dx2 * dx2 + dy2 * dy2;
479
+ if (distSquared < minDistance) {
480
+ minDistance = distSquared;
481
+ closestPoint = {
482
+ x: (closestOnSegment.x + closestOnEdge.x) / 2,
483
+ y: (closestOnSegment.y + closestOnEdge.y) / 2
484
+ };
485
+ }
486
+ }
487
+ return closestPoint;
488
+ };
415
489
 
416
490
  // lib/check-each-pcb-trace-non-overlapping/getClosestPointBetweenSegments.ts
417
491
  var getClosestPointBetweenSegments = (segmentA, segmentB) => {
@@ -558,6 +632,43 @@ var clamp = (value, min, max) => {
558
632
  return Math.max(min, Math.min(max, value));
559
633
  };
560
634
 
635
+ // lib/check-each-pcb-trace-non-overlapping/getCollidableBounds.ts
636
+ import { getBoundsOfPcbElements as getBoundsOfPcbElements2 } from "@tscircuit/circuit-json-util";
637
+ var getCollidableBounds = (collidable) => {
638
+ if (collidable.type === "pcb_trace_segment") {
639
+ return {
640
+ minX: Math.min(collidable.x1, collidable.x2),
641
+ minY: Math.min(collidable.y1, collidable.y2),
642
+ maxX: Math.max(collidable.x1, collidable.x2),
643
+ maxY: Math.max(collidable.y1, collidable.y2)
644
+ };
645
+ }
646
+ return getBoundsOfPcbElements2([collidable]);
647
+ };
648
+
649
+ // lib/check-each-pcb-trace-non-overlapping/getPcbPortIdsConnectedToTraces.ts
650
+ function getPcbPortIdsConnectedToTrace(trace) {
651
+ const connectedPcbPorts = /* @__PURE__ */ new Set();
652
+ for (const segment of trace.route) {
653
+ if (segment.route_type === "wire") {
654
+ if (segment.start_pcb_port_id)
655
+ connectedPcbPorts.add(segment.start_pcb_port_id);
656
+ if (segment.end_pcb_port_id)
657
+ connectedPcbPorts.add(segment.end_pcb_port_id);
658
+ }
659
+ }
660
+ return Array.from(connectedPcbPorts);
661
+ }
662
+ function getPcbPortIdsConnectedToTraces(traces) {
663
+ const connectedPorts = /* @__PURE__ */ new Set();
664
+ for (const trace of traces) {
665
+ for (const portId of getPcbPortIdsConnectedToTrace(trace)) {
666
+ connectedPorts.add(portId);
667
+ }
668
+ }
669
+ return Array.from(connectedPorts);
670
+ }
671
+
561
672
  // lib/check-each-pcb-trace-non-overlapping/getRadiusOfCircuitJsonElement.ts
562
673
  var getRadiusOfCircuitJsonElement = (obj) => {
563
674
  if (obj.type === "pcb_via") {
@@ -577,123 +688,6 @@ var getRadiusOfCircuitJsonElement = (obj) => {
577
688
  );
578
689
  };
579
690
 
580
- // lib/check-each-pcb-trace-non-overlapping/getClosestPointBetweenSegmentAndBounds.ts
581
- var getClosestPointBetweenSegmentAndBounds = (segment, bounds) => {
582
- const p1 = { x: segment.x1, y: segment.y1 };
583
- const p2 = { x: segment.x2, y: segment.y2 };
584
- const minX = bounds.minX;
585
- const minY = bounds.minY;
586
- const maxX = bounds.maxX;
587
- const maxY = bounds.maxY;
588
- if (p1.x === p2.x && p1.y === p2.y) {
589
- const closestX = Math.max(minX, Math.min(maxX, p1.x));
590
- const closestY = Math.max(minY, Math.min(maxY, p1.y));
591
- if (closestX === p1.x && closestY === p1.y) {
592
- return { x: p1.x, y: p1.y };
593
- }
594
- return { x: closestX, y: closestY };
595
- }
596
- const dx = p2.x - p1.x;
597
- const dy = p2.y - p1.y;
598
- const tMinX = dx !== 0 ? (minX - p1.x) / dx : Number.NEGATIVE_INFINITY;
599
- const tMaxX = dx !== 0 ? (maxX - p1.x) / dx : Number.POSITIVE_INFINITY;
600
- const tMinY = dy !== 0 ? (minY - p1.y) / dy : Number.NEGATIVE_INFINITY;
601
- const tMaxY = dy !== 0 ? (maxY - p1.y) / dy : Number.POSITIVE_INFINITY;
602
- const tEnter = Math.max(Math.min(tMinX, tMaxX), Math.min(tMinY, tMaxY));
603
- const tExit = Math.min(Math.max(tMinX, tMaxX), Math.max(tMinY, tMaxY));
604
- if (tEnter <= tExit && tExit >= 0 && tEnter <= 1) {
605
- const t = Math.max(0, Math.min(1, tEnter));
606
- return {
607
- x: p1.x + t * dx,
608
- y: p1.y + t * dy
609
- };
610
- }
611
- const closestToP1 = {
612
- x: Math.max(minX, Math.min(maxX, p1.x)),
613
- y: Math.max(minY, Math.min(maxY, p1.y))
614
- };
615
- const closestToP2 = {
616
- x: Math.max(minX, Math.min(maxX, p2.x)),
617
- y: Math.max(minY, Math.min(maxY, p2.y))
618
- };
619
- const distToP1Squared = (closestToP1.x - p1.x) ** 2 + (closestToP1.y - p1.y) ** 2;
620
- const distToP2Squared = (closestToP2.x - p2.x) ** 2 + (closestToP2.y - p2.y) ** 2;
621
- const edges = [
622
- { start: { x: minX, y: minY }, end: { x: maxX, y: minY } },
623
- // Bottom edge
624
- { start: { x: maxX, y: minY }, end: { x: maxX, y: maxY } },
625
- // Right edge
626
- { start: { x: maxX, y: maxY }, end: { x: minX, y: maxY } },
627
- // Top edge
628
- { start: { x: minX, y: maxY }, end: { x: minX, y: minY } }
629
- // Left edge
630
- ];
631
- let minDistance = Math.min(distToP1Squared, distToP2Squared);
632
- let closestPoint = distToP1Squared <= distToP2Squared ? closestToP1 : closestToP2;
633
- const clamp2 = (value, min, max) => {
634
- return Math.max(min, Math.min(max, value));
635
- };
636
- for (const edge of edges) {
637
- const va = { x: p2.x - p1.x, y: p2.y - p1.y };
638
- const vb = { x: edge.end.x - edge.start.x, y: edge.end.y - edge.start.y };
639
- const w = { x: p1.x - edge.start.x, y: p1.y - edge.start.y };
640
- const dotAA = va.x * va.x + va.y * va.y;
641
- const dotAB = va.x * vb.x + va.y * vb.y;
642
- const dotAW = va.x * w.x + va.y * w.y;
643
- const dotBB = vb.x * vb.x + vb.y * vb.y;
644
- const dotBW = vb.x * w.x + vb.y * w.y;
645
- const denominator = dotAA * dotBB - dotAB * dotAB;
646
- if (Math.abs(denominator) < 1e-10) continue;
647
- let tA = (dotAB * dotBW - dotBB * dotAW) / denominator;
648
- let tB = (dotAA * dotBW - dotAB * dotAW) / denominator;
649
- tA = clamp2(tA, 0, 1);
650
- tB = clamp2(tB, 0, 1);
651
- const closestOnSegment = {
652
- x: p1.x + tA * va.x,
653
- y: p1.y + tA * va.y
654
- };
655
- const closestOnEdge = {
656
- x: edge.start.x + tB * vb.x,
657
- y: edge.start.y + tB * vb.y
658
- };
659
- const dx2 = closestOnSegment.x - closestOnEdge.x;
660
- const dy2 = closestOnSegment.y - closestOnEdge.y;
661
- const distSquared = dx2 * dx2 + dy2 * dy2;
662
- if (distSquared < minDistance) {
663
- minDistance = distSquared;
664
- closestPoint = {
665
- x: (closestOnSegment.x + closestOnEdge.x) / 2,
666
- y: (closestOnSegment.y + closestOnEdge.y) / 2
667
- };
668
- }
669
- }
670
- return closestPoint;
671
- };
672
-
673
- // lib/util/getLayersOfPcbElement.ts
674
- import { all_layers } from "circuit-json";
675
- function getLayersOfPcbElement(obj) {
676
- if (obj.type === "pcb_trace_segment") {
677
- return [obj.layer];
678
- }
679
- if (obj.type === "pcb_smtpad") {
680
- return [obj.layer];
681
- }
682
- if (obj.type === "pcb_plated_hole") {
683
- return Array.isArray(obj.layers) ? obj.layers : [...all_layers];
684
- }
685
- if (obj.type === "pcb_hole") {
686
- return [...all_layers];
687
- }
688
- if (obj.type === "pcb_via") {
689
- return Array.isArray(obj.layers) ? obj.layers : [...all_layers];
690
- }
691
- if (obj.type === "pcb_keepout") {
692
- return Array.isArray(obj.layers) ? obj.layers : [];
693
- }
694
- return [];
695
- }
696
-
697
691
  // lib/check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping.ts
698
692
  function checkEachPcbTraceNonOverlapping(circuitJson, {
699
693
  connMap,
@@ -743,6 +737,12 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
743
737
  getBounds: getCollidableBounds
744
738
  });
745
739
  const getReadableName = (id) => getReadableNameForElement2(circuitJson, id);
740
+ const constructErrorMessage = (traceName, otherName, gap) => {
741
+ if (gap < 0) {
742
+ return `PCB trace ${traceName} overlaps with ${otherName} (accidental contact)`;
743
+ }
744
+ return `PCB trace ${traceName} is too close to ${otherName} (gap: ${gap.toFixed(3)}mm)`;
745
+ };
746
746
  const errorIds = /* @__PURE__ */ new Set();
747
747
  for (const segmentA of pcbTraceSegments) {
748
748
  const requiredMargin = minClearance;
@@ -776,7 +776,11 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
776
776
  errors.push({
777
777
  type: "pcb_trace_error",
778
778
  error_type: "pcb_trace_error",
779
- message: `PCB trace ${getReadableName(segmentA.pcb_trace_id)} overlaps with ${getReadableName(segmentB.pcb_trace_id)} ${gap2 < 0 ? "(accidental contact)" : `(gap: ${gap2.toFixed(3)}mm)`}`,
779
+ message: constructErrorMessage(
780
+ getReadableName(segmentA.pcb_trace_id),
781
+ getReadableName(segmentB.pcb_trace_id),
782
+ gap2
783
+ ),
780
784
  pcb_trace_id: segmentA.pcb_trace_id,
781
785
  source_trace_id: "",
782
786
  pcb_trace_error_id,
@@ -811,7 +815,11 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
811
815
  errors.push({
812
816
  type: "pcb_trace_error",
813
817
  error_type: "pcb_trace_error",
814
- message: `PCB trace ${getReadableName(segmentA.pcb_trace_id)} overlaps with ${obj.type} "${getReadableName(getPrimaryId(obj))}" ${gap2 < 0 ? "(accidental contact)" : `(gap: ${gap2.toFixed(3)}mm)`}`,
818
+ message: constructErrorMessage(
819
+ getReadableName(segmentA.pcb_trace_id),
820
+ `${obj.type} "${getReadableName(getPrimaryId(obj))}"`,
821
+ gap2
822
+ ),
815
823
  pcb_trace_id: segmentA.pcb_trace_id,
816
824
  center: getClosestPointBetweenSegmentAndBounds(
817
825
  segmentA,
@@ -840,7 +848,11 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
840
848
  errors.push({
841
849
  type: "pcb_trace_error",
842
850
  error_type: "pcb_trace_error",
843
- message: `PCB trace ${getReadableName(segmentA.pcb_trace_id)} overlaps with ${obj.type} "${getReadableName(getPrimaryId(obj))}" ${gap < 0 ? "(accidental contact)" : `(gap: ${gap.toFixed(3)}mm)`}`,
851
+ message: constructErrorMessage(
852
+ getReadableName(segmentA.pcb_trace_id),
853
+ `${obj.type} "${getReadableName(getPrimaryId(obj))}"`,
854
+ gap
855
+ ),
844
856
  pcb_trace_id: segmentA.pcb_trace_id,
845
857
  source_trace_id: "",
846
858
  pcb_trace_error_id,