@tscircuit/checks 0.0.206 → 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
  }
@@ -1267,16 +1433,16 @@ function getReadableNameForFootprintPad(circuitJson, pad, ordinal) {
1267
1433
  // lib/copper-pour-connectivity/get-copper-pour-connectivity.ts
1268
1434
  import {
1269
1435
  getPrimaryId,
1270
- copperPolygonsTouch,
1271
1436
  getPlatedHolePolygon,
1272
- getPourPolygon,
1437
+ getPourPolygon as getPourPolygon2,
1273
1438
  getSmtPadPolygon,
1274
- getTraceSegmentPolygon,
1275
- getViaPolygon
1439
+ getTraceSegmentPolygon as getTraceSegmentPolygon2,
1440
+ getViaPolygon as getViaPolygon2
1276
1441
  } from "@tscircuit/circuit-json-util";
1277
1442
  function getCopperPourConnectivity(circuitJson, connectivity) {
1278
1443
  const scale = 1e6;
1279
1444
  const tolerance = 1e-7 * scale;
1445
+ const copperPolygonsTouch = createCopperPolygonContactTester(tolerance);
1280
1446
  const conductors = [];
1281
1447
  const pouredNets = /* @__PURE__ */ new Set();
1282
1448
  const netForId = (id) => connectivity.getNetConnectedToId(id);
@@ -1294,7 +1460,7 @@ function getCopperPourConnectivity(circuitJson, connectivity) {
1294
1460
  if (!netId) continue;
1295
1461
  pouredNets.add(netId);
1296
1462
  add({
1297
- polygon: getPourPolygon(pour),
1463
+ polygon: getPourPolygon2(pour),
1298
1464
  layers: [pour.layer],
1299
1465
  netId,
1300
1466
  isPour: true
@@ -1315,7 +1481,7 @@ function getCopperPourConnectivity(circuitJson, connectivity) {
1315
1481
  if (start.route_type !== "wire" || end.route_type !== "wire" || start.layer !== end.layer)
1316
1482
  continue;
1317
1483
  add({
1318
- polygon: getTraceSegmentPolygon(start, end, start.width),
1484
+ polygon: getTraceSegmentPolygon2(start, end, start.width),
1319
1485
  layers: [start.layer],
1320
1486
  netId: netId2
1321
1487
  });
@@ -1345,7 +1511,7 @@ function getCopperPourConnectivity(circuitJson, connectivity) {
1345
1511
  });
1346
1512
  } else {
1347
1513
  add({
1348
- polygon: getViaPolygon(
1514
+ polygon: getViaPolygon2(
1349
1515
  copper,
1350
1516
  copper.outer_diameter,
1351
1517
  copper.hole_diameter
@@ -1380,11 +1546,7 @@ function getCopperPourConnectivity(circuitJson, connectivity) {
1380
1546
  (layer) => conductors[j].layers.includes(layer)
1381
1547
  ))
1382
1548
  continue;
1383
- if (copperPolygonsTouch(
1384
- conductors[i].polygon,
1385
- conductors[j].polygon,
1386
- tolerance
1387
- ))
1549
+ if (copperPolygonsTouch(conductors[i].polygon, conductors[j].polygon))
1388
1550
  parent[find(j)] = find(i);
1389
1551
  }
1390
1552
  }
@@ -2000,7 +2162,7 @@ var SpatialObjectIndex = class {
2000
2162
  };
2001
2163
 
2002
2164
  // lib/util/getLayersOfPcbElement.ts
2003
- import { all_layers } from "circuit-json";
2165
+ import { all_layers as all_layers2 } from "circuit-json";
2004
2166
  function getLayersOfPcbElement(obj) {
2005
2167
  if (obj.type === "pcb_trace_segment") {
2006
2168
  return [obj.layer];
@@ -2009,13 +2171,13 @@ function getLayersOfPcbElement(obj) {
2009
2171
  return [obj.layer];
2010
2172
  }
2011
2173
  if (obj.type === "pcb_plated_hole") {
2012
- return Array.isArray(obj.layers) ? obj.layers : [...all_layers];
2174
+ return Array.isArray(obj.layers) ? obj.layers : [...all_layers2];
2013
2175
  }
2014
2176
  if (obj.type === "pcb_hole") {
2015
- return [...all_layers];
2177
+ return [...all_layers2];
2016
2178
  }
2017
2179
  if (obj.type === "pcb_via") {
2018
- return Array.isArray(obj.layers) ? obj.layers : [...all_layers];
2180
+ return Array.isArray(obj.layers) ? obj.layers : [...all_layers2];
2019
2181
  }
2020
2182
  if (obj.type === "pcb_keepout") {
2021
2183
  return Array.isArray(obj.layers) ? obj.layers : [];
@@ -3444,7 +3606,7 @@ import {
3444
3606
 
3445
3607
  // lib/check-traces-are-contiguous/via-contact-index.ts
3446
3608
  import {
3447
- all_layers as all_layers2
3609
+ all_layers as all_layers3
3448
3610
  } from "circuit-json";
3449
3611
  import { getPrimaryId as getPrimaryId4 } from "@tscircuit/circuit-json-util";
3450
3612
  import { pointToSegmentDistance as pointToSegmentDistance2 } from "@tscircuit/math-utils";
@@ -3456,7 +3618,7 @@ function getViaContactIndex(circuitJson, connectivity) {
3456
3618
  const vias = circuitJson.filter((element) => element.type === "pcb_via");
3457
3619
  const board = circuitJson.find((element) => element.type === "pcb_board");
3458
3620
  const layerCount = board?.num_layers;
3459
- const innerLayers = all_layers2.filter((layer) => layer.startsWith("inner"));
3621
+ const innerLayers = all_layers3.filter((layer) => layer.startsWith("inner"));
3460
3622
  const stack = [
3461
3623
  "top",
3462
3624
  ...innerLayers.slice(