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
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
// specifier.
|
|
8
8
|
import { unzlibSync } from 'fflate'
|
|
9
9
|
import { PrintableTextDecoder } from './PrintableTextDecoder.mjs'
|
|
10
|
+
import { PcbShapeBasedBodyGeometryParser } from './PcbShapeBasedBodyGeometryParser.mjs'
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Extracts embedded 3D model payloads and component-body placement metadata
|
|
@@ -17,7 +18,7 @@ export class PcbEmbeddedModelExtractor {
|
|
|
17
18
|
* Extracts embedded model payloads and component-body placements from one
|
|
18
19
|
* stream map.
|
|
19
20
|
* @param {Map<string, Uint8Array>} streams
|
|
20
|
-
* @returns {{ models: { id: string, checksum: number, name: string, format: string, payloadText: string, sourceStream: string, transform: { rotationDeg: { x: number, y: number, z: number }, dzMil: number } }[], componentBodies: { sourceStream: string, layer: string, identifier: string, modelId: string, checksum: number | null, embedded: boolean, name: string, positionMil: { x: number, y: number }, rotationDeg: number, modelRotationDeg: { x: number, y: number, z: number }, dzMil: number, overallHeightMil: number | null, standoffHeightMil: number | null }[] }}
|
|
21
|
+
* @returns {{ models: { id: string, checksum: number, name: string, format: string, payloadText: string, sourceStream: string, transform: { rotationDeg: { x: number, y: number, z: number }, dzMil: number } }[], componentBodies: { sourceStream: string, layer: string, identifier: string, modelId: string, checksum: number | null, embedded: boolean, name: string, positionMil: { x: number, y: number }, rotationDeg: number, modelRotationDeg: { x: number, y: number, z: number }, dzMil: number, overallHeightMil: number | null, standoffHeightMil: number | null, modelType?: number, modelTypeName?: string, bodyColor?: object, bodyOpacity?: number, staticGeometry?: object }[] }}
|
|
21
22
|
*/
|
|
22
23
|
static extractFromStreams(streams) {
|
|
23
24
|
const modelStreamPrefix =
|
|
@@ -51,7 +52,7 @@ export class PcbEmbeddedModelExtractor {
|
|
|
51
52
|
streams.get('ComponentBodies6/Data'),
|
|
52
53
|
'ComponentBodies6/Data'
|
|
53
54
|
),
|
|
54
|
-
...PcbEmbeddedModelExtractor.#
|
|
55
|
+
...PcbEmbeddedModelExtractor.#parseShapeBasedComponentBodyStream(
|
|
55
56
|
streams.get('ShapeBasedComponentBodies6/Data'),
|
|
56
57
|
'ShapeBasedComponentBodies6/Data'
|
|
57
58
|
)
|
|
@@ -134,9 +135,10 @@ export class PcbEmbeddedModelExtractor {
|
|
|
134
135
|
* Parses one component-body printable stream into model-placement records.
|
|
135
136
|
* @param {Uint8Array | undefined} bytes
|
|
136
137
|
* @param {string} sourceStream
|
|
137
|
-
* @
|
|
138
|
+
* @param {{ inferStaticGeometry?: boolean }} [options]
|
|
139
|
+
* @returns {{ sourceStream: string, layer: string, identifier: string, modelId: string, checksum: number | null, embedded: boolean, name: string, positionMil: { x: number, y: number }, rotationDeg: number, modelRotationDeg: { x: number, y: number, z: number }, dzMil: number, overallHeightMil: number | null, standoffHeightMil: number | null, modelType?: number, modelTypeName?: string, bodyColor?: object, bodyOpacity?: number, staticGeometry?: object }[]}
|
|
138
140
|
*/
|
|
139
|
-
static #parseComponentBodyStream(bytes, sourceStream) {
|
|
141
|
+
static #parseComponentBodyStream(bytes, sourceStream, options = {}) {
|
|
140
142
|
if (!(bytes instanceof Uint8Array) || !bytes.byteLength) {
|
|
141
143
|
return []
|
|
142
144
|
}
|
|
@@ -153,12 +155,53 @@ export class PcbEmbeddedModelExtractor {
|
|
|
153
155
|
.map((fields) =>
|
|
154
156
|
PcbEmbeddedModelExtractor.#normalizeComponentBody(
|
|
155
157
|
fields,
|
|
156
|
-
sourceStream
|
|
158
|
+
sourceStream,
|
|
159
|
+
options.inferStaticGeometry
|
|
160
|
+
? {
|
|
161
|
+
staticGeometry:
|
|
162
|
+
PcbShapeBasedBodyGeometryParser.buildStaticGeometry(
|
|
163
|
+
fields,
|
|
164
|
+
[]
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
: {}
|
|
157
168
|
)
|
|
158
169
|
)
|
|
159
170
|
.filter(Boolean)
|
|
160
171
|
}
|
|
161
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Parses shape-based component bodies, including optional vertex payloads.
|
|
175
|
+
* @param {Uint8Array | undefined} bytes
|
|
176
|
+
* @param {string} sourceStream
|
|
177
|
+
* @returns {{ sourceStream: string, layer: string, identifier: string, modelId: string, checksum: number | null, embedded: boolean, name: string, positionMil: { x: number, y: number }, rotationDeg: number, modelRotationDeg: { x: number, y: number, z: number }, dzMil: number, overallHeightMil: number | null, standoffHeightMil: number | null, modelType?: number, modelTypeName?: string, bodyColor?: object, bodyOpacity?: number, staticGeometry?: object }[]}
|
|
178
|
+
*/
|
|
179
|
+
static #parseShapeBasedComponentBodyStream(bytes, sourceStream) {
|
|
180
|
+
if (!(bytes instanceof Uint8Array) || !bytes.byteLength) {
|
|
181
|
+
return []
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const records = PcbShapeBasedBodyGeometryParser.parse(bytes)
|
|
185
|
+
|
|
186
|
+
if (records.length) {
|
|
187
|
+
return records
|
|
188
|
+
.map((record) =>
|
|
189
|
+
PcbEmbeddedModelExtractor.#normalizeComponentBody(
|
|
190
|
+
record.fields,
|
|
191
|
+
sourceStream,
|
|
192
|
+
{ staticGeometry: record.staticGeometry }
|
|
193
|
+
)
|
|
194
|
+
)
|
|
195
|
+
.filter(Boolean)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return PcbEmbeddedModelExtractor.#parseComponentBodyStream(
|
|
199
|
+
bytes,
|
|
200
|
+
sourceStream,
|
|
201
|
+
{ inferStaticGeometry: true }
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
|
|
162
205
|
/**
|
|
163
206
|
* Parses one printable field record without requiring a specific leading
|
|
164
207
|
* marker such as `|RECORD=` or `|KIND=`.
|
|
@@ -266,9 +309,10 @@ export class PcbEmbeddedModelExtractor {
|
|
|
266
309
|
* Normalizes one component-body record into model-placement metadata.
|
|
267
310
|
* @param {Record<string, string | string[]>} fields
|
|
268
311
|
* @param {string} sourceStream
|
|
269
|
-
* @
|
|
312
|
+
* @param {{ staticGeometry?: object }} [extra]
|
|
313
|
+
* @returns {{ sourceStream: string, layer: string, identifier: string, modelId: string, checksum: number | null, embedded: boolean, name: string, positionMil: { x: number, y: number }, rotationDeg: number, modelRotationDeg: { x: number, y: number, z: number }, dzMil: number, overallHeightMil: number | null, standoffHeightMil: number | null, modelType?: number, modelTypeName?: string, bodyColor?: object, bodyOpacity?: number, staticGeometry?: object } | null}
|
|
270
314
|
*/
|
|
271
|
-
static #normalizeComponentBody(fields, sourceStream) {
|
|
315
|
+
static #normalizeComponentBody(fields, sourceStream, extra = {}) {
|
|
272
316
|
const modelId = PcbEmbeddedModelExtractor.#getField(fields, 'MODELID')
|
|
273
317
|
const name = PcbEmbeddedModelExtractor.#getField(fields, 'MODEL.NAME')
|
|
274
318
|
|
|
@@ -339,7 +383,82 @@ export class PcbEmbeddedModelExtractor {
|
|
|
339
383
|
standoffHeightMil: PcbEmbeddedModelExtractor.#parseMilLikeField(
|
|
340
384
|
fields,
|
|
341
385
|
'STANDOFFHEIGHT'
|
|
342
|
-
)
|
|
386
|
+
),
|
|
387
|
+
...PcbEmbeddedModelExtractor.#shapeBodyMetadata(fields),
|
|
388
|
+
...PcbEmbeddedModelExtractor.#stripUndefined(extra)
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Normalizes shape-based body display metadata.
|
|
394
|
+
* @param {Record<string, string | string[]>} fields Native fields.
|
|
395
|
+
* @returns {object}
|
|
396
|
+
*/
|
|
397
|
+
static #shapeBodyMetadata(fields) {
|
|
398
|
+
const modelType = PcbEmbeddedModelExtractor.#parseIntegerField(
|
|
399
|
+
fields,
|
|
400
|
+
'MODEL.MODELTYPE'
|
|
401
|
+
)
|
|
402
|
+
const bodyColorRaw = PcbEmbeddedModelExtractor.#parseIntegerField(
|
|
403
|
+
fields,
|
|
404
|
+
'BODYCOLOR3D'
|
|
405
|
+
)
|
|
406
|
+
const bodyOpacity = PcbEmbeddedModelExtractor.#parseNumberField(
|
|
407
|
+
fields,
|
|
408
|
+
'BODYOPACITY3D'
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
return PcbEmbeddedModelExtractor.#stripUndefined({
|
|
412
|
+
modelType: modelType === null ? undefined : modelType,
|
|
413
|
+
modelTypeName:
|
|
414
|
+
modelType === null
|
|
415
|
+
? undefined
|
|
416
|
+
: PcbEmbeddedModelExtractor.#modelTypeName(modelType),
|
|
417
|
+
bodyColor:
|
|
418
|
+
bodyColorRaw === null
|
|
419
|
+
? undefined
|
|
420
|
+
: PcbEmbeddedModelExtractor.#decodeBodyColor(bodyColorRaw),
|
|
421
|
+
bodyOpacity:
|
|
422
|
+
bodyOpacity === null || !Number.isFinite(bodyOpacity)
|
|
423
|
+
? undefined
|
|
424
|
+
: bodyOpacity
|
|
425
|
+
})
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Maps a numeric body model type to a stable semantic label.
|
|
430
|
+
* @param {number} modelType Numeric model type.
|
|
431
|
+
* @returns {string}
|
|
432
|
+
*/
|
|
433
|
+
static #modelTypeName(modelType) {
|
|
434
|
+
const modelTypeNames = new Map([
|
|
435
|
+
[0, 'extruded-polygon'],
|
|
436
|
+
[1, 'cone'],
|
|
437
|
+
[2, 'cylinder'],
|
|
438
|
+
[3, 'sphere']
|
|
439
|
+
])
|
|
440
|
+
|
|
441
|
+
return modelTypeNames.get(modelType) || 'unknown-' + modelType
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Decodes one packed native RGB color value.
|
|
446
|
+
* @param {number} rawColor Packed color value.
|
|
447
|
+
* @returns {{ raw: number, hex: string, rgb: { red: number, green: number, blue: number } }}
|
|
448
|
+
*/
|
|
449
|
+
static #decodeBodyColor(rawColor) {
|
|
450
|
+
const red = rawColor & 0xff
|
|
451
|
+
const green = (rawColor >> 8) & 0xff
|
|
452
|
+
const blue = (rawColor >> 16) & 0xff
|
|
453
|
+
|
|
454
|
+
return {
|
|
455
|
+
raw: rawColor,
|
|
456
|
+
hex:
|
|
457
|
+
'#' +
|
|
458
|
+
[red, green, blue]
|
|
459
|
+
.map((component) => component.toString(16).padStart(2, '0'))
|
|
460
|
+
.join(''),
|
|
461
|
+
rgb: { red, green, blue }
|
|
343
462
|
}
|
|
344
463
|
}
|
|
345
464
|
|
|
@@ -359,8 +478,8 @@ export class PcbEmbeddedModelExtractor {
|
|
|
359
478
|
|
|
360
479
|
/**
|
|
361
480
|
* Deduplicates shape-based body records shared across body streams.
|
|
362
|
-
* @param {{ sourceStream: string, layer: string, identifier: string, modelId: string, checksum: number | null, embedded: boolean, name: string, positionMil: { x: number, y: number }, rotationDeg: number, modelRotationDeg: { x: number, y: number, z: number }, dzMil: number, overallHeightMil: number | null, standoffHeightMil: number | null }[]} componentBodies
|
|
363
|
-
* @returns {{ sourceStream: string, layer: string, identifier: string, modelId: string, checksum: number | null, embedded: boolean, name: string, positionMil: { x: number, y: number }, rotationDeg: number, modelRotationDeg: { x: number, y: number, z: number }, dzMil: number, overallHeightMil: number | null, standoffHeightMil: number | null }[]}
|
|
481
|
+
* @param {{ sourceStream: string, layer: string, identifier: string, modelId: string, checksum: number | null, embedded: boolean, name: string, positionMil: { x: number, y: number }, rotationDeg: number, modelRotationDeg: { x: number, y: number, z: number }, dzMil: number, overallHeightMil: number | null, standoffHeightMil: number | null, modelType?: number, modelTypeName?: string, bodyColor?: object, bodyOpacity?: number, staticGeometry?: object }[]} componentBodies
|
|
482
|
+
* @returns {{ sourceStream: string, layer: string, identifier: string, modelId: string, checksum: number | null, embedded: boolean, name: string, positionMil: { x: number, y: number }, rotationDeg: number, modelRotationDeg: { x: number, y: number, z: number }, dzMil: number, overallHeightMil: number | null, standoffHeightMil: number | null, modelType?: number, modelTypeName?: string, bodyColor?: object, bodyOpacity?: number, staticGeometry?: object }[]}
|
|
364
483
|
*/
|
|
365
484
|
static #dedupeComponentBodies(componentBodies) {
|
|
366
485
|
const uniqueBodies = new Map()
|
|
@@ -747,4 +866,15 @@ export class PcbEmbeddedModelExtractor {
|
|
|
747
866
|
.filter(Number.isInteger)
|
|
748
867
|
)
|
|
749
868
|
}
|
|
869
|
+
|
|
870
|
+
/**
|
|
871
|
+
* Removes undefined values from a normalized metadata row.
|
|
872
|
+
* @param {object} row Metadata row.
|
|
873
|
+
* @returns {object}
|
|
874
|
+
*/
|
|
875
|
+
static #stripUndefined(row) {
|
|
876
|
+
return Object.fromEntries(
|
|
877
|
+
Object.entries(row).filter(([, value]) => value !== undefined)
|
|
878
|
+
)
|
|
879
|
+
}
|
|
750
880
|
}
|