altium-toolkit 0.1.0

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 (82) hide show
  1. package/AGENTS.md +67 -0
  2. package/COMMERCIAL-LICENSE.md +20 -0
  3. package/CONTRIBUTING.md +19 -0
  4. package/LICENSE +22 -0
  5. package/LICENSES/CC-BY-SA-4.0.txt +170 -0
  6. package/LICENSES/GPL-3.0-or-later.txt +232 -0
  7. package/NOTICE.md +32 -0
  8. package/README.md +116 -0
  9. package/docs/api.md +73 -0
  10. package/docs/model-format.md +36 -0
  11. package/docs/testing.md +25 -0
  12. package/examples/README.md +47 -0
  13. package/examples/arduino-uno/PcbThreeSceneRenderer.mjs +635 -0
  14. package/examples/arduino-uno/SvgViewportController.mjs +306 -0
  15. package/examples/arduino-uno/example.mjs +480 -0
  16. package/examples/arduino-uno/index.html +163 -0
  17. package/examples/arduino-uno/styles.css +552 -0
  18. package/examples/server.mjs +212 -0
  19. package/package.json +53 -0
  20. package/spec/library-scope.md +32 -0
  21. package/src/core/BinaryReader.mjs +127 -0
  22. package/src/core/altium/AltiumLayoutParser.mjs +485 -0
  23. package/src/core/altium/AltiumParser.mjs +1007 -0
  24. package/src/core/altium/AsciiRecordParser.mjs +151 -0
  25. package/src/core/altium/ParserUtils.mjs +173 -0
  26. package/src/core/altium/PcbBinaryPrimitiveParser.mjs +424 -0
  27. package/src/core/altium/PcbEmbeddedModelExtractor.mjs +505 -0
  28. package/src/core/altium/PcbModelParser.mjs +336 -0
  29. package/src/core/altium/PcbOutlineRasterizer.mjs +852 -0
  30. package/src/core/altium/PcbOutlineRecovery.mjs +957 -0
  31. package/src/core/altium/PcbStreamExtractor.mjs +210 -0
  32. package/src/core/altium/PrintableTextDecoder.mjs +156 -0
  33. package/src/core/altium/SchematicAnnotationParser.mjs +220 -0
  34. package/src/core/altium/SchematicBusEntryParser.mjs +48 -0
  35. package/src/core/altium/SchematicDirectiveParser.mjs +47 -0
  36. package/src/core/altium/SchematicImageParser.mjs +173 -0
  37. package/src/core/altium/SchematicJunctionParser.mjs +43 -0
  38. package/src/core/altium/SchematicMultipartOwnerMatcher.mjs +564 -0
  39. package/src/core/altium/SchematicNetlistBuilder.mjs +351 -0
  40. package/src/core/altium/SchematicPinParser.mjs +767 -0
  41. package/src/core/altium/SchematicPrimitiveParser.mjs +716 -0
  42. package/src/core/altium/SchematicSheetParser.mjs +241 -0
  43. package/src/core/altium/SchematicSheetStyleResolver.mjs +46 -0
  44. package/src/core/altium/SchematicStandaloneCalloutNormalizer.mjs +592 -0
  45. package/src/core/altium/SchematicTextParser.mjs +708 -0
  46. package/src/core/altium/SchematicTextPostProcessor.mjs +801 -0
  47. package/src/core/ole/OleCompoundDocument.mjs +439 -0
  48. package/src/core/ole/OleConstants.mjs +64 -0
  49. package/src/core/ole/OleDirectoryEntry.mjs +95 -0
  50. package/src/index.mjs +7 -0
  51. package/src/parser.mjs +21 -0
  52. package/src/renderers.mjs +15 -0
  53. package/src/scene3d.mjs +9 -0
  54. package/src/styles/altium-renderers.css +358 -0
  55. package/src/ui/BomTableRenderer.mjs +46 -0
  56. package/src/ui/PcbArcUtils.mjs +189 -0
  57. package/src/ui/PcbEdgeFacingGlyphNormalizer.mjs +808 -0
  58. package/src/ui/PcbFootprintPrimitiveSelector.mjs +128 -0
  59. package/src/ui/PcbScene3dBuilder.mjs +742 -0
  60. package/src/ui/PcbScene3dModelRegistry.mjs +309 -0
  61. package/src/ui/PcbScene3dPackages.mjs +137 -0
  62. package/src/ui/PcbScene3dScenePreparator.mjs +36 -0
  63. package/src/ui/PcbScene3dSummaryRenderer.mjs +65 -0
  64. package/src/ui/PcbSvgRenderer.mjs +906 -0
  65. package/src/ui/SchematicColorResolver.mjs +132 -0
  66. package/src/ui/SchematicContentLayout.mjs +661 -0
  67. package/src/ui/SchematicDirectiveRenderer.mjs +184 -0
  68. package/src/ui/SchematicImageRenderer.mjs +135 -0
  69. package/src/ui/SchematicJunctionRenderer.mjs +381 -0
  70. package/src/ui/SchematicNoteRenderer.mjs +427 -0
  71. package/src/ui/SchematicOwnerPinLabelLayout.mjs +173 -0
  72. package/src/ui/SchematicPinSvgRenderer.mjs +495 -0
  73. package/src/ui/SchematicPortRenderer.mjs +558 -0
  74. package/src/ui/SchematicPowerPortRenderer.mjs +574 -0
  75. package/src/ui/SchematicRegionRenderer.mjs +94 -0
  76. package/src/ui/SchematicShapeRenderer.mjs +398 -0
  77. package/src/ui/SchematicSheetChromeRenderer.mjs +1025 -0
  78. package/src/ui/SchematicSheetSymbolRenderer.mjs +228 -0
  79. package/src/ui/SchematicSvgRenderer.mjs +756 -0
  80. package/src/ui/SchematicSvgUtils.mjs +182 -0
  81. package/src/ui/SchematicTypography.mjs +204 -0
  82. package/src/workers/altium-parser.worker.mjs +29 -0
@@ -0,0 +1,228 @@
1
+ // SPDX-FileCopyrightText: 2026 André Fiedler
2
+ //
3
+ // SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ import { SchematicSvgUtils } from './SchematicSvgUtils.mjs'
6
+ import { SchematicColorResolver } from './SchematicColorResolver.mjs'
7
+
8
+ const { createSvgText, escapeHtml, formatNumber, projectSchematicY } =
9
+ SchematicSvgUtils
10
+
11
+ /**
12
+ * Renders normalized schematic sheet symbols and sheet entries.
13
+ */
14
+ export class SchematicSheetSymbolRenderer {
15
+ /**
16
+ * Builds markup for normalized sheet symbols.
17
+ * @param {{ x: number, y: number, width: number, height: number, color: string, fill: string, isSolid: boolean, transparent: boolean }[]} sheetSymbols
18
+ * @param {number} sheetHeight
19
+ * @returns {string}
20
+ */
21
+ static buildSheetSymbolMarkup(sheetSymbols, sheetHeight) {
22
+ return sheetSymbols
23
+ .map((sheetSymbol) =>
24
+ SchematicSheetSymbolRenderer.#buildSingleSheetSymbolMarkup(
25
+ sheetSymbol,
26
+ sheetHeight
27
+ )
28
+ )
29
+ .join('')
30
+ }
31
+
32
+ /**
33
+ * Builds markup for normalized sheet entries.
34
+ * @param {{ name: string, side: 'left' | 'right' | 'top' | 'bottom', x: number, y: number, color: string, fill: string, textColor: string }} sheetEntries
35
+ * @param {number} sheetHeight
36
+ * @returns {string}
37
+ */
38
+ static buildSheetEntryMarkup(sheetEntries, sheetHeight) {
39
+ return sheetEntries
40
+ .map((sheetEntry) =>
41
+ SchematicSheetSymbolRenderer.#buildSingleSheetEntryMarkup(
42
+ sheetEntry,
43
+ sheetHeight
44
+ )
45
+ )
46
+ .join('')
47
+ }
48
+
49
+ /**
50
+ * Builds one sheet symbol rectangle.
51
+ * @param {{ x: number, y: number, width: number, height: number, color: string, fill: string, isSolid: boolean, transparent: boolean }} sheetSymbol
52
+ * @param {number} sheetHeight
53
+ * @returns {string}
54
+ */
55
+ static #buildSingleSheetSymbolMarkup(sheetSymbol, sheetHeight) {
56
+ const fill =
57
+ sheetSymbol.transparent || !sheetSymbol.isSolid
58
+ ? 'none'
59
+ : SchematicColorResolver.resolveFill(
60
+ sheetSymbol.fill,
61
+ '--schematic-fill-color'
62
+ )
63
+
64
+ return (
65
+ '<rect class="schematic-sheet-symbol" x="' +
66
+ formatNumber(sheetSymbol.x) +
67
+ '" y="' +
68
+ formatNumber(projectSchematicY(sheetHeight, sheetSymbol.y)) +
69
+ '" width="' +
70
+ formatNumber(sheetSymbol.width) +
71
+ '" height="' +
72
+ formatNumber(sheetSymbol.height) +
73
+ '" fill="' +
74
+ escapeHtml(fill) +
75
+ '" stroke="' +
76
+ escapeHtml(
77
+ SchematicColorResolver.resolveColor(
78
+ sheetSymbol.color,
79
+ '--schematic-default-ink-color'
80
+ )
81
+ ) +
82
+ '" stroke-width="1" />'
83
+ )
84
+ }
85
+
86
+ /**
87
+ * Builds one sheet entry shape and label.
88
+ * @param {{ name: string, side: 'left' | 'right' | 'top' | 'bottom', x: number, y: number, color: string, fill: string, textColor: string }} sheetEntry
89
+ * @param {number} sheetHeight
90
+ * @returns {string}
91
+ */
92
+ static #buildSingleSheetEntryMarkup(sheetEntry, sheetHeight) {
93
+ const points =
94
+ SchematicSheetSymbolRenderer.#buildSheetEntryPoints(sheetEntry)
95
+ const projectedPoints = points
96
+ .map(
97
+ (point) =>
98
+ formatNumber(point.x) +
99
+ ',' +
100
+ formatNumber(projectSchematicY(sheetHeight, point.y))
101
+ )
102
+ .join(' ')
103
+ const labelPlacement =
104
+ SchematicSheetSymbolRenderer.#resolveSheetEntryLabelPlacement(
105
+ sheetEntry,
106
+ sheetHeight
107
+ )
108
+
109
+ return (
110
+ '<g class="schematic-sheet-entry">' +
111
+ '<polygon class="schematic-sheet-entry-shape" points="' +
112
+ escapeHtml(projectedPoints) +
113
+ '" fill="' +
114
+ escapeHtml(
115
+ SchematicColorResolver.resolveFill(
116
+ sheetEntry.fill,
117
+ '--schematic-fill-color'
118
+ )
119
+ ) +
120
+ '" stroke="' +
121
+ escapeHtml(
122
+ SchematicColorResolver.resolveColor(
123
+ sheetEntry.color,
124
+ '--schematic-port-color'
125
+ )
126
+ ) +
127
+ '" stroke-width="1" />' +
128
+ createSvgText(
129
+ 'schematic-sheet-entry-label',
130
+ labelPlacement.x,
131
+ labelPlacement.y,
132
+ sheetEntry.name,
133
+ SchematicColorResolver.resolveColor(
134
+ sheetEntry.textColor,
135
+ '--schematic-text-color'
136
+ ),
137
+ labelPlacement.anchor,
138
+ {
139
+ fontSize: 10,
140
+ fontFamily: 'Times New Roman'
141
+ }
142
+ ) +
143
+ '</g>'
144
+ )
145
+ }
146
+
147
+ /**
148
+ * Builds one simple entry polygon from its edge anchor.
149
+ * @param {{ side: 'left' | 'right' | 'top' | 'bottom', x: number, y: number }} sheetEntry
150
+ * @returns {{ x: number, y: number }[]}
151
+ */
152
+ static #buildSheetEntryPoints(sheetEntry) {
153
+ const arm = 25
154
+ const halfHeight = 5
155
+
156
+ switch (sheetEntry.side) {
157
+ case 'right':
158
+ return [
159
+ { x: sheetEntry.x, y: sheetEntry.y },
160
+ { x: sheetEntry.x + 8, y: sheetEntry.y - halfHeight },
161
+ { x: sheetEntry.x + arm, y: sheetEntry.y - halfHeight },
162
+ { x: sheetEntry.x + arm, y: sheetEntry.y + halfHeight },
163
+ { x: sheetEntry.x + 8, y: sheetEntry.y + halfHeight }
164
+ ]
165
+ case 'top':
166
+ return [
167
+ { x: sheetEntry.x, y: sheetEntry.y },
168
+ { x: sheetEntry.x - halfHeight, y: sheetEntry.y + 8 },
169
+ { x: sheetEntry.x - halfHeight, y: sheetEntry.y + arm },
170
+ { x: sheetEntry.x + halfHeight, y: sheetEntry.y + arm },
171
+ { x: sheetEntry.x + halfHeight, y: sheetEntry.y + 8 }
172
+ ]
173
+ case 'bottom':
174
+ return [
175
+ { x: sheetEntry.x, y: sheetEntry.y },
176
+ { x: sheetEntry.x - halfHeight, y: sheetEntry.y - 8 },
177
+ { x: sheetEntry.x - halfHeight, y: sheetEntry.y - arm },
178
+ { x: sheetEntry.x + halfHeight, y: sheetEntry.y - arm },
179
+ { x: sheetEntry.x + halfHeight, y: sheetEntry.y - 8 }
180
+ ]
181
+ case 'left':
182
+ default:
183
+ return [
184
+ { x: sheetEntry.x, y: sheetEntry.y },
185
+ { x: sheetEntry.x - 8, y: sheetEntry.y - halfHeight },
186
+ { x: sheetEntry.x - arm, y: sheetEntry.y - halfHeight },
187
+ { x: sheetEntry.x - arm, y: sheetEntry.y + halfHeight },
188
+ { x: sheetEntry.x - 8, y: sheetEntry.y + halfHeight }
189
+ ]
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Resolves sheet-entry label placement.
195
+ * @param {{ side: 'left' | 'right' | 'top' | 'bottom', x: number, y: number }} sheetEntry
196
+ * @param {number} sheetHeight
197
+ * @returns {{ x: number, y: number, anchor: 'start' | 'middle' | 'end' }}
198
+ */
199
+ static #resolveSheetEntryLabelPlacement(sheetEntry, sheetHeight) {
200
+ switch (sheetEntry.side) {
201
+ case 'right':
202
+ return {
203
+ x: sheetEntry.x + 30,
204
+ y: projectSchematicY(sheetHeight, sheetEntry.y) + 3,
205
+ anchor: 'start'
206
+ }
207
+ case 'top':
208
+ return {
209
+ x: sheetEntry.x + 8,
210
+ y: projectSchematicY(sheetHeight, sheetEntry.y + 30),
211
+ anchor: 'start'
212
+ }
213
+ case 'bottom':
214
+ return {
215
+ x: sheetEntry.x + 8,
216
+ y: projectSchematicY(sheetHeight, sheetEntry.y - 12),
217
+ anchor: 'start'
218
+ }
219
+ case 'left':
220
+ default:
221
+ return {
222
+ x: sheetEntry.x - 30,
223
+ y: projectSchematicY(sheetHeight, sheetEntry.y) + 3,
224
+ anchor: 'end'
225
+ }
226
+ }
227
+ }
228
+ }