@tscircuit/checks 0.0.168 → 0.0.170

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
@@ -332,7 +332,10 @@ var addStartAndEndPortIdsIfMissing = (soup) => {
332
332
  };
333
333
 
334
334
  // lib/check-each-pcb-port-connected-to-pcb-trace.ts
335
- import { getFullConnectivityMapFromCircuitJson } from "circuit-json-to-connectivity-map";
335
+ import {
336
+ getFullConnectivityMapFromCircuitJson,
337
+ PcbConnectivityMap
338
+ } from "circuit-json-to-connectivity-map";
336
339
 
337
340
  // lib/util/get-readable-names.ts
338
341
  import {
@@ -470,14 +473,41 @@ function checkEachPcbPortConnectedToPcbTraces(circuitJson) {
470
473
  const pcbPorts = circuitJson.filter(
471
474
  (item) => item.type === "pcb_port"
472
475
  );
476
+ const sourceNets = circuitJson.filter(
477
+ (item) => item.type === "source_net"
478
+ );
473
479
  const errors = [];
474
480
  const connectivityMap = getFullConnectivityMapFromCircuitJson(circuitJson);
481
+ const pcbConnectivityMap = new PcbConnectivityMap(circuitJson);
475
482
  const sourcePortToPcbPort = /* @__PURE__ */ new Map();
476
483
  for (const pcbPort of pcbPorts) {
477
484
  sourcePortToPcbPort.set(pcbPort.source_port_id, pcbPort);
478
485
  }
486
+ const sourceNetNameById = new Map(
487
+ sourceNets.map((sourceNet) => [sourceNet.source_net_id, sourceNet.name])
488
+ );
479
489
  for (const sourceTrace of sourceTraces) {
480
490
  const connectedSourcePortIds = sourceTrace.connected_source_port_ids;
491
+ if (connectedSourcePortIds.length === 1 && sourceTrace.connected_source_net_ids.length > 0) {
492
+ const pcbPort = sourcePortToPcbPort.get(connectedSourcePortIds[0]);
493
+ if (!pcbPort) continue;
494
+ const connectedPcbTraces = pcbConnectivityMap.getAllTracesConnectedToPort(
495
+ pcbPort.pcb_port_id
496
+ );
497
+ if (connectedPcbTraces.length === 0) {
498
+ const connectedNetNames = sourceTrace.connected_source_net_ids.map((sourceNetId) => sourceNetNameById.get(sourceNetId)).filter((name) => Boolean(name));
499
+ const netDescription = connectedNetNames.length > 0 ? `net [${connectedNetNames.join(", ")}]` : "its connected net";
500
+ errors.push({
501
+ type: "pcb_port_not_connected_error",
502
+ message: `Port [${getReadableNameForPort(circuitJson, pcbPort.pcb_port_id)}] is not connected to ${netDescription} by a PCB trace.`,
503
+ error_type: "pcb_port_not_connected_error",
504
+ pcb_port_ids: [pcbPort.pcb_port_id],
505
+ pcb_component_ids: pcbPort.pcb_component_id ? [pcbPort.pcb_component_id] : [],
506
+ pcb_port_not_connected_error_id: `pcb_port_not_connected_error_trace_${sourceTrace.source_trace_id}`
507
+ });
508
+ }
509
+ continue;
510
+ }
481
511
  if (connectedSourcePortIds.length < 2) {
482
512
  continue;
483
513
  }
@@ -2552,7 +2582,10 @@ import {
2552
2582
  getReadableNameForPcbPort as getReadableNameForPcbPort2,
2553
2583
  getReadableNameForPcbTrace
2554
2584
  } from "@tscircuit/circuit-json-util";
2555
- import { PcbConnectivityMap } from "circuit-json-to-connectivity-map";
2585
+ import { PcbConnectivityMap as PcbConnectivityMap2 } from "circuit-json-to-connectivity-map";
2586
+ function routePointTouchesPad(point, pad) {
2587
+ return point.route_type === "wire" && getLayersOfPcbElement(pad).includes(point.layer) && isPointInPad(point, pad);
2588
+ }
2556
2589
  function getRoutePointCenter(point) {
2557
2590
  if (point.route_type === "through_pad") {
2558
2591
  return {
@@ -2563,15 +2596,12 @@ function getRoutePointCenter(point) {
2563
2596
  return { x: point.x, y: point.y };
2564
2597
  }
2565
2598
  function routePointConnectsToAnotherExpectedPort(point, expectedPorts, missingPcbPortId, padMap) {
2566
- if (point.route_type !== "wire") return false;
2567
2599
  return expectedPorts.some((expectedPort) => {
2568
2600
  if (!expectedPort.pcb_port_id || expectedPort.pcb_port_id === missingPcbPortId) {
2569
2601
  return false;
2570
2602
  }
2571
2603
  const expectedPads = padMap.get(expectedPort.pcb_port_id);
2572
- return expectedPads?.some(
2573
- (pad) => isPointInPad({ x: point.x, y: point.y }, pad)
2574
- ) ?? false;
2604
+ return expectedPads?.some((pad) => routePointTouchesPad(point, pad)) ?? false;
2575
2605
  });
2576
2606
  }
2577
2607
  function getMissingConnectionErrorCenter({
@@ -2640,7 +2670,7 @@ function checkTracesAreContiguous(circuitJson) {
2640
2670
  (el) => el.type === "pcb_plated_hole"
2641
2671
  );
2642
2672
  const padMap = /* @__PURE__ */ new Map();
2643
- const pcbConnectivityMap = new PcbConnectivityMap(circuitJson);
2673
+ const pcbConnectivityMap = new PcbConnectivityMap2(circuitJson);
2644
2674
  const checkedSourceTraceIds = /* @__PURE__ */ new Set();
2645
2675
  for (const pad of pcbSmtPads) {
2646
2676
  if (pad.pcb_port_id) {
@@ -2655,6 +2685,65 @@ function checkTracesAreContiguous(circuitJson) {
2655
2685
  ]);
2656
2686
  }
2657
2687
  }
2688
+ const touchedPortIdsByTraceId = /* @__PURE__ */ new Map();
2689
+ const traceIdsByTouchedPortId = /* @__PURE__ */ new Map();
2690
+ for (const trace of pcbTraces) {
2691
+ const touchedPortIds = /* @__PURE__ */ new Set();
2692
+ const firstPoint = trace.route[0];
2693
+ const lastPoint = trace.route.at(-1);
2694
+ for (const point of [firstPoint, lastPoint]) {
2695
+ if (!point) continue;
2696
+ for (const [pcbPortId, pads] of padMap) {
2697
+ if (pads.some((pad) => routePointTouchesPad(point, pad))) {
2698
+ touchedPortIds.add(pcbPortId);
2699
+ }
2700
+ }
2701
+ }
2702
+ touchedPortIdsByTraceId.set(trace.pcb_trace_id, touchedPortIds);
2703
+ for (const pcbPortId of touchedPortIds) {
2704
+ const traceIds = traceIdsByTouchedPortId.get(pcbPortId) ?? /* @__PURE__ */ new Set();
2705
+ traceIds.add(trace.pcb_trace_id);
2706
+ traceIdsByTouchedPortId.set(pcbPortId, traceIds);
2707
+ }
2708
+ }
2709
+ const physicallyConnectedTracesByTraceId = /* @__PURE__ */ new Map();
2710
+ const getPhysicallyConnectedTraces = (startTrace) => {
2711
+ const cached = physicallyConnectedTracesByTraceId.get(
2712
+ startTrace.pcb_trace_id
2713
+ );
2714
+ if (cached) return cached;
2715
+ const connectedTraceIds = /* @__PURE__ */ new Set();
2716
+ const pendingTraceIds = [startTrace.pcb_trace_id];
2717
+ while (pendingTraceIds.length > 0) {
2718
+ const traceId = pendingTraceIds.pop();
2719
+ if (connectedTraceIds.has(traceId)) continue;
2720
+ connectedTraceIds.add(traceId);
2721
+ for (const connectedTrace of pcbConnectivityMap.getAllTracesConnectedToTrace(
2722
+ traceId
2723
+ )) {
2724
+ if (!connectedTraceIds.has(connectedTrace.pcb_trace_id)) {
2725
+ pendingTraceIds.push(connectedTrace.pcb_trace_id);
2726
+ }
2727
+ }
2728
+ for (const pcbPortId of touchedPortIdsByTraceId.get(traceId) ?? []) {
2729
+ for (const touchingTraceId of traceIdsByTouchedPortId.get(pcbPortId) ?? []) {
2730
+ if (!connectedTraceIds.has(touchingTraceId)) {
2731
+ pendingTraceIds.push(touchingTraceId);
2732
+ }
2733
+ }
2734
+ }
2735
+ }
2736
+ const connectedTraces = pcbTraces.filter(
2737
+ (trace) => connectedTraceIds.has(trace.pcb_trace_id)
2738
+ );
2739
+ for (const trace of connectedTraces) {
2740
+ physicallyConnectedTracesByTraceId.set(
2741
+ trace.pcb_trace_id,
2742
+ connectedTraces
2743
+ );
2744
+ }
2745
+ return connectedTraces;
2746
+ };
2658
2747
  for (const trace of pcbTraces) {
2659
2748
  if (trace.route.length === 0) continue;
2660
2749
  const firstPoint = trace.route[0];
@@ -2706,39 +2795,17 @@ function checkTracesAreContiguous(circuitJson) {
2706
2795
  if (!port.pcb_port_id) continue;
2707
2796
  const pads = padMap.get(port.pcb_port_id);
2708
2797
  if (!pads?.length) continue;
2709
- let isConnectedByRoutedSourceTrace = false;
2710
- const candidateTraces = [
2711
- ...pcbConnectivityMap.getAllTracesConnectedToTrace(trace.pcb_trace_id),
2712
- ...pcbTraces.filter(
2713
- (candidateTrace) => candidateTrace.source_trace_id === sourceTrace?.source_trace_id
2714
- )
2715
- ];
2716
- for (const candidateTrace of candidateTraces) {
2717
- if (candidateTrace.pcb_trace_id === trace.pcb_trace_id) continue;
2718
- if (!candidateTrace.source_trace_id) continue;
2719
- const candidateSourceTrace = sourceTraces.find(
2720
- (st) => st.source_trace_id === candidateTrace.source_trace_id
2721
- );
2722
- if (!sourceTrace || candidateTrace.source_trace_id !== sourceTrace.source_trace_id && !candidateSourceTrace?.connected_source_port_ids.includes(
2723
- port.source_port_id
2724
- )) {
2725
- continue;
2726
- }
2727
- const candidateFirstPoint = candidateTrace.route[0];
2728
- const candidateLastPoint = candidateTrace.route.at(-1);
2729
- if (getPcbPortIdsConnectedToTrace(candidateTrace).includes(
2730
- port.pcb_port_id
2731
- ) || candidateFirstPoint?.route_type === "wire" && pads.some((pad) => isPointInPad(candidateFirstPoint, pad)) || candidateLastPoint?.route_type === "wire" && pads.some((pad) => isPointInPad(candidateLastPoint, pad))) {
2732
- isConnectedByRoutedSourceTrace = true;
2733
- break;
2734
- }
2735
- }
2798
+ const isConnectedByRoutedSourceTrace = getPhysicallyConnectedTraces(
2799
+ trace
2800
+ ).some(
2801
+ (candidateTrace) => touchedPortIdsByTraceId.get(candidateTrace.pcb_trace_id)?.has(port.pcb_port_id)
2802
+ );
2736
2803
  if (isConnectedByRoutedSourceTrace) continue;
2737
- const isFirstPointConnected = firstPoint.route_type === "wire" && pads.some(
2738
- (pad) => isPointInPad({ x: firstPoint.x, y: firstPoint.y }, pad)
2804
+ const isFirstPointConnected = pads.some(
2805
+ (pad) => routePointTouchesPad(firstPoint, pad)
2739
2806
  );
2740
- const isLastPointConnected = lastPoint.route_type === "wire" && pads.some(
2741
- (pad) => isPointInPad({ x: lastPoint.x, y: lastPoint.y }, pad)
2807
+ const isLastPointConnected = pads.some(
2808
+ (pad) => routePointTouchesPad(lastPoint, pad)
2742
2809
  );
2743
2810
  if (!isFirstPointConnected && !isLastPointConnected) {
2744
2811
  const portName = getReadableNameForPcbPort2(
@@ -2771,14 +2838,10 @@ function checkTracesAreContiguous(circuitJson) {
2771
2838
  let lastConnectsToAnyPad = false;
2772
2839
  const endpointsAreSame = firstPoint.route_type === "wire" && lastPoint.route_type === "wire" && Math.abs(firstPoint.x - lastPoint.x) < 0.01 && Math.abs(firstPoint.y - lastPoint.y) < 0.01;
2773
2840
  for (const pads of padMap.values()) {
2774
- if (firstPoint.route_type === "wire" && pads.some(
2775
- (pad) => isPointInPad({ x: firstPoint.x, y: firstPoint.y }, pad)
2776
- )) {
2841
+ if (pads.some((pad) => routePointTouchesPad(firstPoint, pad))) {
2777
2842
  firstConnectsToAnyPad = true;
2778
2843
  }
2779
- if (lastPoint.route_type === "wire" && pads.some(
2780
- (pad) => isPointInPad({ x: lastPoint.x, y: lastPoint.y }, pad)
2781
- )) {
2844
+ if (pads.some((pad) => routePointTouchesPad(lastPoint, pad))) {
2782
2845
  lastConnectsToAnyPad = true;
2783
2846
  }
2784
2847
  }