@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 +179 -62
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
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:
|
|
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:
|
|
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:
|
|
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 : [...
|
|
2174
|
+
return Array.isArray(obj.layers) ? obj.layers : [...all_layers2];
|
|
2058
2175
|
}
|
|
2059
2176
|
if (obj.type === "pcb_hole") {
|
|
2060
|
-
return [...
|
|
2177
|
+
return [...all_layers2];
|
|
2061
2178
|
}
|
|
2062
2179
|
if (obj.type === "pcb_via") {
|
|
2063
|
-
return Array.isArray(obj.layers) ? obj.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
|
|
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 =
|
|
3621
|
+
const innerLayers = all_layers3.filter((layer) => layer.startsWith("inner"));
|
|
3505
3622
|
const stack = [
|
|
3506
3623
|
"top",
|
|
3507
3624
|
...innerLayers.slice(
|