@tscircuit/checks 0.0.187 → 0.0.189
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/README.md +20 -0
- package/dist/index.d.ts +25 -7
- package/dist/index.js +145 -30
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -44,6 +44,26 @@ and output an array of arrays for any issues found.
|
|
|
44
44
|
| [`runAllRoutingChecks`](./lib/run-all-checks.ts) | Runs all routing checks currently enabled (`checkEachPcbPortConnectedToPcbTraces`, `checkSourceTracesHavePcbTraces`, `checkEachPcbTraceNonOverlapping`, `checkPadTraceClearance`, `checkViaTraceClearance`, same/different net via spacing, and `checkPcbTracesOutOfBoard`). Trace-obstacle pairs are classified before aggregation, so each pair produces one overlap or clearance diagnostic, never both. |
|
|
45
45
|
| [`runAllChecks`](./lib/run-all-checks.ts) | Runs placement, schematic, netlist, pin specification, and routing checks and returns a combined list of issues. |
|
|
46
46
|
|
|
47
|
+
## Consolidated placement overlaps
|
|
48
|
+
|
|
49
|
+
`runAllPlacementChecks` and `runAllChecks` report one placement conflict per
|
|
50
|
+
component pair when footprint overlap causes multiple footprint, pad clearance,
|
|
51
|
+
and courtyard diagnostics. The message names the components, counts the conflicts,
|
|
52
|
+
and suggests moving them apart. Separate component pairs, clearance-only issues,
|
|
53
|
+
standalone elements, and unrelated routing diagnostics remain separate.
|
|
54
|
+
|
|
55
|
+
The result uses the existing `pcb_footprint_overlap_error` type and retains the
|
|
56
|
+
union of affected pad and hole IDs for rendering. The exported
|
|
57
|
+
`PcbComponentOverlapError` interface adds `pcb_component_ids` and `related_errors`
|
|
58
|
+
with the original diagnostics, including measured clearances. These extra context
|
|
59
|
+
fields are provided by checks; older Circuit JSON schema parsers may strip them.
|
|
60
|
+
|
|
61
|
+
Individual checks still return detailed diagnostics. Use
|
|
62
|
+
`runAllPlacementChecks(circuitJson, { consolidateOverlaps: false })` to obtain raw
|
|
63
|
+
aggregate results, for example to apply exclusions before calling
|
|
64
|
+
`consolidatePcbOverlapErrors(circuitJson, errors)`. Consolidation does not mutate
|
|
65
|
+
its inputs and can be applied again when combining runners.
|
|
66
|
+
|
|
47
67
|
## Implementation Details
|
|
48
68
|
|
|
49
69
|
> [!NOTE]
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as circuit_json from 'circuit-json';
|
|
2
|
-
import { AnyCircuitElement, PcbPortNotConnectedError, PcbTraceError, PcbPlacementError, PcbComponentOutsideBoardError, PcbViaClearanceError, PcbTraceWarning, PcbTraceMissingError, PcbFootprintOverlapError, PcbComponentMissingCourtyardWarning, PcbTraceTooLongWarning,
|
|
2
|
+
import { AnyCircuitElement, PcbPortNotConnectedError, PcbTraceError, PcbPlacementError, PcbComponentOutsideBoardError, PcbViaClearanceError, PcbTraceWarning, PcbTraceMissingError, PcbFootprintOverlapError, PcbPadPadClearanceError, PcbCourtyardOverlapError, PcbComponentMissingCourtyardWarning, PcbTraceTooLongWarning, PcbPadTraceClearanceError, PcbViaTraceClearanceError, SourceComponentMisconfiguredError, SourceComponentPinsUnderspecifiedWarning, SourceNoPowerPinDefinedWarning, SourceNoGroundPinDefinedWarning, SchematicComponentStylingWarning, PcbConnectorNotInAccessibleOrientationWarning, SourceConfusingNetNameWarning } from 'circuit-json';
|
|
3
3
|
import { ConnectivityMap } from 'circuit-json-to-connectivity-map';
|
|
4
4
|
|
|
5
5
|
declare function checkEachPcbPortConnectedToPcbTraces(circuitJson: AnyCircuitElement[]): PcbPortNotConnectedError[];
|
|
@@ -77,11 +77,27 @@ interface TraceBoardCheckConfig {
|
|
|
77
77
|
*/
|
|
78
78
|
declare function checkPcbTracesOutOfBoard(circuitJson: AnyCircuitElement[], config?: TraceBoardCheckConfig): PcbTraceError[];
|
|
79
79
|
|
|
80
|
+
/** Additional diagnostic context supplied by checks; the base error remains
|
|
81
|
+
* compatible with existing Circuit JSON renderers. */
|
|
82
|
+
interface PcbComponentOverlapError extends PcbFootprintOverlapError {
|
|
83
|
+
pcb_component_ids?: string[];
|
|
84
|
+
related_errors?: PlacementOverlapError[];
|
|
85
|
+
}
|
|
86
|
+
type PlacementOverlapError = PcbComponentOverlapError | PcbPadPadClearanceError | PcbCourtyardOverlapError;
|
|
87
|
+
/**
|
|
88
|
+
* Consolidate placement conflicts for the same exact pair of components, only
|
|
89
|
+
* when a footprint overlap was detected. Keep raw details and all affected
|
|
90
|
+
* pad/hole IDs on one renderer-compatible error. Unrelated checks, standalone
|
|
91
|
+
* elements, same-component clearance errors and clearance-only pairs survive.
|
|
92
|
+
* Does not mutate inputs; safe to apply again when combining check runners.
|
|
93
|
+
*/
|
|
94
|
+
declare function consolidatePcbOverlapErrors<T extends AnyCircuitElement>(circuitJson: AnyCircuitElement[], errors: T[]): (T | PcbComponentOverlapError)[];
|
|
95
|
+
|
|
80
96
|
/**
|
|
81
97
|
* Check for overlapping PCB components
|
|
82
98
|
* Returns errors for components that overlap inappropriately
|
|
83
99
|
*/
|
|
84
|
-
declare function checkPcbComponentOverlap(circuitJson: AnyCircuitElement[]):
|
|
100
|
+
declare function checkPcbComponentOverlap(circuitJson: AnyCircuitElement[]): PcbComponentOverlapError[];
|
|
85
101
|
|
|
86
102
|
/** Returns a warning for every PCB component without a courtyard. */
|
|
87
103
|
declare function checkPcbComponentsMissingCourtyard(circuitJson: AnyCircuitElement[]): PcbComponentMissingCourtyardWarning[];
|
|
@@ -183,7 +199,9 @@ declare function checkSchematicComponentMissingReferenceDesignatorText(circuitJs
|
|
|
183
199
|
*/
|
|
184
200
|
declare function checkSchematicComponentPortsOutsideBody(circuitJson: AnyCircuitElement[]): SchematicComponentStylingWarning[];
|
|
185
201
|
|
|
186
|
-
declare function runAllPlacementChecks(circuitJson: AnyCircuitElement[]
|
|
202
|
+
declare function runAllPlacementChecks(circuitJson: AnyCircuitElement[], { consolidateOverlaps }?: {
|
|
203
|
+
consolidateOverlaps?: boolean | undefined;
|
|
204
|
+
}): Promise<(circuit_json.PcbPlacementError | circuit_json.PcbComponentOutsideBoardError | PcbComponentOverlapError | circuit_json.PcbPadPadClearanceError | circuit_json.PcbCourtyardOverlapError | circuit_json.PcbComponentMissingCourtyardWarning | circuit_json.PcbConnectorNotInAccessibleOrientationWarning)[]>;
|
|
187
205
|
declare function runAllNetlistChecks(circuitJson: AnyCircuitElement[]): Promise<({
|
|
188
206
|
type: "source_pin_must_be_connected_error";
|
|
189
207
|
source_pin_must_be_connected_error_id: string;
|
|
@@ -195,8 +213,8 @@ declare function runAllNetlistChecks(circuitJson: AnyCircuitElement[]): Promise<
|
|
|
195
213
|
} | circuit_json.SourceComponentMisconfiguredError | circuit_json.SourceConfusingNetNameWarning)[]>;
|
|
196
214
|
declare function runAllSchematicChecks(circuitJson: AnyCircuitElement[]): Promise<circuit_json.SchematicComponentStylingWarning[]>;
|
|
197
215
|
declare function runAllPinSpecificationChecks(circuitJson: AnyCircuitElement[]): Promise<(circuit_json.SourceComponentPinsUnderspecifiedWarning | circuit_json.SourceNoPowerPinDefinedWarning | circuit_json.SourceNoGroundPinDefinedWarning)[]>;
|
|
198
|
-
declare function runAllRoutingChecks(circuitJson: AnyCircuitElement[]): Promise<(circuit_json.PcbPortNotConnectedError | circuit_json.PcbTraceError | circuit_json.PcbViaClearanceError | circuit_json.PcbTraceMissingError | circuit_json.
|
|
199
|
-
declare function runAllChecks(circuitJson: AnyCircuitElement[]): Promise<(circuit_json.PcbPortNotConnectedError | circuit_json.PcbTraceError | circuit_json.PcbPlacementError | circuit_json.PcbComponentOutsideBoardError | circuit_json.PcbViaClearanceError | circuit_json.PcbTraceMissingError | circuit_json.
|
|
216
|
+
declare function runAllRoutingChecks(circuitJson: AnyCircuitElement[]): Promise<(circuit_json.PcbPortNotConnectedError | circuit_json.PcbTraceError | circuit_json.PcbViaClearanceError | circuit_json.PcbTraceMissingError | circuit_json.PcbPadPadClearanceError | circuit_json.PcbTraceTooLongWarning | circuit_json.PcbPadTraceClearanceError | circuit_json.PcbViaTraceClearanceError)[]>;
|
|
217
|
+
declare function runAllChecks(circuitJson: AnyCircuitElement[]): Promise<(circuit_json.PcbPortNotConnectedError | circuit_json.PcbTraceError | circuit_json.PcbPlacementError | circuit_json.PcbComponentOutsideBoardError | circuit_json.PcbViaClearanceError | circuit_json.PcbTraceMissingError | PcbComponentOverlapError | circuit_json.PcbPadPadClearanceError | circuit_json.PcbCourtyardOverlapError | circuit_json.PcbComponentMissingCourtyardWarning | circuit_json.PcbTraceTooLongWarning | circuit_json.PcbPadTraceClearanceError | circuit_json.PcbViaTraceClearanceError | {
|
|
200
218
|
type: "source_pin_must_be_connected_error";
|
|
201
219
|
source_pin_must_be_connected_error_id: string;
|
|
202
220
|
error_type: "source_pin_must_be_connected_error";
|
|
@@ -204,7 +222,7 @@ declare function runAllChecks(circuitJson: AnyCircuitElement[]): Promise<(circui
|
|
|
204
222
|
source_component_id: string;
|
|
205
223
|
source_port_id: string;
|
|
206
224
|
subcircuit_id?: string;
|
|
207
|
-
} | circuit_json.SourceComponentMisconfiguredError | circuit_json.SourceComponentPinsUnderspecifiedWarning | circuit_json.SourceNoPowerPinDefinedWarning | circuit_json.SourceNoGroundPinDefinedWarning | circuit_json.SchematicComponentStylingWarning | circuit_json.SourceConfusingNetNameWarning | circuit_json.PcbConnectorNotInAccessibleOrientationWarning
|
|
225
|
+
} | circuit_json.SourceComponentMisconfiguredError | circuit_json.SourceComponentPinsUnderspecifiedWarning | circuit_json.SourceNoPowerPinDefinedWarning | circuit_json.SourceNoGroundPinDefinedWarning | circuit_json.SchematicComponentStylingWarning | circuit_json.SourceConfusingNetNameWarning | circuit_json.PcbConnectorNotInAccessibleOrientationWarning)[]>;
|
|
208
226
|
|
|
209
227
|
declare function checkConnectorAccessibleOrientation(circuitJson: AnyCircuitElement[]): PcbConnectorNotInAccessibleOrientationWarning[];
|
|
210
228
|
|
|
@@ -218,4 +236,4 @@ declare function checkTestPointAccessibility(circuitJson: AnyCircuitElement[]):
|
|
|
218
236
|
/** Warn once per name when its source nets belong to multiple electrical islands. */
|
|
219
237
|
declare function checkSameNameNetsAreConnected(circuitJson: AnyCircuitElement[]): SourceConfusingNetNameWarning[];
|
|
220
238
|
|
|
221
|
-
export { NetManager, checkAllPinsInComponentAreUnderspecified, checkConnectorAccessibleOrientation, checkCopperToBoardEdgeClearance, checkDifferentNetViaSpacing, checkEachPcbPortConnectedToPcbTraces, checkEachPcbTraceNonOverlapping, checkNoGroundPinDefined, checkNoPowerPinDefined, checkPadPadClearance, checkPadTraceClearance, checkPcbComponentOverCutout, checkPcbComponentOverlap, checkPcbComponentsMissingCourtyard, checkPcbComponentsOutOfBoard, checkPcbCopperOverKeepout, checkPcbTraceLengths, checkPcbTraceViaCounts, checkPcbTracesOutOfBoard, checkPinMustBeConnected, checkSameNameNetsAreConnected, checkSameNetViaSpacing, checkSchematicComponentExcessiveVerticalPadding, checkSchematicComponentMissingReferenceDesignatorText, checkSchematicComponentPortsOutsideBody, checkSourceTracesHavePcbTraces, checkSourceTracesMatchPcbTraceThickness, checkTestPointAccessibility, checkTracesAreContiguous, checkTwoTerminalSwitchContactsOnDifferentNets, checkViaPadClearance, checkViaTraceClearance, checkViasInPads, checkViasOffBoard, dedupePcbDrcErrors, runAllChecks, runAllNetlistChecks, runAllPinSpecificationChecks, runAllPlacementChecks, runAllRoutingChecks, runAllSchematicChecks };
|
|
239
|
+
export { NetManager, type PcbComponentOverlapError, checkAllPinsInComponentAreUnderspecified, checkConnectorAccessibleOrientation, checkCopperToBoardEdgeClearance, checkDifferentNetViaSpacing, checkEachPcbPortConnectedToPcbTraces, checkEachPcbTraceNonOverlapping, checkNoGroundPinDefined, checkNoPowerPinDefined, checkPadPadClearance, checkPadTraceClearance, checkPcbComponentOverCutout, checkPcbComponentOverlap, checkPcbComponentsMissingCourtyard, checkPcbComponentsOutOfBoard, checkPcbCopperOverKeepout, checkPcbTraceLengths, checkPcbTraceViaCounts, checkPcbTracesOutOfBoard, checkPinMustBeConnected, checkSameNameNetsAreConnected, checkSameNetViaSpacing, checkSchematicComponentExcessiveVerticalPadding, checkSchematicComponentMissingReferenceDesignatorText, checkSchematicComponentPortsOutsideBody, checkSourceTracesHavePcbTraces, checkSourceTracesMatchPcbTraceThickness, checkTestPointAccessibility, checkTracesAreContiguous, checkTwoTerminalSwitchContactsOnDifferentNets, checkViaPadClearance, checkViaTraceClearance, checkViasInPads, checkViasOffBoard, consolidatePcbOverlapErrors, dedupePcbDrcErrors, runAllChecks, runAllNetlistChecks, runAllPinSpecificationChecks, runAllPlacementChecks, runAllRoutingChecks, runAllSchematicChecks };
|
package/dist/index.js
CHANGED
|
@@ -799,23 +799,6 @@ var getTraceSegments = (circuitJson) => {
|
|
|
799
799
|
return segments;
|
|
800
800
|
});
|
|
801
801
|
};
|
|
802
|
-
var getTraceCenter = (segment) => {
|
|
803
|
-
const routePoints = segment._pcbTrace.route.flatMap((routePoint) => {
|
|
804
|
-
if (routePoint.route_type === "through_pad") {
|
|
805
|
-
return [routePoint.start, routePoint.end];
|
|
806
|
-
}
|
|
807
|
-
return [{ x: routePoint.x, y: routePoint.y }];
|
|
808
|
-
});
|
|
809
|
-
const firstPoint = routePoints[0];
|
|
810
|
-
const lastPoint = routePoints[routePoints.length - 1];
|
|
811
|
-
if (!firstPoint || !lastPoint) {
|
|
812
|
-
return midpoint(
|
|
813
|
-
{ x: segment.x1, y: segment.y1 },
|
|
814
|
-
{ x: segment.x2, y: segment.y2 }
|
|
815
|
-
);
|
|
816
|
-
}
|
|
817
|
-
return midpoint(firstPoint, lastPoint);
|
|
818
|
-
};
|
|
819
802
|
var getCenterBetweenCopperEdges = ({
|
|
820
803
|
tracePoint,
|
|
821
804
|
obstaclePoint,
|
|
@@ -828,6 +811,15 @@ var getCenterBetweenCopperEdges = ({
|
|
|
828
811
|
if (distance3 === 0) return midpoint(tracePoint, obstaclePoint);
|
|
829
812
|
const unitX = dx / distance3;
|
|
830
813
|
const unitY = dy / distance3;
|
|
814
|
+
if (distance3 <= traceRadius + obstacleRadius) {
|
|
815
|
+
const overlapStart = Math.max(-traceRadius, distance3 - obstacleRadius);
|
|
816
|
+
const overlapEnd = Math.min(traceRadius, distance3 + obstacleRadius);
|
|
817
|
+
const offset = (overlapStart + overlapEnd) / 2;
|
|
818
|
+
return {
|
|
819
|
+
x: tracePoint.x + unitX * offset,
|
|
820
|
+
y: tracePoint.y + unitY * offset
|
|
821
|
+
};
|
|
822
|
+
}
|
|
831
823
|
const traceEdge = {
|
|
832
824
|
x: tracePoint.x + unitX * traceRadius,
|
|
833
825
|
y: tracePoint.y + unitY * traceRadius
|
|
@@ -887,6 +879,16 @@ var getTraceObstacleClearance = (segment, obstacle) => {
|
|
|
887
879
|
};
|
|
888
880
|
};
|
|
889
881
|
var isTraceObstacleOverlap = (gap) => gap <= 0;
|
|
882
|
+
var getViaPadClearanceCenter = (via, pad) => getTraceObstacleClearance(
|
|
883
|
+
{
|
|
884
|
+
x1: via.x,
|
|
885
|
+
y1: via.y,
|
|
886
|
+
x2: via.x,
|
|
887
|
+
y2: via.y,
|
|
888
|
+
thickness: via.outer_diameter
|
|
889
|
+
},
|
|
890
|
+
pad
|
|
891
|
+
).center;
|
|
890
892
|
|
|
891
893
|
// lib/data-structures/SpatialIndex.ts
|
|
892
894
|
var SpatialObjectIndex = class {
|
|
@@ -3508,7 +3510,11 @@ function checkPcbComponentOverlap(circuitJson) {
|
|
|
3508
3510
|
type: "pcb_footprint_overlap_error",
|
|
3509
3511
|
pcb_error_id: `pcb_footprint_overlap_${id1}_${id2}`,
|
|
3510
3512
|
error_type: "pcb_footprint_overlap_error",
|
|
3511
|
-
message: `${elem1.type} ${elem1Description} overlaps with ${elem2.type} ${elem2Description}
|
|
3513
|
+
message: `${elem1.type} ${elem1Description} overlaps with ${elem2.type} ${elem2Description}`,
|
|
3514
|
+
pcb_component_ids: [
|
|
3515
|
+
elem1.pcb_component_id,
|
|
3516
|
+
elem2.pcb_component_id
|
|
3517
|
+
].filter((id) => Boolean(id))
|
|
3512
3518
|
};
|
|
3513
3519
|
if (elem1.type === "pcb_smtpad" || elem2.type === "pcb_smtpad") {
|
|
3514
3520
|
error.pcb_smtpad_ids = [];
|
|
@@ -3840,7 +3846,7 @@ function checkPadTraceClearance(circuitJson, {
|
|
|
3840
3846
|
if (!getLayersOfPcbElement(pad).includes(segment.layer)) continue;
|
|
3841
3847
|
if (connMap.areIdsConnected(segment.pcb_trace_id, padId)) continue;
|
|
3842
3848
|
const pairId = `${padId}_${segment.pcb_trace_id}`;
|
|
3843
|
-
const { gap } = getTraceObstacleClearance(segment, pad);
|
|
3849
|
+
const { gap, center } = getTraceObstacleClearance(segment, pad);
|
|
3844
3850
|
if (isTraceObstacleOverlap(gap)) {
|
|
3845
3851
|
errors.delete(pairId);
|
|
3846
3852
|
overlappingPairIds.add(pairId);
|
|
@@ -3857,7 +3863,7 @@ function checkPadTraceClearance(circuitJson, {
|
|
|
3857
3863
|
pcb_trace_id: segment.pcb_trace_id,
|
|
3858
3864
|
minimum_clearance: minClearance,
|
|
3859
3865
|
actual_clearance: gap,
|
|
3860
|
-
center
|
|
3866
|
+
center
|
|
3861
3867
|
};
|
|
3862
3868
|
const current = errors.get(pairId);
|
|
3863
3869
|
if (!current || gap < current.gap) {
|
|
@@ -3892,7 +3898,7 @@ function checkViaTraceClearance(circuitJson, {
|
|
|
3892
3898
|
if (connMap.areIdsConnected(segment.pcb_trace_id, via.pcb_via_id))
|
|
3893
3899
|
continue;
|
|
3894
3900
|
const pairId = `${via.pcb_via_id}_${segment.pcb_trace_id}`;
|
|
3895
|
-
const { gap } = getTraceObstacleClearance(segment, via);
|
|
3901
|
+
const { gap, center } = getTraceObstacleClearance(segment, via);
|
|
3896
3902
|
if (isTraceObstacleOverlap(gap)) {
|
|
3897
3903
|
errors.delete(pairId);
|
|
3898
3904
|
overlappingPairIds.add(pairId);
|
|
@@ -3909,7 +3915,7 @@ function checkViaTraceClearance(circuitJson, {
|
|
|
3909
3915
|
pcb_trace_id: segment.pcb_trace_id,
|
|
3910
3916
|
minimum_clearance: minClearance,
|
|
3911
3917
|
actual_clearance: gap,
|
|
3912
|
-
center
|
|
3918
|
+
center
|
|
3913
3919
|
};
|
|
3914
3920
|
const current = errors.get(pairId);
|
|
3915
3921
|
if (!current || gap < current.gap) {
|
|
@@ -3962,8 +3968,6 @@ function checkViaPadClearance(circuitJson, {
|
|
|
3962
3968
|
if (connMap.areIdsConnected(via.pcb_via_id, padId)) continue;
|
|
3963
3969
|
const gap = getPadToPadGap(via, pad);
|
|
3964
3970
|
if (gap + EPSILON >= requiredClearance) continue;
|
|
3965
|
-
const viaCenter = getPadCenter(via);
|
|
3966
|
-
const padCenter = getPadCenter(pad);
|
|
3967
3971
|
errors.push({
|
|
3968
3972
|
type: "pcb_pad_pad_clearance_error",
|
|
3969
3973
|
pcb_pad_pad_clearance_error_id: `via_pad_clearance_${via.pcb_via_id}_${padId}`,
|
|
@@ -3972,10 +3976,7 @@ function checkViaPadClearance(circuitJson, {
|
|
|
3972
3976
|
pcb_pad_ids: [via.pcb_via_id, padId],
|
|
3973
3977
|
minimum_clearance: requiredClearance,
|
|
3974
3978
|
actual_clearance: gap,
|
|
3975
|
-
center:
|
|
3976
|
-
x: (viaCenter.x + padCenter.x) / 2,
|
|
3977
|
-
y: (viaCenter.y + padCenter.y) / 2
|
|
3978
|
-
}
|
|
3979
|
+
center: getViaPadClearanceCenter(via, pad)
|
|
3979
3980
|
});
|
|
3980
3981
|
}
|
|
3981
3982
|
}
|
|
@@ -4556,6 +4557,118 @@ function checkSchematicComponentPortsOutsideBody(circuitJson) {
|
|
|
4556
4557
|
return warnings;
|
|
4557
4558
|
}
|
|
4558
4559
|
|
|
4560
|
+
// lib/consolidate-pcb-overlap-errors.ts
|
|
4561
|
+
import { getPrimaryId as getPrimaryId9 } from "@tscircuit/circuit-json-util";
|
|
4562
|
+
function consolidatePcbOverlapErrors(circuitJson, errors) {
|
|
4563
|
+
const ownerByElementId = /* @__PURE__ */ new Map();
|
|
4564
|
+
const elementById = /* @__PURE__ */ new Map();
|
|
4565
|
+
for (const element of circuitJson) {
|
|
4566
|
+
const id = getPrimaryId9(element);
|
|
4567
|
+
elementById.set(id, element);
|
|
4568
|
+
if ("pcb_component_id" in element && element.pcb_component_id) {
|
|
4569
|
+
ownerByElementId.set(id, element.pcb_component_id);
|
|
4570
|
+
}
|
|
4571
|
+
}
|
|
4572
|
+
const rawErrors = errors.flatMap(
|
|
4573
|
+
(error) => error.type === "pcb_footprint_overlap_error" ? error.related_errors ?? [error] : [error]
|
|
4574
|
+
);
|
|
4575
|
+
const groups = /* @__PURE__ */ new Map();
|
|
4576
|
+
const keyByError = /* @__PURE__ */ new Map();
|
|
4577
|
+
for (const error of rawErrors) {
|
|
4578
|
+
let componentIds;
|
|
4579
|
+
if (error.type === "pcb_footprint_overlap_error") {
|
|
4580
|
+
if (error.pcb_keepout_ids?.length) continue;
|
|
4581
|
+
const overlap = error;
|
|
4582
|
+
const elementIds = [
|
|
4583
|
+
...error.pcb_smtpad_ids ?? [],
|
|
4584
|
+
...error.pcb_plated_hole_ids ?? [],
|
|
4585
|
+
...error.pcb_hole_ids ?? []
|
|
4586
|
+
];
|
|
4587
|
+
if (elementIds.some((id) => !ownerByElementId.has(id))) continue;
|
|
4588
|
+
componentIds = overlap.pcb_component_ids ?? elementIds.map((id) => ownerByElementId.get(id));
|
|
4589
|
+
} else if (error.type === "pcb_pad_pad_clearance_error") {
|
|
4590
|
+
if (error.pcb_pad_ids.some((id) => !ownerByElementId.has(id))) continue;
|
|
4591
|
+
componentIds = error.pcb_pad_ids.map((id) => ownerByElementId.get(id));
|
|
4592
|
+
} else if (error.type === "pcb_courtyard_overlap_error") {
|
|
4593
|
+
componentIds = error.pcb_component_ids;
|
|
4594
|
+
} else {
|
|
4595
|
+
continue;
|
|
4596
|
+
}
|
|
4597
|
+
componentIds = [...new Set(componentIds)].sort();
|
|
4598
|
+
if (componentIds.length !== 2) continue;
|
|
4599
|
+
const key = JSON.stringify(componentIds);
|
|
4600
|
+
keyByError.set(error, key);
|
|
4601
|
+
const group = groups.get(key) ?? [];
|
|
4602
|
+
group.push(error);
|
|
4603
|
+
groups.set(key, group);
|
|
4604
|
+
}
|
|
4605
|
+
const summaries = /* @__PURE__ */ new Map();
|
|
4606
|
+
for (const [key, group] of groups) {
|
|
4607
|
+
const overlaps = group.filter(
|
|
4608
|
+
(e) => e.type === "pcb_footprint_overlap_error"
|
|
4609
|
+
);
|
|
4610
|
+
if (group.length < 2 || overlaps.length === 0) continue;
|
|
4611
|
+
const componentIds = JSON.parse(key);
|
|
4612
|
+
const names = componentIds.map((id) => {
|
|
4613
|
+
const component = elementById.get(id);
|
|
4614
|
+
const source = component?.type === "pcb_component" ? elementById.get(component.source_component_id) : void 0;
|
|
4615
|
+
return source?.type === "source_component" ? source.name : id;
|
|
4616
|
+
});
|
|
4617
|
+
const counts = [
|
|
4618
|
+
[overlaps.length, "footprint overlap"],
|
|
4619
|
+
[
|
|
4620
|
+
group.filter((e) => e.type === "pcb_pad_pad_clearance_error").length,
|
|
4621
|
+
"pad clearance violation"
|
|
4622
|
+
],
|
|
4623
|
+
[
|
|
4624
|
+
group.filter((e) => e.type === "pcb_courtyard_overlap_error").length,
|
|
4625
|
+
"courtyard conflict"
|
|
4626
|
+
]
|
|
4627
|
+
];
|
|
4628
|
+
const details = counts.filter(([count]) => count > 0).map(([count, label]) => `${count} ${label}${count === 1 ? "" : "s"}`);
|
|
4629
|
+
const summary = {
|
|
4630
|
+
type: "pcb_footprint_overlap_error",
|
|
4631
|
+
error_type: "pcb_footprint_overlap_error",
|
|
4632
|
+
pcb_error_id: `pcb_component_overlap_${componentIds.join("_")}`,
|
|
4633
|
+
pcb_component_ids: componentIds,
|
|
4634
|
+
message: `${names.join(" overlaps ")}: ${details.join(", ")}. Move the components apart.`,
|
|
4635
|
+
related_errors: group
|
|
4636
|
+
};
|
|
4637
|
+
if (group.some((error) => error.is_fatal)) summary.is_fatal = true;
|
|
4638
|
+
const affectedIds = /* @__PURE__ */ new Set();
|
|
4639
|
+
for (const error of group) {
|
|
4640
|
+
if (error.type === "pcb_footprint_overlap_error") {
|
|
4641
|
+
for (const id of [
|
|
4642
|
+
...error.pcb_smtpad_ids ?? [],
|
|
4643
|
+
...error.pcb_plated_hole_ids ?? [],
|
|
4644
|
+
...error.pcb_hole_ids ?? []
|
|
4645
|
+
])
|
|
4646
|
+
affectedIds.add(id);
|
|
4647
|
+
} else if (error.type === "pcb_pad_pad_clearance_error") {
|
|
4648
|
+
for (const id of error.pcb_pad_ids) affectedIds.add(id);
|
|
4649
|
+
}
|
|
4650
|
+
}
|
|
4651
|
+
for (const [field, type] of [
|
|
4652
|
+
["pcb_smtpad_ids", "pcb_smtpad"],
|
|
4653
|
+
["pcb_plated_hole_ids", "pcb_plated_hole"],
|
|
4654
|
+
["pcb_hole_ids", "pcb_hole"]
|
|
4655
|
+
]) {
|
|
4656
|
+
const ids = [...affectedIds].filter((id) => elementById.get(id)?.type === type).sort();
|
|
4657
|
+
if (ids.length) summary[field] = ids;
|
|
4658
|
+
}
|
|
4659
|
+
summaries.set(key, summary);
|
|
4660
|
+
}
|
|
4661
|
+
const emitted = /* @__PURE__ */ new Set();
|
|
4662
|
+
return rawErrors.flatMap((error) => {
|
|
4663
|
+
const key = keyByError.get(error);
|
|
4664
|
+
const summary = key === void 0 ? void 0 : summaries.get(key);
|
|
4665
|
+
if (!summary || key === void 0) return [error];
|
|
4666
|
+
if (emitted.has(key)) return [];
|
|
4667
|
+
emitted.add(key);
|
|
4668
|
+
return [summary];
|
|
4669
|
+
});
|
|
4670
|
+
}
|
|
4671
|
+
|
|
4559
4672
|
// lib/check-same-name-nets-are-connected.ts
|
|
4560
4673
|
function checkSameNameNetsAreConnected(circuitJson) {
|
|
4561
4674
|
const parents = /* @__PURE__ */ new Map();
|
|
@@ -4884,8 +4997,8 @@ function checkTestPointAccessibility(circuitJson) {
|
|
|
4884
4997
|
}
|
|
4885
4998
|
|
|
4886
4999
|
// lib/run-all-checks.ts
|
|
4887
|
-
async function runAllPlacementChecks(circuitJson) {
|
|
4888
|
-
|
|
5000
|
+
async function runAllPlacementChecks(circuitJson, { consolidateOverlaps = true } = {}) {
|
|
5001
|
+
const errors = [
|
|
4889
5002
|
...checkCopperToBoardEdgeClearance(circuitJson),
|
|
4890
5003
|
...checkViasInPads(circuitJson),
|
|
4891
5004
|
...checkPcbComponentsOutOfBoard(circuitJson),
|
|
@@ -4898,6 +5011,7 @@ async function runAllPlacementChecks(circuitJson) {
|
|
|
4898
5011
|
...checkConnectorAccessibleOrientation(circuitJson),
|
|
4899
5012
|
...checkTestPointAccessibility(circuitJson)
|
|
4900
5013
|
];
|
|
5014
|
+
return consolidateOverlaps ? consolidatePcbOverlapErrors(circuitJson, errors) : errors;
|
|
4901
5015
|
}
|
|
4902
5016
|
async function runAllNetlistChecks(circuitJson) {
|
|
4903
5017
|
return [
|
|
@@ -4980,6 +5094,7 @@ export {
|
|
|
4980
5094
|
checkViaTraceClearance,
|
|
4981
5095
|
checkViasInPads,
|
|
4982
5096
|
checkViasOffBoard,
|
|
5097
|
+
consolidatePcbOverlapErrors,
|
|
4983
5098
|
dedupePcbDrcErrors,
|
|
4984
5099
|
runAllChecks,
|
|
4985
5100
|
runAllNetlistChecks,
|