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.
- package/README.md +18 -6
- package/docs/api.md +78 -16
- package/docs/model-format.md +229 -8
- package/docs/schemas/altium_toolkit/netlist_a1.schema.json +47 -0
- package/docs/schemas/altium_toolkit/normalized_model_a1.schema.json +1661 -104
- package/docs/schemas/altium_toolkit/pcb_svg_semantics_a1.schema.json +59 -0
- package/docs/schemas/altium_toolkit/project_bundle_a1.schema.json +57 -0
- package/docs/schemas/altium_toolkit/schematic_svg_semantics_a1.schema.json +50 -0
- package/docs/testing.md +9 -3
- package/package.json +1 -1
- package/spec/library-scope.md +7 -1
- package/src/core/altium/AltiumLayoutParser.mjs +104 -8
- package/src/core/altium/AltiumParser.mjs +191 -45
- package/src/core/altium/EmbeddedFileInventoryBuilder.mjs +255 -0
- package/src/core/altium/IntLibModelParser.mjs +240 -0
- package/src/core/altium/IntLibStreamExtractor.mjs +366 -0
- package/src/core/altium/LibraryRenderManifestBuilder.mjs +417 -0
- package/src/core/altium/LibrarySearchIndex.mjs +215 -0
- package/src/core/altium/NormalizedModelSchema.mjs +36 -0
- package/src/core/altium/PcbCustomPadShapeParser.mjs +244 -0
- package/src/core/altium/PcbDefaultsParser.mjs +171 -0
- package/src/core/altium/PcbDimensionParser.mjs +229 -0
- package/src/core/altium/PcbEmbeddedModelExtractor.mjs +232 -6
- package/src/core/altium/PcbExtendedPrimitiveInformationParser.mjs +256 -0
- package/src/core/altium/PcbLibModelParser.mjs +235 -14
- package/src/core/altium/PcbLibStreamExtractor.mjs +62 -4
- package/src/core/altium/PcbMaskPasteResolver.mjs +354 -0
- package/src/core/altium/PcbMechanicalLayerPairParser.mjs +204 -0
- package/src/core/altium/PcbModelParser.mjs +466 -28
- package/src/core/altium/PcbOwnershipGraphBuilder.mjs +245 -0
- package/src/core/altium/PcbPadPrimitiveParser.mjs +78 -65
- package/src/core/altium/PcbPadStackParser.mjs +58 -0
- package/src/core/altium/PcbPickPlacePositionResolver.mjs +217 -0
- package/src/core/altium/PcbPrimitiveParameterParser.mjs +3 -2
- package/src/core/altium/PcbRawRecordRegistry.mjs +121 -130
- package/src/core/altium/PcbRegionPrimitiveParser.mjs +5 -1
- package/src/core/altium/PcbRuleParser.mjs +354 -33
- package/src/core/altium/PcbSidecarRecordParser.mjs +177 -0
- package/src/core/altium/PcbSpecialStringResolver.mjs +220 -0
- package/src/core/altium/PcbStatisticsBuilder.mjs +532 -0
- package/src/core/altium/PcbStreamExtractor.mjs +111 -4
- package/src/core/altium/PcbTextPrimitiveParser.mjs +60 -0
- package/src/core/altium/PcbUnionParser.mjs +307 -0
- package/src/core/altium/PcbViaStackParser.mjs +98 -10
- package/src/core/altium/PcbViaStructureParser.mjs +335 -0
- package/src/core/altium/PrintableTextDecoder.mjs +53 -3
- package/src/core/altium/PrjPcbModelParser.mjs +257 -5
- package/src/core/altium/ProjectAnnotationParser.mjs +205 -0
- package/src/core/altium/ProjectDesignBundleBuilder.mjs +477 -0
- package/src/core/altium/ProjectNetlistExporter.mjs +499 -0
- package/src/core/altium/ProjectOutJobDigestBuilder.mjs +109 -0
- package/src/core/altium/ProjectVariantViewBuilder.mjs +334 -0
- package/src/core/altium/SchematicBindingProvenanceParser.mjs +223 -0
- package/src/core/altium/SchematicComponentOwnerTextResolver.mjs +312 -0
- package/src/core/altium/SchematicComponentTextResolver.mjs +72 -19
- package/src/core/altium/SchematicConnectivityQaBuilder.mjs +271 -0
- package/src/core/altium/SchematicCrossSheetConnectorParser.mjs +140 -0
- package/src/core/altium/SchematicDirectiveParser.mjs +312 -0
- package/src/core/altium/SchematicDisplayModeCatalogParser.mjs +231 -0
- package/src/core/altium/SchematicHarnessParser.mjs +302 -0
- package/src/core/altium/SchematicImageParser.mjs +474 -3
- package/src/core/altium/SchematicImplementationParser.mjs +518 -0
- package/src/core/altium/SchematicNetlistBuilder.mjs +15 -2
- package/src/core/altium/SchematicOwnershipGraphParser.mjs +195 -0
- package/src/core/altium/SchematicPinParser.mjs +84 -1
- package/src/core/altium/SchematicPrimitiveParser.mjs +301 -0
- package/src/core/altium/SchematicProjectParameterResolver.mjs +361 -0
- package/src/core/altium/SchematicQaReportBuilder.mjs +284 -0
- package/src/core/altium/SchematicRecordTypeRegistry.mjs +137 -0
- package/src/core/altium/SchematicRepeatedChannelParser.mjs +229 -0
- package/src/core/altium/SchematicStreamExtractor.mjs +10 -1
- package/src/core/altium/SchematicTemplateParser.mjs +256 -0
- package/src/core/altium/SchematicTextParser.mjs +123 -0
- package/src/core/ole/OleCompoundDocument.mjs +20 -0
- package/src/parser.mjs +29 -0
- package/src/renderers.mjs +3 -0
- package/src/styles/altium-renderers.css +25 -0
- package/src/ui/PcbBarcodeTextRenderer.mjs +436 -0
- package/src/ui/PcbInteractionGeometry.mjs +350 -0
- package/src/ui/PcbInteractionIndex.mjs +593 -0
- package/src/ui/PcbInteractionItemRegistry.mjs +66 -0
- package/src/ui/PcbInteractionLayerModel.mjs +99 -0
- package/src/ui/PcbScene3dBoardOutlineRefiner.mjs +74 -9
- package/src/ui/PcbScene3dBuilder.mjs +169 -7
- package/src/ui/PcbScene3dModelRegistry.mjs +74 -0
- package/src/ui/PcbSvgRenderer.mjs +1187 -34
- package/src/ui/PcbTextPrimitiveRenderer.mjs +193 -7
- package/src/ui/SchematicNoteRenderer.mjs +9 -2
- package/src/ui/SchematicOwnerPinLabelLayout.mjs +206 -0
- package/src/ui/SchematicShapeRenderer.mjs +362 -0
- package/src/ui/SchematicSvgRenderer.mjs +1442 -92
- package/src/ui/SchematicTypography.mjs +48 -5
- package/src/ui/TextGeometrySidecarBuilder.mjs +147 -0
|
@@ -9,19 +9,34 @@ export class SchematicTypography {
|
|
|
9
9
|
/**
|
|
10
10
|
* Returns true when the schematic already contains a visible designator text
|
|
11
11
|
* close to one component origin.
|
|
12
|
-
* @param {{ x?: number, y?: number }} component
|
|
12
|
+
* @param {{ x?: number, y?: number, designator?: string }} component
|
|
13
13
|
* @param {{ x: number, y: number, name?: string }[]} texts
|
|
14
14
|
* @returns {boolean}
|
|
15
15
|
*/
|
|
16
16
|
static hasNearbyVisibleDesignatorText(component, texts) {
|
|
17
|
-
|
|
18
|
-
(
|
|
17
|
+
const designatorPattern =
|
|
18
|
+
SchematicTypography.#componentDesignatorPattern(
|
|
19
|
+
component?.designator
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
return texts.some((text) => {
|
|
23
|
+
if (
|
|
19
24
|
String(text.name || '')
|
|
20
25
|
.trim()
|
|
21
|
-
.toLowerCase()
|
|
26
|
+
.toLowerCase() !== 'designator'
|
|
27
|
+
) {
|
|
28
|
+
return false
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (designatorPattern?.test(String(text.text || '').trim())) {
|
|
32
|
+
return true
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return (
|
|
22
36
|
Math.abs(Number(text.x) - Number(component.x)) <= 80 &&
|
|
23
37
|
Math.abs(Number(text.y) - Number(component.y)) <= 80
|
|
24
|
-
|
|
38
|
+
)
|
|
39
|
+
})
|
|
25
40
|
}
|
|
26
41
|
|
|
27
42
|
/**
|
|
@@ -57,6 +72,34 @@ export class SchematicTypography {
|
|
|
57
72
|
)
|
|
58
73
|
}
|
|
59
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Builds a strict matcher for a base designator or one multipart suffix.
|
|
77
|
+
* @param {string | undefined} designator Component designator.
|
|
78
|
+
* @returns {RegExp | null}
|
|
79
|
+
*/
|
|
80
|
+
static #componentDesignatorPattern(designator) {
|
|
81
|
+
const normalizedDesignator = String(designator || '').trim()
|
|
82
|
+
if (!normalizedDesignator || normalizedDesignator === 'U?') {
|
|
83
|
+
return null
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return new RegExp(
|
|
87
|
+
'^' +
|
|
88
|
+
SchematicTypography.#escapeRegExp(normalizedDesignator) +
|
|
89
|
+
'[A-Z]?$',
|
|
90
|
+
'i'
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Escapes user-controlled text for inclusion in a regular expression.
|
|
96
|
+
* @param {string} value Source text.
|
|
97
|
+
* @returns {string}
|
|
98
|
+
*/
|
|
99
|
+
static #escapeRegExp(value) {
|
|
100
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
101
|
+
}
|
|
102
|
+
|
|
60
103
|
/**
|
|
61
104
|
* Builds render options for one schematic text label, including the signed
|
|
62
105
|
* SVG rotation derived from the original Altium orientation and mirrored
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 André Fiedler
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Builds deterministic estimated text geometry sidecars for SVG exports.
|
|
7
|
+
*/
|
|
8
|
+
export class TextGeometrySidecarBuilder {
|
|
9
|
+
static SCHEMA_ID = 'altium-toolkit.text-geometry.a1'
|
|
10
|
+
static #DEFAULT_WIDTH_RATIO = 0.6
|
|
11
|
+
static #HEIGHT_PADDING = 2
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Builds sidecar metadata for schematic text rows.
|
|
15
|
+
* @param {object[]} texts Text rows.
|
|
16
|
+
* @param {Map<object, number>} textIndexes Stable text index map.
|
|
17
|
+
* @returns {{ schema: string, entries: object[] }}
|
|
18
|
+
*/
|
|
19
|
+
static buildSchematic(texts, textIndexes) {
|
|
20
|
+
return TextGeometrySidecarBuilder.#build(texts, textIndexes, {
|
|
21
|
+
prefix: 'schematic-text-',
|
|
22
|
+
defaultFontSize: 10,
|
|
23
|
+
defaultFontFamily: ''
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Builds sidecar metadata for PCB text rows.
|
|
29
|
+
* @param {object[]} texts Text rows.
|
|
30
|
+
* @param {Map<object, number>} textIndexes Stable text index map.
|
|
31
|
+
* @returns {{ schema: string, entries: object[] }}
|
|
32
|
+
*/
|
|
33
|
+
static buildPcb(texts, textIndexes) {
|
|
34
|
+
return TextGeometrySidecarBuilder.#build(texts, textIndexes, {
|
|
35
|
+
prefix: 'pcb-text-',
|
|
36
|
+
defaultFontSize: 10,
|
|
37
|
+
defaultFontFamily: ''
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Builds generic text sidecar metadata.
|
|
43
|
+
* @param {object[]} texts Text rows.
|
|
44
|
+
* @param {Map<object, number>} textIndexes Stable text index map.
|
|
45
|
+
* @param {{ prefix: string, defaultFontSize: number, defaultFontFamily: string }} options Build options.
|
|
46
|
+
* @returns {{ schema: string, entries: object[] }}
|
|
47
|
+
*/
|
|
48
|
+
static #build(texts, textIndexes, options) {
|
|
49
|
+
return {
|
|
50
|
+
schema: TextGeometrySidecarBuilder.SCHEMA_ID,
|
|
51
|
+
entries: (texts || [])
|
|
52
|
+
.map((text, fallbackIndex) =>
|
|
53
|
+
TextGeometrySidecarBuilder.#entry(
|
|
54
|
+
text,
|
|
55
|
+
textIndexes,
|
|
56
|
+
fallbackIndex,
|
|
57
|
+
options
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
.filter(Boolean)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Builds one text sidecar entry.
|
|
66
|
+
* @param {object} text Text row.
|
|
67
|
+
* @param {Map<object, number>} textIndexes Stable text index map.
|
|
68
|
+
* @param {number} fallbackIndex Fallback text index.
|
|
69
|
+
* @param {{ prefix: string, defaultFontSize: number, defaultFontFamily: string }} options Build options.
|
|
70
|
+
* @returns {object | null}
|
|
71
|
+
*/
|
|
72
|
+
static #entry(text, textIndexes, fallbackIndex, options) {
|
|
73
|
+
const value = String(text?.resolvedText || text?.text || '')
|
|
74
|
+
if (!value.trim()) {
|
|
75
|
+
return null
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const index = textIndexes?.get(text) ?? fallbackIndex
|
|
79
|
+
const fontSize = Number(text?.fontSize || text?.height || 0) || 10
|
|
80
|
+
const width = value.length * fontSize * this.#DEFAULT_WIDTH_RATIO
|
|
81
|
+
const height = fontSize + this.#HEIGHT_PADDING
|
|
82
|
+
const x = Number(text?.x || 0)
|
|
83
|
+
const y = Number(text?.y || 0)
|
|
84
|
+
|
|
85
|
+
return TextGeometrySidecarBuilder.#stripEmpty({
|
|
86
|
+
elementKey: options.prefix + index,
|
|
87
|
+
recordId: TextGeometrySidecarBuilder.#recordId(text),
|
|
88
|
+
text: value,
|
|
89
|
+
fontFamily: text?.fontFamily || options.defaultFontFamily,
|
|
90
|
+
fontSize,
|
|
91
|
+
fontWeight: Number(text?.fontWeight || 400),
|
|
92
|
+
geometryKind: 'estimated-bounds-polygon',
|
|
93
|
+
polygon: [
|
|
94
|
+
{ x, y },
|
|
95
|
+
{ x: TextGeometrySidecarBuilder.#round(x + width), y },
|
|
96
|
+
{
|
|
97
|
+
x: TextGeometrySidecarBuilder.#round(x + width),
|
|
98
|
+
y: TextGeometrySidecarBuilder.#round(y - height)
|
|
99
|
+
},
|
|
100
|
+
{ x, y: TextGeometrySidecarBuilder.#round(y - height) }
|
|
101
|
+
]
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Resolves a stable source record id for one text row.
|
|
107
|
+
* @param {object} text Text row.
|
|
108
|
+
* @returns {string}
|
|
109
|
+
*/
|
|
110
|
+
static #recordId(text) {
|
|
111
|
+
const recordId =
|
|
112
|
+
text?.recordId ?? text?.sourceRecordId ?? text?.sourceRecordIndex
|
|
113
|
+
|
|
114
|
+
return recordId === null || recordId === undefined
|
|
115
|
+
? ''
|
|
116
|
+
: String(recordId)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Rounds numbers to a concise JSON-friendly precision.
|
|
121
|
+
* @param {number} value Raw number.
|
|
122
|
+
* @returns {number}
|
|
123
|
+
*/
|
|
124
|
+
static #round(value) {
|
|
125
|
+
return Number(Number(value).toFixed(4))
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Removes empty fields while preserving zero and false.
|
|
130
|
+
* @param {Record<string, unknown>} value Candidate entry.
|
|
131
|
+
* @returns {Record<string, unknown>}
|
|
132
|
+
*/
|
|
133
|
+
static #stripEmpty(value) {
|
|
134
|
+
return Object.fromEntries(
|
|
135
|
+
Object.entries(value || {}).filter(([, entryValue]) => {
|
|
136
|
+
if (Array.isArray(entryValue)) {
|
|
137
|
+
return entryValue.length > 0
|
|
138
|
+
}
|
|
139
|
+
return (
|
|
140
|
+
entryValue !== null &&
|
|
141
|
+
entryValue !== undefined &&
|
|
142
|
+
entryValue !== ''
|
|
143
|
+
)
|
|
144
|
+
})
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
}
|