altium-toolkit 1.0.8 → 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 (88) 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/styles/altium-renderers.css +19 -0
  77. package/src/ui/PcbBarcodeTextRenderer.mjs +436 -0
  78. package/src/ui/PcbInteractionIndex.mjs +9 -4
  79. package/src/ui/PcbScene3dBuilder.mjs +137 -3
  80. package/src/ui/PcbScene3dModelRegistry.mjs +74 -0
  81. package/src/ui/PcbSvgRenderer.mjs +1187 -34
  82. package/src/ui/PcbTextPrimitiveRenderer.mjs +193 -7
  83. package/src/ui/SchematicNoteRenderer.mjs +9 -2
  84. package/src/ui/SchematicOwnerPinLabelLayout.mjs +206 -0
  85. package/src/ui/SchematicShapeRenderer.mjs +362 -0
  86. package/src/ui/SchematicSvgRenderer.mjs +1442 -92
  87. package/src/ui/SchematicTypography.mjs +48 -5
  88. package/src/ui/TextGeometrySidecarBuilder.mjs +147 -0
@@ -66,6 +66,164 @@ export class SchematicShapeRenderer {
66
66
  )
67
67
  }
68
68
 
69
+ /**
70
+ * Builds one schematic cubic Bezier primitive.
71
+ * @param {{ segments: { start: { x: number, y: number }, control1: { x: number, y: number }, control2: { x: number, y: number }, end: { x: number, y: number } }[], color: string, width: number, lineStyle?: number }} bezier
72
+ * @param {number} sheetHeight
73
+ * @returns {string}
74
+ */
75
+ static buildBezierMarkup(bezier, sheetHeight) {
76
+ const segments = Array.isArray(bezier?.segments) ? bezier.segments : []
77
+ if (!segments.length) {
78
+ return ''
79
+ }
80
+
81
+ const path = segments
82
+ .map((segment, index) => {
83
+ const start = SchematicShapeRenderer.#projectPoint(
84
+ segment.start,
85
+ sheetHeight
86
+ )
87
+ const control1 = SchematicShapeRenderer.#projectPoint(
88
+ segment.control1,
89
+ sheetHeight
90
+ )
91
+ const control2 = SchematicShapeRenderer.#projectPoint(
92
+ segment.control2,
93
+ sheetHeight
94
+ )
95
+ const end = SchematicShapeRenderer.#projectPoint(
96
+ segment.end,
97
+ sheetHeight
98
+ )
99
+ const move =
100
+ index === 0
101
+ ? 'M ' +
102
+ formatNumber(start.x) +
103
+ ' ' +
104
+ formatNumber(start.y) +
105
+ ' '
106
+ : ''
107
+
108
+ return (
109
+ move +
110
+ 'C ' +
111
+ formatNumber(control1.x) +
112
+ ' ' +
113
+ formatNumber(control1.y) +
114
+ ' ' +
115
+ formatNumber(control2.x) +
116
+ ' ' +
117
+ formatNumber(control2.y) +
118
+ ' ' +
119
+ formatNumber(end.x) +
120
+ ' ' +
121
+ formatNumber(end.y)
122
+ )
123
+ })
124
+ .join(' ')
125
+
126
+ return (
127
+ '<path class="schematic-bezier" d="' +
128
+ escapeHtml(path) +
129
+ '" stroke="' +
130
+ escapeHtml(
131
+ SchematicColorResolver.resolveColor(
132
+ bezier.color,
133
+ '--schematic-default-ink-color'
134
+ )
135
+ ) +
136
+ '" stroke-width="' +
137
+ formatNumber(Math.max(bezier.width || 1, 0.8)) +
138
+ '"' +
139
+ SchematicShapeRenderer.#buildSchematicStrokeStyleAttributes(
140
+ bezier.width,
141
+ bezier.lineStyle
142
+ ) +
143
+ ' fill="none" />'
144
+ )
145
+ }
146
+
147
+ /**
148
+ * Builds one schematic pie/wedge primitive.
149
+ * @param {{ x: number, y: number, radius: number, radiusY?: number, startAngle: number, endAngle: number, color: string, fill: string, isSolid: boolean, transparent: boolean, lineWidth: number }} pie
150
+ * @param {number} sheetHeight
151
+ * @returns {string}
152
+ */
153
+ static buildPieMarkup(pie, sheetHeight) {
154
+ const radiusX = Math.max(Number(pie.radius) || 0, 0.8)
155
+ const radiusY = Math.max(Number(pie.radiusY ?? pie.radius) || 0, 0.8)
156
+ const delta = SchematicShapeRenderer.#normalizeArcDelta(
157
+ pie.startAngle,
158
+ pie.endAngle
159
+ )
160
+ const sweep = delta >= 0 ? 0 : 1
161
+ const center = {
162
+ x: Number(pie.x) || 0,
163
+ y: projectSchematicY(sheetHeight, Number(pie.y) || 0)
164
+ }
165
+ const start = SchematicShapeRenderer.#projectArcPoint(
166
+ pie,
167
+ pie.startAngle,
168
+ sheetHeight,
169
+ radiusX,
170
+ radiusY
171
+ )
172
+ const end = SchematicShapeRenderer.#projectArcPoint(
173
+ pie,
174
+ pie.endAngle,
175
+ sheetHeight,
176
+ radiusX,
177
+ radiusY
178
+ )
179
+ const largeArc = Math.abs(delta) > 180 ? 1 : 0
180
+ const path =
181
+ 'M ' +
182
+ formatNumber(center.x) +
183
+ ' ' +
184
+ formatNumber(center.y) +
185
+ ' L ' +
186
+ formatNumber(start.x) +
187
+ ' ' +
188
+ formatNumber(start.y) +
189
+ ' A ' +
190
+ formatNumber(radiusX) +
191
+ ' ' +
192
+ formatNumber(radiusY) +
193
+ ' 0 ' +
194
+ largeArc +
195
+ ' ' +
196
+ sweep +
197
+ ' ' +
198
+ formatNumber(end.x) +
199
+ ' ' +
200
+ formatNumber(end.y) +
201
+ ' Z'
202
+
203
+ return (
204
+ '<path class="schematic-pie" d="' +
205
+ escapeHtml(path) +
206
+ '" fill="' +
207
+ escapeHtml(
208
+ SchematicColorResolver.resolveFill(
209
+ SchematicShapeRenderer.#resolveSchematicPieFill(pie),
210
+ '--schematic-fill-color',
211
+ true
212
+ )
213
+ ) +
214
+ '" stroke="' +
215
+ escapeHtml(
216
+ SchematicColorResolver.resolveColor(
217
+ pie.color,
218
+ '--schematic-default-ink-color'
219
+ )
220
+ ) +
221
+ '" stroke-width="' +
222
+ formatNumber(Math.max(pie.lineWidth || 1, 0.8)) +
223
+ '" />'
224
+ )
225
+ }
226
+
69
227
  /**
70
228
  * Builds one schematic rectangle primitive.
71
229
  * @param {{ x: number, y: number, width: number, height: number, color: string, fill: string, isSolid: boolean, transparent: boolean, lineWidth: number, lineStyle?: number }} rectangle
@@ -111,6 +269,92 @@ export class SchematicShapeRenderer {
111
269
  )
112
270
  }
113
271
 
272
+ /**
273
+ * Builds one schematic rounded-rectangle primitive.
274
+ * @param {{ x: number, y: number, width: number, height: number, radius?: number, color: string, fill: string, isSolid: boolean, transparent: boolean, lineWidth: number, lineStyle?: number }} rectangle
275
+ * @param {number} sheetHeight
276
+ * @returns {string}
277
+ */
278
+ static buildRoundedRectangleMarkup(rectangle, sheetHeight) {
279
+ const radius = Math.max(Number(rectangle.radius || 0), 0)
280
+
281
+ return (
282
+ '<rect class="schematic-rounded-rectangle" x="' +
283
+ formatNumber(rectangle.x) +
284
+ '" y="' +
285
+ formatNumber(
286
+ projectSchematicY(sheetHeight, rectangle.y + rectangle.height)
287
+ ) +
288
+ '" width="' +
289
+ formatNumber(rectangle.width) +
290
+ '" height="' +
291
+ formatNumber(rectangle.height) +
292
+ '" rx="' +
293
+ formatNumber(radius) +
294
+ '" ry="' +
295
+ formatNumber(radius) +
296
+ '" fill="' +
297
+ escapeHtml(
298
+ SchematicColorResolver.resolveFill(
299
+ SchematicShapeRenderer.#resolveSchematicRectangleFill(
300
+ rectangle
301
+ ),
302
+ '--schematic-fill-color'
303
+ )
304
+ ) +
305
+ '" stroke="' +
306
+ escapeHtml(
307
+ SchematicColorResolver.resolveColor(
308
+ rectangle.color,
309
+ '--schematic-default-ink-color'
310
+ )
311
+ ) +
312
+ '" stroke-width="' +
313
+ formatNumber(Math.max(rectangle.lineWidth || 1, 0.8)) +
314
+ '"' +
315
+ SchematicShapeRenderer.#buildSchematicStrokeStyleAttributes(
316
+ rectangle.lineWidth,
317
+ rectangle.lineStyle
318
+ ) +
319
+ ' />'
320
+ )
321
+ }
322
+
323
+ /**
324
+ * Builds one schematic IEEE-symbol primitive.
325
+ * @param {{ x: number, y: number, symbolName?: string, size?: number, color: string, lineWidth?: number }} symbol
326
+ * @param {number} sheetHeight
327
+ * @returns {string}
328
+ */
329
+ static buildIeeeSymbolMarkup(symbol, sheetHeight) {
330
+ const name = String(symbol?.symbolName || 'unknown')
331
+ const size = Math.max(Number(symbol?.size || 12), 1)
332
+ const x = Number(symbol?.x || 0)
333
+ const y = projectSchematicY(sheetHeight, Number(symbol?.y || 0))
334
+ const color = escapeHtml(
335
+ SchematicColorResolver.resolveColor(
336
+ symbol.color,
337
+ '--schematic-default-ink-color'
338
+ )
339
+ )
340
+ const strokeWidth = formatNumber(Math.max(symbol.lineWidth || 1, 0.8))
341
+
342
+ return (
343
+ '<g class="schematic-ieee-symbol schematic-ieee-symbol--' +
344
+ escapeHtml(name) +
345
+ '">' +
346
+ SchematicShapeRenderer.#buildIeeeSymbolShape(
347
+ name,
348
+ x,
349
+ y,
350
+ size,
351
+ color,
352
+ strokeWidth
353
+ ) +
354
+ '</g>'
355
+ )
356
+ }
357
+
114
358
  /**
115
359
  * Builds one schematic arc primitive as an SVG path.
116
360
  * Record-11 curves may supply `radiusY` for ellipse segments.
@@ -224,6 +468,124 @@ export class SchematicShapeRenderer {
224
468
  return ellipse.fill || 'none'
225
469
  }
226
470
 
471
+ /**
472
+ * Resolves the visible fill for one schematic pie primitive.
473
+ * @param {{ fill: string, isSolid: boolean, transparent: boolean }} pie
474
+ * @returns {string}
475
+ */
476
+ static #resolveSchematicPieFill(pie) {
477
+ if (pie.transparent || !pie.isSolid) {
478
+ return 'none'
479
+ }
480
+
481
+ return pie.fill || 'none'
482
+ }
483
+
484
+ /**
485
+ * Builds the inner geometry for one normalized IEEE symbol.
486
+ * @param {string} name Symbol name.
487
+ * @param {number} x Center X.
488
+ * @param {number} y Center Y.
489
+ * @param {number} size Symbol size.
490
+ * @param {string} color SVG color.
491
+ * @param {string} strokeWidth SVG stroke width.
492
+ * @returns {string}
493
+ */
494
+ static #buildIeeeSymbolShape(name, x, y, size, color, strokeWidth) {
495
+ const half = size / 2
496
+ const left = x - half
497
+ const right = x + half
498
+ const top = y - half
499
+ const bottom = y + half
500
+
501
+ if (name === 'inverter' || name === 'buffer') {
502
+ const bubble =
503
+ name === 'inverter'
504
+ ? '<circle cx="' +
505
+ formatNumber(right + size * 0.18) +
506
+ '" cy="' +
507
+ formatNumber(y) +
508
+ '" r="' +
509
+ formatNumber(size * 0.18) +
510
+ '" fill="none" stroke="' +
511
+ color +
512
+ '" stroke-width="' +
513
+ strokeWidth +
514
+ '" />'
515
+ : ''
516
+
517
+ return (
518
+ '<path d="M ' +
519
+ formatNumber(left) +
520
+ ' ' +
521
+ formatNumber(top) +
522
+ ' L ' +
523
+ formatNumber(left) +
524
+ ' ' +
525
+ formatNumber(bottom) +
526
+ ' L ' +
527
+ formatNumber(right) +
528
+ ' ' +
529
+ formatNumber(y) +
530
+ ' Z" fill="none" stroke="' +
531
+ color +
532
+ '" stroke-width="' +
533
+ strokeWidth +
534
+ '" stroke-linejoin="round" />' +
535
+ bubble
536
+ )
537
+ }
538
+
539
+ if (name === 'clock') {
540
+ return (
541
+ '<path d="M ' +
542
+ formatNumber(left) +
543
+ ' ' +
544
+ formatNumber(top) +
545
+ ' L ' +
546
+ formatNumber(x) +
547
+ ' ' +
548
+ formatNumber(y) +
549
+ ' L ' +
550
+ formatNumber(left) +
551
+ ' ' +
552
+ formatNumber(bottom) +
553
+ '" fill="none" stroke="' +
554
+ color +
555
+ '" stroke-width="' +
556
+ strokeWidth +
557
+ '" stroke-linejoin="round" />'
558
+ )
559
+ }
560
+
561
+ return (
562
+ '<circle cx="' +
563
+ formatNumber(x) +
564
+ '" cy="' +
565
+ formatNumber(y) +
566
+ '" r="' +
567
+ formatNumber(half) +
568
+ '" fill="none" stroke="' +
569
+ color +
570
+ '" stroke-width="' +
571
+ strokeWidth +
572
+ '" />'
573
+ )
574
+ }
575
+
576
+ /**
577
+ * Projects one schematic coordinate pair into SVG coordinates.
578
+ * @param {{ x: number, y: number }} point Source point.
579
+ * @param {number} sheetHeight Sheet height.
580
+ * @returns {{ x: number, y: number }}
581
+ */
582
+ static #projectPoint(point, sheetHeight) {
583
+ return {
584
+ x: Number(point?.x) || 0,
585
+ y: projectSchematicY(sheetHeight, Number(point?.y) || 0)
586
+ }
587
+ }
588
+
227
589
  /**
228
590
  * Returns SVG stroke attributes for one schematic outline style.
229
591
  * @param {number | undefined} lineWidth