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
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { AltiumLayoutParser } from './AltiumLayoutParser.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Removes ownerless Altium free graphics that sit fully outside the authored
|
|
5
|
+
* schematic sheet bounds.
|
|
6
|
+
*/
|
|
7
|
+
export class AltiumSchematicSheetBoundsNormalizer {
|
|
8
|
+
static #BOUNDS_TOLERANCE = 0.001
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Applies sheet-bound filtering to parsed schematic primitives.
|
|
12
|
+
* @param {object} documentModel Parsed document model.
|
|
13
|
+
* @returns {object}
|
|
14
|
+
*/
|
|
15
|
+
static normalize(documentModel) {
|
|
16
|
+
const schematic = documentModel?.schematic
|
|
17
|
+
const bounds =
|
|
18
|
+
AltiumSchematicSheetBoundsNormalizer.#resolveSourceBounds(
|
|
19
|
+
schematic?.sheet
|
|
20
|
+
)
|
|
21
|
+
if (!schematic || !bounds) {
|
|
22
|
+
return documentModel
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const originalLines = schematic.lines
|
|
26
|
+
schematic.lines = AltiumSchematicSheetBoundsNormalizer.#filterLines(
|
|
27
|
+
schematic.lines,
|
|
28
|
+
bounds
|
|
29
|
+
)
|
|
30
|
+
if (
|
|
31
|
+
Array.isArray(originalLines) &&
|
|
32
|
+
schematic.lines.length !== originalLines.length
|
|
33
|
+
) {
|
|
34
|
+
AltiumSchematicSheetBoundsNormalizer.#shrinkSheetToRemainingContent(
|
|
35
|
+
schematic
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return documentModel
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Returns the source sheet rectangle when finite dimensions are available.
|
|
44
|
+
* @param {object | null | undefined} sheet Parsed sheet metadata.
|
|
45
|
+
* @returns {{ minX: number, minY: number, maxX: number, maxY: number } | null}
|
|
46
|
+
*/
|
|
47
|
+
static #resolveSourceBounds(sheet) {
|
|
48
|
+
const width =
|
|
49
|
+
AltiumSchematicSheetBoundsNormalizer.#finitePositiveNumber(
|
|
50
|
+
sheet?.sourceWidth
|
|
51
|
+
) ||
|
|
52
|
+
AltiumSchematicSheetBoundsNormalizer.#finitePositiveNumber(
|
|
53
|
+
sheet?.width
|
|
54
|
+
)
|
|
55
|
+
const height =
|
|
56
|
+
AltiumSchematicSheetBoundsNormalizer.#finitePositiveNumber(
|
|
57
|
+
sheet?.sourceHeight
|
|
58
|
+
) ||
|
|
59
|
+
AltiumSchematicSheetBoundsNormalizer.#finitePositiveNumber(
|
|
60
|
+
sheet?.height
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
if (!width || !height) {
|
|
64
|
+
return null
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
minX: 0,
|
|
69
|
+
minY: 0,
|
|
70
|
+
maxX: width,
|
|
71
|
+
maxY: height
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Filters unsupported off-sheet free graphic lines while preserving wires.
|
|
77
|
+
* @param {object[] | undefined} lines Parsed line primitives.
|
|
78
|
+
* @param {{ minX: number, minY: number, maxX: number, maxY: number }} bounds Source sheet bounds.
|
|
79
|
+
* @returns {object[] | undefined}
|
|
80
|
+
*/
|
|
81
|
+
static #filterLines(lines, bounds) {
|
|
82
|
+
if (!Array.isArray(lines)) {
|
|
83
|
+
return lines
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return lines.filter(
|
|
87
|
+
(line) =>
|
|
88
|
+
!AltiumSchematicSheetBoundsNormalizer.#isOffSheetFreeLine(
|
|
89
|
+
line,
|
|
90
|
+
bounds
|
|
91
|
+
)
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Recomputes inflated sheet dimensions from the remaining drawable model.
|
|
97
|
+
* @param {object} schematic Parsed schematic model.
|
|
98
|
+
* @returns {void}
|
|
99
|
+
*/
|
|
100
|
+
static #shrinkSheetToRemainingContent(schematic) {
|
|
101
|
+
const sheet = schematic?.sheet
|
|
102
|
+
if (!sheet || typeof sheet !== 'object') {
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const baseSheet =
|
|
107
|
+
AltiumSchematicSheetBoundsNormalizer.#sourceSizedSheet(sheet)
|
|
108
|
+
const resolvedSheet = AltiumLayoutParser.resolveSchematicSheetSize(
|
|
109
|
+
baseSheet,
|
|
110
|
+
[],
|
|
111
|
+
schematic.lines || [],
|
|
112
|
+
schematic.texts || [],
|
|
113
|
+
schematic.components || [],
|
|
114
|
+
schematic.pins || [],
|
|
115
|
+
[
|
|
116
|
+
...(schematic.rectangles || []),
|
|
117
|
+
...(schematic.roundedRectangles || [])
|
|
118
|
+
],
|
|
119
|
+
schematic.regions || [],
|
|
120
|
+
schematic.ports || [],
|
|
121
|
+
schematic.crosses || []
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
AltiumSchematicSheetBoundsNormalizer.#applyShrunkenSheetSize(
|
|
125
|
+
sheet,
|
|
126
|
+
resolvedSheet
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Builds a sheet metadata copy using source dimensions as declared bounds.
|
|
132
|
+
* @param {object} sheet Parsed sheet metadata.
|
|
133
|
+
* @returns {object}
|
|
134
|
+
*/
|
|
135
|
+
static #sourceSizedSheet(sheet) {
|
|
136
|
+
return {
|
|
137
|
+
...sheet,
|
|
138
|
+
width:
|
|
139
|
+
AltiumSchematicSheetBoundsNormalizer.#finitePositiveNumber(
|
|
140
|
+
sheet?.sourceWidth
|
|
141
|
+
) ||
|
|
142
|
+
AltiumSchematicSheetBoundsNormalizer.#finitePositiveNumber(
|
|
143
|
+
sheet?.width
|
|
144
|
+
),
|
|
145
|
+
height:
|
|
146
|
+
AltiumSchematicSheetBoundsNormalizer.#finitePositiveNumber(
|
|
147
|
+
sheet?.sourceHeight
|
|
148
|
+
) ||
|
|
149
|
+
AltiumSchematicSheetBoundsNormalizer.#finitePositiveNumber(
|
|
150
|
+
sheet?.height
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Applies only smaller resolved dimensions to avoid expanding content.
|
|
157
|
+
* @param {object} sheet Mutable sheet metadata.
|
|
158
|
+
* @param {object | null | undefined} resolvedSheet Recomputed sheet metadata.
|
|
159
|
+
* @returns {void}
|
|
160
|
+
*/
|
|
161
|
+
static #applyShrunkenSheetSize(sheet, resolvedSheet) {
|
|
162
|
+
const currentWidth =
|
|
163
|
+
AltiumSchematicSheetBoundsNormalizer.#finitePositiveNumber(
|
|
164
|
+
sheet?.width
|
|
165
|
+
)
|
|
166
|
+
const currentHeight =
|
|
167
|
+
AltiumSchematicSheetBoundsNormalizer.#finitePositiveNumber(
|
|
168
|
+
sheet?.height
|
|
169
|
+
)
|
|
170
|
+
const resolvedWidth =
|
|
171
|
+
AltiumSchematicSheetBoundsNormalizer.#finitePositiveNumber(
|
|
172
|
+
resolvedSheet?.width
|
|
173
|
+
)
|
|
174
|
+
const resolvedHeight =
|
|
175
|
+
AltiumSchematicSheetBoundsNormalizer.#finitePositiveNumber(
|
|
176
|
+
resolvedSheet?.height
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
if (resolvedWidth && currentWidth && resolvedWidth < currentWidth) {
|
|
180
|
+
sheet.width = resolvedWidth
|
|
181
|
+
}
|
|
182
|
+
if (resolvedHeight && currentHeight && resolvedHeight < currentHeight) {
|
|
183
|
+
sheet.height = resolvedHeight
|
|
184
|
+
}
|
|
185
|
+
if (
|
|
186
|
+
(sheet.width === resolvedWidth ||
|
|
187
|
+
sheet.height === resolvedHeight) &&
|
|
188
|
+
Object.prototype.hasOwnProperty.call(
|
|
189
|
+
resolvedSheet || {},
|
|
190
|
+
'paperSize'
|
|
191
|
+
)
|
|
192
|
+
) {
|
|
193
|
+
sheet.paperSize = resolvedSheet.paperSize
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Returns true when one free drawing line lies wholly outside the sheet.
|
|
199
|
+
* @param {object | null | undefined} line Parsed line primitive.
|
|
200
|
+
* @param {{ minX: number, minY: number, maxX: number, maxY: number }} bounds Source sheet bounds.
|
|
201
|
+
* @returns {boolean}
|
|
202
|
+
*/
|
|
203
|
+
static #isOffSheetFreeLine(line, bounds) {
|
|
204
|
+
if (
|
|
205
|
+
String(line?.recordType || '') !== '6' ||
|
|
206
|
+
AltiumSchematicSheetBoundsNormalizer.#hasOwner(line) ||
|
|
207
|
+
line?.isBus === true
|
|
208
|
+
) {
|
|
209
|
+
return false
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const lineBounds =
|
|
213
|
+
AltiumSchematicSheetBoundsNormalizer.#resolveLineBounds(line)
|
|
214
|
+
if (!lineBounds) {
|
|
215
|
+
return false
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return !AltiumSchematicSheetBoundsNormalizer.#boundsIntersect(
|
|
219
|
+
lineBounds,
|
|
220
|
+
bounds
|
|
221
|
+
)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Returns true when a primitive has an owner reference.
|
|
226
|
+
* @param {object | null | undefined} primitive Parsed primitive.
|
|
227
|
+
* @returns {boolean}
|
|
228
|
+
*/
|
|
229
|
+
static #hasOwner(primitive) {
|
|
230
|
+
return String(primitive?.ownerIndex || '').trim() !== ''
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Resolves finite bounds for one line primitive.
|
|
235
|
+
* @param {object | null | undefined} line Parsed line primitive.
|
|
236
|
+
* @returns {{ minX: number, minY: number, maxX: number, maxY: number } | null}
|
|
237
|
+
*/
|
|
238
|
+
static #resolveLineBounds(line) {
|
|
239
|
+
const x1 = Number(line?.x1)
|
|
240
|
+
const y1 = Number(line?.y1)
|
|
241
|
+
const x2 = Number(line?.x2)
|
|
242
|
+
const y2 = Number(line?.y2)
|
|
243
|
+
|
|
244
|
+
if (
|
|
245
|
+
!Number.isFinite(x1) ||
|
|
246
|
+
!Number.isFinite(y1) ||
|
|
247
|
+
!Number.isFinite(x2) ||
|
|
248
|
+
!Number.isFinite(y2)
|
|
249
|
+
) {
|
|
250
|
+
return null
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return {
|
|
254
|
+
minX: Math.min(x1, x2),
|
|
255
|
+
minY: Math.min(y1, y2),
|
|
256
|
+
maxX: Math.max(x1, x2),
|
|
257
|
+
maxY: Math.max(y1, y2)
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Returns true when two bounds overlap within tolerance.
|
|
263
|
+
* @param {{ minX: number, minY: number, maxX: number, maxY: number }} left First bounds.
|
|
264
|
+
* @param {{ minX: number, minY: number, maxX: number, maxY: number }} right Second bounds.
|
|
265
|
+
* @returns {boolean}
|
|
266
|
+
*/
|
|
267
|
+
static #boundsIntersect(left, right) {
|
|
268
|
+
const tolerance = AltiumSchematicSheetBoundsNormalizer.#BOUNDS_TOLERANCE
|
|
269
|
+
|
|
270
|
+
return !(
|
|
271
|
+
left.maxX < right.minX - tolerance ||
|
|
272
|
+
left.minX > right.maxX + tolerance ||
|
|
273
|
+
left.maxY < right.minY - tolerance ||
|
|
274
|
+
left.minY > right.maxY + tolerance
|
|
275
|
+
)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Converts a value to a finite positive number.
|
|
280
|
+
* @param {unknown} value Candidate number.
|
|
281
|
+
* @returns {number | null}
|
|
282
|
+
*/
|
|
283
|
+
static #finitePositiveNumber(value) {
|
|
284
|
+
const number = Number(value)
|
|
285
|
+
return Number.isFinite(number) && number > 0 ? number : null
|
|
286
|
+
}
|
|
287
|
+
}
|
package/src/parser.mjs
CHANGED
|
@@ -16,6 +16,11 @@ export { SourceBundleExporter } from './core/altium/SourceBundleExporter.mjs'
|
|
|
16
16
|
export { SourceComponentBundleNormalizer } from './core/altium/SourceComponentBundleNormalizer.mjs'
|
|
17
17
|
export { SourceComponentClient } from './core/altium/SourceComponentClient.mjs'
|
|
18
18
|
export { AltiumParser } from './core/altium/AltiumParser.mjs'
|
|
19
|
+
export { AltiumSchematicArcAngleNormalizer } from './core/altium/AltiumSchematicArcAngleNormalizer.mjs'
|
|
20
|
+
export { AltiumSchematicFreeGraphicStrokeNormalizer } from './core/altium/AltiumSchematicFreeGraphicStrokeNormalizer.mjs'
|
|
21
|
+
export { AltiumSchematicHiddenDesignatorResolver } from './core/altium/AltiumSchematicHiddenDesignatorResolver.mjs'
|
|
22
|
+
export { AltiumSchematicPackedImageResolver } from './core/altium/AltiumSchematicPackedImageResolver.mjs'
|
|
23
|
+
export { AltiumSchematicSheetBoundsNormalizer } from './core/altium/AltiumSchematicSheetBoundsNormalizer.mjs'
|
|
19
24
|
export { AltiumLayoutParser } from './core/altium/AltiumLayoutParser.mjs'
|
|
20
25
|
export { AsciiRecordParser } from './core/altium/AsciiRecordParser.mjs'
|
|
21
26
|
export { HostCapabilityDiagnosticsBuilder } from './core/altium/HostCapabilityDiagnosticsBuilder.mjs'
|
package/src/renderers.mjs
CHANGED
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
4
|
|
|
5
5
|
export { BomTableRenderer } from './ui/BomTableRenderer.mjs'
|
|
6
|
+
export { AltiumPcbBottomViewMirror } from './ui/AltiumPcbBottomViewMirror.mjs'
|
|
6
7
|
export { PcbArcUtils } from './ui/PcbArcUtils.mjs'
|
|
7
8
|
export { PcbEdgeFacingGlyphNormalizer } from './ui/PcbEdgeFacingGlyphNormalizer.mjs'
|
|
8
9
|
export { PcbFootprintPrimitiveSelector } from './ui/PcbFootprintPrimitiveSelector.mjs'
|
|
10
|
+
export { PcbFootprintPadAxisNormalizer } from './ui/PcbFootprintPadAxisNormalizer.mjs'
|
|
9
11
|
export { PcbInteractionIndex } from './ui/PcbInteractionIndex.mjs'
|
|
10
12
|
export { PcbInteractionItemRegistry } from './ui/PcbInteractionItemRegistry.mjs'
|
|
11
13
|
export { PcbInteractionLayerModel } from './ui/PcbInteractionLayerModel.mjs'
|
package/src/scene3d.mjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
4
|
|
|
5
5
|
export { PcbScene3dBuilder } from './ui/PcbScene3dBuilder.mjs'
|
|
6
|
+
export { AltiumScene3dAuthoredBodyAnchorAdapter } from './ui/AltiumScene3dAuthoredBodyAnchorAdapter.mjs'
|
|
6
7
|
export { PcbScene3dModelRegistry } from './ui/PcbScene3dModelRegistry.mjs'
|
|
7
8
|
export { PcbScene3dPackages } from './ui/PcbScene3dPackages.mjs'
|
|
8
9
|
export { PcbScene3dScenePreparator } from './ui/PcbScene3dScenePreparator.mjs'
|