altium-toolkit 1.1.35 → 1.1.37
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/AGENTS.md +17 -2
- package/package.json +1 -1
- package/src/core/altium/PrintableTextDecoder.mjs +20 -1
- package/src/core/altium/SchematicMultipartOwnerMatcher.mjs +288 -7
- package/src/core/altium/SchematicTextParser.mjs +18 -253
- package/src/core/altium/SchematicTitleBlockParser.mjs +410 -0
- package/src/core/altium/SelectedPartAltiumExportAdapter.mjs +215 -0
- package/src/parser.mjs +1 -0
- package/src/ui/AltiumScene3dAuthoredBodyAnchorAdapter.mjs +245 -12
- package/src/ui/AltiumScene3dAuthoredConnectorYawPolicy.mjs +176 -0
- package/src/ui/AltiumScene3dBottomSourceHalfTurnPolicy.mjs +93 -0
- package/src/ui/AltiumScene3dDisplayModuleYawPolicy.mjs +284 -0
- package/src/ui/AltiumScene3dExternalPlacementAdapter.mjs +521 -23
- package/src/ui/AltiumScene3dPlacementRotationPolicy.mjs +766 -19
- package/src/ui/AltiumScene3dQfnFootprintDetector.mjs +307 -0
- package/src/ui/AltiumScene3dRepeatedFullFootprintBodyCollapse.mjs +518 -0
- package/src/ui/AltiumScene3dRepeatedModelOwnerRepair.mjs +121 -17
- package/src/ui/AltiumScene3dShapeStackOwnerAdapter.mjs +286 -135
- package/src/ui/AltiumScene3dShapeStackOwnerConflictPolicy.mjs +299 -0
- package/src/ui/PcbScene3dBoardOutlineRefiner.mjs +22 -0
- package/src/ui/PcbScene3dBuilder.mjs +613 -117
- package/src/ui/PcbScene3dModelRegistry.mjs +222 -10
- package/src/ui/PcbScene3dPackages.mjs +16 -1
- package/src/ui/PcbScene3dPlacementSideResolver.mjs +262 -22
- package/src/ui/PcbScene3dStaticBodyOwnerPromotion.mjs +984 -0
- package/src/ui/PcbScene3dStaticBodyPadOwnerPromotion.mjs +378 -0
- package/src/ui/PcbScene3dStaticBodyPlacementBuilder.mjs +169 -23
- package/src/ui/PcbScene3dStaticBodyPrototypeRecovery.mjs +445 -0
- package/src/ui/PcbScene3dStaticBodyRecovery.mjs +7 -1
- package/src/ui/PcbScene3dStaticBodySelectionKeyBuilder.mjs +199 -2
- package/src/ui/PcbScene3dStaticBodySymmetricOwnerPromotion.mjs +439 -0
- package/src/ui/PcbScene3dStaticBodySymmetryRecovery.mjs +23 -2
- package/src/ui/SchematicColorResolver.mjs +44 -0
- package/src/ui/SchematicLineColorResolver.mjs +1 -1
- package/src/ui/SchematicShapeRenderer.mjs +13 -13
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PcbScene3dPackageDimensionResolver } from './PcbScene3dPackageDimensionResolver.mjs'
|
|
2
|
+
import { AltiumScene3dRepeatedFullFootprintBodyCollapse } from './AltiumScene3dRepeatedFullFootprintBodyCollapse.mjs'
|
|
2
3
|
|
|
3
4
|
const CONNECTOR_TOKENS = new Set([
|
|
4
5
|
'antenna',
|
|
@@ -12,6 +13,8 @@ const PASSIVE_BODY_PATTERN =
|
|
|
12
13
|
/(?:^|[^a-z0-9])(?:cap|capacitor|res|resistor|ind|inductor|ferrite|bead|lqw|lqg)(?:$|[^a-z0-9])/i
|
|
13
14
|
const COMPACT_IC_BODY_PATTERN =
|
|
14
15
|
/(?:^|[^a-z0-9])(?:u?qfn(?:[-_ ]?\d+)?|dfn(?:[-_ ]?\d+)?|qfp(?:[-_ ]?\d+)?|bga(?:[-_ ]?\d+)?|lga(?:[-_ ]?\d+)?)(?:$|[^a-z0-9])/i
|
|
16
|
+
const MODEL_BOUNDS_CORNER_ORIGIN_PACKAGE_PATTERN =
|
|
17
|
+
/(?:^|[^a-z0-9])(?:[a-z0-9]*qfn[a-z0-9]*|[a-z0-9]*dfn[a-z0-9]*|qfp|lqfp|tqfp|bga|lga)(?:[-_ ]?\d+)?(?:$|[^a-z0-9])/i
|
|
15
18
|
const TIMING_PACKAGE_PATTERN =
|
|
16
19
|
/(?:^|[^a-z0-9])(?:clock|crystal|osc|oscillator|resonator|tcxo|txco|xtal)(?:$|[^a-z0-9])/i
|
|
17
20
|
const TIMING_DESIGNATOR_PATTERN = /^(?:y|xo)\d+[a-z]?$/i
|
|
@@ -52,6 +55,9 @@ export class AltiumScene3dRepeatedModelOwnerRepair {
|
|
|
52
55
|
const components = Array.isArray(documentModel?.pcb?.components)
|
|
53
56
|
? documentModel.pcb.components
|
|
54
57
|
: []
|
|
58
|
+
const pads = Array.isArray(documentModel?.pcb?.pads)
|
|
59
|
+
? documentModel.pcb.pads
|
|
60
|
+
: []
|
|
55
61
|
if (!components.length) {
|
|
56
62
|
return sceneDescription
|
|
57
63
|
}
|
|
@@ -62,15 +68,18 @@ export class AltiumScene3dRepeatedModelOwnerRepair {
|
|
|
62
68
|
component
|
|
63
69
|
])
|
|
64
70
|
)
|
|
65
|
-
const
|
|
66
|
-
(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
const collapsedPlacements =
|
|
72
|
+
AltiumScene3dRepeatedFullFootprintBodyCollapse.apply(
|
|
73
|
+
sceneDescription.externalPlacements,
|
|
74
|
+
componentByDesignator,
|
|
75
|
+
pads
|
|
76
|
+
)
|
|
77
|
+
const placements = collapsedPlacements.map((placement) =>
|
|
78
|
+
AltiumScene3dRepeatedModelOwnerRepair.#withSingleOwnerCenter(
|
|
79
|
+
placement,
|
|
80
|
+
componentByDesignator.get(String(placement?.designator || '')),
|
|
81
|
+
sceneDescription.board
|
|
82
|
+
)
|
|
74
83
|
)
|
|
75
84
|
|
|
76
85
|
for (const group of AltiumScene3dRepeatedModelOwnerRepair.#groups(
|
|
@@ -178,9 +187,13 @@ export class AltiumScene3dRepeatedModelOwnerRepair {
|
|
|
178
187
|
* @returns {object}
|
|
179
188
|
*/
|
|
180
189
|
static #withSingleOwnerCenter(placement, component, board) {
|
|
190
|
+
const projectionSource = String(
|
|
191
|
+
placement?.projection?.source || ''
|
|
192
|
+
).toLowerCase()
|
|
181
193
|
if (
|
|
182
194
|
!component ||
|
|
183
|
-
|
|
195
|
+
(projectionSource !== 'pad-fallback' &&
|
|
196
|
+
projectionSource !== 'model-bounds') ||
|
|
184
197
|
AltiumScene3dRepeatedModelOwnerRepair.#isAuthoredAnchorPlacement(
|
|
185
198
|
placement,
|
|
186
199
|
component
|
|
@@ -204,13 +217,58 @@ export class AltiumScene3dRepeatedModelOwnerRepair {
|
|
|
204
217
|
) {
|
|
205
218
|
return placement
|
|
206
219
|
}
|
|
220
|
+
if (
|
|
221
|
+
projectionSource === 'model-bounds' &&
|
|
222
|
+
!AltiumScene3dRepeatedModelOwnerRepair.#hasModelBoundsCornerSourceOriginOffset(
|
|
223
|
+
placement,
|
|
224
|
+
component,
|
|
225
|
+
offset
|
|
226
|
+
)
|
|
227
|
+
) {
|
|
228
|
+
return placement
|
|
229
|
+
}
|
|
207
230
|
|
|
208
231
|
return AltiumScene3dRepeatedModelOwnerRepair.#withOwner(
|
|
209
232
|
placement,
|
|
210
233
|
component,
|
|
211
234
|
board,
|
|
212
235
|
offset,
|
|
213
|
-
{
|
|
236
|
+
{
|
|
237
|
+
preserveRotation: true,
|
|
238
|
+
carryRenderableSourceOffset: projectionSource !== 'model-bounds'
|
|
239
|
+
}
|
|
240
|
+
)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Checks whether a model-bounds IC body uses a package-corner source
|
|
245
|
+
* origin rather than an authored mechanical anchor.
|
|
246
|
+
* @param {object} placement Scene placement.
|
|
247
|
+
* @param {object} component PCB component.
|
|
248
|
+
* @param {{ x: number, y: number }} offset Body anchor offset from owner.
|
|
249
|
+
* @returns {boolean}
|
|
250
|
+
*/
|
|
251
|
+
static #hasModelBoundsCornerSourceOriginOffset(
|
|
252
|
+
placement,
|
|
253
|
+
component,
|
|
254
|
+
offset
|
|
255
|
+
) {
|
|
256
|
+
const identityText =
|
|
257
|
+
AltiumScene3dRepeatedModelOwnerRepair.#identityTextForPlacement(
|
|
258
|
+
placement,
|
|
259
|
+
component
|
|
260
|
+
)
|
|
261
|
+
if (!MODEL_BOUNDS_CORNER_ORIGIN_PACKAGE_PATTERN.test(identityText)) {
|
|
262
|
+
return false
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const offsetWidth = Math.abs(Number(offset?.x || 0)) * 2
|
|
266
|
+
const offsetDepth = Math.abs(Number(offset?.y || 0)) * 2
|
|
267
|
+
|
|
268
|
+
return AltiumScene3dRepeatedModelOwnerRepair.#matchesCornerOriginBounds(
|
|
269
|
+
offsetWidth,
|
|
270
|
+
offsetDepth,
|
|
271
|
+
placement?.projection?.boundsMil
|
|
214
272
|
)
|
|
215
273
|
}
|
|
216
274
|
|
|
@@ -387,6 +445,35 @@ export class AltiumScene3dRepeatedModelOwnerRepair {
|
|
|
387
445
|
)
|
|
388
446
|
}
|
|
389
447
|
|
|
448
|
+
/**
|
|
449
|
+
* Checks whether doubled owner offsets match any planar pair from measured
|
|
450
|
+
* model bounds. Tilted STEP bodies often expose package height on `depth`
|
|
451
|
+
* and package width/depth on `width` plus `height`.
|
|
452
|
+
* @param {number} offsetWidth Doubled X offset.
|
|
453
|
+
* @param {number} offsetDepth Doubled Y offset.
|
|
454
|
+
* @param {{ width?: number, depth?: number, height?: number } | null | undefined} bounds Model bounds.
|
|
455
|
+
* @returns {boolean}
|
|
456
|
+
*/
|
|
457
|
+
static #matchesCornerOriginBounds(offsetWidth, offsetDepth, bounds) {
|
|
458
|
+
const dimensions = [
|
|
459
|
+
Number(bounds?.width),
|
|
460
|
+
Number(bounds?.depth),
|
|
461
|
+
Number(bounds?.height)
|
|
462
|
+
].filter((dimension) => Number.isFinite(dimension) && dimension > 0)
|
|
463
|
+
|
|
464
|
+
return dimensions.some((width, widthIndex) =>
|
|
465
|
+
dimensions.some(
|
|
466
|
+
(depth, depthIndex) =>
|
|
467
|
+
widthIndex !== depthIndex &&
|
|
468
|
+
AltiumScene3dRepeatedModelOwnerRepair.#matchesCornerOriginSize(
|
|
469
|
+
offsetWidth,
|
|
470
|
+
offsetDepth,
|
|
471
|
+
{ width, depth }
|
|
472
|
+
)
|
|
473
|
+
)
|
|
474
|
+
)
|
|
475
|
+
}
|
|
476
|
+
|
|
390
477
|
/**
|
|
391
478
|
* Checks whether two dimensions are close enough for source-origin repair.
|
|
392
479
|
* @param {number} actual Actual dimension.
|
|
@@ -656,7 +743,7 @@ export class AltiumScene3dRepeatedModelOwnerRepair {
|
|
|
656
743
|
* @param {object} component Resolved owner.
|
|
657
744
|
* @param {object} board Scene board metadata.
|
|
658
745
|
* @param {{ x: number, y: number }} offset Source-origin offset.
|
|
659
|
-
* @param {{ preserveRotation?: boolean }} [options] Repair options.
|
|
746
|
+
* @param {{ preserveRotation?: boolean, carryRenderableSourceOffset?: boolean }} [options] Repair options.
|
|
660
747
|
* @returns {object}
|
|
661
748
|
*/
|
|
662
749
|
static #withOwner(placement, component, board, offset, options = {}) {
|
|
@@ -671,11 +758,15 @@ export class AltiumScene3dRepeatedModelOwnerRepair {
|
|
|
671
758
|
component?.rotation
|
|
672
759
|
)
|
|
673
760
|
const renderableOffset =
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
761
|
+
options?.carryRenderableSourceOffset === false
|
|
762
|
+
? AltiumScene3dRepeatedModelOwnerRepair.#renderableZOffset(
|
|
763
|
+
placement?.modelTransform
|
|
764
|
+
)
|
|
765
|
+
: AltiumScene3dRepeatedModelOwnerRepair.#renderableOwnerAnchorOffset(
|
|
766
|
+
{ mountSide, rotationDeg },
|
|
767
|
+
offset,
|
|
768
|
+
placement?.modelTransform
|
|
769
|
+
)
|
|
679
770
|
|
|
680
771
|
return {
|
|
681
772
|
...placement,
|
|
@@ -702,6 +793,19 @@ export class AltiumScene3dRepeatedModelOwnerRepair {
|
|
|
702
793
|
}
|
|
703
794
|
}
|
|
704
795
|
|
|
796
|
+
/**
|
|
797
|
+
* Preserves authored model Z offset while suppressing lateral render shift.
|
|
798
|
+
* @param {object | undefined} modelTransform Existing model transform.
|
|
799
|
+
* @returns {{ x: number, y: number, z: number }}
|
|
800
|
+
*/
|
|
801
|
+
static #renderableZOffset(modelTransform) {
|
|
802
|
+
const z = Number(
|
|
803
|
+
modelTransform?.offsetMil?.z ?? modelTransform?.dzMil ?? 0
|
|
804
|
+
)
|
|
805
|
+
|
|
806
|
+
return { x: 0, y: 0, z: Number.isFinite(z) ? z : 0 }
|
|
807
|
+
}
|
|
808
|
+
|
|
705
809
|
/**
|
|
706
810
|
* Converts a board-space source-origin offset into mount-rig local XY.
|
|
707
811
|
* @param {{ mountSide?: string, rotationDeg?: number }} placement Resolved placement fields.
|