altium-toolkit 0.1.20 → 0.1.21
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 +15 -3
- package/src/core/altium/PcbFontMetricsParser.mjs +33 -6
- package/src/core/altium/PcbModelParser.mjs +82 -0
- package/src/core/altium/PcbTextPrimitiveParser.mjs +48 -3
- package/src/core/altium/SchematicPinDesignatorInferer.mjs +401 -0
- package/src/core/altium/SchematicPinParser.mjs +136 -56
- package/src/core/altium/SchematicStreamExtractor.mjs +93 -0
- package/src/core/altium/SchematicTextPostProcessor.mjs +40 -12
- package/src/ui/PcbScene3dBuilder.mjs +593 -53
- package/src/ui/PcbScene3dDrillCutoutBuilder.mjs +453 -0
- package/src/ui/SchematicImageRenderer.mjs +134 -32
- package/src/ui/SchematicJunctionRenderer.mjs +22 -4
- package/src/ui/SchematicNoteRenderer.mjs +73 -9
- package/src/ui/SchematicPinSvgRenderer.mjs +25 -3
- package/src/ui/SchematicPowerPortRenderer.mjs +183 -36
- package/src/ui/SchematicSvgRenderer.mjs +2 -2
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 André Fiedler
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
|
|
5
|
+
import { AsciiRecordParser } from './AsciiRecordParser.mjs'
|
|
6
|
+
import { OleCompoundDocument } from '../ole/OleCompoundDocument.mjs'
|
|
7
|
+
import { OleConstants } from '../ole/OleConstants.mjs'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Extracts stream-scoped printable schematic content from OLE-backed SchDoc
|
|
11
|
+
* containers.
|
|
12
|
+
*/
|
|
13
|
+
export class SchematicStreamExtractor {
|
|
14
|
+
/**
|
|
15
|
+
* Returns true when one buffer starts with the OLE compound-document
|
|
16
|
+
* signature.
|
|
17
|
+
* @param {ArrayBuffer} arrayBuffer
|
|
18
|
+
* @returns {boolean}
|
|
19
|
+
*/
|
|
20
|
+
static isCompoundDocument(arrayBuffer) {
|
|
21
|
+
const bytes = new Uint8Array(
|
|
22
|
+
arrayBuffer,
|
|
23
|
+
0,
|
|
24
|
+
Math.min(
|
|
25
|
+
arrayBuffer.byteLength,
|
|
26
|
+
OleConstants.HEADER_SIGNATURE.length
|
|
27
|
+
)
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
if (bytes.byteLength < OleConstants.HEADER_SIGNATURE.length) {
|
|
31
|
+
return false
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return OleConstants.HEADER_SIGNATURE.every(
|
|
35
|
+
(value, index) => bytes[index] === value
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Extracts schematic records from the logical `FileHeader` stream.
|
|
41
|
+
* @param {ArrayBuffer} arrayBuffer
|
|
42
|
+
* @returns {{ records: Array<{ raw: string, fields: Record<string, string | string[]>, sourceStream: string }>, streamNames: string[] } | null}
|
|
43
|
+
*/
|
|
44
|
+
static extractFromArrayBuffer(arrayBuffer) {
|
|
45
|
+
if (!SchematicStreamExtractor.isCompoundDocument(arrayBuffer)) {
|
|
46
|
+
return null
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let compoundDocument
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
compoundDocument = OleCompoundDocument.fromArrayBuffer(arrayBuffer)
|
|
53
|
+
} catch {
|
|
54
|
+
return null
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let fileHeaderBytes
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
fileHeaderBytes = compoundDocument.getStream('FileHeader')
|
|
61
|
+
} catch {
|
|
62
|
+
return null
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const records = AsciiRecordParser.parse(
|
|
66
|
+
SchematicStreamExtractor.#toArrayBuffer(fileHeaderBytes)
|
|
67
|
+
).map((record) => ({
|
|
68
|
+
...record,
|
|
69
|
+
sourceStream: 'FileHeader'
|
|
70
|
+
}))
|
|
71
|
+
|
|
72
|
+
if (!records.length) {
|
|
73
|
+
return null
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
records,
|
|
78
|
+
streamNames: compoundDocument.listStreams()
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Returns an ArrayBuffer view over one byte slice.
|
|
84
|
+
* @param {Uint8Array} bytes
|
|
85
|
+
* @returns {ArrayBuffer}
|
|
86
|
+
*/
|
|
87
|
+
static #toArrayBuffer(bytes) {
|
|
88
|
+
return bytes.buffer.slice(
|
|
89
|
+
bytes.byteOffset,
|
|
90
|
+
bytes.byteOffset + bytes.byteLength
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -136,13 +136,25 @@ export class SchematicTextPostProcessor {
|
|
|
136
136
|
* @param {{ x1: number, y1: number, x2: number, y2: number, ownerIndex?: string }[]} lines
|
|
137
137
|
* @param {{ x: number, y: number, ownerIndex: string, length: number, orientation: 'left' | 'right' | 'top' | 'bottom' }[]} pins
|
|
138
138
|
* @param {{ x: number, y: number, width: number, direction?: 'left' | 'right' | 'up' | 'down' }[]} ports
|
|
139
|
+
* @param {{ x: number, y: number, width: number, height: number, ownerIndex?: string }[]} rectangles
|
|
139
140
|
* @returns {{ x: number, y: number, text: string, name?: string, ownerIndex?: string, recordType?: string, rotation?: number, anchor?: 'start' | 'middle' | 'end' }[]}
|
|
140
141
|
*/
|
|
141
|
-
static anchorComponentTextsFromOwnerBounds(
|
|
142
|
+
static anchorComponentTextsFromOwnerBounds(
|
|
143
|
+
texts,
|
|
144
|
+
lines,
|
|
145
|
+
pins,
|
|
146
|
+
ports = [],
|
|
147
|
+
rectangles = []
|
|
148
|
+
) {
|
|
142
149
|
const ownerBounds = SchematicTextPostProcessor.#buildOwnerBounds(
|
|
143
150
|
lines,
|
|
144
151
|
pins
|
|
145
152
|
)
|
|
153
|
+
const ownerBodyBounds = SchematicTextPostProcessor.#buildOwnerBounds(
|
|
154
|
+
lines,
|
|
155
|
+
pins,
|
|
156
|
+
rectangles
|
|
157
|
+
)
|
|
146
158
|
const ownerPinCounts =
|
|
147
159
|
SchematicTextPostProcessor.#buildOwnerPinCounts(pins)
|
|
148
160
|
const ownerPinOrientations =
|
|
@@ -164,14 +176,11 @@ export class SchematicTextPostProcessor {
|
|
|
164
176
|
return text
|
|
165
177
|
}
|
|
166
178
|
|
|
167
|
-
const paddedText =
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
bounds
|
|
173
|
-
)
|
|
174
|
-
: text
|
|
179
|
+
const paddedText =
|
|
180
|
+
SchematicTextPostProcessor.#padDesignatorAboveOwner(
|
|
181
|
+
text,
|
|
182
|
+
ownerBodyBounds.get(text.ownerIndex) || bounds
|
|
183
|
+
)
|
|
175
184
|
const ownerPinCount = ownerPinCounts.get(text.ownerIndex) || 0
|
|
176
185
|
|
|
177
186
|
if (text.y > bounds.maxY) {
|
|
@@ -290,12 +299,13 @@ export class SchematicTextPostProcessor {
|
|
|
290
299
|
}
|
|
291
300
|
|
|
292
301
|
/**
|
|
293
|
-
* Builds per-owner primitive bounds from drawable lines and
|
|
302
|
+
* Builds per-owner primitive bounds from drawable lines, pins, and bodies.
|
|
294
303
|
* @param {{ x1: number, y1: number, x2: number, y2: number, ownerIndex?: string }[]} lines
|
|
295
304
|
* @param {{ x: number, y: number, ownerIndex: string }[]} pins
|
|
305
|
+
* @param {{ x: number, y: number, width: number, height: number, ownerIndex?: string }[]} rectangles
|
|
296
306
|
* @returns {Map<string, { minX: number, minY: number, maxX: number, maxY: number }>}
|
|
297
307
|
*/
|
|
298
|
-
static #buildOwnerBounds(lines, pins) {
|
|
308
|
+
static #buildOwnerBounds(lines, pins, rectangles = []) {
|
|
299
309
|
const ownerBounds = new Map()
|
|
300
310
|
|
|
301
311
|
for (const line of lines) {
|
|
@@ -325,6 +335,24 @@ export class SchematicTextPostProcessor {
|
|
|
325
335
|
)
|
|
326
336
|
}
|
|
327
337
|
|
|
338
|
+
for (const rectangle of rectangles) {
|
|
339
|
+
if (!rectangle.ownerIndex) {
|
|
340
|
+
continue
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
SchematicTextPostProcessor.#extendBounds(
|
|
344
|
+
ownerBounds,
|
|
345
|
+
rectangle.ownerIndex,
|
|
346
|
+
[
|
|
347
|
+
{ x: rectangle.x, y: rectangle.y },
|
|
348
|
+
{
|
|
349
|
+
x: rectangle.x + rectangle.width,
|
|
350
|
+
y: rectangle.y + rectangle.height
|
|
351
|
+
}
|
|
352
|
+
]
|
|
353
|
+
)
|
|
354
|
+
}
|
|
355
|
+
|
|
328
356
|
return ownerBounds
|
|
329
357
|
}
|
|
330
358
|
|
|
@@ -506,7 +534,7 @@ export class SchematicTextPostProcessor {
|
|
|
506
534
|
* @returns {{ x: number, y: number }}
|
|
507
535
|
*/
|
|
508
536
|
static #padDesignatorAboveOwner(text, bounds) {
|
|
509
|
-
if (text.y
|
|
537
|
+
if (text.y < bounds.maxY - 1 || text.y >= bounds.maxY + 4) {
|
|
510
538
|
return text
|
|
511
539
|
}
|
|
512
540
|
|