@tscircuit/checks 0.0.198 → 0.0.200

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.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as circuit_json from 'circuit-json';
2
- import { AnyCircuitElement, PcbPlacementError, PcbPortNotConnectedError, PcbTraceError, PcbComponentOutsideBoardError, PcbViaClearanceError, PcbTraceWarning, PcbTraceMissingError, PcbFootprintOverlapError, PcbPadPadClearanceError, PcbCourtyardOverlapError, PcbComponentMissingCourtyardWarning, PcbTraceTooLongWarning, PcbPadTraceClearanceError, PcbViaTraceClearanceError, SourceComponentMisconfiguredError, SourceComponentPinsUnderspecifiedWarning, SourceNoPowerPinDefinedWarning, SourceNoGroundPinDefinedWarning, SchematicComponentStylingWarning, PcbConnectorNotInAccessibleOrientationWarning, SourceConfusingNetNameWarning } from 'circuit-json';
2
+ import { AnyCircuitElement, PcbPlacementError, PcbPortNotConnectedError, PcbTraceError, PcbComponentOutsideBoardError, PcbViaClearanceError, PcbTraceWarning, PcbTraceMissingError, PcbFootprintOverlapError, PcbPadPadClearanceError, PcbCourtyardOverlapError, PcbComponentMissingCourtyardWarning, PcbTraceTooLongError, PcbPadTraceClearanceError, PcbViaTraceClearanceError, SourceComponentMisconfiguredError, SourceComponentPinsUnderspecifiedWarning, SourceNoPowerPinDefinedWarning, SourceNoGroundPinDefinedWarning, SchematicComponentStylingWarning, PcbConnectorNotInAccessibleOrientationWarning, SourceConfusingNetNameWarning, PcbBusLengthSkewError } from 'circuit-json';
3
3
  import { ConnectivityMap } from 'circuit-json-to-connectivity-map';
4
4
 
5
5
  /** Detect accidental copper contact, rather than enforcing a clearance margin. */
@@ -105,7 +105,7 @@ declare function checkPcbComponentOverlap(circuitJson: AnyCircuitElement[]): Pcb
105
105
  /** Returns warnings for PCB components without courtyards, excluding manually placed vias. */
106
106
  declare function checkPcbComponentsMissingCourtyard(circuitJson: AnyCircuitElement[]): PcbComponentMissingCourtyardWarning[];
107
107
 
108
- declare const checkPcbTraceLengths: (circuitJson: AnyCircuitElement[]) => PcbTraceTooLongWarning[];
108
+ declare const checkPcbTraceLengths: (circuitJson: AnyCircuitElement[]) => PcbTraceTooLongError[];
109
109
 
110
110
  /** Return a routing error when a source trace exceeds its maximum via count. */
111
111
  declare const checkPcbTraceViaCounts: (circuitJson: AnyCircuitElement[]) => PcbTraceError[];
@@ -216,8 +216,8 @@ declare function runAllNetlistChecks(circuitJson: AnyCircuitElement[]): Promise<
216
216
  } | circuit_json.SourceComponentMisconfiguredError | circuit_json.SourceConfusingNetNameWarning)[]>;
217
217
  declare function runAllSchematicChecks(circuitJson: AnyCircuitElement[]): Promise<circuit_json.SchematicComponentStylingWarning[]>;
218
218
  declare function runAllPinSpecificationChecks(circuitJson: AnyCircuitElement[]): Promise<(circuit_json.SourceComponentPinsUnderspecifiedWarning | circuit_json.SourceNoPowerPinDefinedWarning | circuit_json.SourceNoGroundPinDefinedWarning)[]>;
219
- declare function runAllRoutingChecks(circuitJson: AnyCircuitElement[]): Promise<(circuit_json.PcbPlacementError | circuit_json.PcbPortNotConnectedError | circuit_json.PcbTraceError | circuit_json.PcbViaClearanceError | circuit_json.PcbTraceMissingError | circuit_json.PcbPadPadClearanceError | circuit_json.PcbTraceTooLongWarning | circuit_json.PcbPadTraceClearanceError | circuit_json.PcbViaTraceClearanceError)[]>;
220
- declare function runAllChecks(circuitJson: AnyCircuitElement[]): Promise<(circuit_json.PcbPlacementError | circuit_json.PcbPortNotConnectedError | circuit_json.PcbTraceError | 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 | {
219
+ declare function runAllRoutingChecks(circuitJson: AnyCircuitElement[]): Promise<(circuit_json.PcbPlacementError | circuit_json.PcbPortNotConnectedError | circuit_json.PcbTraceError | circuit_json.PcbViaClearanceError | circuit_json.PcbTraceMissingError | circuit_json.PcbPadPadClearanceError | circuit_json.PcbTraceTooLongError | circuit_json.PcbPadTraceClearanceError | circuit_json.PcbViaTraceClearanceError | circuit_json.PcbBusLengthSkewError)[]>;
220
+ declare function runAllChecks(circuitJson: AnyCircuitElement[]): Promise<(circuit_json.PcbPlacementError | circuit_json.PcbPortNotConnectedError | circuit_json.PcbTraceError | circuit_json.PcbComponentOutsideBoardError | circuit_json.PcbViaClearanceError | circuit_json.PcbTraceMissingError | PcbComponentOverlapError | circuit_json.PcbPadPadClearanceError | circuit_json.PcbCourtyardOverlapError | circuit_json.PcbComponentMissingCourtyardWarning | circuit_json.PcbTraceTooLongError | circuit_json.PcbPadTraceClearanceError | circuit_json.PcbViaTraceClearanceError | {
221
221
  type: "source_pin_must_be_connected_error";
222
222
  source_pin_must_be_connected_error_id: string;
223
223
  error_type: "source_pin_must_be_connected_error";
@@ -225,7 +225,7 @@ declare function runAllChecks(circuitJson: AnyCircuitElement[]): Promise<(circui
225
225
  source_component_id: string;
226
226
  source_port_id: string;
227
227
  subcircuit_id?: string;
228
- } | circuit_json.SourceComponentMisconfiguredError | circuit_json.SourceComponentPinsUnderspecifiedWarning | circuit_json.SourceNoPowerPinDefinedWarning | circuit_json.SourceNoGroundPinDefinedWarning | circuit_json.SchematicComponentStylingWarning | circuit_json.SourceConfusingNetNameWarning | circuit_json.PcbConnectorNotInAccessibleOrientationWarning)[]>;
228
+ } | circuit_json.SourceComponentMisconfiguredError | circuit_json.SourceComponentPinsUnderspecifiedWarning | circuit_json.SourceNoPowerPinDefinedWarning | circuit_json.SourceNoGroundPinDefinedWarning | circuit_json.SchematicComponentStylingWarning | circuit_json.PcbBusLengthSkewError | circuit_json.SourceConfusingNetNameWarning | circuit_json.PcbConnectorNotInAccessibleOrientationWarning)[]>;
229
229
 
230
230
  declare function checkConnectorAccessibleOrientation(circuitJson: AnyCircuitElement[]): PcbConnectorNotInAccessibleOrientationWarning[];
231
231
 
@@ -239,4 +239,9 @@ declare function checkTestPointAccessibility(circuitJson: AnyCircuitElement[]):
239
239
  /** Warn once per name when its source nets belong to multiple electrical islands. */
240
240
  declare function checkSameNameNetsAreConnected(circuitJson: AnyCircuitElement[]): SourceConfusingNetNameWarning[];
241
241
 
242
- export { NetManager, type PcbComponentOverlapError, checkAllPinsInComponentAreUnderspecified, checkConnectorAccessibleOrientation, checkCopperPourShorts, 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 };
242
+ /** Compare total routed lengths of resolved bus members. Unrouted members are
243
+ * left to connectivity checks, never treated as zero-length routes.
244
+ */
245
+ declare const checkPcbBusLengthSkew: (circuitJson: AnyCircuitElement[]) => PcbBusLengthSkewError[];
246
+
247
+ export { NetManager, type PcbComponentOverlapError, checkAllPinsInComponentAreUnderspecified, checkConnectorAccessibleOrientation, checkCopperPourShorts, checkCopperToBoardEdgeClearance, checkDifferentNetViaSpacing, checkEachPcbPortConnectedToPcbTraces, checkEachPcbTraceNonOverlapping, checkNoGroundPinDefined, checkNoPowerPinDefined, checkPadPadClearance, checkPadTraceClearance, checkPcbBusLengthSkew, 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
@@ -1239,7 +1239,7 @@ function getCopperPourConnectivity(circuitJson, connectivity) {
1239
1239
  polygon: getSmtPadPolygon(copper),
1240
1240
  layers: [copper.layer],
1241
1241
  netId,
1242
- portId: copper.pcb_port_id
1242
+ portIds: copper.pcb_port_id ? [copper.pcb_port_id] : []
1243
1243
  });
1244
1244
  } else if (copper.type === "pcb_plated_hole") {
1245
1245
  add({
@@ -1249,7 +1249,7 @@ function getCopperPourConnectivity(circuitJson, connectivity) {
1249
1249
  ),
1250
1250
  layers: copper.layers,
1251
1251
  netId,
1252
- portId: copper.pcb_port_id
1252
+ portIds: copper.pcb_port_id ? [copper.pcb_port_id] : []
1253
1253
  });
1254
1254
  } else {
1255
1255
  add({
@@ -1259,7 +1259,8 @@ function getCopperPourConnectivity(circuitJson, connectivity) {
1259
1259
  copper.hole_diameter
1260
1260
  ),
1261
1261
  layers: copper.layers,
1262
- netId
1262
+ netId,
1263
+ portIds: copper.pcb_port_ids
1263
1264
  });
1264
1265
  }
1265
1266
  }
@@ -1300,10 +1301,12 @@ function getCopperPourConnectivity(circuitJson, connectivity) {
1300
1301
  );
1301
1302
  const rootsByPort = /* @__PURE__ */ new Map();
1302
1303
  for (const [i, conductor] of conductors.entries()) {
1303
- if (!conductor.portId || !pourRoots.has(find(i))) continue;
1304
- const roots = rootsByPort.get(conductor.portId) ?? /* @__PURE__ */ new Set();
1305
- roots.add(find(i));
1306
- rootsByPort.set(conductor.portId, roots);
1304
+ if (!pourRoots.has(find(i))) continue;
1305
+ for (const portId of conductor.portIds ?? []) {
1306
+ const roots = rootsByPort.get(portId) ?? /* @__PURE__ */ new Set();
1307
+ roots.add(find(i));
1308
+ rootsByPort.set(portId, roots);
1309
+ }
1307
1310
  }
1308
1311
  const portsByNet = /* @__PURE__ */ new Map();
1309
1312
  for (const port of circuitJson) {
@@ -4134,30 +4137,30 @@ function checkPcbComponentsMissingCourtyard(circuitJson) {
4134
4137
  });
4135
4138
  }
4136
4139
 
4137
- // lib/check-pcb-trace-lengths.ts
4138
- var DEFAULT_VIA_LENGTH_MM = 1.6;
4139
- var getRoutePointPosition = (routePoint) => routePoint.route_type === "through_pad" ? routePoint.start : { x: routePoint.x, y: routePoint.y };
4140
- var getPcbTraceLength = (pcbTrace) => {
4141
- if (pcbTrace.trace_length !== void 0) return pcbTrace.trace_length;
4142
- let traceLength = 0;
4143
- for (let routePointIndex = 0; routePointIndex < pcbTrace.route.length; routePointIndex++) {
4144
- const routePoint = pcbTrace.route[routePointIndex];
4145
- if (!routePoint) continue;
4146
- if (routePoint.route_type === "via") {
4147
- traceLength += DEFAULT_VIA_LENGTH_MM;
4148
- continue;
4140
+ // lib/util/get-pcb-trace-length.ts
4141
+ var getPcbTraceLength = (trace) => {
4142
+ if (trace.trace_length !== void 0) return trace.trace_length;
4143
+ let length = 0;
4144
+ let previousEnd;
4145
+ for (const point2 of trace.route) {
4146
+ const start = point2.route_type === "through_pad" ? point2.start : point2;
4147
+ if (previousEnd)
4148
+ length += Math.hypot(start.x - previousEnd.x, start.y - previousEnd.y);
4149
+ if (point2.route_type === "via") length += 1.6;
4150
+ if (point2.route_type === "through_pad") {
4151
+ length += Math.hypot(
4152
+ point2.end.x - point2.start.x,
4153
+ point2.end.y - point2.start.y
4154
+ );
4155
+ previousEnd = point2.end;
4156
+ } else {
4157
+ previousEnd = point2;
4149
4158
  }
4150
- const nextRoutePoint = pcbTrace.route[routePointIndex + 1];
4151
- if (!nextRoutePoint) continue;
4152
- const routePointPosition = getRoutePointPosition(routePoint);
4153
- const nextRoutePointPosition = getRoutePointPosition(nextRoutePoint);
4154
- traceLength += Math.hypot(
4155
- nextRoutePointPosition.x - routePointPosition.x,
4156
- nextRoutePointPosition.y - routePointPosition.y
4157
- );
4158
4159
  }
4159
- return traceLength;
4160
+ return length;
4160
4161
  };
4162
+
4163
+ // lib/check-pcb-trace-lengths.ts
4161
4164
  var getReferencedPcbPortIds = (pcbTrace) => {
4162
4165
  const pcbPortIds = /* @__PURE__ */ new Set();
4163
4166
  for (const routePoint of pcbTrace.route) {
@@ -4171,7 +4174,7 @@ var getReferencedPcbPortIds = (pcbTrace) => {
4171
4174
  }
4172
4175
  return pcbPortIds;
4173
4176
  };
4174
- var checkPcbTraceLengths = (circuitJson) => {
4177
+ var getPcbTracesBySourceTraceId = (circuitJson) => {
4175
4178
  const sourceTraces = circuitJson.filter(
4176
4179
  (element) => element.type === "source_trace"
4177
4180
  );
@@ -4181,12 +4184,6 @@ var checkPcbTraceLengths = (circuitJson) => {
4181
4184
  const pcbPorts = circuitJson.filter(
4182
4185
  (element) => element.type === "pcb_port"
4183
4186
  );
4184
- const sourceTracesById = new Map(
4185
- sourceTraces.map((sourceTrace) => [
4186
- sourceTrace.source_trace_id,
4187
- sourceTrace
4188
- ])
4189
- );
4190
4187
  const pcbPortIdsBySourcePortId = /* @__PURE__ */ new Map();
4191
4188
  for (const pcbPort of pcbPorts) {
4192
4189
  if (!pcbPort.source_port_id) continue;
@@ -4225,23 +4222,35 @@ var checkPcbTraceLengths = (circuitJson) => {
4225
4222
  );
4226
4223
  }
4227
4224
  }
4228
- const warnings = [];
4229
- for (const pcbTrace of pcbTraces) {
4230
- if (!pcbTrace.source_trace_id) continue;
4231
- const sourceTrace = sourceTracesById.get(pcbTrace.source_trace_id);
4225
+ for (const [id, exactIds] of exactEndpointPcbTraceIdsBySourceTraceId) {
4226
+ pcbTracesBySourceTraceId.set(
4227
+ id,
4228
+ pcbTracesBySourceTraceId.get(id).filter((trace) => exactIds.has(trace.pcb_trace_id))
4229
+ );
4230
+ }
4231
+ return pcbTracesBySourceTraceId;
4232
+ };
4233
+ var checkPcbTraceLengths = (circuitJson) => {
4234
+ const sourceTracesById = new Map(
4235
+ circuitJson.filter((e) => e.type === "source_trace").map((e) => [e.source_trace_id, e])
4236
+ );
4237
+ const pcbTracesBySourceTraceId = getPcbTracesBySourceTraceId(circuitJson);
4238
+ const errors = [];
4239
+ for (const [sourceTraceId, pcbTraces] of pcbTracesBySourceTraceId) {
4240
+ const pcbTrace = pcbTraces[0];
4241
+ const sourceTrace = sourceTracesById.get(sourceTraceId);
4232
4242
  if (!sourceTrace) continue;
4233
4243
  const maximumTraceLength = sourceTrace.max_length;
4234
4244
  if (typeof maximumTraceLength !== "number") continue;
4235
- const exactEndpointPcbTraceIds = exactEndpointPcbTraceIdsBySourceTraceId.get(sourceTrace.source_trace_id);
4236
- if (exactEndpointPcbTraceIds && !exactEndpointPcbTraceIds.has(pcbTrace.pcb_trace_id)) {
4237
- continue;
4238
- }
4239
- const actualTraceLength = getPcbTraceLength(pcbTrace);
4240
- if (actualTraceLength <= maximumTraceLength) continue;
4241
- warnings.push({
4242
- type: "pcb_trace_too_long_warning",
4243
- pcb_trace_too_long_warning_id: `pcb_trace_too_long_warning_${pcbTrace.pcb_trace_id}`,
4244
- warning_type: "pcb_trace_too_long_warning",
4245
+ const actualTraceLength = pcbTraces.reduce(
4246
+ (sum, trace) => sum + getPcbTraceLength(trace),
4247
+ 0
4248
+ );
4249
+ if (actualTraceLength <= maximumTraceLength + 1e-9) continue;
4250
+ errors.push({
4251
+ type: "pcb_trace_too_long_error",
4252
+ pcb_trace_too_long_error_id: `pcb_trace_too_long_error_${pcbTrace.pcb_trace_id}`,
4253
+ error_type: "pcb_trace_too_long_error",
4245
4254
  message: `PCB trace is ${actualTraceLength.toFixed(2)}mm long, exceeding the ${maximumTraceLength}mm maximum`,
4246
4255
  pcb_trace_id: pcbTrace.pcb_trace_id,
4247
4256
  source_trace_id: sourceTrace.source_trace_id,
@@ -4251,7 +4260,7 @@ var checkPcbTraceLengths = (circuitJson) => {
4251
4260
  subcircuit_id: pcbTrace.subcircuit_id ?? sourceTrace.subcircuit_id
4252
4261
  });
4253
4262
  }
4254
- return warnings;
4263
+ return errors;
4255
4264
  };
4256
4265
 
4257
4266
  // lib/check-pcb-trace-via-counts.ts
@@ -5116,6 +5125,47 @@ function checkSchematicComponentPortsOutsideBody(circuitJson) {
5116
5125
  return warnings;
5117
5126
  }
5118
5127
 
5128
+ // lib/check-pcb-bus-length-skew.ts
5129
+ var checkPcbBusLengthSkew = (circuitJson) => {
5130
+ const buses = circuitJson.filter(
5131
+ (e) => e.type === "source_bus"
5132
+ );
5133
+ const tracesBySource = getPcbTracesBySourceTraceId(circuitJson);
5134
+ const errors = [];
5135
+ for (const bus of buses) {
5136
+ if (bus.max_length_skew === void 0) continue;
5137
+ const members = [...new Set(bus.source_trace_ids)].flatMap((id) => {
5138
+ const traces = tracesBySource.get(id)?.filter((t) => t.trace_length !== void 0 || t.route.length > 1);
5139
+ if (!traces?.length) return [];
5140
+ return [
5141
+ {
5142
+ id,
5143
+ traces,
5144
+ length: traces.reduce((sum, t) => sum + getPcbTraceLength(t), 0)
5145
+ }
5146
+ ];
5147
+ });
5148
+ if (members.length < 2) continue;
5149
+ const skew = Math.max(...members.map((m) => m.length)) - Math.min(...members.map((m) => m.length));
5150
+ if (skew <= bus.max_length_skew + 1e-9) continue;
5151
+ errors.push({
5152
+ type: "pcb_bus_length_skew_error",
5153
+ pcb_bus_length_skew_error_id: `pcb_bus_length_skew_error_${bus.source_bus_id}`,
5154
+ error_type: "pcb_bus_length_skew_error",
5155
+ message: `PCB bus ${bus.name ?? bus.source_bus_id} has ${skew.toFixed(2)}mm length skew, exceeding the ${bus.max_length_skew}mm maximum`,
5156
+ source_bus_id: bus.source_bus_id,
5157
+ source_trace_ids: members.map((m) => m.id),
5158
+ pcb_trace_ids: members.flatMap(
5159
+ (m) => m.traces.map((t) => t.pcb_trace_id)
5160
+ ),
5161
+ actual_length_skew: skew,
5162
+ maximum_length_skew: bus.max_length_skew,
5163
+ subcircuit_id: bus.subcircuit_id
5164
+ });
5165
+ }
5166
+ return errors;
5167
+ };
5168
+
5119
5169
  // lib/consolidate-pcb-overlap-errors.ts
5120
5170
  import { getPrimaryId as getPrimaryId10 } from "@tscircuit/circuit-json-util";
5121
5171
  function consolidatePcbOverlapErrors(circuitJson, errors) {
@@ -5604,6 +5654,7 @@ async function runAllRoutingChecks(circuitJson) {
5604
5654
  ...checkEachPcbPortConnectedToPcbTraces(circuitJson),
5605
5655
  ...checkSourceTracesHavePcbTraces(circuitJson),
5606
5656
  ...checkPcbTraceLengths(circuitJson),
5657
+ ...checkPcbBusLengthSkew(circuitJson),
5607
5658
  ...checkPcbTraceViaCounts(circuitJson),
5608
5659
  ...checkEachPcbTraceNonOverlapping(circuitJson),
5609
5660
  ...checkCopperPourShorts(circuitJson),
@@ -5638,6 +5689,7 @@ export {
5638
5689
  checkNoPowerPinDefined,
5639
5690
  checkPadPadClearance,
5640
5691
  checkPadTraceClearance,
5692
+ checkPcbBusLengthSkew,
5641
5693
  checkPcbComponentOverCutout,
5642
5694
  checkPcbComponentOverlap,
5643
5695
  checkPcbComponentsMissingCourtyard,