@tscircuit/checks 0.0.207 → 0.0.208

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
@@ -714,7 +714,172 @@ import {
714
714
  findConnectedNetworks,
715
715
  PcbConnectivityMap
716
716
  } from "circuit-json-to-connectivity-map";
717
+ import Flatbush3 from "flatbush";
718
+
719
+ // lib/util/get-via-and-pour-connections.ts
720
+ import { all_layers } from "circuit-json";
721
+ import {
722
+ getPourPolygon,
723
+ getTraceSegmentPolygon,
724
+ getViaPolygon
725
+ } from "@tscircuit/circuit-json-util";
717
726
  import Flatbush2 from "flatbush";
727
+
728
+ // lib/copper-pour-connectivity/create-copper-polygon-contact-tester.ts
729
+ import {
730
+ Box
731
+ } from "@flatten-js/core";
732
+ function createCopperPolygonContactTester(tolerance) {
733
+ const cache = /* @__PURE__ */ new WeakMap();
734
+ const getGeometry = (polygon) => {
735
+ let geometry = cache.get(polygon);
736
+ if (!geometry) {
737
+ geometry = {
738
+ box: polygon.box,
739
+ facePoints: [...polygon.faces].map((face) => face.first.start),
740
+ edges: [...polygon.edges].map((edge) => edge.shape),
741
+ edgeIndex: polygon.edges
742
+ };
743
+ cache.set(polygon, geometry);
744
+ }
745
+ return geometry;
746
+ };
747
+ return (a, b) => {
748
+ if (a.isEmpty() || b.isEmpty()) return false;
749
+ const ga = getGeometry(a);
750
+ const gb = getGeometry(b);
751
+ if (ga.box.xmin > gb.box.xmax + tolerance || ga.box.xmax < gb.box.xmin - tolerance || ga.box.ymin > gb.box.ymax + tolerance || ga.box.ymax < gb.box.ymin - tolerance)
752
+ return false;
753
+ if (ga.facePoints.some(
754
+ (point2) => gb.box.contains(point2) && b.contains(point2)
755
+ ) || gb.facePoints.some((point2) => ga.box.contains(point2) && a.contains(point2)))
756
+ return true;
757
+ const [small, large] = ga.edges.length <= gb.edges.length ? [ga, gb] : [gb, ga];
758
+ for (const edge of small.edges) {
759
+ const box = edge.box;
760
+ const queryBox = new Box(
761
+ box.xmin - tolerance,
762
+ box.ymin - tolerance,
763
+ box.xmax + tolerance,
764
+ box.ymax + tolerance
765
+ );
766
+ const candidates = large.edgeIndex.search(
767
+ queryBox
768
+ );
769
+ for (const candidate of candidates) {
770
+ if (edge.distanceTo(candidate.shape)[0] <= tolerance) return true;
771
+ }
772
+ }
773
+ return false;
774
+ };
775
+ }
776
+
777
+ // lib/util/get-via-and-pour-connections.ts
778
+ function getViaAndPourConnections(circuit) {
779
+ if (!circuit.some(
780
+ (e) => e.type === "pcb_via" || e.type === "pcb_copper_pour" || e.type === "pcb_trace" && e.route.some((p) => p.route_type === "via")
781
+ ))
782
+ return [];
783
+ const conductors = [];
784
+ const connections = [];
785
+ const scale = 1e6;
786
+ const tolerance = 1e-7 * scale;
787
+ const touches = createCopperPolygonContactTester(tolerance);
788
+ const add = (id, layers, polygon, bridges = false) => {
789
+ if (!polygon.isEmpty())
790
+ conductors.push({
791
+ id,
792
+ layers,
793
+ polygon: polygon.scale(scale, scale),
794
+ bridges
795
+ });
796
+ };
797
+ const vias = circuit.filter((e) => e.type === "pcb_via");
798
+ const board = circuit.find((e) => e.type === "pcb_board");
799
+ const stack = [
800
+ "top",
801
+ ...all_layers.filter((l) => l.startsWith("inner")).slice(0, board ? Math.max(0, board.num_layers - 2) : void 0),
802
+ ...board?.num_layers === 1 ? [] : ["bottom"]
803
+ ];
804
+ for (const copper of circuit) {
805
+ if (copper.type === "pcb_via") {
806
+ add(
807
+ copper.pcb_via_id,
808
+ copper.layers,
809
+ getViaPolygon(copper, copper.outer_diameter, 0),
810
+ true
811
+ );
812
+ if (copper.pcb_trace_id)
813
+ connections.push([copper.pcb_via_id, copper.pcb_trace_id]);
814
+ } else if (copper.type === "pcb_copper_pour") {
815
+ add(
816
+ copper.pcb_copper_pour_id,
817
+ [copper.layer],
818
+ getPourPolygon(copper),
819
+ true
820
+ );
821
+ } else if (copper.type === "pcb_trace") {
822
+ if (copper.route_thickness_mode === "interpolated") continue;
823
+ for (let i = 0; i < copper.route.length; i++) {
824
+ const a = copper.route[i], b = copper.route[i + 1];
825
+ if (a.route_type === "wire" && b?.route_type === "wire" && a.layer === b.layer && (a.x !== b.x || a.y !== b.y)) {
826
+ add(
827
+ copper.pcb_trace_id,
828
+ [a.layer],
829
+ getTraceSegmentPolygon(a, b, a.width)
830
+ );
831
+ } else if (a.route_type === "via") {
832
+ if (vias.some(
833
+ (v) => v.pcb_trace_id === copper.pcb_trace_id && Math.hypot(v.x - a.x, v.y - a.y) < 1e-9
834
+ ))
835
+ continue;
836
+ const from = stack.indexOf(a.from_layer), to = stack.indexOf(a.to_layer);
837
+ if (from < 0 || to < 0 || !a.outer_diameter) continue;
838
+ const via = {
839
+ type: "pcb_via",
840
+ pcb_via_id: `${copper.pcb_trace_id}_via_${i}`,
841
+ x: a.x,
842
+ y: a.y,
843
+ outer_diameter: a.outer_diameter,
844
+ hole_diameter: 0,
845
+ layers: stack.slice(Math.min(from, to), Math.max(from, to) + 1)
846
+ };
847
+ add(
848
+ copper.pcb_trace_id,
849
+ via.layers,
850
+ getViaPolygon(via, via.outer_diameter, 0),
851
+ true
852
+ );
853
+ }
854
+ }
855
+ }
856
+ }
857
+ if (!conductors.length) return connections;
858
+ const index = new Flatbush2(conductors.length);
859
+ for (const { polygon } of conductors) {
860
+ const b = polygon.box;
861
+ index.add(b.xmin, b.ymin, b.xmax, b.ymax);
862
+ }
863
+ index.finish();
864
+ for (const [i, a] of conductors.entries()) {
865
+ if (!a.bridges) continue;
866
+ const box = a.polygon.box;
867
+ for (const j of index.search(
868
+ box.xmin - tolerance,
869
+ box.ymin - tolerance,
870
+ box.xmax + tolerance,
871
+ box.ymax + tolerance
872
+ )) {
873
+ const b = conductors[j];
874
+ if (i === j || a.id === b.id || b.bridges && j < i || !a.layers.some((l) => b.layers.includes(l)))
875
+ continue;
876
+ if (touches(a.polygon, b.polygon)) connections.push([a.id, b.id]);
877
+ }
878
+ }
879
+ return connections;
880
+ }
881
+
882
+ // lib/util/create-indexed-pcb-connectivity-map.ts
718
883
  function createIndexedPcbConnectivityMap(circuitJson) {
719
884
  const map = new PcbConnectivityMap();
720
885
  map.circuitJson = circuitJson;
@@ -756,7 +921,7 @@ function createIndexedPcbConnectivityMap(circuitJson) {
756
921
  }
757
922
  const candidates = traces.map(() => /* @__PURE__ */ new Set());
758
923
  for (const bounds of boundsByLayer.values()) {
759
- const index = new Flatbush2(bounds.length);
924
+ const index = new Flatbush3(bounds.length);
760
925
  for (const box of bounds) index.add(box.minX, box.minY, box.maxX, box.maxY);
761
926
  index.finish();
762
927
  for (const box of bounds) {
@@ -795,6 +960,7 @@ function createIndexedPcbConnectivityMap(circuitJson) {
795
960
  for (const connection of connectionsByPort.get(portId) ?? [])
796
961
  connections.push(connection);
797
962
  }
963
+ connections.push(...getViaAndPourConnections(circuitJson));
798
964
  map.connMap = new ConnectivityMap(findConnectedNetworks(connections));
799
965
  return map;
800
966
  }
@@ -1264,63 +1430,14 @@ function getReadableNameForFootprintPad(circuitJson, pad, ordinal) {
1264
1430
  return `${padKind} #${ordinal + 1} at ${location}`;
1265
1431
  }
1266
1432
 
1267
- // lib/copper-pour-connectivity/create-copper-polygon-contact-tester.ts
1268
- import {
1269
- Box
1270
- } from "@flatten-js/core";
1271
- function createCopperPolygonContactTester(tolerance) {
1272
- const cache = /* @__PURE__ */ new WeakMap();
1273
- const getGeometry = (polygon) => {
1274
- let geometry = cache.get(polygon);
1275
- if (!geometry) {
1276
- geometry = {
1277
- box: polygon.box,
1278
- facePoints: [...polygon.faces].map((face) => face.first.start),
1279
- edges: [...polygon.edges].map((edge) => edge.shape),
1280
- edgeIndex: polygon.edges
1281
- };
1282
- cache.set(polygon, geometry);
1283
- }
1284
- return geometry;
1285
- };
1286
- return (a, b) => {
1287
- if (a.isEmpty() || b.isEmpty()) return false;
1288
- const ga = getGeometry(a);
1289
- const gb = getGeometry(b);
1290
- if (ga.box.xmin > gb.box.xmax + tolerance || ga.box.xmax < gb.box.xmin - tolerance || ga.box.ymin > gb.box.ymax + tolerance || ga.box.ymax < gb.box.ymin - tolerance)
1291
- return false;
1292
- if (ga.facePoints.some(
1293
- (point2) => gb.box.contains(point2) && b.contains(point2)
1294
- ) || gb.facePoints.some((point2) => ga.box.contains(point2) && a.contains(point2)))
1295
- return true;
1296
- const [small, large] = ga.edges.length <= gb.edges.length ? [ga, gb] : [gb, ga];
1297
- for (const edge of small.edges) {
1298
- const box = edge.box;
1299
- const queryBox = new Box(
1300
- box.xmin - tolerance,
1301
- box.ymin - tolerance,
1302
- box.xmax + tolerance,
1303
- box.ymax + tolerance
1304
- );
1305
- const candidates = large.edgeIndex.search(
1306
- queryBox
1307
- );
1308
- for (const candidate of candidates) {
1309
- if (edge.distanceTo(candidate.shape)[0] <= tolerance) return true;
1310
- }
1311
- }
1312
- return false;
1313
- };
1314
- }
1315
-
1316
1433
  // lib/copper-pour-connectivity/get-copper-pour-connectivity.ts
1317
1434
  import {
1318
1435
  getPrimaryId,
1319
1436
  getPlatedHolePolygon,
1320
- getPourPolygon,
1437
+ getPourPolygon as getPourPolygon2,
1321
1438
  getSmtPadPolygon,
1322
- getTraceSegmentPolygon,
1323
- getViaPolygon
1439
+ getTraceSegmentPolygon as getTraceSegmentPolygon2,
1440
+ getViaPolygon as getViaPolygon2
1324
1441
  } from "@tscircuit/circuit-json-util";
1325
1442
  function getCopperPourConnectivity(circuitJson, connectivity) {
1326
1443
  const scale = 1e6;
@@ -1343,7 +1460,7 @@ function getCopperPourConnectivity(circuitJson, connectivity) {
1343
1460
  if (!netId) continue;
1344
1461
  pouredNets.add(netId);
1345
1462
  add({
1346
- polygon: getPourPolygon(pour),
1463
+ polygon: getPourPolygon2(pour),
1347
1464
  layers: [pour.layer],
1348
1465
  netId,
1349
1466
  isPour: true
@@ -1364,7 +1481,7 @@ function getCopperPourConnectivity(circuitJson, connectivity) {
1364
1481
  if (start.route_type !== "wire" || end.route_type !== "wire" || start.layer !== end.layer)
1365
1482
  continue;
1366
1483
  add({
1367
- polygon: getTraceSegmentPolygon(start, end, start.width),
1484
+ polygon: getTraceSegmentPolygon2(start, end, start.width),
1368
1485
  layers: [start.layer],
1369
1486
  netId: netId2
1370
1487
  });
@@ -1394,7 +1511,7 @@ function getCopperPourConnectivity(circuitJson, connectivity) {
1394
1511
  });
1395
1512
  } else {
1396
1513
  add({
1397
- polygon: getViaPolygon(
1514
+ polygon: getViaPolygon2(
1398
1515
  copper,
1399
1516
  copper.outer_diameter,
1400
1517
  copper.hole_diameter
@@ -2045,7 +2162,7 @@ var SpatialObjectIndex = class {
2045
2162
  };
2046
2163
 
2047
2164
  // lib/util/getLayersOfPcbElement.ts
2048
- import { all_layers } from "circuit-json";
2165
+ import { all_layers as all_layers2 } from "circuit-json";
2049
2166
  function getLayersOfPcbElement(obj) {
2050
2167
  if (obj.type === "pcb_trace_segment") {
2051
2168
  return [obj.layer];
@@ -2054,13 +2171,13 @@ function getLayersOfPcbElement(obj) {
2054
2171
  return [obj.layer];
2055
2172
  }
2056
2173
  if (obj.type === "pcb_plated_hole") {
2057
- return Array.isArray(obj.layers) ? obj.layers : [...all_layers];
2174
+ return Array.isArray(obj.layers) ? obj.layers : [...all_layers2];
2058
2175
  }
2059
2176
  if (obj.type === "pcb_hole") {
2060
- return [...all_layers];
2177
+ return [...all_layers2];
2061
2178
  }
2062
2179
  if (obj.type === "pcb_via") {
2063
- return Array.isArray(obj.layers) ? obj.layers : [...all_layers];
2180
+ return Array.isArray(obj.layers) ? obj.layers : [...all_layers2];
2064
2181
  }
2065
2182
  if (obj.type === "pcb_keepout") {
2066
2183
  return Array.isArray(obj.layers) ? obj.layers : [];
@@ -3489,7 +3606,7 @@ import {
3489
3606
 
3490
3607
  // lib/check-traces-are-contiguous/via-contact-index.ts
3491
3608
  import {
3492
- all_layers as all_layers2
3609
+ all_layers as all_layers3
3493
3610
  } from "circuit-json";
3494
3611
  import { getPrimaryId as getPrimaryId4 } from "@tscircuit/circuit-json-util";
3495
3612
  import { pointToSegmentDistance as pointToSegmentDistance2 } from "@tscircuit/math-utils";
@@ -3501,7 +3618,7 @@ function getViaContactIndex(circuitJson, connectivity) {
3501
3618
  const vias = circuitJson.filter((element) => element.type === "pcb_via");
3502
3619
  const board = circuitJson.find((element) => element.type === "pcb_board");
3503
3620
  const layerCount = board?.num_layers;
3504
- const innerLayers = all_layers2.filter((layer) => layer.startsWith("inner"));
3621
+ const innerLayers = all_layers3.filter((layer) => layer.startsWith("inner"));
3505
3622
  const stack = [
3506
3623
  "top",
3507
3624
  ...innerLayers.slice(