altium-toolkit 1.0.7 → 1.0.9

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 (93) hide show
  1. package/README.md +18 -6
  2. package/docs/api.md +78 -16
  3. package/docs/model-format.md +229 -8
  4. package/docs/schemas/altium_toolkit/netlist_a1.schema.json +47 -0
  5. package/docs/schemas/altium_toolkit/normalized_model_a1.schema.json +1661 -104
  6. package/docs/schemas/altium_toolkit/pcb_svg_semantics_a1.schema.json +59 -0
  7. package/docs/schemas/altium_toolkit/project_bundle_a1.schema.json +57 -0
  8. package/docs/schemas/altium_toolkit/schematic_svg_semantics_a1.schema.json +50 -0
  9. package/docs/testing.md +9 -3
  10. package/package.json +1 -1
  11. package/spec/library-scope.md +7 -1
  12. package/src/core/altium/AltiumLayoutParser.mjs +104 -8
  13. package/src/core/altium/AltiumParser.mjs +191 -45
  14. package/src/core/altium/EmbeddedFileInventoryBuilder.mjs +255 -0
  15. package/src/core/altium/IntLibModelParser.mjs +240 -0
  16. package/src/core/altium/IntLibStreamExtractor.mjs +366 -0
  17. package/src/core/altium/LibraryRenderManifestBuilder.mjs +417 -0
  18. package/src/core/altium/LibrarySearchIndex.mjs +215 -0
  19. package/src/core/altium/NormalizedModelSchema.mjs +36 -0
  20. package/src/core/altium/PcbCustomPadShapeParser.mjs +244 -0
  21. package/src/core/altium/PcbDefaultsParser.mjs +171 -0
  22. package/src/core/altium/PcbDimensionParser.mjs +229 -0
  23. package/src/core/altium/PcbEmbeddedModelExtractor.mjs +232 -6
  24. package/src/core/altium/PcbExtendedPrimitiveInformationParser.mjs +256 -0
  25. package/src/core/altium/PcbLibModelParser.mjs +235 -14
  26. package/src/core/altium/PcbLibStreamExtractor.mjs +62 -4
  27. package/src/core/altium/PcbMaskPasteResolver.mjs +354 -0
  28. package/src/core/altium/PcbMechanicalLayerPairParser.mjs +204 -0
  29. package/src/core/altium/PcbModelParser.mjs +466 -28
  30. package/src/core/altium/PcbOwnershipGraphBuilder.mjs +245 -0
  31. package/src/core/altium/PcbPadPrimitiveParser.mjs +78 -65
  32. package/src/core/altium/PcbPadStackParser.mjs +58 -0
  33. package/src/core/altium/PcbPickPlacePositionResolver.mjs +217 -0
  34. package/src/core/altium/PcbPrimitiveParameterParser.mjs +3 -2
  35. package/src/core/altium/PcbRawRecordRegistry.mjs +121 -130
  36. package/src/core/altium/PcbRegionPrimitiveParser.mjs +5 -1
  37. package/src/core/altium/PcbRuleParser.mjs +354 -33
  38. package/src/core/altium/PcbSidecarRecordParser.mjs +177 -0
  39. package/src/core/altium/PcbSpecialStringResolver.mjs +220 -0
  40. package/src/core/altium/PcbStatisticsBuilder.mjs +532 -0
  41. package/src/core/altium/PcbStreamExtractor.mjs +111 -4
  42. package/src/core/altium/PcbTextPrimitiveParser.mjs +60 -0
  43. package/src/core/altium/PcbUnionParser.mjs +307 -0
  44. package/src/core/altium/PcbViaStackParser.mjs +98 -10
  45. package/src/core/altium/PcbViaStructureParser.mjs +335 -0
  46. package/src/core/altium/PrintableTextDecoder.mjs +53 -3
  47. package/src/core/altium/PrjPcbModelParser.mjs +257 -5
  48. package/src/core/altium/ProjectAnnotationParser.mjs +205 -0
  49. package/src/core/altium/ProjectDesignBundleBuilder.mjs +477 -0
  50. package/src/core/altium/ProjectNetlistExporter.mjs +499 -0
  51. package/src/core/altium/ProjectOutJobDigestBuilder.mjs +109 -0
  52. package/src/core/altium/ProjectVariantViewBuilder.mjs +334 -0
  53. package/src/core/altium/SchematicBindingProvenanceParser.mjs +223 -0
  54. package/src/core/altium/SchematicComponentOwnerTextResolver.mjs +312 -0
  55. package/src/core/altium/SchematicComponentTextResolver.mjs +72 -19
  56. package/src/core/altium/SchematicConnectivityQaBuilder.mjs +271 -0
  57. package/src/core/altium/SchematicCrossSheetConnectorParser.mjs +140 -0
  58. package/src/core/altium/SchematicDirectiveParser.mjs +312 -0
  59. package/src/core/altium/SchematicDisplayModeCatalogParser.mjs +231 -0
  60. package/src/core/altium/SchematicHarnessParser.mjs +302 -0
  61. package/src/core/altium/SchematicImageParser.mjs +474 -3
  62. package/src/core/altium/SchematicImplementationParser.mjs +518 -0
  63. package/src/core/altium/SchematicNetlistBuilder.mjs +15 -2
  64. package/src/core/altium/SchematicOwnershipGraphParser.mjs +195 -0
  65. package/src/core/altium/SchematicPinParser.mjs +84 -1
  66. package/src/core/altium/SchematicPrimitiveParser.mjs +301 -0
  67. package/src/core/altium/SchematicProjectParameterResolver.mjs +361 -0
  68. package/src/core/altium/SchematicQaReportBuilder.mjs +284 -0
  69. package/src/core/altium/SchematicRecordTypeRegistry.mjs +137 -0
  70. package/src/core/altium/SchematicRepeatedChannelParser.mjs +229 -0
  71. package/src/core/altium/SchematicStreamExtractor.mjs +10 -1
  72. package/src/core/altium/SchematicTemplateParser.mjs +256 -0
  73. package/src/core/altium/SchematicTextParser.mjs +123 -0
  74. package/src/core/ole/OleCompoundDocument.mjs +20 -0
  75. package/src/parser.mjs +29 -0
  76. package/src/renderers.mjs +3 -0
  77. package/src/styles/altium-renderers.css +25 -0
  78. package/src/ui/PcbBarcodeTextRenderer.mjs +436 -0
  79. package/src/ui/PcbInteractionGeometry.mjs +350 -0
  80. package/src/ui/PcbInteractionIndex.mjs +593 -0
  81. package/src/ui/PcbInteractionItemRegistry.mjs +66 -0
  82. package/src/ui/PcbInteractionLayerModel.mjs +99 -0
  83. package/src/ui/PcbScene3dBoardOutlineRefiner.mjs +74 -9
  84. package/src/ui/PcbScene3dBuilder.mjs +169 -7
  85. package/src/ui/PcbScene3dModelRegistry.mjs +74 -0
  86. package/src/ui/PcbSvgRenderer.mjs +1187 -34
  87. package/src/ui/PcbTextPrimitiveRenderer.mjs +193 -7
  88. package/src/ui/SchematicNoteRenderer.mjs +9 -2
  89. package/src/ui/SchematicOwnerPinLabelLayout.mjs +206 -0
  90. package/src/ui/SchematicShapeRenderer.mjs +362 -0
  91. package/src/ui/SchematicSvgRenderer.mjs +1442 -92
  92. package/src/ui/SchematicTypography.mjs +48 -5
  93. package/src/ui/TextGeometrySidecarBuilder.mjs +147 -0
@@ -0,0 +1,518 @@
1
+ // SPDX-FileCopyrightText: 2026 André Fiedler
2
+ //
3
+ // SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ import { ParserUtils } from './ParserUtils.mjs'
6
+
7
+ const { getDisplayText, parseBoolean, parseNumericField } = ParserUtils
8
+
9
+ /**
10
+ * Parses schematic implementation/model-link records into a read-only model.
11
+ */
12
+ export class SchematicImplementationParser {
13
+ static SCHEMA_ID = 'altium-toolkit.schematic.implementations.a1'
14
+
15
+ /**
16
+ * Parses implementation lists, model links, map definers, and parameters.
17
+ * @param {{ fields: Record<string, string | string[]>, recordIndex?: number }[]} records Schematic records.
18
+ * @returns {object | null}
19
+ */
20
+ static parse(records) {
21
+ const components = SchematicImplementationParser.#componentRows(records)
22
+ const listsByIndex =
23
+ SchematicImplementationParser.#implementationLists(records)
24
+ const implementations = (records || [])
25
+ .filter(
26
+ (record) =>
27
+ SchematicImplementationParser.#field(
28
+ record.fields,
29
+ 'RECORD'
30
+ ) === '45'
31
+ )
32
+ .map((record) =>
33
+ SchematicImplementationParser.#implementation(
34
+ record,
35
+ records,
36
+ components,
37
+ listsByIndex
38
+ )
39
+ )
40
+ .filter(Boolean)
41
+
42
+ if (!implementations.length) {
43
+ return null
44
+ }
45
+
46
+ return {
47
+ schema: SchematicImplementationParser.SCHEMA_ID,
48
+ components: SchematicImplementationParser.#componentLinks(
49
+ components,
50
+ implementations
51
+ ),
52
+ implementations
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Builds normalized schematic component rows for implementation linking.
58
+ * @param {object[]} records Schematic records.
59
+ * @returns {object[]}
60
+ */
61
+ static #componentRows(records) {
62
+ return (records || [])
63
+ .filter(
64
+ (record) =>
65
+ SchematicImplementationParser.#field(
66
+ record.fields,
67
+ 'RECORD'
68
+ ) === '1'
69
+ )
70
+ .map((record) => {
71
+ const indexInSheet = parseNumericField(
72
+ record.fields,
73
+ 'IndexInSheet'
74
+ )
75
+ return {
76
+ indexInSheet,
77
+ componentKey:
78
+ 'schematic-component-' +
79
+ String(indexInSheet ?? record.recordIndex ?? 0),
80
+ recordKey: SchematicImplementationParser.#recordKey(record),
81
+ uniqueId: SchematicImplementationParser.#field(
82
+ record.fields,
83
+ 'UniqueID'
84
+ ),
85
+ libReference:
86
+ SchematicImplementationParser.#field(
87
+ record.fields,
88
+ 'LibReference'
89
+ ) ||
90
+ SchematicImplementationParser.#field(
91
+ record.fields,
92
+ 'DesignItemId'
93
+ )
94
+ }
95
+ })
96
+ }
97
+
98
+ /**
99
+ * Builds implementation-list rows keyed by native sheet indexes.
100
+ * @param {object[]} records Schematic records.
101
+ * @returns {Map<string, object>}
102
+ */
103
+ static #implementationLists(records) {
104
+ const lists = new Map()
105
+
106
+ for (const record of records || []) {
107
+ if (
108
+ SchematicImplementationParser.#field(
109
+ record.fields,
110
+ 'RECORD'
111
+ ) !== '44'
112
+ ) {
113
+ continue
114
+ }
115
+
116
+ const indexInSheet = parseNumericField(
117
+ record.fields,
118
+ 'IndexInSheet'
119
+ )
120
+ if (indexInSheet === null) {
121
+ continue
122
+ }
123
+
124
+ lists.set(String(indexInSheet), {
125
+ key: 'schematic-implementation-list-' + indexInSheet,
126
+ recordKey: SchematicImplementationParser.#recordKey(record),
127
+ ownerIndex: SchematicImplementationParser.#field(
128
+ record.fields,
129
+ 'OwnerIndex'
130
+ )
131
+ })
132
+ }
133
+
134
+ return lists
135
+ }
136
+
137
+ /**
138
+ * Parses one implementation record.
139
+ * @param {object} record Implementation record.
140
+ * @param {object[]} records All schematic records.
141
+ * @param {object[]} components Component rows.
142
+ * @param {Map<string, object>} listsByIndex Implementation lists.
143
+ * @returns {object | null}
144
+ */
145
+ static #implementation(record, records, components, listsByIndex) {
146
+ const indexInSheet =
147
+ parseNumericField(record.fields, 'IndexInSheet') ??
148
+ record.recordIndex ??
149
+ 0
150
+ const ownerIndex = SchematicImplementationParser.#field(
151
+ record.fields,
152
+ 'OwnerIndex'
153
+ )
154
+ const ownerList = listsByIndex.get(ownerIndex) || null
155
+ const ownerComponent = SchematicImplementationParser.#componentForOwner(
156
+ ownerIndex,
157
+ ownerList,
158
+ components
159
+ )
160
+
161
+ return SchematicImplementationParser.#stripEmpty({
162
+ key: 'schematic-implementation-' + String(indexInSheet),
163
+ recordKey: SchematicImplementationParser.#recordKey(record),
164
+ ownerComponentKey: ownerComponent?.componentKey || '',
165
+ ownerListKey: ownerList?.key || '',
166
+ modelName: SchematicImplementationParser.#field(
167
+ record.fields,
168
+ 'ModelName'
169
+ ),
170
+ modelType: SchematicImplementationParser.#normalizeToken(
171
+ SchematicImplementationParser.#field(record.fields, 'ModelType')
172
+ ),
173
+ description: SchematicImplementationParser.#field(
174
+ record.fields,
175
+ 'Description'
176
+ ),
177
+ isCurrent: parseBoolean(
178
+ SchematicImplementationParser.#field(record.fields, 'IsCurrent')
179
+ ),
180
+ targetLibraries: SchematicImplementationParser.#targetLibraries(
181
+ record.fields
182
+ ),
183
+ searchPaths: SchematicImplementationParser.#searchPaths(
184
+ record.fields
185
+ ),
186
+ mapDefiners: SchematicImplementationParser.#mapDefiners(
187
+ records,
188
+ indexInSheet
189
+ ),
190
+ parameters: SchematicImplementationParser.#parameters(
191
+ records,
192
+ indexInSheet
193
+ )
194
+ })
195
+ }
196
+
197
+ /**
198
+ * Resolves the owning component for an implementation record.
199
+ * @param {string} ownerIndex Implementation owner index.
200
+ * @param {object | null} ownerList Implementation-list row.
201
+ * @param {object[]} components Component rows.
202
+ * @returns {object | null}
203
+ */
204
+ static #componentForOwner(ownerIndex, ownerList, components) {
205
+ const direct = (components || []).find(
206
+ (component) => String(component.indexInSheet) === ownerIndex
207
+ )
208
+ if (direct) {
209
+ return direct
210
+ }
211
+
212
+ return (
213
+ (components || []).find(
214
+ (component) =>
215
+ ownerList &&
216
+ String(component.indexInSheet) === ownerList.ownerIndex
217
+ ) || null
218
+ )
219
+ }
220
+
221
+ /**
222
+ * Builds component-level implementation-key rows.
223
+ * @param {object[]} components Component rows.
224
+ * @param {object[]} implementations Implementation rows.
225
+ * @returns {object[]}
226
+ */
227
+ static #componentLinks(components, implementations) {
228
+ return (components || [])
229
+ .map((component) =>
230
+ SchematicImplementationParser.#stripEmpty({
231
+ componentKey: component.componentKey,
232
+ recordKey: component.recordKey,
233
+ uniqueId: component.uniqueId,
234
+ libReference: component.libReference,
235
+ implementationKeys: implementations
236
+ .filter(
237
+ (implementation) =>
238
+ implementation.ownerComponentKey ===
239
+ component.componentKey
240
+ )
241
+ .map((implementation) => implementation.key)
242
+ })
243
+ )
244
+ .filter((component) => component.implementationKeys?.length)
245
+ }
246
+
247
+ /**
248
+ * Parses indexed target-library fields from one implementation.
249
+ * @param {Record<string, string | string[]>} fields Record fields.
250
+ * @returns {object[]}
251
+ */
252
+ static #targetLibraries(fields) {
253
+ const count =
254
+ parseNumericField(fields, 'DatafileCount') ??
255
+ SchematicImplementationParser.#countIndexedFields(
256
+ fields,
257
+ 'ModelDatafileEntity'
258
+ )
259
+ const libraries = []
260
+
261
+ for (let index = 0; index < count; index += 1) {
262
+ const entity = SchematicImplementationParser.#field(
263
+ fields,
264
+ 'ModelDatafileEntity' + index
265
+ )
266
+ const kind = SchematicImplementationParser.#normalizeToken(
267
+ SchematicImplementationParser.#field(
268
+ fields,
269
+ 'ModelDatafileKind' + index
270
+ )
271
+ )
272
+
273
+ if (!entity && !kind) {
274
+ continue
275
+ }
276
+
277
+ libraries.push(
278
+ SchematicImplementationParser.#stripEmpty({
279
+ index,
280
+ entity,
281
+ kind,
282
+ fileName: SchematicImplementationParser.#libraryFileName(
283
+ entity,
284
+ kind
285
+ )
286
+ })
287
+ )
288
+ }
289
+
290
+ return libraries
291
+ }
292
+
293
+ /**
294
+ * Parses indexed search-path fields.
295
+ * @param {Record<string, string | string[]>} fields Record fields.
296
+ * @returns {string[]}
297
+ */
298
+ static #searchPaths(fields) {
299
+ const count =
300
+ parseNumericField(fields, 'SearchPathCount') ??
301
+ SchematicImplementationParser.#countIndexedFields(
302
+ fields,
303
+ 'SearchPath'
304
+ )
305
+ const paths = []
306
+
307
+ for (let index = 0; index < count; index += 1) {
308
+ const value = SchematicImplementationParser.#field(
309
+ fields,
310
+ 'SearchPath' + index
311
+ )
312
+ if (value) {
313
+ paths.push(value)
314
+ }
315
+ }
316
+
317
+ for (const key of ['SearchPath', 'LibraryPath', 'Path']) {
318
+ const value = SchematicImplementationParser.#field(fields, key)
319
+ if (value && !paths.includes(value)) {
320
+ paths.push(value)
321
+ }
322
+ }
323
+
324
+ return paths
325
+ }
326
+
327
+ /**
328
+ * Parses map-definer child records for one implementation.
329
+ * @param {object[]} records All schematic records.
330
+ * @param {number} implementationIndex Implementation index.
331
+ * @returns {object[]}
332
+ */
333
+ static #mapDefiners(records, implementationIndex) {
334
+ return (records || [])
335
+ .filter(
336
+ (record) =>
337
+ SchematicImplementationParser.#field(
338
+ record.fields,
339
+ 'RECORD'
340
+ ) === '47' &&
341
+ SchematicImplementationParser.#field(
342
+ record.fields,
343
+ 'OwnerIndex'
344
+ ) === String(implementationIndex)
345
+ )
346
+ .map((record) =>
347
+ SchematicImplementationParser.#stripEmpty({
348
+ recordKey: SchematicImplementationParser.#recordKey(record),
349
+ designatorInterface: SchematicImplementationParser.#field(
350
+ record.fields,
351
+ 'DesIntf'
352
+ ),
353
+ implementationDesignators:
354
+ SchematicImplementationParser.#implementationDesignators(
355
+ record.fields
356
+ )
357
+ })
358
+ )
359
+ }
360
+
361
+ /**
362
+ * Parses implementation parameter child records.
363
+ * @param {object[]} records All schematic records.
364
+ * @param {number} implementationIndex Implementation index.
365
+ * @returns {object[]}
366
+ */
367
+ static #parameters(records, implementationIndex) {
368
+ return (records || [])
369
+ .filter((record) => {
370
+ const recordType = SchematicImplementationParser.#field(
371
+ record.fields,
372
+ 'RECORD'
373
+ )
374
+ return (
375
+ (recordType === '48' || recordType === '41') &&
376
+ SchematicImplementationParser.#field(
377
+ record.fields,
378
+ 'OwnerIndex'
379
+ ) === String(implementationIndex)
380
+ )
381
+ })
382
+ .map((record) =>
383
+ SchematicImplementationParser.#stripEmpty({
384
+ recordKey: SchematicImplementationParser.#recordKey(record),
385
+ name: SchematicImplementationParser.#field(
386
+ record.fields,
387
+ 'Name'
388
+ ),
389
+ value:
390
+ getDisplayText(record.fields) ||
391
+ SchematicImplementationParser.#field(
392
+ record.fields,
393
+ 'Value'
394
+ )
395
+ })
396
+ )
397
+ }
398
+
399
+ /**
400
+ * Parses indexed implementation designator fields from a map definer.
401
+ * @param {Record<string, string | string[]>} fields Record fields.
402
+ * @returns {string[]}
403
+ */
404
+ static #implementationDesignators(fields) {
405
+ const count =
406
+ parseNumericField(fields, 'DesImpCount') ??
407
+ SchematicImplementationParser.#countIndexedFields(fields, 'DesImp')
408
+ const designators = []
409
+
410
+ for (let index = 0; index < count; index += 1) {
411
+ const value = SchematicImplementationParser.#field(
412
+ fields,
413
+ 'DesImp' + index
414
+ )
415
+ if (value) {
416
+ designators.push(value)
417
+ }
418
+ }
419
+
420
+ return designators
421
+ }
422
+
423
+ /**
424
+ * Counts fields with an indexed prefix.
425
+ * @param {Record<string, string | string[]>} fields Record fields.
426
+ * @param {string} prefix Prefix before numeric index.
427
+ * @returns {number}
428
+ */
429
+ static #countIndexedFields(fields, prefix) {
430
+ const normalizedPrefix = prefix.toLowerCase()
431
+
432
+ return Object.keys(fields || {}).filter((key) =>
433
+ key.toLowerCase().startsWith(normalizedPrefix)
434
+ ).length
435
+ }
436
+
437
+ /**
438
+ * Builds an inferred file name from library entity and kind.
439
+ * @param {string} entity Library entity.
440
+ * @param {string} kind Library kind.
441
+ * @returns {string}
442
+ */
443
+ static #libraryFileName(entity, kind) {
444
+ if (!entity) {
445
+ return ''
446
+ }
447
+
448
+ if (/\.[^.]+$/.test(entity)) {
449
+ return entity
450
+ }
451
+
452
+ const extension =
453
+ {
454
+ pcblib: 'PcbLib',
455
+ schlib: 'SchLib',
456
+ intlib: 'IntLib',
457
+ sim: 'SimModel'
458
+ }[kind] || kind
459
+
460
+ return extension ? entity + '.' + extension : entity
461
+ }
462
+
463
+ /**
464
+ * Returns a lower-case token.
465
+ * @param {string} value Raw token.
466
+ * @returns {string}
467
+ */
468
+ static #normalizeToken(value) {
469
+ return String(value || '')
470
+ .trim()
471
+ .toLowerCase()
472
+ }
473
+
474
+ /**
475
+ * Builds a stable schematic record key.
476
+ * @param {object} record Schematic record.
477
+ * @returns {string}
478
+ */
479
+ static #recordKey(record) {
480
+ return 'schematic-record-' + String(record?.recordIndex ?? 0)
481
+ }
482
+
483
+ /**
484
+ * Reads one field case-insensitively.
485
+ * @param {Record<string, string | string[]> | undefined} fields Record fields.
486
+ * @param {string} key Requested key.
487
+ * @returns {string}
488
+ */
489
+ static #field(fields, key) {
490
+ const direct = ParserUtils.getField(fields, key)
491
+ if (direct) {
492
+ return direct
493
+ }
494
+
495
+ const normalizedKey = String(key || '').toLowerCase()
496
+ const matchedKey = Object.keys(fields || {}).find(
497
+ (fieldKey) => fieldKey.toLowerCase() === normalizedKey
498
+ )
499
+
500
+ return matchedKey ? ParserUtils.getField(fields, matchedKey) : ''
501
+ }
502
+
503
+ /**
504
+ * Drops empty optional fields while keeping explicit booleans and arrays.
505
+ * @param {Record<string, unknown>} value Source object.
506
+ * @returns {Record<string, unknown>}
507
+ */
508
+ static #stripEmpty(value) {
509
+ return Object.fromEntries(
510
+ Object.entries(value).filter(([, entry]) => {
511
+ if (Array.isArray(entry)) {
512
+ return entry.length > 0
513
+ }
514
+ return entry !== null && entry !== undefined && entry !== ''
515
+ })
516
+ )
517
+ }
518
+ }
@@ -9,8 +9,8 @@
9
9
  export class SchematicNetlistBuilder {
10
10
  /**
11
11
  * Builds normalized nets and connectivity diagnostics.
12
- * @param {{ lines: { x1: number, y1: number, x2: number, y2: number, ownerIndex?: string, isBus?: boolean }[], texts: { x: number, y: number, text: string, recordType?: string }[], pins?: { x: number, y: number, length: number, orientation: 'left' | 'right' | 'top' | 'bottom', name: string, designator: string }[], ports?: { x: number, y: number, width: number, direction?: 'left' | 'right' | 'up' | 'down', name: string }[], junctions?: { x: number, y: number, color: string }[], busEntries?: { x1: number, y1: number, x2: number, y2: number }[], sheetEntries?: { x: number, y: number, name: string }[] }} schematic
13
- * @returns {{ nets: { name: string, segments: { x1: number, y1: number, x2: number, y2: number, ownerIndex?: string, isBus?: boolean }[], labels: { x: number, y: number, text: string, recordType?: string }[], powerPorts: { x: number, y: number, text: string, recordType?: string }[], pins: { x: number, y: number, length: number, orientation: 'left' | 'right' | 'top' | 'bottom', name: string, designator: string }[], ports: { x: number, y: number, width: number, direction?: 'left' | 'right' | 'up' | 'down', name: string }[], junctions: { x: number, y: number, color: string }[], busEntries: { x1: number, y1: number, x2: number, y2: number }[], sheetEntries: { x: number, y: number, name: string }[] }[], diagnostics: { severity: 'warning', message: string }[] }}
12
+ * @param {{ lines: { x1: number, y1: number, x2: number, y2: number, ownerIndex?: string, isBus?: boolean }[], texts: { x: number, y: number, text: string, recordType?: string }[], pins?: { x: number, y: number, length: number, orientation: 'left' | 'right' | 'top' | 'bottom', name: string, designator: string }[], ports?: { x: number, y: number, width: number, direction?: 'left' | 'right' | 'up' | 'down', name: string }[], crossSheetConnectors?: { key: string, x: number, y: number, name: string, style?: string }[], junctions?: { x: number, y: number, color: string }[], busEntries?: { x1: number, y1: number, x2: number, y2: number }[], sheetEntries?: { x: number, y: number, name: string }[] }} schematic
13
+ * @returns {{ nets: { name: string, segments: { x1: number, y1: number, x2: number, y2: number, ownerIndex?: string, isBus?: boolean }[], labels: { x: number, y: number, text: string, recordType?: string }[], powerPorts: { x: number, y: number, text: string, recordType?: string }[], crossSheetConnectors: { key: string, name: string, x: number, y: number, style?: string }[], pins: { x: number, y: number, length: number, orientation: 'left' | 'right' | 'top' | 'bottom', name: string, designator: string }[], ports: { x: number, y: number, width: number, direction?: 'left' | 'right' | 'up' | 'down', name: string }[], junctions: { x: number, y: number, color: string }[], busEntries: { x1: number, y1: number, x2: number, y2: number }[], sheetEntries: { x: number, y: number, name: string }[] }[], diagnostics: { severity: 'warning', message: string }[] }}
14
14
  */
15
15
  static build(schematic) {
16
16
  const diagnostics = []
@@ -38,6 +38,11 @@ export class SchematicNetlistBuilder {
38
38
  text.recordType === '17' &&
39
39
  SchematicNetlistBuilder.#groupContainsPoint(group, text)
40
40
  )
41
+ const crossSheetConnectors = (
42
+ schematic.crossSheetConnectors || []
43
+ ).filter((connector) =>
44
+ SchematicNetlistBuilder.#groupContainsPoint(group, connector)
45
+ )
41
46
  const pins = (schematic.pins || []).filter((pin) =>
42
47
  SchematicNetlistBuilder.#groupContainsPoint(
43
48
  group,
@@ -77,6 +82,7 @@ export class SchematicNetlistBuilder {
77
82
  ...new Set(
78
83
  [
79
84
  ...powerPorts.map((item) => item.text),
85
+ ...crossSheetConnectors.map((item) => item.name),
80
86
  ...labels.map((item) => item.text)
81
87
  ].filter(Boolean)
82
88
  )
@@ -99,6 +105,13 @@ export class SchematicNetlistBuilder {
99
105
  segments: group,
100
106
  labels,
101
107
  powerPorts,
108
+ crossSheetConnectors: crossSheetConnectors.map((connector) => ({
109
+ key: connector.key,
110
+ name: connector.name,
111
+ x: connector.x,
112
+ y: connector.y,
113
+ style: connector.style
114
+ })),
102
115
  pins,
103
116
  ports,
104
117
  junctions,