altium-toolkit 1.1.24 → 1.1.25
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/docs/api.md +34 -13
- package/docs/model-format.md +37 -8
- package/docs/schemas/altium_toolkit/unsupported_features_a1.schema.json +58 -1
- package/package.json +1 -1
- package/src/core/altium/AltiumParser.mjs +8 -1
- package/src/core/altium/PcbDimensionParser.mjs +1 -0
- package/src/core/altium/PcbDimensionReportBuilder.mjs +190 -0
- package/src/core/altium/PcbEmbeddedModelExtractor.mjs +140 -10
- package/src/core/altium/PcbFabricationReadinessReportBuilder.mjs +607 -0
- package/src/core/altium/PcbInspectionReportBuilder.mjs +56 -3
- package/src/core/altium/PcbLayerStackReadModelBuilder.mjs +138 -3
- package/src/core/altium/PcbPrimitiveFieldOffsetReportBuilder.mjs +352 -0
- package/src/core/altium/PcbRuleImpactReportBuilder.mjs +313 -0
- package/src/core/altium/PcbShapeBasedBodyGeometryParser.mjs +514 -0
- package/src/core/altium/SchematicConnectivityQaBuilder.mjs +442 -2
- package/src/core/altium/SchematicImageDiagnosticsBuilder.mjs +214 -0
- package/src/core/altium/UnsupportedFeatureReportBuilder.mjs +523 -3
- package/src/parser.mjs +7 -0
- package/src/styles/altium-renderers.css +36 -0
- package/src/ui/PcbDimensionPrimitiveRenderer.mjs +422 -0
- package/src/ui/PcbScene3dBoardOutlineRefiner.mjs +6 -0
- package/src/ui/PcbScene3dBuilder.mjs +10 -2
- package/src/ui/PcbScene3dStaticBodyPlacementBuilder.mjs +225 -0
- package/src/ui/PcbSvgRenderer.mjs +78 -2
package/docs/api.md
CHANGED
|
@@ -76,8 +76,10 @@ Specialized parser helpers are exported for lower-level integrations, including
|
|
|
76
76
|
`EmbeddedAssetReportBuilder`, `LibraryDiffReportBuilder`,
|
|
77
77
|
`LibraryInspectionReportBuilder`,
|
|
78
78
|
`PcbBoardRegionSemanticsParser`, `PcbComponentPrimitiveIndexer`,
|
|
79
|
-
`PcbCustomPadShapeParser`, `PcbDimensionParser`,
|
|
80
|
-
`
|
|
79
|
+
`PcbCustomPadShapeParser`, `PcbDimensionParser`,
|
|
80
|
+
`PcbDimensionReportBuilder`, `PcbEmbeddedFontExtractor`,
|
|
81
|
+
`PcbExtendedPrimitiveInformationParser`,
|
|
82
|
+
`PcbFabricationReadinessReportBuilder`, `PcbFontMetricsParser`,
|
|
81
83
|
`LibraryRenderManifestBuilder`, `LibraryCatalogArtifactBuilder`,
|
|
82
84
|
`LibrarySearchIndex`, `SchLibModelParser`, `SchLibStreamExtractor`,
|
|
83
85
|
`PcbBomProfileBuilder`, `PcbClassReportBuilder`,
|
|
@@ -88,8 +90,10 @@ Specialized parser helpers are exported for lower-level integrations, including
|
|
|
88
90
|
`ProjectDesignBundleBuilder`, `ProjectHierarchyReportBuilder`,
|
|
89
91
|
`ProjectNetlistExporter`, `ProjectVariantViewBuilder`,
|
|
90
92
|
`PcbMechanicalLayerPairParser`, `PcbSpecialStringResolver`, `PcbUnionParser`,
|
|
91
|
-
`PcbViaStackParser`, `
|
|
93
|
+
`PcbViaStackParser`, `PcbRuleImpactReportBuilder`, `PcbRuleParser`,
|
|
94
|
+
`PcbRawRecordRegistry`, `PcbReviewPolygonRealizationBuilder`,
|
|
92
95
|
`PcbStatisticsBuilder`, `SchematicCodeSymbolParser`,
|
|
96
|
+
`SchematicConnectivityQaBuilder`, `SchematicImageDiagnosticsBuilder`,
|
|
93
97
|
`SchematicOwnershipGraphParser`,
|
|
94
98
|
`SchematicProjectParameterResolver`, `SchematicRecordStreamParser`, and
|
|
95
99
|
`SchematicTextRunParser`.
|
|
@@ -100,6 +104,15 @@ the native component-index grouping used to populate
|
|
|
100
104
|
rule helpers expose the same mask/cache, stack, and typed-constraint
|
|
101
105
|
normalization used by `.PcbDoc` parsing. `PcbDimensionParser` exposes the
|
|
102
106
|
parser-only Dimensions6 normalization used by `.PcbDoc` parsing.
|
|
107
|
+
`PcbDimensionReportBuilder` classifies recovered PCB dimensions as renderable
|
|
108
|
+
or unresolved from their reference geometry, and
|
|
109
|
+
`PcbRuleImpactReportBuilder` summarizes enabled/disabled design rules by
|
|
110
|
+
affected primitive family, scope predicate, manufacturing category, and
|
|
111
|
+
length-valued constraints. `PcbFabricationReadinessReportBuilder` summarizes
|
|
112
|
+
pad/via fabrication review items such as local pad stacks, offsets, slots,
|
|
113
|
+
mask overrides, thermal relief, via spans, and via protection metadata.
|
|
114
|
+
`PcbReviewPolygonRealizationBuilder` exposes the polygon-pour realization rows
|
|
115
|
+
used by `pcb.reviewMetadata`.
|
|
103
116
|
`SchematicOwnershipGraphParser` and `PcbOwnershipGraphBuilder` expose the
|
|
104
117
|
read-only ownership sidecars that parser roots attach under
|
|
105
118
|
`schematic.ownership` and `pcb.ownership`.
|
|
@@ -190,10 +203,16 @@ implementation child rows where available.
|
|
|
190
203
|
`LibrarySearchIndex` expose deterministic SchLib/PcbLib render/export
|
|
191
204
|
manifests, static catalog artifacts, search metadata, plus exact, keyword, and
|
|
192
205
|
fuzzy lookup helpers. `PcbStatisticsBuilder`, `PcbNetMembershipReportBuilder`,
|
|
193
|
-
`PcbClassReportBuilder`,
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
206
|
+
`PcbClassReportBuilder`, `PcbDimensionReportBuilder`,
|
|
207
|
+
`PcbRuleImpactReportBuilder`, `PcbFabricationReadinessReportBuilder`, and
|
|
208
|
+
`PcbInspectionReportBuilder` emit board QA, net-ownership, class-membership,
|
|
209
|
+
dimension QA, rule-impact, fabrication-readiness, and combined inspection
|
|
210
|
+
artifacts for `.PcbDoc` models. `SchematicImageDiagnosticsBuilder` and
|
|
211
|
+
`SchematicConnectivityQaBuilder` expose the same image payload and connectivity
|
|
212
|
+
QA sidecars attached to parsed schematic models.
|
|
213
|
+
`SchematicProjectParameterResolver`
|
|
214
|
+
resolves dot-prefixed and equals-prefixed schematic special strings for parser
|
|
215
|
+
and SVG integrations.
|
|
197
216
|
`SchematicTextRunParser` parses schematic backslash suffix markers into display
|
|
198
217
|
text plus overline run metadata reused by pin and text rendering.
|
|
199
218
|
|
|
@@ -303,13 +322,14 @@ available. The embedded `schematic-semantic-metadata` JSON sidecar uses schema
|
|
|
303
322
|
components, and pins for downstream highlighting.
|
|
304
323
|
|
|
305
324
|
PCB SVG output includes stable semantic `data-*` attributes on recovered board,
|
|
306
|
-
copper, pad, via, component, and
|
|
325
|
+
copper, pad, via, component, text, and dimension elements. The embedded
|
|
307
326
|
`pcb-semantic-metadata` JSON sidecar uses schema
|
|
308
327
|
`altium-toolkit.pcb.svg.semantics.a1` and links SVG element keys to primitive
|
|
309
|
-
kind, layer, net/class, component, pad number,
|
|
310
|
-
identity where that metadata is available. The
|
|
311
|
-
context, including board centroid, included layer
|
|
312
|
-
pad/via drill render state (`open`, `covered`,
|
|
328
|
+
kind, layer, net/class, component, pad number, dimension kind/text, hole
|
|
329
|
+
ownership, and board-outline identity where that metadata is available. The
|
|
330
|
+
same sidecar also records view context, including board centroid, included layer
|
|
331
|
+
ids, layer roles, cutouts, and pad/via drill render state (`open`, `covered`,
|
|
332
|
+
`filled`, or `capped`).
|
|
313
333
|
`PcbSvgRenderer.renderLayerSvgs()` uses the same semantic sidecar shape with
|
|
314
334
|
`view.kind: 'layer'` and a layer-specific `layerSet`.
|
|
315
335
|
|
|
@@ -331,7 +351,8 @@ import {
|
|
|
331
351
|
`drillCutouts` plus fill holes for drilled pads and vias. External model
|
|
332
352
|
placements include `projection` diagnostics indicating whether bounds came
|
|
333
353
|
from authored overrides, resolved model bounds, nearby pad spans, procedural
|
|
334
|
-
component fallback, or only the model anchor.
|
|
354
|
+
component fallback, or only the model anchor. Shape-based 3D bodies with
|
|
355
|
+
complete native geometry are exposed as `staticBodyPlacements`.
|
|
335
356
|
- `PcbScene3dModelRegistry` resolves embedded or session model candidates for
|
|
336
357
|
component placements.
|
|
337
358
|
- `PcbScene3dScenePreparator.prepare(documentModel, options)` prepares the same
|
package/docs/model-format.md
CHANGED
|
@@ -170,10 +170,23 @@ possible unrouted pad-only nets are split into deterministic lists.
|
|
|
170
170
|
`PcbClassReportBuilder.build()` emits
|
|
171
171
|
`altium-toolkit.pcb.class-report.a1` reports that summarize PCB classes by
|
|
172
172
|
kind, enabled state, member resolution, empty classes, and unresolved members.
|
|
173
|
+
`PcbDimensionReportBuilder.build()` emits
|
|
174
|
+
`altium-toolkit.pcb.dimensions.a1` reports that classify recovered PCB
|
|
175
|
+
dimensions as renderable or unresolved from reference geometry and summarize
|
|
176
|
+
dimension kinds. `PcbRuleImpactReportBuilder.build()` emits
|
|
177
|
+
`altium-toolkit.pcb.rule-impact.a1` reports that group design rules by enabled
|
|
178
|
+
state, affected primitive families, scope predicates, manufacturing category,
|
|
179
|
+
and length-valued constraints.
|
|
180
|
+
`PcbFabricationReadinessReportBuilder.build()` emits
|
|
181
|
+
`altium-toolkit.pcb.fabrication-readiness.a1` reports that summarize pad/via
|
|
182
|
+
fabrication review items such as non-simple pad stack modes, local pad
|
|
183
|
+
offsets, slotted and non-plated holes, mask overrides, thermal relief, via
|
|
184
|
+
spans, via protection, and microvia-like geometry.
|
|
173
185
|
`PcbInspectionReportBuilder.build()` emits `altium-toolkit.pcb.inspection.a1`
|
|
174
186
|
reports that compose board statistics, primitive counts, design-rule counts,
|
|
175
|
-
diagnostics, net membership, class membership,
|
|
176
|
-
into one
|
|
187
|
+
diagnostics, net membership, class membership, dimension QA, rule-impact,
|
|
188
|
+
review metadata, fabrication-readiness, and route-analysis summaries into one
|
|
189
|
+
inspection artifact.
|
|
177
190
|
|
|
178
191
|
`PcbLayerGroups` provides stable layer-group names, deterministic display
|
|
179
192
|
colors, and draw priorities for legacy PCB layer ids. `AltiumUnits` provides
|
|
@@ -254,7 +267,12 @@ multi-sheet design netlist.
|
|
|
254
267
|
`schematic.connectivityQa` reports read-only connectivity findings, including
|
|
255
268
|
implicit generated net names, dangling labels, orphan ports, unconnected pins,
|
|
256
269
|
ambiguous junctions, and un-junctioned tee contacts where one wire endpoint
|
|
257
|
-
touches another wire interior without an authored junction.
|
|
270
|
+
touches another wire interior without an authored junction. Harness-specific
|
|
271
|
+
findings flag sheet entries whose local harness type cannot be resolved,
|
|
272
|
+
harness entries without linked signal-harness geometry, and connector type-label
|
|
273
|
+
mismatches. Pin interpretation findings flag hidden name/number labels,
|
|
274
|
+
endpoint symbol markers, and power-like pin names whose recovered electrical
|
|
275
|
+
type is not a power pin.
|
|
258
276
|
|
|
259
277
|
Embedded schematic images preserve the raw record geometry and expose
|
|
260
278
|
browser-facing payload metadata. When an embedded stream contains a native
|
|
@@ -262,6 +280,10 @@ PNG/JPEG/GIF/SVG/WebP payload alongside a preview, `mimeType` and `dataBase64`
|
|
|
262
280
|
refer to the native payload while `sourceMimeType` records the preview format.
|
|
263
281
|
Alpha-bearing 32-bit BMP previews are converted to PNG and marked with
|
|
264
282
|
`hasAlpha` so SVG renderers can display transparency deterministically.
|
|
283
|
+
`schematic.imageDiagnostics` reports embedded payload counts, external image
|
|
284
|
+
references, missing embedded payloads, unsupported MIME states, converted
|
|
285
|
+
preview/native payloads, and alpha-bearing images without attempting any file
|
|
286
|
+
system lookups.
|
|
265
287
|
When a schematic OLE container exposes preview metadata, `schematic.thumbnails`
|
|
266
288
|
contains PNG thumbnail sidecars with `kind`, dimensions, `sourceStream`,
|
|
267
289
|
`pixelFormat`, `mimeType`, and `dataBase64` fields.
|
|
@@ -378,9 +400,11 @@ consumers.
|
|
|
378
400
|
|
|
379
401
|
PCB dimensions from `Dimensions6/Data` are exposed through `pcb.dimensions`.
|
|
380
402
|
Dimension entries preserve native kind codes and raw fields while adding a
|
|
381
|
-
normalized `kind` (`linear`, `angular`, `radial`, `
|
|
382
|
-
`ordinate`), reference points, optional text location,
|
|
383
|
-
measured value, angle value, and unit.
|
|
403
|
+
normalized `kind` (`linear`, `angular`, `radial`, `diameter`, `datum`,
|
|
404
|
+
`baseline`, or `ordinate`), reference points, optional text location,
|
|
405
|
+
prefix/suffix, precision, measured value, angle value, and unit. PCB SVG output
|
|
406
|
+
renders dimensions with sufficient reference geometry as static mechanical
|
|
407
|
+
dimension primitives and includes them in the semantic sidecar.
|
|
384
408
|
|
|
385
409
|
`pcb.extendedPrimitiveInformation` exposes
|
|
386
410
|
`ExtendedPrimitiveInformation/Data` entries keyed by primitive index and, when
|
|
@@ -454,7 +478,9 @@ preserves raw mixed-format primitive records with the same registry metadata
|
|
|
454
478
|
shape used by PcbDoc raw records. Library-level `embeddedFonts` uses the same
|
|
455
479
|
payload and metric shape as PCB documents. Library-level `embeddedModels` and
|
|
456
480
|
`componentBodies` preserve embedded 3D payloads and body references when
|
|
457
|
-
present.
|
|
481
|
+
present. Shape-based component bodies may include `staticGeometry` for
|
|
482
|
+
extruded-polygon, cone, cylinder, and sphere bodies, with dimensions and
|
|
483
|
+
vertices expressed in mils when native evidence is available.
|
|
458
484
|
`pcbLibrary.indexes.footprintsByName` provides read-only footprint lookup and
|
|
459
485
|
search metadata, including source storage, primitive counts, pad/text counts,
|
|
460
486
|
and keyword tokens from footprint and component parameters. Footprint entries
|
|
@@ -540,7 +566,10 @@ External model placements in the 3D scene description include a `projection`
|
|
|
540
566
|
diagnostic object. The `source` explains whether bounds came from an authored
|
|
541
567
|
projection override, resolved model bounds, nearby pad-span fallback,
|
|
542
568
|
procedural component fallback, or model-anchor fallback. The diagnostic does
|
|
543
|
-
not alter placement coordinates.
|
|
569
|
+
not alter placement coordinates. Shape-based component bodies with complete
|
|
570
|
+
static geometry are exposed separately as `staticBodyPlacements`, using the
|
|
571
|
+
same board-centered coordinate convention as component and external-model
|
|
572
|
+
placements.
|
|
544
573
|
|
|
545
574
|
## Integrated Library Fields
|
|
546
575
|
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"recordTypes",
|
|
11
11
|
"rawRecords",
|
|
12
12
|
"opaqueRecords",
|
|
13
|
-
"diagnostics"
|
|
13
|
+
"diagnostics",
|
|
14
|
+
"edgeCases"
|
|
14
15
|
],
|
|
15
16
|
"properties": {
|
|
16
17
|
"schema": {
|
|
@@ -25,6 +26,7 @@
|
|
|
25
26
|
"rawRecordCount",
|
|
26
27
|
"opaqueRecordCount",
|
|
27
28
|
"diagnosticCount",
|
|
29
|
+
"edgeCaseCount",
|
|
28
30
|
"itemCount",
|
|
29
31
|
"status"
|
|
30
32
|
],
|
|
@@ -44,6 +46,9 @@
|
|
|
44
46
|
"diagnosticCount": {
|
|
45
47
|
"type": "number"
|
|
46
48
|
},
|
|
49
|
+
"edgeCaseCount": {
|
|
50
|
+
"type": "number"
|
|
51
|
+
},
|
|
47
52
|
"itemCount": {
|
|
48
53
|
"type": "number"
|
|
49
54
|
},
|
|
@@ -75,6 +80,12 @@
|
|
|
75
80
|
"items": {
|
|
76
81
|
"$ref": "#/$defs/diagnostic"
|
|
77
82
|
}
|
|
83
|
+
},
|
|
84
|
+
"edgeCases": {
|
|
85
|
+
"type": "array",
|
|
86
|
+
"items": {
|
|
87
|
+
"$ref": "#/$defs/edgeCase"
|
|
88
|
+
}
|
|
78
89
|
}
|
|
79
90
|
},
|
|
80
91
|
"$defs": {
|
|
@@ -207,6 +218,52 @@
|
|
|
207
218
|
"type": "string"
|
|
208
219
|
}
|
|
209
220
|
}
|
|
221
|
+
},
|
|
222
|
+
"edgeCase": {
|
|
223
|
+
"type": "object",
|
|
224
|
+
"additionalProperties": true,
|
|
225
|
+
"required": ["code"],
|
|
226
|
+
"properties": {
|
|
227
|
+
"fileName": {
|
|
228
|
+
"type": "string"
|
|
229
|
+
},
|
|
230
|
+
"domain": {
|
|
231
|
+
"type": "string"
|
|
232
|
+
},
|
|
233
|
+
"code": {
|
|
234
|
+
"type": "string"
|
|
235
|
+
},
|
|
236
|
+
"feature": {
|
|
237
|
+
"type": "string"
|
|
238
|
+
},
|
|
239
|
+
"supportState": {
|
|
240
|
+
"type": "string"
|
|
241
|
+
},
|
|
242
|
+
"severity": {
|
|
243
|
+
"enum": ["info", "warning", "error"]
|
|
244
|
+
},
|
|
245
|
+
"message": {
|
|
246
|
+
"type": "string"
|
|
247
|
+
},
|
|
248
|
+
"source": {
|
|
249
|
+
"type": "string"
|
|
250
|
+
},
|
|
251
|
+
"sourceStream": {
|
|
252
|
+
"type": "string"
|
|
253
|
+
},
|
|
254
|
+
"sourceStorage": {
|
|
255
|
+
"type": "string"
|
|
256
|
+
},
|
|
257
|
+
"recordIndex": {
|
|
258
|
+
"type": "number"
|
|
259
|
+
},
|
|
260
|
+
"recordType": {
|
|
261
|
+
"type": "number"
|
|
262
|
+
},
|
|
263
|
+
"errorKind": {
|
|
264
|
+
"type": "string"
|
|
265
|
+
}
|
|
266
|
+
}
|
|
210
267
|
}
|
|
211
268
|
}
|
|
212
269
|
}
|
package/package.json
CHANGED
|
@@ -32,6 +32,7 @@ import { SchematicSheetParser } from './SchematicSheetParser.mjs'
|
|
|
32
32
|
import { SchematicJunctionParser } from './SchematicJunctionParser.mjs'
|
|
33
33
|
import { SchematicBusEntryParser } from './SchematicBusEntryParser.mjs'
|
|
34
34
|
import { SchematicImageParser } from './SchematicImageParser.mjs'
|
|
35
|
+
import { SchematicImageDiagnosticsBuilder } from './SchematicImageDiagnosticsBuilder.mjs'
|
|
35
36
|
import { SchematicNetlistBuilder } from './SchematicNetlistBuilder.mjs'
|
|
36
37
|
import { SchematicRecordTypeRegistry } from './SchematicRecordTypeRegistry.mjs'
|
|
37
38
|
import { SchematicComponentTextResolver } from './SchematicComponentTextResolver.mjs'
|
|
@@ -617,6 +618,9 @@ export class AltiumParser {
|
|
|
617
618
|
recordIndexAwareRecords,
|
|
618
619
|
arrayBuffer
|
|
619
620
|
)
|
|
621
|
+
const imageDiagnosticsReport = SchematicImageDiagnosticsBuilder.build({
|
|
622
|
+
images
|
|
623
|
+
})
|
|
620
624
|
const { thumbnails, diagnostics: thumbnailDiagnostics } =
|
|
621
625
|
SchematicThumbnailParser.parse(arrayBuffer)
|
|
622
626
|
const template = SchematicTemplateParser.parse(
|
|
@@ -832,7 +836,9 @@ export class AltiumParser {
|
|
|
832
836
|
texts: anchoredTexts,
|
|
833
837
|
pins,
|
|
834
838
|
ports,
|
|
835
|
-
junctions
|
|
839
|
+
junctions,
|
|
840
|
+
sheetEntries,
|
|
841
|
+
harnesses
|
|
836
842
|
})
|
|
837
843
|
const embeddedFiles = schematicExtraction?.embeddedFiles || null
|
|
838
844
|
const nativeStreams = schematicExtraction?.nativeStreams || null
|
|
@@ -905,6 +911,7 @@ export class AltiumParser {
|
|
|
905
911
|
junctions,
|
|
906
912
|
busEntries,
|
|
907
913
|
images,
|
|
914
|
+
imageDiagnostics: imageDiagnosticsReport,
|
|
908
915
|
...(thumbnails.length ? { thumbnails } : {}),
|
|
909
916
|
nets,
|
|
910
917
|
ownership,
|
|
@@ -173,6 +173,7 @@ export class PcbDimensionParser {
|
|
|
173
173
|
static #normalizeKind(kindCode) {
|
|
174
174
|
const normalized = String(kindCode || '').toLowerCase()
|
|
175
175
|
if (/ang/u.test(normalized)) return 'angular'
|
|
176
|
+
if (/diam/u.test(normalized)) return 'diameter'
|
|
176
177
|
if (/radial|radius/u.test(normalized)) return 'radial'
|
|
177
178
|
if (/datum/u.test(normalized)) return 'datum'
|
|
178
179
|
if (/baseline/u.test(normalized)) return 'baseline'
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 André Fiedler
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Builds deterministic QA summaries for recovered PCB dimension records.
|
|
7
|
+
*/
|
|
8
|
+
export class PcbDimensionReportBuilder {
|
|
9
|
+
static SCHEMA = 'altium-toolkit.pcb.dimensions.a1'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Builds a dimension QA report from a parser root or PCB model.
|
|
13
|
+
* @param {object} input Parser root, PCB model, or options object.
|
|
14
|
+
* @returns {object}
|
|
15
|
+
*/
|
|
16
|
+
static build(input = {}) {
|
|
17
|
+
const pcb = PcbDimensionReportBuilder.#pcb(input)
|
|
18
|
+
const dimensions = PcbDimensionReportBuilder.#dimensions(pcb)
|
|
19
|
+
const rows = dimensions.map((dimension) =>
|
|
20
|
+
PcbDimensionReportBuilder.#dimensionRow(dimension)
|
|
21
|
+
)
|
|
22
|
+
const findings = rows.flatMap((row) =>
|
|
23
|
+
PcbDimensionReportBuilder.#findingsForRow(row)
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
schema: PcbDimensionReportBuilder.SCHEMA,
|
|
28
|
+
summary: PcbDimensionReportBuilder.#summary(rows),
|
|
29
|
+
dimensions: rows,
|
|
30
|
+
findings
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Resolves the PCB payload from a parser root or direct PCB model.
|
|
36
|
+
* @param {object} input Parser root or PCB model.
|
|
37
|
+
* @returns {object}
|
|
38
|
+
*/
|
|
39
|
+
static #pcb(input) {
|
|
40
|
+
return input?.pcb || input || {}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Resolves normalized dimension rows from a PCB payload.
|
|
45
|
+
* @param {object} pcb PCB model.
|
|
46
|
+
* @returns {object[]}
|
|
47
|
+
*/
|
|
48
|
+
static #dimensions(pcb) {
|
|
49
|
+
return Array.isArray(pcb?.dimensions) ? pcb.dimensions : []
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Builds one public dimension report row.
|
|
54
|
+
* @param {object} dimension Normalized dimension record.
|
|
55
|
+
* @returns {object}
|
|
56
|
+
*/
|
|
57
|
+
static #dimensionRow(dimension) {
|
|
58
|
+
const kind = PcbDimensionReportBuilder.#kind(dimension)
|
|
59
|
+
const references = Array.isArray(dimension?.references)
|
|
60
|
+
? dimension.references
|
|
61
|
+
: []
|
|
62
|
+
const requiredReferenceCount =
|
|
63
|
+
PcbDimensionReportBuilder.#requiredReferenceCount(kind)
|
|
64
|
+
const status =
|
|
65
|
+
references.length >= requiredReferenceCount
|
|
66
|
+
? 'renderable'
|
|
67
|
+
: 'unresolved'
|
|
68
|
+
|
|
69
|
+
return PcbDimensionReportBuilder.#stripEmpty({
|
|
70
|
+
dimensionIndex: PcbDimensionReportBuilder.#integerOrFallback(
|
|
71
|
+
dimension?.dimensionIndex,
|
|
72
|
+
0
|
|
73
|
+
),
|
|
74
|
+
kind,
|
|
75
|
+
name: dimension?.name || '',
|
|
76
|
+
layer: dimension?.layer || '',
|
|
77
|
+
text: dimension?.text || '',
|
|
78
|
+
status,
|
|
79
|
+
referenceCount: references.length,
|
|
80
|
+
requiredReferenceCount,
|
|
81
|
+
hasTextLocation: Boolean(dimension?.textLocation),
|
|
82
|
+
measuredValue: dimension?.measuredValue ?? undefined,
|
|
83
|
+
angleValue: dimension?.angleValue ?? undefined,
|
|
84
|
+
unit: dimension?.unit || ''
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Builds report findings for one dimension row.
|
|
90
|
+
* @param {object} row Dimension report row.
|
|
91
|
+
* @returns {object[]}
|
|
92
|
+
*/
|
|
93
|
+
static #findingsForRow(row) {
|
|
94
|
+
if (row.status !== 'unresolved') {
|
|
95
|
+
return []
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return [
|
|
99
|
+
PcbDimensionReportBuilder.#stripEmpty({
|
|
100
|
+
code: 'pcb.dimension.missing-reference',
|
|
101
|
+
severity: 'warning',
|
|
102
|
+
dimensionIndex: row.dimensionIndex,
|
|
103
|
+
kind: row.kind,
|
|
104
|
+
name: row.name,
|
|
105
|
+
referenceCount: row.referenceCount,
|
|
106
|
+
requiredReferenceCount: row.requiredReferenceCount
|
|
107
|
+
})
|
|
108
|
+
]
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Builds top-level dimension counters.
|
|
113
|
+
* @param {object[]} rows Dimension report rows.
|
|
114
|
+
* @returns {object}
|
|
115
|
+
*/
|
|
116
|
+
static #summary(rows) {
|
|
117
|
+
const byKind = new Map()
|
|
118
|
+
|
|
119
|
+
for (const row of rows) {
|
|
120
|
+
byKind.set(row.kind, Number(byKind.get(row.kind) || 0) + 1)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
dimensionCount: rows.length,
|
|
125
|
+
renderableCount: rows.filter((row) => row.status === 'renderable')
|
|
126
|
+
.length,
|
|
127
|
+
unresolvedCount: rows.filter((row) => row.status === 'unresolved')
|
|
128
|
+
.length,
|
|
129
|
+
missingTextLocationCount: rows.filter(
|
|
130
|
+
(row) => row.hasTextLocation !== true
|
|
131
|
+
).length,
|
|
132
|
+
byKind: [...byKind.entries()]
|
|
133
|
+
.map(([kind, count]) => ({ kind, count }))
|
|
134
|
+
.sort((left, right) =>
|
|
135
|
+
left.kind.localeCompare(right.kind, undefined, {
|
|
136
|
+
numeric: true
|
|
137
|
+
})
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Resolves the canonical dimension kind.
|
|
144
|
+
* @param {object} dimension Dimension record.
|
|
145
|
+
* @returns {string}
|
|
146
|
+
*/
|
|
147
|
+
static #kind(dimension) {
|
|
148
|
+
const kind = String(dimension?.kind || '')
|
|
149
|
+
.trim()
|
|
150
|
+
.toLowerCase()
|
|
151
|
+
return kind || 'linear'
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Returns the minimum reference count needed for deterministic rendering.
|
|
156
|
+
* @param {string} kind Dimension kind.
|
|
157
|
+
* @returns {number}
|
|
158
|
+
*/
|
|
159
|
+
static #requiredReferenceCount(kind) {
|
|
160
|
+
if (kind === 'angular') {
|
|
161
|
+
return 3
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return 2
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Parses an integer with a fallback.
|
|
169
|
+
* @param {unknown} value Numeric candidate.
|
|
170
|
+
* @param {number} fallback Fallback value.
|
|
171
|
+
* @returns {number}
|
|
172
|
+
*/
|
|
173
|
+
static #integerOrFallback(value, fallback) {
|
|
174
|
+
const number = Number(value)
|
|
175
|
+
return Number.isInteger(number) ? number : fallback
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Removes undefined and blank-string values from a shallow object.
|
|
180
|
+
* @param {Record<string, unknown>} row Input row.
|
|
181
|
+
* @returns {Record<string, unknown>}
|
|
182
|
+
*/
|
|
183
|
+
static #stripEmpty(row) {
|
|
184
|
+
return Object.fromEntries(
|
|
185
|
+
Object.entries(row).filter(
|
|
186
|
+
([, value]) => value !== undefined && value !== ''
|
|
187
|
+
)
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
}
|