altium-toolkit 1.1.22 → 1.1.24

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.
Files changed (99) hide show
  1. package/README.md +34 -5
  2. package/docs/api.md +134 -23
  3. package/docs/model-format.md +174 -20
  4. package/docs/schemas/altium_toolkit/embedded_assets_a1.schema.json +56 -0
  5. package/docs/schemas/altium_toolkit/fixture_coverage_matrix_a1.schema.json +89 -0
  6. package/docs/schemas/altium_toolkit/geometry_bounds_a1.schema.json +86 -0
  7. package/docs/schemas/altium_toolkit/library_catalog_a1.schema.json +65 -0
  8. package/docs/schemas/altium_toolkit/library_diff_a1.schema.json +54 -0
  9. package/docs/schemas/altium_toolkit/library_inspection_a1.schema.json +94 -0
  10. package/docs/schemas/altium_toolkit/library_qa_a1.schema.json +4 -0
  11. package/docs/schemas/altium_toolkit/native_stream_inventory_a1.schema.json +66 -0
  12. package/docs/schemas/altium_toolkit/normalized_model_a1.schema.json +511 -1
  13. package/docs/schemas/altium_toolkit/parameter_record_inventory_a1.schema.json +84 -0
  14. package/docs/schemas/altium_toolkit/parser_diagnostics_a1.schema.json +63 -0
  15. package/docs/schemas/altium_toolkit/parser_value_verification_a1.schema.json +74 -0
  16. package/docs/schemas/altium_toolkit/pcb_class_report_a1.schema.json +79 -0
  17. package/docs/schemas/altium_toolkit/pcb_inspection_a1.schema.json +65 -0
  18. package/docs/schemas/altium_toolkit/pcb_net_membership_a1.schema.json +98 -0
  19. package/docs/schemas/altium_toolkit/project_bundle_a1.schema.json +3 -0
  20. package/docs/schemas/altium_toolkit/project_hierarchy_a1.schema.json +79 -0
  21. package/docs/schemas/altium_toolkit/unsupported_features_a1.schema.json +212 -0
  22. package/docs/testing.md +2 -0
  23. package/examples/README.md +21 -0
  24. package/examples/cli-utils.mjs +148 -0
  25. package/examples/corpus-smoke.mjs +523 -0
  26. package/examples/extract-bom.mjs +47 -0
  27. package/examples/generate-pnp.mjs +59 -0
  28. package/examples/inspect-board.mjs +70 -0
  29. package/examples/inspect-schematic.mjs +406 -0
  30. package/examples/library-catalog.mjs +115 -0
  31. package/examples/net-report.mjs +61 -0
  32. package/examples/validate-library.mjs +59 -0
  33. package/package.json +1 -1
  34. package/src/core/BinaryReader.mjs +213 -2
  35. package/src/core/altium/AltiumParser.mjs +352 -14
  36. package/src/core/altium/AltiumUnits.mjs +205 -0
  37. package/src/core/altium/AsciiRecordParser.mjs +9 -0
  38. package/src/core/altium/EmbeddedAssetReportBuilder.mjs +383 -0
  39. package/src/core/altium/FixtureCoverageMatrixBuilder.mjs +304 -0
  40. package/src/core/altium/GeometryBoundsReportBuilder.mjs +935 -0
  41. package/src/core/altium/LibraryCatalogArtifactBuilder.mjs +296 -0
  42. package/src/core/altium/LibraryDiffReportBuilder.mjs +260 -0
  43. package/src/core/altium/LibraryInspectionReportBuilder.mjs +156 -0
  44. package/src/core/altium/LibraryQaReportBuilder.mjs +374 -1
  45. package/src/core/altium/NativeStreamInventoryBuilder.mjs +177 -0
  46. package/src/core/altium/NormalizedModelSchema.mjs +3 -31
  47. package/src/core/altium/ParameterCollection.mjs +431 -0
  48. package/src/core/altium/ParameterRecordInventoryBuilder.mjs +274 -0
  49. package/src/core/altium/ParserCompatibilityFuzzer.mjs +106 -2
  50. package/src/core/altium/ParserDiagnosticNormalizer.mjs +213 -0
  51. package/src/core/altium/ParserErrors.mjs +90 -0
  52. package/src/core/altium/ParserFieldCoverageReportBuilder.mjs +656 -0
  53. package/src/core/altium/ParserUtils.mjs +24 -0
  54. package/src/core/altium/ParserValueVerificationReportBuilder.mjs +323 -0
  55. package/src/core/altium/PcbClassReportBuilder.mjs +366 -0
  56. package/src/core/altium/PcbInspectionReportBuilder.mjs +313 -0
  57. package/src/core/altium/PcbLayerGroups.mjs +308 -0
  58. package/src/core/altium/PcbLayerStackCustomDataParser.mjs +183 -0
  59. package/src/core/altium/PcbLayerStackInterchangeParser.mjs +473 -4
  60. package/src/core/altium/PcbLayerStackReadModelBuilder.mjs +83 -15
  61. package/src/core/altium/PcbLayerStackSourceMetadataParser.mjs +74 -4
  62. package/src/core/altium/PcbLibModelParser.mjs +20 -4
  63. package/src/core/altium/PcbLibStreamExtractor.mjs +49 -6
  64. package/src/core/altium/PcbModelParser.mjs +223 -4
  65. package/src/core/altium/PcbNetMembershipReportBuilder.mjs +270 -0
  66. package/src/core/altium/PcbStreamExtractor.mjs +130 -6
  67. package/src/core/altium/PcbTrackPrimitiveParser.mjs +66 -2
  68. package/src/core/altium/ProjectDesignBundleBuilder.mjs +15 -0
  69. package/src/core/altium/ProjectHierarchyReportBuilder.mjs +660 -0
  70. package/src/core/altium/ProjectNetlistExporter.mjs +2 -0
  71. package/src/core/altium/RawDataPreservationReportBuilder.mjs +348 -0
  72. package/src/core/altium/SchLibModelParser.mjs +840 -0
  73. package/src/core/altium/SchLibStreamExtractor.mjs +586 -0
  74. package/src/core/altium/SchematicBusEntryParser.mjs +3 -2
  75. package/src/core/altium/SchematicCodeSymbolParser.mjs +663 -0
  76. package/src/core/altium/SchematicConnectivityQaBuilder.mjs +177 -2
  77. package/src/core/altium/SchematicDisplayModeCatalogParser.mjs +10 -1
  78. package/src/core/altium/SchematicFieldCoverageReportBuilder.mjs +549 -0
  79. package/src/core/altium/SchematicHarnessParser.mjs +9 -3
  80. package/src/core/altium/SchematicHyperlinkParser.mjs +122 -0
  81. package/src/core/altium/SchematicNetlistBuilder.mjs +271 -8
  82. package/src/core/altium/SchematicOwnershipGraphParser.mjs +102 -3
  83. package/src/core/altium/SchematicPinParser.mjs +12 -45
  84. package/src/core/altium/SchematicPrimitiveParser.mjs +9 -14
  85. package/src/core/altium/SchematicQaReportBuilder.mjs +2 -0
  86. package/src/core/altium/SchematicRecordStreamParser.mjs +183 -0
  87. package/src/core/altium/SchematicRecordTypeRegistry.mjs +6 -1
  88. package/src/core/altium/SchematicSheetParser.mjs +8 -2
  89. package/src/core/altium/SchematicStreamExtractor.mjs +64 -25
  90. package/src/core/altium/SchematicTextOrientationResolver.mjs +76 -0
  91. package/src/core/altium/SchematicTextParser.mjs +28 -12
  92. package/src/core/altium/SchematicTextRunParser.mjs +81 -0
  93. package/src/core/altium/SchematicThumbnailParser.mjs +425 -0
  94. package/src/core/altium/UnsupportedFeatureReportBuilder.mjs +380 -0
  95. package/src/parser.mjs +35 -1
  96. package/src/renderers.mjs +1 -0
  97. package/src/ui/SchematicShapeRenderer.mjs +49 -6
  98. package/src/ui/SchematicSvgRenderer.mjs +37 -8
  99. package/src/ui/SchematicTypography.mjs +4 -3
@@ -0,0 +1,380 @@
1
+ // SPDX-FileCopyrightText: 2026 André Fiedler
2
+ //
3
+ // SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ /**
6
+ * Builds summaries of preserved parser data that is not modeled yet.
7
+ */
8
+ export class UnsupportedFeatureReportBuilder {
9
+ static SCHEMA = 'altium-toolkit.unsupported-features.a1'
10
+
11
+ /**
12
+ * Builds an unsupported feature report.
13
+ * @param {{ models?: object[], recordTypes?: object[], rawRecords?: object[], opaqueRecords?: object[], diagnostics?: object[] }} [input] Report input.
14
+ * @returns {object}
15
+ */
16
+ static build(input = {}) {
17
+ const models = UnsupportedFeatureReportBuilder.#models(input)
18
+ const recordTypes =
19
+ UnsupportedFeatureReportBuilder.#unsupportedRecordTypes(
20
+ input,
21
+ models
22
+ )
23
+ const rawRecords = UnsupportedFeatureReportBuilder.#rawRecords(
24
+ input,
25
+ models
26
+ )
27
+ const opaqueRecords = UnsupportedFeatureReportBuilder.#opaqueRecords(
28
+ input,
29
+ models
30
+ )
31
+ const diagnostics = UnsupportedFeatureReportBuilder.#diagnostics(
32
+ input,
33
+ models
34
+ )
35
+ const itemCount =
36
+ recordTypes.length +
37
+ rawRecords.length +
38
+ opaqueRecords.length +
39
+ diagnostics.length
40
+
41
+ return {
42
+ schema: UnsupportedFeatureReportBuilder.SCHEMA,
43
+ summary: {
44
+ modelCount: models.length,
45
+ unsupportedRecordTypeCount: recordTypes.length,
46
+ rawRecordCount: rawRecords.length,
47
+ opaqueRecordCount: opaqueRecords.length,
48
+ diagnosticCount: diagnostics.length,
49
+ itemCount,
50
+ status: itemCount ? 'unsupported' : 'supported'
51
+ },
52
+ recordTypes,
53
+ rawRecords,
54
+ opaqueRecords,
55
+ diagnostics
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Normalizes model input.
61
+ * @param {object} input Report input.
62
+ * @returns {object[]}
63
+ */
64
+ static #models(input) {
65
+ return Array.isArray(input?.models) ? input.models : []
66
+ }
67
+
68
+ /**
69
+ * Collects unsupported record type summaries.
70
+ * @param {object} input Report input.
71
+ * @param {object[]} models Parser roots.
72
+ * @returns {object[]}
73
+ */
74
+ static #unsupportedRecordTypes(input, models) {
75
+ return [
76
+ ...(Array.isArray(input?.recordTypes)
77
+ ? input.recordTypes.map((recordType) => ({
78
+ row: recordType || {},
79
+ fileName: recordType?.fileName || '',
80
+ domain: recordType?.domain || ''
81
+ }))
82
+ : []),
83
+ ...models.flatMap((model) =>
84
+ UnsupportedFeatureReportBuilder.#modelRecordTypes(model)
85
+ )
86
+ ]
87
+ .filter(({ row }) => row?.supported === false)
88
+ .map(({ row, fileName, domain }) =>
89
+ UnsupportedFeatureReportBuilder.#stripUndefined({
90
+ fileName,
91
+ domain,
92
+ recordType:
93
+ UnsupportedFeatureReportBuilder.#finiteOrUndefined(
94
+ row.recordType
95
+ ),
96
+ name: row.name,
97
+ family: row.family,
98
+ count: UnsupportedFeatureReportBuilder.#finiteOrUndefined(
99
+ row.count
100
+ )
101
+ })
102
+ )
103
+ }
104
+
105
+ /**
106
+ * Collects record type rows from one parser root.
107
+ * @param {object} model Parser root.
108
+ * @returns {{ row: object, fileName: string, domain: string }[]}
109
+ */
110
+ static #modelRecordTypes(model) {
111
+ const fileName = String(model?.fileName || '')
112
+ return [
113
+ ...UnsupportedFeatureReportBuilder.#domainRows(
114
+ model?.schematic?.recordTypes,
115
+ fileName,
116
+ 'schematic'
117
+ ),
118
+ ...UnsupportedFeatureReportBuilder.#domainRows(
119
+ model?.schematicLibrary?.recordTypes,
120
+ fileName,
121
+ 'schematic-library'
122
+ )
123
+ ]
124
+ }
125
+
126
+ /**
127
+ * Wraps domain rows with file and domain metadata.
128
+ * @param {object[] | undefined} rows Rows.
129
+ * @param {string} fileName Source file name.
130
+ * @param {string} domain Parser domain.
131
+ * @returns {{ row: object, fileName: string, domain: string }[]}
132
+ */
133
+ static #domainRows(rows, fileName, domain) {
134
+ return (Array.isArray(rows) ? rows : []).map((row) => ({
135
+ row,
136
+ fileName,
137
+ domain
138
+ }))
139
+ }
140
+
141
+ /**
142
+ * Collects unsupported or unparsed raw record rows.
143
+ * @param {object} input Report input.
144
+ * @param {object[]} models Parser roots.
145
+ * @returns {object[]}
146
+ */
147
+ static #rawRecords(input, models) {
148
+ return [
149
+ ...(Array.isArray(input?.rawRecords)
150
+ ? input.rawRecords.map((record) => ({
151
+ record: record || {},
152
+ fileName: record?.fileName || '',
153
+ domain: record?.domain || 'pcb'
154
+ }))
155
+ : []),
156
+ ...models.flatMap((model) =>
157
+ UnsupportedFeatureReportBuilder.#modelRawRecords(model)
158
+ )
159
+ ]
160
+ .filter(
161
+ ({ record }) =>
162
+ record?.supported === false || record?.parsed === false
163
+ )
164
+ .map(({ record, fileName, domain }) =>
165
+ UnsupportedFeatureReportBuilder.#stripUndefined({
166
+ fileName,
167
+ domain,
168
+ sourceStream: record.sourceStream,
169
+ sourceStorage: record.sourceStorage,
170
+ recordIndex:
171
+ UnsupportedFeatureReportBuilder.#finiteOrUndefined(
172
+ record.recordIndex
173
+ ),
174
+ family: record.family,
175
+ type: record.type,
176
+ typeId: UnsupportedFeatureReportBuilder.#finiteOrUndefined(
177
+ record.typeId
178
+ ),
179
+ byteLength:
180
+ UnsupportedFeatureReportBuilder.#finiteOrUndefined(
181
+ record.byteLength
182
+ ),
183
+ supported: record.supported,
184
+ parsed: record.parsed
185
+ })
186
+ )
187
+ }
188
+
189
+ /**
190
+ * Collects raw records from one parser root.
191
+ * @param {object} model Parser root.
192
+ * @returns {{ record: object, fileName: string, domain: string }[]}
193
+ */
194
+ static #modelRawRecords(model) {
195
+ const fileName = String(model?.fileName || '')
196
+ return [
197
+ ...UnsupportedFeatureReportBuilder.#rawDomainRows(
198
+ model?.pcb?.rawRecords,
199
+ fileName,
200
+ 'pcb'
201
+ ),
202
+ ...UnsupportedFeatureReportBuilder.#rawDomainRows(
203
+ (model?.pcbLibrary?.footprints || []).flatMap(
204
+ (footprint) => footprint.rawRecords || []
205
+ ),
206
+ fileName,
207
+ 'pcb-library'
208
+ ),
209
+ ...UnsupportedFeatureReportBuilder.#rawDomainRows(
210
+ model?.schematic?.rawRecords,
211
+ fileName,
212
+ 'schematic'
213
+ ),
214
+ ...UnsupportedFeatureReportBuilder.#rawDomainRows(
215
+ (model?.schematicLibrary?.components || []).flatMap(
216
+ (component) => component.rawRecords || []
217
+ ),
218
+ fileName,
219
+ 'schematic-library'
220
+ )
221
+ ]
222
+ }
223
+
224
+ /**
225
+ * Wraps raw record rows with source metadata.
226
+ * @param {object[] | undefined} records Raw records.
227
+ * @param {string} fileName Source file name.
228
+ * @param {string} domain Parser domain.
229
+ * @returns {{ record: object, fileName: string, domain: string }[]}
230
+ */
231
+ static #rawDomainRows(records, fileName, domain) {
232
+ return (Array.isArray(records) ? records : []).map((record) => ({
233
+ record,
234
+ fileName,
235
+ domain
236
+ }))
237
+ }
238
+
239
+ /**
240
+ * Collects opaque preserved rows.
241
+ * @param {object} input Report input.
242
+ * @param {object[]} models Parser roots.
243
+ * @returns {object[]}
244
+ */
245
+ static #opaqueRecords(input, models) {
246
+ return [
247
+ ...(Array.isArray(input?.opaqueRecords)
248
+ ? input.opaqueRecords.map((record) => ({
249
+ record: record || {},
250
+ fileName: record?.fileName || '',
251
+ domain: record?.domain || 'schematic'
252
+ }))
253
+ : []),
254
+ ...models.flatMap((model) =>
255
+ UnsupportedFeatureReportBuilder.#modelOpaqueRecords(model)
256
+ )
257
+ ].map(({ record, fileName, domain }) =>
258
+ UnsupportedFeatureReportBuilder.#stripUndefined({
259
+ fileName,
260
+ domain,
261
+ sourceStream: record.sourceStream,
262
+ sourceStorage: record.sourceStorage,
263
+ frameType: UnsupportedFeatureReportBuilder.#finiteOrUndefined(
264
+ record.frameType
265
+ ),
266
+ recordIndex: UnsupportedFeatureReportBuilder.#finiteOrUndefined(
267
+ record.recordIndex
268
+ ),
269
+ byteLength: UnsupportedFeatureReportBuilder.#finiteOrUndefined(
270
+ record.byteLength
271
+ )
272
+ })
273
+ )
274
+ }
275
+
276
+ /**
277
+ * Collects opaque rows from one parser root.
278
+ * @param {object} model Parser root.
279
+ * @returns {{ record: object, fileName: string, domain: string }[]}
280
+ */
281
+ static #modelOpaqueRecords(model) {
282
+ const fileName = String(model?.fileName || '')
283
+ return [
284
+ ...UnsupportedFeatureReportBuilder.#rawDomainRows(
285
+ model?.schematic?.opaqueRecords,
286
+ fileName,
287
+ 'schematic'
288
+ ),
289
+ ...UnsupportedFeatureReportBuilder.#rawDomainRows(
290
+ (model?.schematicLibrary?.components || []).flatMap(
291
+ (component) => component.opaqueRecords || []
292
+ ),
293
+ fileName,
294
+ 'schematic-library'
295
+ )
296
+ ].map(({ record, ...metadata }) => ({ record, ...metadata }))
297
+ }
298
+
299
+ /**
300
+ * Collects unsupported diagnostics.
301
+ * @param {object} input Report input.
302
+ * @param {object[]} models Parser roots.
303
+ * @returns {object[]}
304
+ */
305
+ static #diagnostics(input, models) {
306
+ return [
307
+ ...(Array.isArray(input?.diagnostics)
308
+ ? input.diagnostics.map((diagnostic) => ({
309
+ diagnostic: diagnostic || {},
310
+ fileName: diagnostic?.fileName || ''
311
+ }))
312
+ : []),
313
+ ...models.flatMap((model) =>
314
+ (model?.diagnostics || []).map((diagnostic) => ({
315
+ diagnostic,
316
+ fileName: model.fileName || diagnostic.fileName || ''
317
+ }))
318
+ )
319
+ ]
320
+ .filter(({ diagnostic }) =>
321
+ UnsupportedFeatureReportBuilder.#isUnsupportedDiagnostic(
322
+ diagnostic
323
+ )
324
+ )
325
+ .map(({ diagnostic, fileName }) =>
326
+ UnsupportedFeatureReportBuilder.#stripUndefined({
327
+ fileName,
328
+ code: diagnostic.code,
329
+ severity: diagnostic.severity,
330
+ message: diagnostic.message,
331
+ source: diagnostic.source,
332
+ sourceStream: diagnostic.sourceStream,
333
+ sourceStorage: diagnostic.sourceStorage,
334
+ recordIndex:
335
+ UnsupportedFeatureReportBuilder.#finiteOrUndefined(
336
+ diagnostic.recordIndex
337
+ ),
338
+ recordType:
339
+ UnsupportedFeatureReportBuilder.#finiteOrUndefined(
340
+ diagnostic.recordType
341
+ ),
342
+ errorKind: diagnostic.errorKind
343
+ })
344
+ )
345
+ }
346
+
347
+ /**
348
+ * Returns true for unsupported-feature diagnostics.
349
+ * @param {object} diagnostic Diagnostic row.
350
+ * @returns {boolean}
351
+ */
352
+ static #isUnsupportedDiagnostic(diagnostic) {
353
+ if (!diagnostic || typeof diagnostic !== 'object') return false
354
+ if (diagnostic.errorKind === 'unsupported-feature') return true
355
+ return String(diagnostic.code || '')
356
+ .toLowerCase()
357
+ .includes('unsupported')
358
+ }
359
+
360
+ /**
361
+ * Returns a finite number or undefined.
362
+ * @param {unknown} value Candidate value.
363
+ * @returns {number | undefined}
364
+ */
365
+ static #finiteOrUndefined(value) {
366
+ const number = Number(value)
367
+ return Number.isFinite(number) ? number : undefined
368
+ }
369
+
370
+ /**
371
+ * Removes undefined values from one row.
372
+ * @param {object} row Row to clean.
373
+ * @returns {object}
374
+ */
375
+ static #stripUndefined(row) {
376
+ return Object.fromEntries(
377
+ Object.entries(row).filter(([, value]) => value !== undefined)
378
+ )
379
+ }
380
+ }
package/src/parser.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  //
3
3
  // SPDX-License-Identifier: GPL-3.0-or-later
4
4
 
5
- export { BinaryReader } from './core/BinaryReader.mjs'
5
+ export { BinaryReader, BinaryReaderCursor } from './core/BinaryReader.mjs'
6
6
  export { OleCompoundDocument } from './core/ole/OleCompoundDocument.mjs'
7
7
  export { OleConstants } from './core/ole/OleConstants.mjs'
8
8
  export { OleDirectoryEntry } from './core/ole/OleDirectoryEntry.mjs'
@@ -11,6 +11,7 @@ export { AltiumLibraryBatchExporter } from './core/altium/AltiumLibraryBatchExpo
11
11
  export { AltiumLibraryRecordBuilder } from './core/altium/AltiumLibraryRecordBuilder.mjs'
12
12
  export { AltiumPcbLibExporter } from './core/altium/AltiumPcbLibExporter.mjs'
13
13
  export { AltiumSchLibExporter } from './core/altium/AltiumSchLibExporter.mjs'
14
+ export { AltiumUnits } from './core/altium/AltiumUnits.mjs'
14
15
  export { SourceBundleExporter } from './core/altium/SourceBundleExporter.mjs'
15
16
  export { SourceComponentBundleNormalizer } from './core/altium/SourceComponentBundleNormalizer.mjs'
16
17
  export { SourceComponentClient } from './core/altium/SourceComponentClient.mjs'
@@ -20,19 +21,41 @@ export { AsciiRecordParser } from './core/altium/AsciiRecordParser.mjs'
20
21
  export { HostCapabilityDiagnosticsBuilder } from './core/altium/HostCapabilityDiagnosticsBuilder.mjs'
21
22
  export { IntLibModelParser } from './core/altium/IntLibModelParser.mjs'
22
23
  export { IntLibStreamExtractor } from './core/altium/IntLibStreamExtractor.mjs'
24
+ export { EmbeddedAssetReportBuilder } from './core/altium/EmbeddedAssetReportBuilder.mjs'
23
25
  export { EmbeddedFileInventoryBuilder } from './core/altium/EmbeddedFileInventoryBuilder.mjs'
26
+ export { FixtureCoverageMatrixBuilder } from './core/altium/FixtureCoverageMatrixBuilder.mjs'
27
+ export { GeometryBoundsReportBuilder } from './core/altium/GeometryBoundsReportBuilder.mjs'
28
+ export {
29
+ ParameterCollection,
30
+ ParameterValue
31
+ } from './core/altium/ParameterCollection.mjs'
32
+ export {
33
+ AltiumFileError,
34
+ AltiumParseError,
35
+ AltiumCorruptFileError,
36
+ AltiumUnsupportedFeatureError
37
+ } from './core/altium/ParserErrors.mjs'
24
38
  export { CiArtifactBundleBuilder } from './core/altium/CiArtifactBundleBuilder.mjs'
25
39
  export { ContractGateReportBuilder } from './core/altium/ContractGateReportBuilder.mjs'
26
40
  export { DraftsmanBoardViewMetadataBuilder } from './core/altium/DraftsmanBoardViewMetadataBuilder.mjs'
27
41
  export { DraftsmanDigestParser } from './core/altium/DraftsmanDigestParser.mjs'
28
42
  export { LibraryRenderManifestBuilder } from './core/altium/LibraryRenderManifestBuilder.mjs'
43
+ export { LibraryCatalogArtifactBuilder } from './core/altium/LibraryCatalogArtifactBuilder.mjs'
44
+ export { LibraryDiffReportBuilder } from './core/altium/LibraryDiffReportBuilder.mjs'
45
+ export { LibraryInspectionReportBuilder } from './core/altium/LibraryInspectionReportBuilder.mjs'
29
46
  export { LibraryQaReportBuilder } from './core/altium/LibraryQaReportBuilder.mjs'
30
47
  export { LibrarySearchIndex } from './core/altium/LibrarySearchIndex.mjs'
48
+ export { NativeStreamInventoryBuilder } from './core/altium/NativeStreamInventoryBuilder.mjs'
31
49
  export { ParserUtils } from './core/altium/ParserUtils.mjs'
50
+ export { ParameterRecordInventoryBuilder } from './core/altium/ParameterRecordInventoryBuilder.mjs'
32
51
  export { ParserCompatibilityFuzzer } from './core/altium/ParserCompatibilityFuzzer.mjs'
52
+ export { ParserDiagnosticNormalizer } from './core/altium/ParserDiagnosticNormalizer.mjs'
53
+ export { ParserFieldCoverageReportBuilder } from './core/altium/ParserFieldCoverageReportBuilder.mjs'
54
+ export { ParserValueVerificationReportBuilder } from './core/altium/ParserValueVerificationReportBuilder.mjs'
33
55
  export { NormalizedModelSchema } from './core/altium/NormalizedModelSchema.mjs'
34
56
  export { ProjectDesignBundleBuilder } from './core/altium/ProjectDesignBundleBuilder.mjs'
35
57
  export { ProjectDocumentGraphBuilder } from './core/altium/ProjectDocumentGraphBuilder.mjs'
58
+ export { ProjectHierarchyReportBuilder } from './core/altium/ProjectHierarchyReportBuilder.mjs'
36
59
  export { ProjectAnnotationParser } from './core/altium/ProjectAnnotationParser.mjs'
37
60
  export { ProjectNetlistExporter } from './core/altium/ProjectNetlistExporter.mjs'
38
61
  export { ProjectVariantViewBuilder } from './core/altium/ProjectVariantViewBuilder.mjs'
@@ -50,6 +73,7 @@ export { PcbEmbeddedModelExtractor } from './core/altium/PcbEmbeddedModelExtract
50
73
  export { PcbExtendedPrimitiveInformationParser } from './core/altium/PcbExtendedPrimitiveInformationParser.mjs'
51
74
  export { PcbFontMetricsParser } from './core/altium/PcbFontMetricsParser.mjs'
52
75
  export { PcbLayerIdCodec } from './core/altium/PcbLayerIdCodec.mjs'
76
+ export { PcbLayerGroups } from './core/altium/PcbLayerGroups.mjs'
53
77
  export { PcbLayerStackFidelityReportBuilder } from './core/altium/PcbLayerStackFidelityReportBuilder.mjs'
54
78
  export { PcbLayerStackInterchangeParser } from './core/altium/PcbLayerStackInterchangeParser.mjs'
55
79
  export { PcbLayerStackQueryHelper } from './core/altium/PcbLayerStackQueryHelper.mjs'
@@ -67,6 +91,9 @@ export { PcbPickPlacePositionResolver } from './core/altium/PcbPickPlacePosition
67
91
  export { PcbPolygonRecordParser } from './core/altium/PcbPolygonRecordParser.mjs'
68
92
  export { PcbRawRecordRegistry } from './core/altium/PcbRawRecordRegistry.mjs'
69
93
  export { PcbReviewMetadataBuilder } from './core/altium/PcbReviewMetadataBuilder.mjs'
94
+ export { PcbClassReportBuilder } from './core/altium/PcbClassReportBuilder.mjs'
95
+ export { PcbInspectionReportBuilder } from './core/altium/PcbInspectionReportBuilder.mjs'
96
+ export { PcbNetMembershipReportBuilder } from './core/altium/PcbNetMembershipReportBuilder.mjs'
70
97
  export { PcbRouteAnalysisBuilder } from './core/altium/PcbRouteAnalysisBuilder.mjs'
71
98
  export { PcbRuleParser } from './core/altium/PcbRuleParser.mjs'
72
99
  export { PcbSidecarRecordParser } from './core/altium/PcbSidecarRecordParser.mjs'
@@ -80,15 +107,22 @@ export { PrintableTextDecoder } from './core/altium/PrintableTextDecoder.mjs'
80
107
  export { PrjPcbModelParser } from './core/altium/PrjPcbModelParser.mjs'
81
108
  export { PrjScrModelParser } from './core/altium/PrjScrModelParser.mjs'
82
109
  export { ProjectBomPnpReconciliationBuilder } from './core/altium/ProjectBomPnpReconciliationBuilder.mjs'
110
+ export { RawDataPreservationReportBuilder } from './core/altium/RawDataPreservationReportBuilder.mjs'
111
+ export { SchLibModelParser } from './core/altium/SchLibModelParser.mjs'
112
+ export { SchLibStreamExtractor } from './core/altium/SchLibStreamExtractor.mjs'
83
113
  export { SchematicMultipartOwnerMatcher } from './core/altium/SchematicMultipartOwnerMatcher.mjs'
84
114
  export { SchematicCrossSheetConnectorParser } from './core/altium/SchematicCrossSheetConnectorParser.mjs'
115
+ export { SchematicCodeSymbolParser } from './core/altium/SchematicCodeSymbolParser.mjs'
85
116
  export { SchematicHarnessParser } from './core/altium/SchematicHarnessParser.mjs'
86
117
  export { SchematicImplementationParser } from './core/altium/SchematicImplementationParser.mjs'
87
118
  export { SchematicOwnershipGraphParser } from './core/altium/SchematicOwnershipGraphParser.mjs'
88
119
  export { SchematicProjectParameterResolver } from './core/altium/SchematicProjectParameterResolver.mjs'
89
120
  export { SchematicQaReportBuilder } from './core/altium/SchematicQaReportBuilder.mjs'
121
+ export { SchematicRecordStreamParser } from './core/altium/SchematicRecordStreamParser.mjs'
90
122
  export { SchematicRecordTypeRegistry } from './core/altium/SchematicRecordTypeRegistry.mjs'
91
123
  export { SchematicRepeatedChannelParser } from './core/altium/SchematicRepeatedChannelParser.mjs'
92
124
  export { SchematicTemplateParser } from './core/altium/SchematicTemplateParser.mjs'
125
+ export { UnsupportedFeatureReportBuilder } from './core/altium/UnsupportedFeatureReportBuilder.mjs'
126
+ export { SchematicTextRunParser } from './core/altium/SchematicTextRunParser.mjs'
93
127
  export { SchematicStandaloneCalloutNormalizer } from './core/altium/SchematicStandaloneCalloutNormalizer.mjs'
94
128
  export { SvgModelCrossLinkValidator } from './core/altium/SvgModelCrossLinkValidator.mjs'
package/src/renderers.mjs CHANGED
@@ -9,6 +9,7 @@ export { PcbFootprintPrimitiveSelector } from './ui/PcbFootprintPrimitiveSelecto
9
9
  export { PcbInteractionIndex } from './ui/PcbInteractionIndex.mjs'
10
10
  export { PcbInteractionItemRegistry } from './ui/PcbInteractionItemRegistry.mjs'
11
11
  export { PcbInteractionLayerModel } from './ui/PcbInteractionLayerModel.mjs'
12
+ export { PcbLayerGroups } from './core/altium/PcbLayerGroups.mjs'
12
13
  export {
13
14
  PcbSideResolvedRenderModel,
14
15
  isCopperPrimitive,
@@ -711,14 +711,16 @@ export class SchematicShapeRenderer {
711
711
  arc.startAngle,
712
712
  sheetHeight,
713
713
  radiusX,
714
- radiusY
714
+ radiusY,
715
+ true
715
716
  )
716
717
  const end = SchematicShapeRenderer.#projectArcPoint(
717
718
  arc,
718
719
  arc.endAngle,
719
720
  sheetHeight,
720
721
  radiusX,
721
- radiusY
722
+ radiusY,
723
+ true
722
724
  )
723
725
 
724
726
  return (
@@ -758,14 +760,16 @@ export class SchematicShapeRenderer {
758
760
  startAngle,
759
761
  sheetHeight,
760
762
  radiusX,
761
- radiusY
763
+ radiusY,
764
+ true
762
765
  )
763
766
  const mid = SchematicShapeRenderer.#projectArcPoint(
764
767
  arc,
765
768
  midAngle,
766
769
  sheetHeight,
767
770
  radiusX,
768
- radiusY
771
+ radiusY,
772
+ true
769
773
  )
770
774
 
771
775
  return (
@@ -803,10 +807,25 @@ export class SchematicShapeRenderer {
803
807
  * @param {number} sheetHeight
804
808
  * @param {number} radiusX
805
809
  * @param {number} radiusY
810
+ * @param {boolean} [convertEllipseAngle]
806
811
  * @returns {{ x: number, y: number }}
807
812
  */
808
- static #projectArcPoint(arc, angle, sheetHeight, radiusX, radiusY) {
809
- const radians = (Number(angle) * Math.PI) / 180
813
+ static #projectArcPoint(
814
+ arc,
815
+ angle,
816
+ sheetHeight,
817
+ radiusX,
818
+ radiusY,
819
+ convertEllipseAngle = false
820
+ ) {
821
+ const projectedAngle = convertEllipseAngle
822
+ ? SchematicShapeRenderer.#projectEllipticalArcAngle(
823
+ angle,
824
+ radiusX,
825
+ radiusY
826
+ )
827
+ : Number(angle)
828
+ const radians = (projectedAngle * Math.PI) / 180
810
829
 
811
830
  return {
812
831
  x: Number(arc.x) + radiusX * Math.cos(radians),
@@ -817,6 +836,30 @@ export class SchematicShapeRenderer {
817
836
  }
818
837
  }
819
838
 
839
+ /**
840
+ * Converts an authored physical ellipse angle into an SVG parametric
841
+ * ellipse angle.
842
+ * @param {number} angle Source angle in degrees.
843
+ * @param {number} radiusX Horizontal radius.
844
+ * @param {number} radiusY Vertical radius.
845
+ * @returns {number}
846
+ */
847
+ static #projectEllipticalArcAngle(angle, radiusX, radiusY) {
848
+ if (Math.abs(Number(radiusX) - Number(radiusY)) < 0.000001) {
849
+ return Number(angle)
850
+ }
851
+
852
+ const radians = (Number(angle) * Math.PI) / 180
853
+ return (
854
+ (Math.atan2(
855
+ Number(radiusX) * Math.sin(radians),
856
+ Number(radiusY) * Math.cos(radians)
857
+ ) *
858
+ 180) /
859
+ Math.PI
860
+ )
861
+ }
862
+
820
863
  /**
821
864
  * Keeps one schematic arc delta inside a single turn.
822
865
  * @param {number} startAngle