altium-toolkit 1.1.30 → 1.1.32
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/package.json +1 -1
- package/src/core/altium/AltiumParser.mjs +34 -1
- package/src/core/altium/AltiumSchematicArcAngleNormalizer.mjs +82 -0
- package/src/core/altium/AltiumSchematicFreeGraphicStrokeNormalizer.mjs +105 -0
- package/src/core/altium/AltiumSchematicHiddenDesignatorResolver.mjs +191 -0
- package/src/core/altium/AltiumSchematicPackedImageResolver.mjs +433 -0
- package/src/core/altium/AltiumSchematicSheetBoundsNormalizer.mjs +287 -0
- package/src/parser.mjs +5 -0
- package/src/renderers.mjs +2 -0
- package/src/scene3d.mjs +1 -0
- package/src/ui/AltiumPcbBottomViewMirror.mjs +394 -0
- package/src/ui/AltiumScene3dAuthoredBodyAnchorAdapter.mjs +300 -0
- package/src/ui/AltiumScene3dExternalPlacementAdapter.mjs +364 -22
- package/src/ui/AltiumScene3dPlacementRotationPolicy.mjs +568 -4
- package/src/ui/AltiumScene3dRepeatedModelOwnerRepair.mjs +149 -23
- package/src/ui/PcbFootprintPadAxisNormalizer.mjs +609 -0
- package/src/ui/PcbScene3dBuilder.mjs +313 -15
- package/src/ui/PcbScene3dPlacementSideResolver.mjs +34 -2
|
@@ -8,9 +8,15 @@ const CONNECTOR_TOKENS = new Set([
|
|
|
8
8
|
])
|
|
9
9
|
const PASSIVE_BODY_PATTERN =
|
|
10
10
|
/(?:^|[^a-z0-9])(?:cap|capacitor|res|resistor|ind|inductor|ferrite|bead|lqw|lqg)(?:$|[^a-z0-9])/i
|
|
11
|
+
const COMPACT_IC_BODY_PATTERN =
|
|
12
|
+
/(?:^|[^a-z0-9])(?:u?qfn(?:[-_ ]?\d+)?|dfn(?:[-_ ]?\d+)?|qfp(?:[-_ ]?\d+)?|bga(?:[-_ ]?\d+)?|lga(?:[-_ ]?\d+)?)(?:$|[^a-z0-9])/i
|
|
11
13
|
const TIMING_PACKAGE_PATTERN =
|
|
12
14
|
/(?:^|[^a-z0-9])(?:clock|crystal|osc|oscillator|resonator|tcxo|txco|xtal)(?:$|[^a-z0-9])/i
|
|
13
15
|
const TIMING_DESIGNATOR_PATTERN = /^(?:y|xo)\d+[a-z]?$/i
|
|
16
|
+
const AUTHORED_ANCHOR_IDENTITY_PATTERN =
|
|
17
|
+
/(?:^|[^a-z0-9])(?:antenna|coax|connector|edge|header|jack|mechanical|module|mount|shield|sma|socket|usb)(?:$|[^a-z0-9])/i
|
|
18
|
+
const PAD_FALLBACK_AUTHORED_ANCHOR_PATTERN =
|
|
19
|
+
/(?:^|[^a-z0-9])(?:antenna|coax|conn|connector|edge|flex|fpc|frame|hardware|header|jack|mechanical|module|shield|sma|socket|usb)(?:$|[^a-z0-9])/i
|
|
14
20
|
|
|
15
21
|
/**
|
|
16
22
|
* Repairs repeated Altium model-anchor bodies by matching their shared source
|
|
@@ -19,6 +25,7 @@ const TIMING_DESIGNATOR_PATTERN = /^(?:y|xo)\d+[a-z]?$/i
|
|
|
19
25
|
export class AltiumScene3dRepeatedModelOwnerRepair {
|
|
20
26
|
static #OFFSET_TOLERANCE_MIL = 8
|
|
21
27
|
static #MIN_OWNER_DISTANCE_MIL = 25
|
|
28
|
+
static #MIN_PAD_FALLBACK_OWNER_OFFSET_MIL = 1
|
|
22
29
|
|
|
23
30
|
/**
|
|
24
31
|
* Applies repeated-model owner repair to an Altium scene.
|
|
@@ -50,7 +57,7 @@ export class AltiumScene3dRepeatedModelOwnerRepair {
|
|
|
50
57
|
)
|
|
51
58
|
const placements = sceneDescription.externalPlacements.map(
|
|
52
59
|
(placement) =>
|
|
53
|
-
AltiumScene3dRepeatedModelOwnerRepair.#
|
|
60
|
+
AltiumScene3dRepeatedModelOwnerRepair.#withSingleOwnerCenter(
|
|
54
61
|
placement,
|
|
55
62
|
componentByDesignator.get(
|
|
56
63
|
String(placement?.designator || '')
|
|
@@ -156,18 +163,18 @@ export class AltiumScene3dRepeatedModelOwnerRepair {
|
|
|
156
163
|
}
|
|
157
164
|
|
|
158
165
|
/**
|
|
159
|
-
* Centers a
|
|
160
|
-
*
|
|
166
|
+
* Centers a single body on its resolved owner when the body anchor carries
|
|
167
|
+
* a source-origin offset that is not an authored mechanical anchor.
|
|
161
168
|
* @param {object} placement Scene placement.
|
|
162
169
|
* @param {object | undefined} component Resolved owner.
|
|
163
170
|
* @param {object} board Scene board metadata.
|
|
164
171
|
* @returns {object}
|
|
165
172
|
*/
|
|
166
|
-
static #
|
|
173
|
+
static #withSingleOwnerCenter(placement, component, board) {
|
|
167
174
|
if (
|
|
168
175
|
!component ||
|
|
169
176
|
String(placement?.projection?.source || '') !== 'pad-fallback' ||
|
|
170
|
-
|
|
177
|
+
AltiumScene3dRepeatedModelOwnerRepair.#isAuthoredAnchorPlacement(
|
|
171
178
|
placement,
|
|
172
179
|
component
|
|
173
180
|
)
|
|
@@ -185,7 +192,8 @@ export class AltiumScene3dRepeatedModelOwnerRepair {
|
|
|
185
192
|
}
|
|
186
193
|
if (
|
|
187
194
|
Math.hypot(offset.x, offset.y) <=
|
|
188
|
-
AltiumScene3dRepeatedModelOwnerRepair
|
|
195
|
+
AltiumScene3dRepeatedModelOwnerRepair
|
|
196
|
+
.#MIN_PAD_FALLBACK_OWNER_OFFSET_MIL
|
|
189
197
|
) {
|
|
190
198
|
return placement
|
|
191
199
|
}
|
|
@@ -194,28 +202,87 @@ export class AltiumScene3dRepeatedModelOwnerRepair {
|
|
|
194
202
|
placement,
|
|
195
203
|
component,
|
|
196
204
|
board,
|
|
197
|
-
offset
|
|
205
|
+
offset,
|
|
206
|
+
{ preserveRotation: true }
|
|
198
207
|
)
|
|
199
208
|
}
|
|
200
209
|
|
|
201
210
|
/**
|
|
202
|
-
* Checks whether a placement/component pair describes
|
|
211
|
+
* Checks whether a placement/component pair describes an authored hardware
|
|
212
|
+
* anchor whose source body position should remain authoritative.
|
|
203
213
|
* @param {object} placement Scene placement.
|
|
204
214
|
* @param {object} component PCB component.
|
|
205
215
|
* @returns {boolean}
|
|
206
216
|
*/
|
|
207
|
-
static #
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
placement
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
217
|
+
static #isAuthoredAnchorPlacement(placement, component) {
|
|
218
|
+
const identityText =
|
|
219
|
+
AltiumScene3dRepeatedModelOwnerRepair.#identityTextForPlacement(
|
|
220
|
+
placement,
|
|
221
|
+
component
|
|
222
|
+
)
|
|
223
|
+
if (PASSIVE_BODY_PATTERN.test(identityText)) {
|
|
224
|
+
return false
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (
|
|
228
|
+
String(placement?.projection?.source || '').toLowerCase() ===
|
|
229
|
+
'pad-fallback'
|
|
230
|
+
) {
|
|
231
|
+
return (
|
|
232
|
+
PAD_FALLBACK_AUTHORED_ANCHOR_PATTERN.test(identityText) ||
|
|
233
|
+
AltiumScene3dRepeatedModelOwnerRepair.#hasCompactIcSourceOriginOffset(
|
|
234
|
+
placement,
|
|
235
|
+
component,
|
|
236
|
+
identityText
|
|
237
|
+
)
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return AUTHORED_ANCHOR_IDENTITY_PATTERN.test(identityText)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Checks whether a compact IC uses a small authored source-origin offset.
|
|
246
|
+
* @param {object} placement Scene placement.
|
|
247
|
+
* @param {object} component PCB component.
|
|
248
|
+
* @param {string} identityText Searchable package identity.
|
|
249
|
+
* @returns {boolean}
|
|
250
|
+
*/
|
|
251
|
+
static #hasCompactIcSourceOriginOffset(placement, component, identityText) {
|
|
252
|
+
const distance = AltiumScene3dRepeatedModelOwnerRepair.#distance(
|
|
253
|
+
placement?.bodyPositionMil,
|
|
254
|
+
component
|
|
218
255
|
)
|
|
256
|
+
|
|
257
|
+
return (
|
|
258
|
+
COMPACT_IC_BODY_PATTERN.test(identityText) &&
|
|
259
|
+
distance >
|
|
260
|
+
AltiumScene3dRepeatedModelOwnerRepair
|
|
261
|
+
.#MIN_PAD_FALLBACK_OWNER_OFFSET_MIL &&
|
|
262
|
+
distance <
|
|
263
|
+
AltiumScene3dRepeatedModelOwnerRepair.#MIN_OWNER_DISTANCE_MIL
|
|
264
|
+
)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Builds searchable identity text for placement-owner policy checks.
|
|
269
|
+
* @param {object} placement Scene placement.
|
|
270
|
+
* @param {object} component PCB component.
|
|
271
|
+
* @returns {string}
|
|
272
|
+
*/
|
|
273
|
+
static #identityTextForPlacement(placement, component) {
|
|
274
|
+
return [
|
|
275
|
+
placement?.designator,
|
|
276
|
+
placement?.externalModel?.name,
|
|
277
|
+
placement?.externalModel?.relativePath,
|
|
278
|
+
placement?.externalModel?.sourceStream,
|
|
279
|
+
component?.pattern,
|
|
280
|
+
component?.source,
|
|
281
|
+
component?.description,
|
|
282
|
+
...Object.values(component?.parameters || {})
|
|
283
|
+
]
|
|
284
|
+
.map((value) => String(value || ''))
|
|
285
|
+
.join(' ')
|
|
219
286
|
}
|
|
220
287
|
|
|
221
288
|
/**
|
|
@@ -428,20 +495,32 @@ export class AltiumScene3dRepeatedModelOwnerRepair {
|
|
|
428
495
|
* @param {object} component Resolved owner.
|
|
429
496
|
* @param {object} board Scene board metadata.
|
|
430
497
|
* @param {{ x: number, y: number }} offset Source-origin offset.
|
|
498
|
+
* @param {{ preserveRotation?: boolean }} [options] Repair options.
|
|
431
499
|
* @returns {object}
|
|
432
500
|
*/
|
|
433
|
-
static #withOwner(placement, component, board, offset) {
|
|
501
|
+
static #withOwner(placement, component, board, offset, options = {}) {
|
|
434
502
|
const mountSide =
|
|
435
503
|
AltiumScene3dRepeatedModelOwnerRepair.#mountSide(component) ||
|
|
436
504
|
placement.mountSide
|
|
505
|
+
const rotationDeg = options?.preserveRotation
|
|
506
|
+
? AltiumScene3dRepeatedModelOwnerRepair.#normalizeAngle(
|
|
507
|
+
placement?.rotationDeg
|
|
508
|
+
)
|
|
509
|
+
: AltiumScene3dRepeatedModelOwnerRepair.#normalizeAngle(
|
|
510
|
+
component?.rotation
|
|
511
|
+
)
|
|
512
|
+
const renderableOffset =
|
|
513
|
+
AltiumScene3dRepeatedModelOwnerRepair.#renderableOwnerAnchorOffset(
|
|
514
|
+
{ mountSide, rotationDeg },
|
|
515
|
+
offset,
|
|
516
|
+
placement?.modelTransform
|
|
517
|
+
)
|
|
437
518
|
|
|
438
519
|
return {
|
|
439
520
|
...placement,
|
|
440
521
|
designator: String(component?.designator || placement.designator),
|
|
441
522
|
mountSide,
|
|
442
|
-
rotationDeg
|
|
443
|
-
component?.rotation
|
|
444
|
-
),
|
|
523
|
+
rotationDeg,
|
|
445
524
|
positionMil: {
|
|
446
525
|
...placement.positionMil,
|
|
447
526
|
x: Number(component?.x || 0) - Number(board?.centerX || 0),
|
|
@@ -453,6 +532,7 @@ export class AltiumScene3dRepeatedModelOwnerRepair {
|
|
|
453
532
|
},
|
|
454
533
|
modelTransform: {
|
|
455
534
|
...(placement.modelTransform || {}),
|
|
535
|
+
offsetMil: renderableOffset,
|
|
456
536
|
ownerAnchorOffsetMil: {
|
|
457
537
|
x: Number(offset?.x || 0),
|
|
458
538
|
y: Number(offset?.y || 0)
|
|
@@ -461,6 +541,52 @@ export class AltiumScene3dRepeatedModelOwnerRepair {
|
|
|
461
541
|
}
|
|
462
542
|
}
|
|
463
543
|
|
|
544
|
+
/**
|
|
545
|
+
* Converts a board-space source-origin offset into mount-rig local XY.
|
|
546
|
+
* @param {{ mountSide?: string, rotationDeg?: number }} placement Resolved placement fields.
|
|
547
|
+
* @param {{ x: number, y: number }} offset Board-space owner offset.
|
|
548
|
+
* @param {object | undefined} modelTransform Existing model transform.
|
|
549
|
+
* @returns {{ x: number, y: number, z: number }}
|
|
550
|
+
*/
|
|
551
|
+
static #renderableOwnerAnchorOffset(placement, offset, modelTransform) {
|
|
552
|
+
const rotationRad =
|
|
553
|
+
(-AltiumScene3dRepeatedModelOwnerRepair.#normalizeAngle(
|
|
554
|
+
Number(placement?.rotationDeg || 0)
|
|
555
|
+
) *
|
|
556
|
+
Math.PI) /
|
|
557
|
+
180
|
|
558
|
+
const cos = Math.cos(rotationRad)
|
|
559
|
+
const sin = Math.sin(rotationRad)
|
|
560
|
+
const x = Number(offset?.x || 0) * cos - Number(offset?.y || 0) * sin
|
|
561
|
+
const y = Number(offset?.x || 0) * sin + Number(offset?.y || 0) * cos
|
|
562
|
+
const z = Number(
|
|
563
|
+
modelTransform?.offsetMil?.z ?? modelTransform?.dzMil ?? 0
|
|
564
|
+
)
|
|
565
|
+
|
|
566
|
+
return {
|
|
567
|
+
x: Math.abs(x) < Number.EPSILON ? 0 : Number(x.toFixed(10)),
|
|
568
|
+
y: AltiumScene3dRepeatedModelOwnerRepair.#isBottomPlacement(
|
|
569
|
+
placement
|
|
570
|
+
)
|
|
571
|
+
? Math.abs(y) < Number.EPSILON
|
|
572
|
+
? 0
|
|
573
|
+
: Number((-y).toFixed(10))
|
|
574
|
+
: Math.abs(y) < Number.EPSILON
|
|
575
|
+
? 0
|
|
576
|
+
: Number(y.toFixed(10)),
|
|
577
|
+
z: Number.isFinite(z) ? z : 0
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* Checks whether one placement mounts on the board bottom face.
|
|
583
|
+
* @param {{ mountSide?: string } | null | undefined} placement Placement.
|
|
584
|
+
* @returns {boolean}
|
|
585
|
+
*/
|
|
586
|
+
static #isBottomPlacement(placement) {
|
|
587
|
+
return String(placement?.mountSide || '').toLowerCase() === 'bottom'
|
|
588
|
+
}
|
|
589
|
+
|
|
464
590
|
/**
|
|
465
591
|
* Scores connector-like identity tokens on one component.
|
|
466
592
|
* @param {object} component PCB component.
|