altium-toolkit 1.0.8 → 1.0.10
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/ci_artifact_bundle_a1.schema.json +76 -0
- package/docs/schemas/altium_toolkit/draftsman_digest_a1.schema.json +35 -0
- package/docs/schemas/altium_toolkit/netlist_a1.schema.json +53 -0
- package/docs/schemas/altium_toolkit/normalized_model_a1.schema.json +1826 -110
- package/docs/schemas/altium_toolkit/parser_compatibility_fuzz_a1.schema.json +25 -0
- package/docs/schemas/altium_toolkit/pcb_svg_semantics_a1.schema.json +86 -0
- package/docs/schemas/altium_toolkit/project_bundle_a1.schema.json +63 -0
- package/docs/schemas/altium_toolkit/project_document_graph_a1.schema.json +33 -0
- package/docs/schemas/altium_toolkit/schematic_svg_semantics_a1.schema.json +50 -0
- package/docs/schemas/altium_toolkit/svg_model_cross_link_a1.schema.json +39 -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 +196 -45
- package/src/core/altium/CiArtifactBundleBuilder.mjs +202 -0
- package/src/core/altium/DraftsmanDigestParser.mjs +689 -0
- 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/ParserCompatibilityFuzzer.mjs +192 -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 +495 -32
- package/src/core/altium/PcbOwnershipGraphBuilder.mjs +245 -0
- package/src/core/altium/PcbPadPrimitiveParser.mjs +78 -65
- package/src/core/altium/PcbPadStackParser.mjs +229 -2
- package/src/core/altium/PcbPickPlacePositionResolver.mjs +224 -0
- package/src/core/altium/PcbPrimitiveParameterParser.mjs +3 -2
- package/src/core/altium/PcbRawRecordRegistry.mjs +121 -130
- package/src/core/altium/PcbRegionPrimitiveParser.mjs +76 -3
- package/src/core/altium/PcbRouteAnalysisBuilder.mjs +730 -0
- 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 +541 -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 +281 -7
- package/src/core/altium/ProjectAnnotationParser.mjs +205 -0
- package/src/core/altium/ProjectDesignBundleBuilder.mjs +492 -0
- package/src/core/altium/ProjectDocumentGraphBuilder.mjs +280 -0
- package/src/core/altium/ProjectNetlistExporter.mjs +503 -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/altium/SvgModelCrossLinkValidator.mjs +402 -0
- package/src/core/circuit-json/CircuitJsonModelAdapter.mjs +136 -96
- package/src/core/circuit-json/CircuitJsonModelAdapterPcbElements.mjs +244 -0
- package/src/core/circuit-json/CircuitJsonModelSchema.mjs +1 -1
- package/src/core/ole/OleCompoundDocument.mjs +20 -0
- package/src/parser.mjs +35 -0
- package/src/styles/altium-renderers.css +19 -0
- package/src/ui/PcbBarcodeTextRenderer.mjs +436 -0
- package/src/ui/PcbInteractionIndex.mjs +9 -4
- package/src/ui/PcbScene3dBuilder.mjs +137 -3
- package/src/ui/PcbScene3dModelRegistry.mjs +74 -0
- package/src/ui/PcbSvgRenderer.mjs +1252 -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
|
@@ -18,6 +18,8 @@ import { SchematicOwnerPinLabelLayout } from './SchematicOwnerPinLabelLayout.mjs
|
|
|
18
18
|
import { SchematicRegionRenderer } from './SchematicRegionRenderer.mjs'
|
|
19
19
|
import { SchematicSheetSymbolRenderer } from './SchematicSheetSymbolRenderer.mjs'
|
|
20
20
|
import { SchematicImageRenderer } from './SchematicImageRenderer.mjs'
|
|
21
|
+
import { TextGeometrySidecarBuilder } from './TextGeometrySidecarBuilder.mjs'
|
|
22
|
+
import { SchematicProjectParameterResolver } from '../core/altium/SchematicProjectParameterResolver.mjs'
|
|
21
23
|
|
|
22
24
|
const { createSvgText, escapeHtml, formatNumber, projectSchematicY } =
|
|
23
25
|
SchematicSvgUtils
|
|
@@ -30,13 +32,25 @@ const SECTION_HEADING_LINE_X_PADDING = 15
|
|
|
30
32
|
* Renders normalized schematic models into presentational SVG.
|
|
31
33
|
*/
|
|
32
34
|
export class SchematicSvgRenderer {
|
|
35
|
+
static #SEMANTIC_SCHEMA = 'altium-toolkit.schematic.svg.semantics.a1'
|
|
36
|
+
|
|
33
37
|
/**
|
|
34
38
|
* Renders a normalized schematic model into SVG markup.
|
|
35
39
|
* @param {{ fileName?: string, summary: { title?: string }, schematic?: { sheet: { width: number, height: number, sourceWidth?: number, sourceHeight?: number, paperSize?: string, borderOn?: boolean, titleBlockOn?: boolean, marginWidth?: number, xZones?: number, yZones?: number, titleBlock?: { title?: string, revision?: string, documentNumber?: string, sheetNumber?: string, sheetTotal?: string, date?: string, drawnBy?: string } }, lines: { x1: number, y1: number, x2: number, y2: number, color: string, width: number, lineStyle?: number, isBus?: boolean, ownerIndex?: string, renderOrder?: number, recordType?: string }[], polygons?: { points: { x: number, y: number }[], color: string, fill: string, isSolid: boolean, transparent: boolean, lineWidth: number, ownerIndex?: string, renderOrder?: number }[], rectangles?: { x: number, y: number, width: number, height: number, color: string, fill: string, isSolid: boolean, transparent: boolean, lineWidth: number, ownerIndex?: string, renderOrder?: number }[], regions?: { x: number, y: number, width: number, height: number, color: string, fill: string, renderOrder?: number }[], ellipses?: { x: number, y: number, radiusX: number, radiusY: number, color: string, fill: string, isSolid: boolean, transparent: boolean, lineWidth: number, ownerIndex?: string, renderOrder?: number }[], arcs?: { x: number, y: number, radius: number, startAngle: number, endAngle: number, color: string, width: number, ownerIndex?: string, renderOrder?: number }[], directives?: { x: number, y: number, color: string, name: string, orientation?: number }[], texts: { x: number, y: number, text: string, color: string, recordType?: string, style?: number, fontSize?: number, fontFamily?: string, fontWeight?: number, fontStyle?: string, rotation?: number, sourceOrientation?: number, isMirrored?: boolean, anchor?: 'start' | 'middle' | 'end', powerPortDirection?: 'up' | 'down' | 'left' | 'right', cornerX?: number, cornerY?: number, fill?: string, borderColor?: string, isSolid?: boolean, showBorder?: boolean, textMargin?: number, noteLines?: string[] }[], components: { x: number, y: number, designator: string }[], pins?: { x: number, y: number, length: number, name: string, nameSegments?: { text: string, overline: boolean }[], designator: string, orientation: 'left' | 'right' | 'top' | 'bottom', electrical?: number, symbolOuter?: number, color: string, labelColor?: string, labelMode?: 'hidden' | 'number-only' | 'name-only' | 'name-and-number', ownerIndex?: string }[], ports?: { x: number, y: number, width: number, height: number, name: string, fill: string, color: string, direction?: 'left' | 'right' | 'up' | 'down', shape?: 'single' | 'double' | 'plain' }[], crosses?: { x: number, y: number, size: number, color: string }[] } }} documentModel
|
|
40
|
+
* @param {{ projectParameters?: Record<string, string | number | boolean | null | undefined> }} options Render options.
|
|
36
41
|
* @returns {string}
|
|
37
42
|
*/
|
|
38
|
-
static render(documentModel) {
|
|
39
|
-
const
|
|
43
|
+
static render(documentModel, options = {}) {
|
|
44
|
+
const renderOptions =
|
|
45
|
+
SchematicSvgRenderer.#normalizeRenderOptions(options)
|
|
46
|
+
const renderModel = options.projectParameters
|
|
47
|
+
? SchematicProjectParameterResolver.applyToDocumentModel(
|
|
48
|
+
documentModel,
|
|
49
|
+
options.projectParameters,
|
|
50
|
+
{ replaceText: true }
|
|
51
|
+
)
|
|
52
|
+
: documentModel
|
|
53
|
+
const schematic = renderModel?.schematic
|
|
40
54
|
if (!schematic) {
|
|
41
55
|
return '<section class="altium-renderer-empty">No schematic entities were recovered from this file.</section>'
|
|
42
56
|
}
|
|
@@ -52,15 +66,22 @@ export class SchematicSvgRenderer {
|
|
|
52
66
|
? schematic
|
|
53
67
|
: { ...schematic, sheet: renderedSheet.contentSheet }
|
|
54
68
|
const allTexts = schematic.texts || []
|
|
55
|
-
const lines = schematic.lines.slice(0, 2500)
|
|
69
|
+
const lines = (schematic.lines || []).slice(0, 2500)
|
|
56
70
|
const polygons = (schematic.polygons || []).slice(0, 1000)
|
|
57
71
|
const rectangles = (schematic.rectangles || []).slice(0, 500)
|
|
72
|
+
const roundedRectangles = (schematic.roundedRectangles || []).slice(
|
|
73
|
+
0,
|
|
74
|
+
500
|
|
75
|
+
)
|
|
58
76
|
const regions = (schematic.regions || []).slice(0, 250)
|
|
59
77
|
const ellipses = (schematic.ellipses || []).slice(0, 500)
|
|
60
78
|
const arcs = (schematic.arcs || []).slice(0, 1000)
|
|
79
|
+
const beziers = (schematic.beziers || []).slice(0, 500)
|
|
80
|
+
const pies = (schematic.pies || []).slice(0, 500)
|
|
81
|
+
const ieeeSymbols = (schematic.ieeeSymbols || []).slice(0, 500)
|
|
61
82
|
const directives = (schematic.directives || []).slice(0, 250)
|
|
62
83
|
const texts = allTexts
|
|
63
|
-
const components = schematic.components.slice(0, 180)
|
|
84
|
+
const components = (schematic.components || []).slice(0, 180)
|
|
64
85
|
const pins = (schematic.pins || []).slice(0, 1000)
|
|
65
86
|
const ports = (schematic.ports || []).slice(0, 250)
|
|
66
87
|
const crosses = (schematic.crosses || []).slice(0, 250)
|
|
@@ -69,6 +90,18 @@ export class SchematicSvgRenderer {
|
|
|
69
90
|
const authoredJunctions = (schematic.junctions || []).slice(0, 500)
|
|
70
91
|
const busEntries = (schematic.busEntries || []).slice(0, 500)
|
|
71
92
|
const images = (schematic.images || []).slice(0, 100)
|
|
93
|
+
const semanticContext =
|
|
94
|
+
SchematicSvgRenderer.#buildSemanticContext(schematic)
|
|
95
|
+
const semanticMetadata = SchematicSvgRenderer.#buildSemanticMetadata(
|
|
96
|
+
schematic,
|
|
97
|
+
semanticContext
|
|
98
|
+
)
|
|
99
|
+
const textGeometryMarkup = renderOptions.includeTextGeometrySidecar
|
|
100
|
+
? SchematicSvgRenderer.#buildTextGeometryMetadataMarkup(
|
|
101
|
+
texts,
|
|
102
|
+
semanticContext
|
|
103
|
+
)
|
|
104
|
+
: ''
|
|
72
105
|
const drawableComponents = components.filter(
|
|
73
106
|
(component) =>
|
|
74
107
|
SchematicSvgRenderer.#isDrawableSchematicComponent(component) &&
|
|
@@ -81,7 +114,7 @@ export class SchematicSvgRenderer {
|
|
|
81
114
|
width,
|
|
82
115
|
height,
|
|
83
116
|
renderedSheet.sheet,
|
|
84
|
-
|
|
117
|
+
renderModel?.fileName
|
|
85
118
|
)
|
|
86
119
|
const regionMarkup = SchematicRegionRenderer.buildMarkup(
|
|
87
120
|
regions,
|
|
@@ -110,10 +143,18 @@ export class SchematicSvgRenderer {
|
|
|
110
143
|
const ownerlessRectangles = rectangles.filter(
|
|
111
144
|
(rectangle) => !rectangle.ownerIndex
|
|
112
145
|
)
|
|
146
|
+
const ownerlessRoundedRectangles = roundedRectangles.filter(
|
|
147
|
+
(rectangle) => !rectangle.ownerIndex
|
|
148
|
+
)
|
|
113
149
|
const ownerlessEllipses = ellipses.filter(
|
|
114
150
|
(ellipse) => !ellipse.ownerIndex
|
|
115
151
|
)
|
|
116
152
|
const ownerlessArcs = arcs.filter((arc) => !arc.ownerIndex)
|
|
153
|
+
const ownerlessBeziers = beziers.filter((bezier) => !bezier.ownerIndex)
|
|
154
|
+
const ownerlessPies = pies.filter((pie) => !pie.ownerIndex)
|
|
155
|
+
const ownerlessIeeeSymbols = ieeeSymbols.filter(
|
|
156
|
+
(symbol) => !symbol.ownerIndex
|
|
157
|
+
)
|
|
117
158
|
const resolvedTexts = texts.map((text) =>
|
|
118
159
|
text.recordType === '17'
|
|
119
160
|
? {
|
|
@@ -128,40 +169,180 @@ export class SchematicSvgRenderer {
|
|
|
128
169
|
: text
|
|
129
170
|
)
|
|
130
171
|
const polygonMarkup = ownerlessPolygons
|
|
131
|
-
.map((polygon) =>
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
172
|
+
.map((polygon, index) =>
|
|
173
|
+
SchematicSvgRenderer.#appendSvgAttributes(
|
|
174
|
+
SchematicShapeRenderer.buildPolygonMarkup(
|
|
175
|
+
polygon,
|
|
176
|
+
contentHeight
|
|
177
|
+
),
|
|
178
|
+
SchematicSvgRenderer.#semanticAttributes(
|
|
179
|
+
'polygon',
|
|
180
|
+
polygon,
|
|
181
|
+
SchematicSvgRenderer.#primitiveIndex(
|
|
182
|
+
semanticContext,
|
|
183
|
+
'polygons',
|
|
184
|
+
polygon,
|
|
185
|
+
index
|
|
186
|
+
),
|
|
187
|
+
semanticContext
|
|
188
|
+
)
|
|
135
189
|
)
|
|
136
190
|
)
|
|
137
191
|
.join('')
|
|
138
192
|
const rectangleMarkup = ownerlessRectangles
|
|
139
|
-
.map((rectangle) =>
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
193
|
+
.map((rectangle, index) =>
|
|
194
|
+
SchematicSvgRenderer.#appendSvgAttributes(
|
|
195
|
+
SchematicShapeRenderer.buildRectangleMarkup(
|
|
196
|
+
rectangle,
|
|
197
|
+
contentHeight
|
|
198
|
+
),
|
|
199
|
+
SchematicSvgRenderer.#semanticAttributes(
|
|
200
|
+
'rectangle',
|
|
201
|
+
rectangle,
|
|
202
|
+
SchematicSvgRenderer.#primitiveIndex(
|
|
203
|
+
semanticContext,
|
|
204
|
+
'rectangles',
|
|
205
|
+
rectangle,
|
|
206
|
+
index
|
|
207
|
+
),
|
|
208
|
+
semanticContext
|
|
209
|
+
)
|
|
210
|
+
)
|
|
211
|
+
)
|
|
212
|
+
.join('')
|
|
213
|
+
const roundedRectangleMarkup = ownerlessRoundedRectangles
|
|
214
|
+
.map((rectangle, index) =>
|
|
215
|
+
SchematicSvgRenderer.#appendSvgAttributes(
|
|
216
|
+
SchematicShapeRenderer.buildRoundedRectangleMarkup(
|
|
217
|
+
rectangle,
|
|
218
|
+
contentHeight
|
|
219
|
+
),
|
|
220
|
+
SchematicSvgRenderer.#semanticAttributes(
|
|
221
|
+
'rounded-rectangle',
|
|
222
|
+
rectangle,
|
|
223
|
+
SchematicSvgRenderer.#primitiveIndex(
|
|
224
|
+
semanticContext,
|
|
225
|
+
'roundedRectangles',
|
|
226
|
+
rectangle,
|
|
227
|
+
index
|
|
228
|
+
),
|
|
229
|
+
semanticContext
|
|
230
|
+
)
|
|
143
231
|
)
|
|
144
232
|
)
|
|
145
233
|
.join('')
|
|
146
234
|
const ellipseMarkup = ownerlessEllipses
|
|
147
|
-
.map((ellipse) =>
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
235
|
+
.map((ellipse, index) =>
|
|
236
|
+
SchematicSvgRenderer.#appendSvgAttributes(
|
|
237
|
+
SchematicShapeRenderer.buildEllipseMarkup(
|
|
238
|
+
ellipse,
|
|
239
|
+
contentHeight
|
|
240
|
+
),
|
|
241
|
+
SchematicSvgRenderer.#semanticAttributes(
|
|
242
|
+
'ellipse',
|
|
243
|
+
ellipse,
|
|
244
|
+
SchematicSvgRenderer.#primitiveIndex(
|
|
245
|
+
semanticContext,
|
|
246
|
+
'ellipses',
|
|
247
|
+
ellipse,
|
|
248
|
+
index
|
|
249
|
+
),
|
|
250
|
+
semanticContext
|
|
251
|
+
)
|
|
151
252
|
)
|
|
152
253
|
)
|
|
153
254
|
.join('')
|
|
154
255
|
const lineMarkup = ownerlessLines
|
|
155
|
-
.map((line) =>
|
|
256
|
+
.map((line, index) =>
|
|
156
257
|
SchematicSvgRenderer.#buildSchematicLineMarkup(
|
|
157
258
|
line,
|
|
158
|
-
contentHeight
|
|
259
|
+
contentHeight,
|
|
260
|
+
SchematicSvgRenderer.#primitiveIndex(
|
|
261
|
+
semanticContext,
|
|
262
|
+
'lines',
|
|
263
|
+
line,
|
|
264
|
+
index
|
|
265
|
+
),
|
|
266
|
+
semanticContext
|
|
159
267
|
)
|
|
160
268
|
)
|
|
161
269
|
.join('')
|
|
162
270
|
const arcMarkup = ownerlessArcs
|
|
163
|
-
.map((arc) =>
|
|
164
|
-
|
|
271
|
+
.map((arc, index) =>
|
|
272
|
+
SchematicSvgRenderer.#appendSvgAttributes(
|
|
273
|
+
SchematicShapeRenderer.buildArcMarkup(arc, contentHeight),
|
|
274
|
+
SchematicSvgRenderer.#semanticAttributes(
|
|
275
|
+
'arc',
|
|
276
|
+
arc,
|
|
277
|
+
SchematicSvgRenderer.#primitiveIndex(
|
|
278
|
+
semanticContext,
|
|
279
|
+
'arcs',
|
|
280
|
+
arc,
|
|
281
|
+
index
|
|
282
|
+
),
|
|
283
|
+
semanticContext
|
|
284
|
+
)
|
|
285
|
+
)
|
|
286
|
+
)
|
|
287
|
+
.join('')
|
|
288
|
+
const bezierMarkup = ownerlessBeziers
|
|
289
|
+
.map((bezier, index) =>
|
|
290
|
+
SchematicSvgRenderer.#appendSvgAttributes(
|
|
291
|
+
SchematicShapeRenderer.buildBezierMarkup(
|
|
292
|
+
bezier,
|
|
293
|
+
contentHeight
|
|
294
|
+
),
|
|
295
|
+
SchematicSvgRenderer.#semanticAttributes(
|
|
296
|
+
'bezier',
|
|
297
|
+
bezier,
|
|
298
|
+
SchematicSvgRenderer.#primitiveIndex(
|
|
299
|
+
semanticContext,
|
|
300
|
+
'beziers',
|
|
301
|
+
bezier,
|
|
302
|
+
index
|
|
303
|
+
),
|
|
304
|
+
semanticContext
|
|
305
|
+
)
|
|
306
|
+
)
|
|
307
|
+
)
|
|
308
|
+
.join('')
|
|
309
|
+
const pieMarkup = ownerlessPies
|
|
310
|
+
.map((pie, index) =>
|
|
311
|
+
SchematicSvgRenderer.#appendSvgAttributes(
|
|
312
|
+
SchematicShapeRenderer.buildPieMarkup(pie, contentHeight),
|
|
313
|
+
SchematicSvgRenderer.#semanticAttributes(
|
|
314
|
+
'pie',
|
|
315
|
+
pie,
|
|
316
|
+
SchematicSvgRenderer.#primitiveIndex(
|
|
317
|
+
semanticContext,
|
|
318
|
+
'pies',
|
|
319
|
+
pie,
|
|
320
|
+
index
|
|
321
|
+
),
|
|
322
|
+
semanticContext
|
|
323
|
+
)
|
|
324
|
+
)
|
|
325
|
+
)
|
|
326
|
+
.join('')
|
|
327
|
+
const ieeeSymbolMarkup = ownerlessIeeeSymbols
|
|
328
|
+
.map((symbol, index) =>
|
|
329
|
+
SchematicSvgRenderer.#appendSvgAttributes(
|
|
330
|
+
SchematicShapeRenderer.buildIeeeSymbolMarkup(
|
|
331
|
+
symbol,
|
|
332
|
+
contentHeight
|
|
333
|
+
),
|
|
334
|
+
SchematicSvgRenderer.#semanticAttributes(
|
|
335
|
+
'ieee-symbol',
|
|
336
|
+
symbol,
|
|
337
|
+
SchematicSvgRenderer.#primitiveIndex(
|
|
338
|
+
semanticContext,
|
|
339
|
+
'ieeeSymbols',
|
|
340
|
+
symbol,
|
|
341
|
+
index
|
|
342
|
+
),
|
|
343
|
+
semanticContext
|
|
344
|
+
)
|
|
345
|
+
)
|
|
165
346
|
)
|
|
166
347
|
.join('')
|
|
167
348
|
const ownerGeometryMarkup =
|
|
@@ -169,9 +350,14 @@ export class SchematicSvgRenderer {
|
|
|
169
350
|
lines,
|
|
170
351
|
polygons,
|
|
171
352
|
rectangles,
|
|
353
|
+
roundedRectangles,
|
|
172
354
|
ellipses,
|
|
173
355
|
arcs,
|
|
174
|
-
|
|
356
|
+
beziers,
|
|
357
|
+
pies,
|
|
358
|
+
ieeeSymbols,
|
|
359
|
+
contentHeight,
|
|
360
|
+
semanticContext
|
|
175
361
|
)
|
|
176
362
|
const sheetSymbolMarkup =
|
|
177
363
|
SchematicSheetSymbolRenderer.buildSheetSymbolMarkup(
|
|
@@ -208,24 +394,40 @@ export class SchematicSvgRenderer {
|
|
|
208
394
|
images,
|
|
209
395
|
contentHeight
|
|
210
396
|
)
|
|
397
|
+
const markerDefsMarkup =
|
|
398
|
+
SchematicSvgRenderer.#buildSchematicLineMarkerDefs(lines)
|
|
211
399
|
|
|
212
400
|
const textMarkup = resolvedTexts
|
|
213
|
-
.map((text) =>
|
|
401
|
+
.map((text, index) =>
|
|
214
402
|
SchematicSvgRenderer.#buildSchematicTextMarkup(
|
|
215
403
|
text,
|
|
216
404
|
contentHeight,
|
|
217
405
|
lines,
|
|
218
|
-
pins
|
|
406
|
+
pins,
|
|
407
|
+
SchematicSvgRenderer.#primitiveIndex(
|
|
408
|
+
semanticContext,
|
|
409
|
+
'texts',
|
|
410
|
+
text,
|
|
411
|
+
index
|
|
412
|
+
),
|
|
413
|
+
semanticContext
|
|
219
414
|
)
|
|
220
415
|
)
|
|
221
416
|
.join('')
|
|
222
417
|
|
|
223
418
|
const componentMarkup = drawableComponents
|
|
224
|
-
.map((component) =>
|
|
419
|
+
.map((component, index) =>
|
|
225
420
|
SchematicSvgRenderer.#buildFallbackComponentMarkup(
|
|
226
421
|
component,
|
|
227
422
|
contentHeight,
|
|
228
|
-
renderedSheet.contentSheet
|
|
423
|
+
renderedSheet.contentSheet,
|
|
424
|
+
SchematicSvgRenderer.#primitiveIndex(
|
|
425
|
+
semanticContext,
|
|
426
|
+
'components',
|
|
427
|
+
component,
|
|
428
|
+
index
|
|
429
|
+
),
|
|
430
|
+
semanticContext
|
|
229
431
|
)
|
|
230
432
|
)
|
|
231
433
|
.join('')
|
|
@@ -240,14 +442,27 @@ export class SchematicSvgRenderer {
|
|
|
240
442
|
pins
|
|
241
443
|
)
|
|
242
444
|
const pinMarkup = pins
|
|
243
|
-
.map((pin) =>
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
445
|
+
.map((pin, index) =>
|
|
446
|
+
SchematicSvgRenderer.#appendSvgAttributes(
|
|
447
|
+
SchematicPinSvgRenderer.buildMarkup(
|
|
448
|
+
pin,
|
|
449
|
+
contentHeight,
|
|
450
|
+
renderedSheet.contentSheet,
|
|
451
|
+
rotatedVerticalNumberOwners,
|
|
452
|
+
explicitOwnerPinNameLabels,
|
|
453
|
+
explicitOwnerPinLabelOffsets
|
|
454
|
+
),
|
|
455
|
+
SchematicSvgRenderer.#semanticAttributes(
|
|
456
|
+
'pin',
|
|
457
|
+
pin,
|
|
458
|
+
SchematicSvgRenderer.#primitiveIndex(
|
|
459
|
+
semanticContext,
|
|
460
|
+
'pins',
|
|
461
|
+
pin,
|
|
462
|
+
index
|
|
463
|
+
),
|
|
464
|
+
semanticContext
|
|
465
|
+
)
|
|
251
466
|
)
|
|
252
467
|
)
|
|
253
468
|
.join('')
|
|
@@ -256,11 +471,28 @@ export class SchematicSvgRenderer {
|
|
|
256
471
|
contentHeight,
|
|
257
472
|
renderedSheet.contentSheet
|
|
258
473
|
)
|
|
259
|
-
const directiveMarkup =
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
474
|
+
const directiveMarkup = directives
|
|
475
|
+
.map((directive, index) =>
|
|
476
|
+
SchematicSvgRenderer.#appendSvgAttributes(
|
|
477
|
+
SchematicDirectiveRenderer.buildMarkup(
|
|
478
|
+
[directive],
|
|
479
|
+
contentHeight,
|
|
480
|
+
renderedSheet.contentSheet
|
|
481
|
+
),
|
|
482
|
+
SchematicSvgRenderer.#semanticAttributes(
|
|
483
|
+
'directive',
|
|
484
|
+
directive,
|
|
485
|
+
SchematicSvgRenderer.#primitiveIndex(
|
|
486
|
+
semanticContext,
|
|
487
|
+
'directives',
|
|
488
|
+
directive,
|
|
489
|
+
index
|
|
490
|
+
),
|
|
491
|
+
semanticContext
|
|
492
|
+
)
|
|
493
|
+
)
|
|
494
|
+
)
|
|
495
|
+
.join('')
|
|
264
496
|
const junctionMarkup = SchematicJunctionRenderer.buildMarkup(
|
|
265
497
|
lines,
|
|
266
498
|
crosses,
|
|
@@ -281,23 +513,38 @@ export class SchematicSvgRenderer {
|
|
|
281
513
|
return (
|
|
282
514
|
'<section class="svg-panel">' +
|
|
283
515
|
'<header class="svg-panel__header"><h3>' +
|
|
284
|
-
escapeHtml(
|
|
516
|
+
escapeHtml(renderModel?.summary?.title || 'Schematic') +
|
|
285
517
|
'</h3><p>' +
|
|
286
518
|
lines.length +
|
|
287
519
|
' line segments, ' +
|
|
288
520
|
components.length +
|
|
289
521
|
' components</p></header>' +
|
|
290
|
-
'<svg class="schematic-svg"
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
522
|
+
'<svg class="schematic-svg"' +
|
|
523
|
+
SchematicSvgRenderer.#renderRootViewBoxAttributes(
|
|
524
|
+
renderOptions,
|
|
525
|
+
'0 0 ' + formatNumber(width) + ' ' + formatNumber(height)
|
|
526
|
+
) +
|
|
527
|
+
' preserveAspectRatio="xMidYMid meet" aria-label="Schematic view" data-semantic-schema="' +
|
|
528
|
+
SchematicSvgRenderer.#SEMANTIC_SCHEMA +
|
|
529
|
+
'"' +
|
|
530
|
+
SchematicSvgRenderer.#renderDataAttributes({
|
|
531
|
+
'data-doc-id': renderOptions.documentId,
|
|
532
|
+
'data-doc-ver': renderOptions.documentVersion
|
|
533
|
+
}) +
|
|
534
|
+
'>' +
|
|
295
535
|
'<rect class="sheet-backdrop" x="0" y="0" width="' +
|
|
296
536
|
formatNumber(width) +
|
|
297
537
|
'" height="' +
|
|
298
538
|
formatNumber(height) +
|
|
299
539
|
'" rx="18" />' +
|
|
300
540
|
contentClipMarkup +
|
|
541
|
+
'<metadata id="schematic-semantic-metadata" data-schema="' +
|
|
542
|
+
SchematicSvgRenderer.#SEMANTIC_SCHEMA +
|
|
543
|
+
'">' +
|
|
544
|
+
escapeHtml(JSON.stringify(semanticMetadata)) +
|
|
545
|
+
'</metadata>' +
|
|
546
|
+
textGeometryMarkup +
|
|
547
|
+
markerDefsMarkup +
|
|
301
548
|
'<g class="schematic-content"' +
|
|
302
549
|
' clip-path="url(#' +
|
|
303
550
|
escapeHtml(contentClipId) +
|
|
@@ -309,6 +556,7 @@ export class SchematicSvgRenderer {
|
|
|
309
556
|
'</g>' +
|
|
310
557
|
'<g class="schematic-rectangles">' +
|
|
311
558
|
rectangleMarkup +
|
|
559
|
+
roundedRectangleMarkup +
|
|
312
560
|
'</g>' +
|
|
313
561
|
'<g class="schematic-ellipses">' +
|
|
314
562
|
ellipseMarkup +
|
|
@@ -319,6 +567,15 @@ export class SchematicSvgRenderer {
|
|
|
319
567
|
'<g class="schematic-arcs" stroke-linecap="round">' +
|
|
320
568
|
arcMarkup +
|
|
321
569
|
'</g>' +
|
|
570
|
+
'<g class="schematic-beziers" stroke-linecap="round">' +
|
|
571
|
+
bezierMarkup +
|
|
572
|
+
'</g>' +
|
|
573
|
+
'<g class="schematic-pies">' +
|
|
574
|
+
pieMarkup +
|
|
575
|
+
'</g>' +
|
|
576
|
+
'<g class="schematic-ieee-symbols">' +
|
|
577
|
+
ieeeSymbolMarkup +
|
|
578
|
+
'</g>' +
|
|
322
579
|
'<g class="schematic-owner-geometry" stroke-linecap="round">' +
|
|
323
580
|
ownerGeometryMarkup +
|
|
324
581
|
'</g>' +
|
|
@@ -365,6 +622,824 @@ export class SchematicSvgRenderer {
|
|
|
365
622
|
)
|
|
366
623
|
}
|
|
367
624
|
|
|
625
|
+
/**
|
|
626
|
+
* Normalizes schematic SVG export options.
|
|
627
|
+
* @param {Record<string, unknown>} options Raw render options.
|
|
628
|
+
* @returns {{ includeViewBox: boolean, documentId: string, documentVersion: string, includeTextGeometrySidecar: boolean }}
|
|
629
|
+
*/
|
|
630
|
+
static #normalizeRenderOptions(options) {
|
|
631
|
+
const includeViewBox =
|
|
632
|
+
options?.includeViewBox ?? options?.include_view_box
|
|
633
|
+
|
|
634
|
+
return {
|
|
635
|
+
includeViewBox: includeViewBox === false ? false : true,
|
|
636
|
+
documentId: String(options?.documentId || options?.docId || ''),
|
|
637
|
+
documentVersion: String(
|
|
638
|
+
options?.documentVersion || options?.documentVer || ''
|
|
639
|
+
),
|
|
640
|
+
includeTextGeometrySidecar:
|
|
641
|
+
options?.includeTextGeometrySidecar === true ||
|
|
642
|
+
options?.textGeometry === 'sidecar'
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Renders root SVG viewBox attributes according to export options.
|
|
648
|
+
* @param {{ includeViewBox: boolean }} options Normalized options.
|
|
649
|
+
* @param {string} viewBox ViewBox value.
|
|
650
|
+
* @returns {string}
|
|
651
|
+
*/
|
|
652
|
+
static #renderRootViewBoxAttributes(options, viewBox) {
|
|
653
|
+
return options.includeViewBox
|
|
654
|
+
? ' viewBox="' + escapeHtml(viewBox) + '"'
|
|
655
|
+
: ''
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Builds optional text geometry metadata markup.
|
|
660
|
+
* @param {object[]} texts Text rows.
|
|
661
|
+
* @param {object} semanticContext Semantic context.
|
|
662
|
+
* @returns {string}
|
|
663
|
+
*/
|
|
664
|
+
static #buildTextGeometryMetadataMarkup(texts, semanticContext) {
|
|
665
|
+
const metadata = TextGeometrySidecarBuilder.buildSchematic(
|
|
666
|
+
texts,
|
|
667
|
+
semanticContext.primitiveIndexes?.texts
|
|
668
|
+
)
|
|
669
|
+
|
|
670
|
+
return (
|
|
671
|
+
'<metadata id="schematic-text-geometry" data-schema="' +
|
|
672
|
+
TextGeometrySidecarBuilder.SCHEMA_ID +
|
|
673
|
+
'">' +
|
|
674
|
+
escapeHtml(JSON.stringify(metadata)) +
|
|
675
|
+
'</metadata>'
|
|
676
|
+
)
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* Builds reusable SVG marker definitions for authored line endpoints.
|
|
681
|
+
* @param {{ startMarker?: object, endMarker?: object }[]} lines Drawable lines.
|
|
682
|
+
* @returns {string}
|
|
683
|
+
*/
|
|
684
|
+
static #buildSchematicLineMarkerDefs(lines) {
|
|
685
|
+
const markers = SchematicSvgRenderer.#collectSchematicLineMarkers(lines)
|
|
686
|
+
|
|
687
|
+
if (!markers.length) {
|
|
688
|
+
return ''
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
return (
|
|
692
|
+
'<defs class="schematic-line-marker-defs">' +
|
|
693
|
+
markers
|
|
694
|
+
.map((marker) =>
|
|
695
|
+
SchematicSvgRenderer.#buildSchematicLineMarkerDef(marker)
|
|
696
|
+
)
|
|
697
|
+
.join('') +
|
|
698
|
+
'</defs>'
|
|
699
|
+
)
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
* Collects unique endpoint markers in stable order.
|
|
704
|
+
* @param {{ startMarker?: object, endMarker?: object }[]} lines Drawable lines.
|
|
705
|
+
* @returns {object[]}
|
|
706
|
+
*/
|
|
707
|
+
static #collectSchematicLineMarkers(lines) {
|
|
708
|
+
const seen = new Set()
|
|
709
|
+
const markers = []
|
|
710
|
+
|
|
711
|
+
for (const line of lines || []) {
|
|
712
|
+
for (const marker of [line.startMarker, line.endMarker]) {
|
|
713
|
+
if (!marker) {
|
|
714
|
+
continue
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
const id = SchematicSvgRenderer.#schematicLineMarkerId(marker)
|
|
718
|
+
if (seen.has(id)) {
|
|
719
|
+
continue
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
seen.add(id)
|
|
723
|
+
markers.push(marker)
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
return markers
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* Builds one SVG marker definition.
|
|
732
|
+
* @param {{ shapeName?: string, size?: number }} marker Marker metadata.
|
|
733
|
+
* @returns {string}
|
|
734
|
+
*/
|
|
735
|
+
static #buildSchematicLineMarkerDef(marker) {
|
|
736
|
+
const id = SchematicSvgRenderer.#schematicLineMarkerId(marker)
|
|
737
|
+
const shapeName = String(marker?.shapeName || '')
|
|
738
|
+
const fill =
|
|
739
|
+
shapeName === 'filled-arrow' || shapeName === 'square'
|
|
740
|
+
? 'context-stroke'
|
|
741
|
+
: 'none'
|
|
742
|
+
const shape =
|
|
743
|
+
shapeName === 'circle'
|
|
744
|
+
? '<circle cx="5" cy="5" r="3.2" fill="none" stroke="context-stroke" stroke-width="1.4" />'
|
|
745
|
+
: shapeName === 'square'
|
|
746
|
+
? '<rect x="2" y="2" width="6" height="6" fill="context-stroke" stroke="context-stroke" stroke-width="1" />'
|
|
747
|
+
: '<path d="M 1 1 L 9 5 L 1 9" fill="' +
|
|
748
|
+
fill +
|
|
749
|
+
'" stroke="context-stroke" stroke-width="1.4" stroke-linejoin="round" />'
|
|
750
|
+
|
|
751
|
+
return (
|
|
752
|
+
'<marker id="' +
|
|
753
|
+
escapeHtml(id) +
|
|
754
|
+
'" viewBox="0 0 10 10" markerWidth="' +
|
|
755
|
+
formatNumber(Math.max(Number(marker?.size || 6), 1)) +
|
|
756
|
+
'" markerHeight="' +
|
|
757
|
+
formatNumber(Math.max(Number(marker?.size || 6), 1)) +
|
|
758
|
+
'" refX="5" refY="5" orient="auto-start-reverse">' +
|
|
759
|
+
shape +
|
|
760
|
+
'</marker>'
|
|
761
|
+
)
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* Builds a deterministic marker id from normalized marker metadata.
|
|
766
|
+
* @param {{ shapeName?: string, size?: number }} marker Marker metadata.
|
|
767
|
+
* @returns {string}
|
|
768
|
+
*/
|
|
769
|
+
static #schematicLineMarkerId(marker) {
|
|
770
|
+
return (
|
|
771
|
+
'schematic-marker-' +
|
|
772
|
+
String(marker?.shapeName || 'marker')
|
|
773
|
+
.trim()
|
|
774
|
+
.toLowerCase()
|
|
775
|
+
.replace(/[^a-z0-9]+/gu, '-') +
|
|
776
|
+
'-' +
|
|
777
|
+
formatNumber(Math.max(Number(marker?.size || 6), 1)).replace(
|
|
778
|
+
/\./gu,
|
|
779
|
+
'-'
|
|
780
|
+
)
|
|
781
|
+
)
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* Builds reusable semantic lookup data for one schematic render.
|
|
786
|
+
* @param {object} schematic Normalized schematic model.
|
|
787
|
+
* @returns {object}
|
|
788
|
+
*/
|
|
789
|
+
static #buildSemanticContext(schematic) {
|
|
790
|
+
const components = schematic?.components || []
|
|
791
|
+
const componentsByOwnerIndex = new Map()
|
|
792
|
+
const componentsByDesignator = new Map()
|
|
793
|
+
|
|
794
|
+
for (const component of components) {
|
|
795
|
+
const ownerKey = String(component?.ownerIndex || '').trim()
|
|
796
|
+
if (ownerKey) {
|
|
797
|
+
componentsByOwnerIndex.set(ownerKey, component)
|
|
798
|
+
}
|
|
799
|
+
if (component?.designator) {
|
|
800
|
+
componentsByDesignator.set(component.designator, component)
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
const netByPrimitive = new Map()
|
|
805
|
+
for (const net of schematic?.nets || []) {
|
|
806
|
+
for (const segment of net.segments || []) {
|
|
807
|
+
netByPrimitive.set(segment, net)
|
|
808
|
+
}
|
|
809
|
+
for (const label of net.labels || []) {
|
|
810
|
+
netByPrimitive.set(label, net)
|
|
811
|
+
}
|
|
812
|
+
for (const powerPort of net.powerPorts || []) {
|
|
813
|
+
netByPrimitive.set(powerPort, net)
|
|
814
|
+
}
|
|
815
|
+
for (const pin of net.pins || []) {
|
|
816
|
+
netByPrimitive.set(pin, net)
|
|
817
|
+
}
|
|
818
|
+
for (const port of net.ports || []) {
|
|
819
|
+
netByPrimitive.set(port, net)
|
|
820
|
+
}
|
|
821
|
+
for (const sheetEntry of net.sheetEntries || []) {
|
|
822
|
+
netByPrimitive.set(sheetEntry, net)
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
return {
|
|
827
|
+
componentsByOwnerIndex,
|
|
828
|
+
componentsByDesignator,
|
|
829
|
+
netByPrimitive,
|
|
830
|
+
primitiveIndexes: {
|
|
831
|
+
lines: SchematicSvgRenderer.#objectIndexMap(
|
|
832
|
+
schematic?.lines || []
|
|
833
|
+
),
|
|
834
|
+
polygons: SchematicSvgRenderer.#objectIndexMap(
|
|
835
|
+
schematic?.polygons || []
|
|
836
|
+
),
|
|
837
|
+
rectangles: SchematicSvgRenderer.#objectIndexMap(
|
|
838
|
+
schematic?.rectangles || []
|
|
839
|
+
),
|
|
840
|
+
roundedRectangles: SchematicSvgRenderer.#objectIndexMap(
|
|
841
|
+
schematic?.roundedRectangles || []
|
|
842
|
+
),
|
|
843
|
+
ellipses: SchematicSvgRenderer.#objectIndexMap(
|
|
844
|
+
schematic?.ellipses || []
|
|
845
|
+
),
|
|
846
|
+
arcs: SchematicSvgRenderer.#objectIndexMap(
|
|
847
|
+
schematic?.arcs || []
|
|
848
|
+
),
|
|
849
|
+
beziers: SchematicSvgRenderer.#objectIndexMap(
|
|
850
|
+
schematic?.beziers || []
|
|
851
|
+
),
|
|
852
|
+
pies: SchematicSvgRenderer.#objectIndexMap(
|
|
853
|
+
schematic?.pies || []
|
|
854
|
+
),
|
|
855
|
+
ieeeSymbols: SchematicSvgRenderer.#objectIndexMap(
|
|
856
|
+
schematic?.ieeeSymbols || []
|
|
857
|
+
),
|
|
858
|
+
directives: SchematicSvgRenderer.#objectIndexMap(
|
|
859
|
+
schematic?.directives || []
|
|
860
|
+
),
|
|
861
|
+
texts: SchematicSvgRenderer.#objectIndexMap(
|
|
862
|
+
schematic?.texts || []
|
|
863
|
+
),
|
|
864
|
+
pins: SchematicSvgRenderer.#objectIndexMap(
|
|
865
|
+
schematic?.pins || []
|
|
866
|
+
),
|
|
867
|
+
ports: SchematicSvgRenderer.#objectIndexMap(
|
|
868
|
+
schematic?.ports || []
|
|
869
|
+
),
|
|
870
|
+
components: SchematicSvgRenderer.#objectIndexMap(components)
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
/**
|
|
876
|
+
* Builds a compact JSON sidecar describing schematic SVG semantic links.
|
|
877
|
+
* @param {object} schematic Normalized schematic model.
|
|
878
|
+
* @param {object} semanticContext Semantic lookup context.
|
|
879
|
+
* @returns {{ schema: string, nets: object[], components: object[] }}
|
|
880
|
+
*/
|
|
881
|
+
static #buildSemanticMetadata(schematic, semanticContext) {
|
|
882
|
+
return {
|
|
883
|
+
schema: SchematicSvgRenderer.#SEMANTIC_SCHEMA,
|
|
884
|
+
elements: SchematicSvgRenderer.#buildElementMetadata(
|
|
885
|
+
schematic,
|
|
886
|
+
semanticContext
|
|
887
|
+
),
|
|
888
|
+
nets: (schematic?.nets || []).map((net) =>
|
|
889
|
+
SchematicSvgRenderer.#buildNetMetadata(net, semanticContext)
|
|
890
|
+
),
|
|
891
|
+
components: (schematic?.components || []).map((component) =>
|
|
892
|
+
SchematicSvgRenderer.#buildComponentMetadata(
|
|
893
|
+
component,
|
|
894
|
+
schematic,
|
|
895
|
+
semanticContext
|
|
896
|
+
)
|
|
897
|
+
)
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
/**
|
|
902
|
+
* Builds one flat element sidecar for every source-addressable schematic
|
|
903
|
+
* primitive family that can participate in SVG review tooling.
|
|
904
|
+
* @param {object} schematic Normalized schematic model.
|
|
905
|
+
* @param {object} semanticContext Semantic lookup context.
|
|
906
|
+
* @returns {object[]}
|
|
907
|
+
*/
|
|
908
|
+
static #buildElementMetadata(schematic, semanticContext) {
|
|
909
|
+
return [
|
|
910
|
+
...SchematicSvgRenderer.#elementMetadataForCollection(
|
|
911
|
+
schematic?.lines || [],
|
|
912
|
+
'lines',
|
|
913
|
+
'line',
|
|
914
|
+
semanticContext
|
|
915
|
+
),
|
|
916
|
+
...SchematicSvgRenderer.#elementMetadataForCollection(
|
|
917
|
+
schematic?.polygons || [],
|
|
918
|
+
'polygons',
|
|
919
|
+
'polygon',
|
|
920
|
+
semanticContext
|
|
921
|
+
),
|
|
922
|
+
...SchematicSvgRenderer.#elementMetadataForCollection(
|
|
923
|
+
schematic?.rectangles || [],
|
|
924
|
+
'rectangles',
|
|
925
|
+
'rectangle',
|
|
926
|
+
semanticContext
|
|
927
|
+
),
|
|
928
|
+
...SchematicSvgRenderer.#elementMetadataForCollection(
|
|
929
|
+
schematic?.roundedRectangles || [],
|
|
930
|
+
'roundedRectangles',
|
|
931
|
+
'rounded-rectangle',
|
|
932
|
+
semanticContext
|
|
933
|
+
),
|
|
934
|
+
...SchematicSvgRenderer.#elementMetadataForCollection(
|
|
935
|
+
schematic?.ellipses || [],
|
|
936
|
+
'ellipses',
|
|
937
|
+
'ellipse',
|
|
938
|
+
semanticContext
|
|
939
|
+
),
|
|
940
|
+
...SchematicSvgRenderer.#elementMetadataForCollection(
|
|
941
|
+
schematic?.arcs || [],
|
|
942
|
+
'arcs',
|
|
943
|
+
'arc',
|
|
944
|
+
semanticContext
|
|
945
|
+
),
|
|
946
|
+
...SchematicSvgRenderer.#elementMetadataForCollection(
|
|
947
|
+
schematic?.beziers || [],
|
|
948
|
+
'beziers',
|
|
949
|
+
'bezier',
|
|
950
|
+
semanticContext
|
|
951
|
+
),
|
|
952
|
+
...SchematicSvgRenderer.#elementMetadataForCollection(
|
|
953
|
+
schematic?.pies || [],
|
|
954
|
+
'pies',
|
|
955
|
+
'pie',
|
|
956
|
+
semanticContext
|
|
957
|
+
),
|
|
958
|
+
...SchematicSvgRenderer.#elementMetadataForCollection(
|
|
959
|
+
schematic?.ieeeSymbols || [],
|
|
960
|
+
'ieeeSymbols',
|
|
961
|
+
'ieee-symbol',
|
|
962
|
+
semanticContext
|
|
963
|
+
),
|
|
964
|
+
...SchematicSvgRenderer.#elementMetadataForCollection(
|
|
965
|
+
schematic?.texts || [],
|
|
966
|
+
'texts',
|
|
967
|
+
'text',
|
|
968
|
+
semanticContext
|
|
969
|
+
),
|
|
970
|
+
...SchematicSvgRenderer.#elementMetadataForCollection(
|
|
971
|
+
schematic?.components || [],
|
|
972
|
+
'components',
|
|
973
|
+
'component',
|
|
974
|
+
semanticContext
|
|
975
|
+
),
|
|
976
|
+
...SchematicSvgRenderer.#elementMetadataForCollection(
|
|
977
|
+
schematic?.pins || [],
|
|
978
|
+
'pins',
|
|
979
|
+
'pin',
|
|
980
|
+
semanticContext
|
|
981
|
+
),
|
|
982
|
+
...SchematicSvgRenderer.#elementMetadataForCollection(
|
|
983
|
+
schematic?.ports || [],
|
|
984
|
+
'ports',
|
|
985
|
+
'port',
|
|
986
|
+
semanticContext
|
|
987
|
+
),
|
|
988
|
+
...SchematicSvgRenderer.#elementMetadataForCollection(
|
|
989
|
+
schematic?.directives || [],
|
|
990
|
+
'directives',
|
|
991
|
+
'directive',
|
|
992
|
+
semanticContext
|
|
993
|
+
)
|
|
994
|
+
]
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
/**
|
|
998
|
+
* Builds semantic metadata entries for one primitive collection.
|
|
999
|
+
* @param {object[]} records Primitive records.
|
|
1000
|
+
* @param {string} collectionKey Collection key.
|
|
1001
|
+
* @param {string} primitiveKind Primitive kind.
|
|
1002
|
+
* @param {object} semanticContext Semantic lookup context.
|
|
1003
|
+
* @returns {object[]}
|
|
1004
|
+
*/
|
|
1005
|
+
static #elementMetadataForCollection(
|
|
1006
|
+
records,
|
|
1007
|
+
collectionKey,
|
|
1008
|
+
primitiveKind,
|
|
1009
|
+
semanticContext
|
|
1010
|
+
) {
|
|
1011
|
+
return (records || []).map((record, fallbackIndex) => {
|
|
1012
|
+
const index = SchematicSvgRenderer.#primitiveIndex(
|
|
1013
|
+
semanticContext,
|
|
1014
|
+
collectionKey,
|
|
1015
|
+
record,
|
|
1016
|
+
fallbackIndex
|
|
1017
|
+
)
|
|
1018
|
+
const component =
|
|
1019
|
+
SchematicSvgRenderer.#componentForSchematicPrimitive(
|
|
1020
|
+
primitiveKind,
|
|
1021
|
+
record,
|
|
1022
|
+
semanticContext
|
|
1023
|
+
)
|
|
1024
|
+
const net = semanticContext.netByPrimitive.get(record)
|
|
1025
|
+
|
|
1026
|
+
return SchematicSvgRenderer.#stripEmptySemanticObject({
|
|
1027
|
+
elementKey:
|
|
1028
|
+
'schematic-' +
|
|
1029
|
+
SchematicSvgRenderer.#elementKeyPrimitiveKind(
|
|
1030
|
+
primitiveKind,
|
|
1031
|
+
record
|
|
1032
|
+
) +
|
|
1033
|
+
'-' +
|
|
1034
|
+
index,
|
|
1035
|
+
primitive: SchematicSvgRenderer.#metadataPrimitiveKind(
|
|
1036
|
+
primitiveKind,
|
|
1037
|
+
record
|
|
1038
|
+
),
|
|
1039
|
+
recordId: SchematicSvgRenderer.#recordId(
|
|
1040
|
+
primitiveKind,
|
|
1041
|
+
record,
|
|
1042
|
+
index
|
|
1043
|
+
),
|
|
1044
|
+
component: component?.designator,
|
|
1045
|
+
componentUniqueId: component?.uniqueId,
|
|
1046
|
+
net: net?.name,
|
|
1047
|
+
pin:
|
|
1048
|
+
primitiveKind === 'pin'
|
|
1049
|
+
? SchematicSvgRenderer.#pinLabel(record)
|
|
1050
|
+
: undefined
|
|
1051
|
+
})
|
|
1052
|
+
})
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
/**
|
|
1056
|
+
* Builds metadata for one schematic net.
|
|
1057
|
+
* @param {object} net Net record.
|
|
1058
|
+
* @param {object} semanticContext Semantic lookup context.
|
|
1059
|
+
* @returns {object}
|
|
1060
|
+
*/
|
|
1061
|
+
static #buildNetMetadata(net, semanticContext) {
|
|
1062
|
+
const pins = []
|
|
1063
|
+
const components = []
|
|
1064
|
+
const elementKeys = [
|
|
1065
|
+
...SchematicSvgRenderer.#elementKeysForObjects(
|
|
1066
|
+
net.segments || [],
|
|
1067
|
+
'lines',
|
|
1068
|
+
'line',
|
|
1069
|
+
semanticContext
|
|
1070
|
+
),
|
|
1071
|
+
...SchematicSvgRenderer.#elementKeysForObjects(
|
|
1072
|
+
[...(net.labels || []), ...(net.powerPorts || [])],
|
|
1073
|
+
'texts',
|
|
1074
|
+
'text',
|
|
1075
|
+
semanticContext
|
|
1076
|
+
),
|
|
1077
|
+
...SchematicSvgRenderer.#elementKeysForObjects(
|
|
1078
|
+
net.pins || [],
|
|
1079
|
+
'pins',
|
|
1080
|
+
'pin',
|
|
1081
|
+
semanticContext
|
|
1082
|
+
),
|
|
1083
|
+
...SchematicSvgRenderer.#elementKeysForObjects(
|
|
1084
|
+
net.ports || [],
|
|
1085
|
+
'ports',
|
|
1086
|
+
'port',
|
|
1087
|
+
semanticContext
|
|
1088
|
+
)
|
|
1089
|
+
]
|
|
1090
|
+
|
|
1091
|
+
for (const pin of net.pins || []) {
|
|
1092
|
+
const component =
|
|
1093
|
+
SchematicSvgRenderer.#componentForSchematicPrimitive(
|
|
1094
|
+
'pin',
|
|
1095
|
+
pin,
|
|
1096
|
+
semanticContext
|
|
1097
|
+
)
|
|
1098
|
+
if (component?.designator) {
|
|
1099
|
+
components.push(component.designator)
|
|
1100
|
+
pins.push(
|
|
1101
|
+
component.designator +
|
|
1102
|
+
':' +
|
|
1103
|
+
SchematicSvgRenderer.#pinLabel(pin)
|
|
1104
|
+
)
|
|
1105
|
+
continue
|
|
1106
|
+
}
|
|
1107
|
+
const pinLabel = SchematicSvgRenderer.#pinLabel(pin)
|
|
1108
|
+
if (pinLabel) {
|
|
1109
|
+
pins.push(pinLabel)
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
return SchematicSvgRenderer.#stripEmptySemanticObject({
|
|
1114
|
+
name: net.name,
|
|
1115
|
+
elementKeys: SchematicSvgRenderer.#dedupe(elementKeys),
|
|
1116
|
+
components: SchematicSvgRenderer.#dedupe(components),
|
|
1117
|
+
pins: SchematicSvgRenderer.#dedupe(pins)
|
|
1118
|
+
})
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
/**
|
|
1122
|
+
* Builds metadata for one schematic component.
|
|
1123
|
+
* @param {object} component Component record.
|
|
1124
|
+
* @param {object} schematic Normalized schematic model.
|
|
1125
|
+
* @param {object} semanticContext Semantic lookup context.
|
|
1126
|
+
* @returns {object}
|
|
1127
|
+
*/
|
|
1128
|
+
static #buildComponentMetadata(component, schematic, semanticContext) {
|
|
1129
|
+
const componentIndex = SchematicSvgRenderer.#primitiveIndex(
|
|
1130
|
+
semanticContext,
|
|
1131
|
+
'components',
|
|
1132
|
+
component,
|
|
1133
|
+
0
|
|
1134
|
+
)
|
|
1135
|
+
const pins = (schematic?.pins || []).filter(
|
|
1136
|
+
(pin) =>
|
|
1137
|
+
SchematicSvgRenderer.#componentForSchematicPrimitive(
|
|
1138
|
+
'pin',
|
|
1139
|
+
pin,
|
|
1140
|
+
semanticContext
|
|
1141
|
+
) === component
|
|
1142
|
+
)
|
|
1143
|
+
const nets = pins
|
|
1144
|
+
.map((pin) => semanticContext.netByPrimitive.get(pin)?.name)
|
|
1145
|
+
.filter(Boolean)
|
|
1146
|
+
const pinLabels = pins
|
|
1147
|
+
.map((pin) => SchematicSvgRenderer.#pinLabel(pin))
|
|
1148
|
+
.filter(Boolean)
|
|
1149
|
+
const elementKeys = [
|
|
1150
|
+
'schematic-component-' + componentIndex,
|
|
1151
|
+
...SchematicSvgRenderer.#elementKeysForObjects(
|
|
1152
|
+
pins,
|
|
1153
|
+
'pins',
|
|
1154
|
+
'pin',
|
|
1155
|
+
semanticContext
|
|
1156
|
+
)
|
|
1157
|
+
]
|
|
1158
|
+
|
|
1159
|
+
return SchematicSvgRenderer.#stripEmptySemanticObject({
|
|
1160
|
+
designator: component.designator,
|
|
1161
|
+
uniqueId: component.uniqueId,
|
|
1162
|
+
elementKeys: SchematicSvgRenderer.#dedupe(elementKeys),
|
|
1163
|
+
pins: SchematicSvgRenderer.#dedupe(pinLabels),
|
|
1164
|
+
nets: SchematicSvgRenderer.#dedupe(nets)
|
|
1165
|
+
})
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
/**
|
|
1169
|
+
* Builds SVG data attributes for one schematic primitive.
|
|
1170
|
+
* @param {string} primitiveKind Public primitive kind.
|
|
1171
|
+
* @param {object} primitive Primitive record.
|
|
1172
|
+
* @param {number} index Stable primitive index.
|
|
1173
|
+
* @param {object | undefined} semanticContext Semantic lookup context.
|
|
1174
|
+
* @returns {string}
|
|
1175
|
+
*/
|
|
1176
|
+
static #semanticAttributes(
|
|
1177
|
+
primitiveKind,
|
|
1178
|
+
primitive,
|
|
1179
|
+
index,
|
|
1180
|
+
semanticContext
|
|
1181
|
+
) {
|
|
1182
|
+
if (!semanticContext) {
|
|
1183
|
+
return ''
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
if (!SchematicSvgRenderer.#hasExplicitRecordId(primitive)) {
|
|
1187
|
+
return ''
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
const component = SchematicSvgRenderer.#componentForSchematicPrimitive(
|
|
1191
|
+
primitiveKind,
|
|
1192
|
+
primitive,
|
|
1193
|
+
semanticContext
|
|
1194
|
+
)
|
|
1195
|
+
const net = semanticContext.netByPrimitive.get(primitive)
|
|
1196
|
+
|
|
1197
|
+
return SchematicSvgRenderer.#renderDataAttributes({
|
|
1198
|
+
'data-primitive': primitiveKind,
|
|
1199
|
+
'data-element-key': 'schematic-' + primitiveKind + '-' + index,
|
|
1200
|
+
'data-record-id': SchematicSvgRenderer.#recordId(
|
|
1201
|
+
primitiveKind,
|
|
1202
|
+
primitive,
|
|
1203
|
+
index
|
|
1204
|
+
),
|
|
1205
|
+
'data-component': component?.designator,
|
|
1206
|
+
'data-component-unique-id': component?.uniqueId,
|
|
1207
|
+
'data-net': net?.name,
|
|
1208
|
+
'data-pin':
|
|
1209
|
+
primitiveKind === 'pin'
|
|
1210
|
+
? SchematicSvgRenderer.#pinLabel(primitive)
|
|
1211
|
+
: undefined
|
|
1212
|
+
})
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
/**
|
|
1216
|
+
* Finds the component associated with a schematic primitive.
|
|
1217
|
+
* @param {string} primitiveKind Public primitive kind.
|
|
1218
|
+
* @param {object} primitive Primitive record.
|
|
1219
|
+
* @param {object} semanticContext Semantic lookup context.
|
|
1220
|
+
* @returns {object | null}
|
|
1221
|
+
*/
|
|
1222
|
+
static #componentForSchematicPrimitive(
|
|
1223
|
+
primitiveKind,
|
|
1224
|
+
primitive,
|
|
1225
|
+
semanticContext
|
|
1226
|
+
) {
|
|
1227
|
+
if (primitiveKind === 'component') {
|
|
1228
|
+
return primitive
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
const explicitDesignator =
|
|
1232
|
+
primitive?.componentDesignator || primitive?.component || ''
|
|
1233
|
+
if (explicitDesignator) {
|
|
1234
|
+
return (
|
|
1235
|
+
semanticContext.componentsByDesignator.get(
|
|
1236
|
+
explicitDesignator
|
|
1237
|
+
) || null
|
|
1238
|
+
)
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
const ownerIndex = String(primitive?.ownerIndex || '').trim()
|
|
1242
|
+
return ownerIndex
|
|
1243
|
+
? semanticContext.componentsByOwnerIndex.get(ownerIndex) || null
|
|
1244
|
+
: null
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
/**
|
|
1248
|
+
* Returns element keys for a primitive object list.
|
|
1249
|
+
* @param {object[]} records Primitive records.
|
|
1250
|
+
* @param {string} collectionKey Primitive collection key.
|
|
1251
|
+
* @param {string} primitiveKind Public primitive kind.
|
|
1252
|
+
* @param {object} semanticContext Semantic lookup context.
|
|
1253
|
+
* @returns {string[]}
|
|
1254
|
+
*/
|
|
1255
|
+
static #elementKeysForObjects(
|
|
1256
|
+
records,
|
|
1257
|
+
collectionKey,
|
|
1258
|
+
primitiveKind,
|
|
1259
|
+
semanticContext
|
|
1260
|
+
) {
|
|
1261
|
+
return (records || [])
|
|
1262
|
+
.map((record) => {
|
|
1263
|
+
const index =
|
|
1264
|
+
semanticContext.primitiveIndexes?.[collectionKey]?.get(
|
|
1265
|
+
record
|
|
1266
|
+
)
|
|
1267
|
+
return Number.isInteger(index)
|
|
1268
|
+
? 'schematic-' + primitiveKind + '-' + index
|
|
1269
|
+
: ''
|
|
1270
|
+
})
|
|
1271
|
+
.filter(Boolean)
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
/**
|
|
1275
|
+
* Returns a stable primitive index from the original schematic collection.
|
|
1276
|
+
* @param {object} semanticContext Semantic lookup context.
|
|
1277
|
+
* @param {string} collectionKey Primitive collection key.
|
|
1278
|
+
* @param {object} primitive Primitive record.
|
|
1279
|
+
* @param {number} fallbackIndex Rendered fallback index.
|
|
1280
|
+
* @returns {number}
|
|
1281
|
+
*/
|
|
1282
|
+
static #primitiveIndex(
|
|
1283
|
+
semanticContext,
|
|
1284
|
+
collectionKey,
|
|
1285
|
+
primitive,
|
|
1286
|
+
fallbackIndex
|
|
1287
|
+
) {
|
|
1288
|
+
const resolved =
|
|
1289
|
+
semanticContext.primitiveIndexes?.[collectionKey]?.get(primitive)
|
|
1290
|
+
|
|
1291
|
+
return Number.isInteger(resolved) ? resolved : fallbackIndex
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
/**
|
|
1295
|
+
* Builds an object identity map for stable primitive indexes.
|
|
1296
|
+
* @param {object[]} records Primitive records.
|
|
1297
|
+
* @returns {Map<object, number>}
|
|
1298
|
+
*/
|
|
1299
|
+
static #objectIndexMap(records) {
|
|
1300
|
+
return new Map((records || []).map((record, index) => [record, index]))
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
/**
|
|
1304
|
+
* Inserts generated attributes into the first SVG element in a markup
|
|
1305
|
+
* fragment.
|
|
1306
|
+
* @param {string} markup SVG markup.
|
|
1307
|
+
* @param {string} attributes Rendered attributes.
|
|
1308
|
+
* @returns {string}
|
|
1309
|
+
*/
|
|
1310
|
+
static #appendSvgAttributes(markup, attributes) {
|
|
1311
|
+
if (!markup || !attributes) {
|
|
1312
|
+
return markup || ''
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
return String(markup).replace(/(\s*\/?>)/u, attributes + '$1')
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
/**
|
|
1319
|
+
* Renders a dictionary as SVG data attributes.
|
|
1320
|
+
* @param {Record<string, unknown>} attributes Attribute dictionary.
|
|
1321
|
+
* @returns {string}
|
|
1322
|
+
*/
|
|
1323
|
+
static #renderDataAttributes(attributes) {
|
|
1324
|
+
return Object.entries(attributes || {})
|
|
1325
|
+
.filter(([, value]) => {
|
|
1326
|
+
if (Array.isArray(value)) {
|
|
1327
|
+
return value.length > 0
|
|
1328
|
+
}
|
|
1329
|
+
return value !== null && value !== undefined && value !== ''
|
|
1330
|
+
})
|
|
1331
|
+
.map(([name, value]) => {
|
|
1332
|
+
const renderedValue = Array.isArray(value)
|
|
1333
|
+
? value.join(',')
|
|
1334
|
+
: String(value)
|
|
1335
|
+
return ' ' + name + '="' + escapeHtml(renderedValue) + '"'
|
|
1336
|
+
})
|
|
1337
|
+
.join('')
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
/**
|
|
1341
|
+
* Returns a stable source record id when present, else a renderer key.
|
|
1342
|
+
* @param {string} primitiveKind Public primitive kind.
|
|
1343
|
+
* @param {object} primitive Primitive record.
|
|
1344
|
+
* @param {number} index Stable primitive index.
|
|
1345
|
+
* @returns {string}
|
|
1346
|
+
*/
|
|
1347
|
+
static #recordId(primitiveKind, primitive, index) {
|
|
1348
|
+
const candidate =
|
|
1349
|
+
primitive?.recordId ??
|
|
1350
|
+
primitive?.sourceRecordId ??
|
|
1351
|
+
primitive?.sourceRecordIndex
|
|
1352
|
+
|
|
1353
|
+
return candidate === null || candidate === undefined || candidate === ''
|
|
1354
|
+
? 'schematic-' + primitiveKind + '-' + index
|
|
1355
|
+
: String(candidate)
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
/**
|
|
1359
|
+
* Resolves the primitive token used by SVG element keys.
|
|
1360
|
+
* @param {string} primitiveKind Public primitive kind.
|
|
1361
|
+
* @param {object} primitive Primitive record.
|
|
1362
|
+
* @returns {string}
|
|
1363
|
+
*/
|
|
1364
|
+
static #elementKeyPrimitiveKind(primitiveKind, primitive) {
|
|
1365
|
+
if (primitiveKind === 'text' && primitive?.recordType === '28') {
|
|
1366
|
+
return 'text'
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
return primitiveKind
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
/**
|
|
1373
|
+
* Resolves the primitive token used by semantic metadata.
|
|
1374
|
+
* @param {string} primitiveKind Public primitive kind.
|
|
1375
|
+
* @param {object} primitive Primitive record.
|
|
1376
|
+
* @returns {string}
|
|
1377
|
+
*/
|
|
1378
|
+
static #metadataPrimitiveKind(primitiveKind, primitive) {
|
|
1379
|
+
if (primitiveKind === 'text' && primitive?.recordType === '28') {
|
|
1380
|
+
return 'text-frame'
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
return primitiveKind
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
/**
|
|
1387
|
+
* Returns true when a primitive carries source record identity.
|
|
1388
|
+
* @param {object} primitive Primitive record.
|
|
1389
|
+
* @returns {boolean}
|
|
1390
|
+
*/
|
|
1391
|
+
static #hasExplicitRecordId(primitive) {
|
|
1392
|
+
return (
|
|
1393
|
+
(primitive?.recordId !== null &&
|
|
1394
|
+
primitive?.recordId !== undefined &&
|
|
1395
|
+
primitive?.recordId !== '') ||
|
|
1396
|
+
(primitive?.sourceRecordId !== null &&
|
|
1397
|
+
primitive?.sourceRecordId !== undefined &&
|
|
1398
|
+
primitive?.sourceRecordId !== '') ||
|
|
1399
|
+
(primitive?.sourceRecordIndex !== null &&
|
|
1400
|
+
primitive?.sourceRecordIndex !== undefined &&
|
|
1401
|
+
primitive?.sourceRecordIndex !== '')
|
|
1402
|
+
)
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
/**
|
|
1406
|
+
* Returns a displayable pin designator.
|
|
1407
|
+
* @param {object} pin Pin record.
|
|
1408
|
+
* @returns {string}
|
|
1409
|
+
*/
|
|
1410
|
+
static #pinLabel(pin) {
|
|
1411
|
+
return String(pin?.designator || pin?.pinNumber || pin?.name || '')
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
/**
|
|
1415
|
+
* Deduplicates values while preserving insertion order.
|
|
1416
|
+
* @param {unknown[]} values Candidate values.
|
|
1417
|
+
* @returns {unknown[]}
|
|
1418
|
+
*/
|
|
1419
|
+
static #dedupe(values) {
|
|
1420
|
+
return [...new Set((values || []).filter(Boolean))]
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
/**
|
|
1424
|
+
* Removes empty fields from a semantic metadata object.
|
|
1425
|
+
* @param {Record<string, unknown>} value Metadata object.
|
|
1426
|
+
* @returns {Record<string, unknown>}
|
|
1427
|
+
*/
|
|
1428
|
+
static #stripEmptySemanticObject(value) {
|
|
1429
|
+
return Object.fromEntries(
|
|
1430
|
+
Object.entries(value || {}).filter(([, entryValue]) => {
|
|
1431
|
+
if (Array.isArray(entryValue)) {
|
|
1432
|
+
return entryValue.length > 0
|
|
1433
|
+
}
|
|
1434
|
+
return (
|
|
1435
|
+
entryValue !== null &&
|
|
1436
|
+
entryValue !== undefined &&
|
|
1437
|
+
entryValue !== ''
|
|
1438
|
+
)
|
|
1439
|
+
})
|
|
1440
|
+
)
|
|
1441
|
+
}
|
|
1442
|
+
|
|
368
1443
|
/**
|
|
369
1444
|
* Resolves the rendered dimensions and sheet metadata used by SVG output.
|
|
370
1445
|
* @param {{ width?: number, height?: number, sourceWidth?: number, sourceHeight?: number, marginWidth?: number, paperSize?: string, borderOn?: boolean } | undefined} sheet
|
|
@@ -446,22 +1521,32 @@ export class SchematicSvgRenderer {
|
|
|
446
1521
|
* @param {{ x1: number, y1: number, x2: number, y2: number, ownerIndex?: string, renderOrder?: number }[]} lines
|
|
447
1522
|
* @param {{ points: { x: number, y: number }[], ownerIndex?: string, renderOrder?: number }[]} polygons
|
|
448
1523
|
* @param {{ x: number, y: number, width: number, height: number, ownerIndex?: string, renderOrder?: number }[]} rectangles
|
|
1524
|
+
* @param {{ x: number, y: number, width: number, height: number, ownerIndex?: string, renderOrder?: number }[]} roundedRectangles
|
|
449
1525
|
* @param {{ x: number, y: number, radiusX: number, radiusY: number, ownerIndex?: string, renderOrder?: number }[]} ellipses
|
|
450
1526
|
* @param {{ x: number, y: number, radius: number, startAngle: number, endAngle: number, ownerIndex?: string, renderOrder?: number }[]} arcs
|
|
1527
|
+
* @param {{ segments: object[], ownerIndex?: string, renderOrder?: number }[]} beziers
|
|
1528
|
+
* @param {{ x: number, y: number, radius: number, radiusY?: number, startAngle: number, endAngle: number, ownerIndex?: string, renderOrder?: number }[]} pies
|
|
1529
|
+
* @param {{ x: number, y: number, symbolName?: string, ownerIndex?: string, renderOrder?: number }[]} ieeeSymbols
|
|
451
1530
|
* @param {number} sheetHeight
|
|
1531
|
+
* @param {object} semanticContext Semantic lookup context.
|
|
452
1532
|
* @returns {string}
|
|
453
1533
|
*/
|
|
454
1534
|
static #buildOwnerGeometryMarkup(
|
|
455
1535
|
lines,
|
|
456
1536
|
polygons,
|
|
457
1537
|
rectangles,
|
|
1538
|
+
roundedRectangles,
|
|
458
1539
|
ellipses,
|
|
459
1540
|
arcs,
|
|
460
|
-
|
|
1541
|
+
beziers,
|
|
1542
|
+
pies,
|
|
1543
|
+
ieeeSymbols,
|
|
1544
|
+
sheetHeight,
|
|
1545
|
+
semanticContext
|
|
461
1546
|
) {
|
|
462
1547
|
const items = []
|
|
463
1548
|
|
|
464
|
-
for (const polygon of polygons) {
|
|
1549
|
+
for (const [index, polygon] of polygons.entries()) {
|
|
465
1550
|
if (!polygon.ownerIndex) {
|
|
466
1551
|
continue
|
|
467
1552
|
}
|
|
@@ -470,14 +1555,27 @@ export class SchematicSvgRenderer {
|
|
|
470
1555
|
renderOrder:
|
|
471
1556
|
SchematicSvgRenderer.#resolvePrimitiveRenderOrder(polygon),
|
|
472
1557
|
typeOrder: 0,
|
|
473
|
-
markup:
|
|
474
|
-
|
|
475
|
-
|
|
1558
|
+
markup: SchematicSvgRenderer.#appendSvgAttributes(
|
|
1559
|
+
SchematicShapeRenderer.buildPolygonMarkup(
|
|
1560
|
+
polygon,
|
|
1561
|
+
sheetHeight
|
|
1562
|
+
),
|
|
1563
|
+
SchematicSvgRenderer.#semanticAttributes(
|
|
1564
|
+
'polygon',
|
|
1565
|
+
polygon,
|
|
1566
|
+
SchematicSvgRenderer.#primitiveIndex(
|
|
1567
|
+
semanticContext,
|
|
1568
|
+
'polygons',
|
|
1569
|
+
polygon,
|
|
1570
|
+
index
|
|
1571
|
+
),
|
|
1572
|
+
semanticContext
|
|
1573
|
+
)
|
|
476
1574
|
)
|
|
477
1575
|
})
|
|
478
1576
|
}
|
|
479
1577
|
|
|
480
|
-
for (const rectangle of rectangles) {
|
|
1578
|
+
for (const [index, rectangle] of rectangles.entries()) {
|
|
481
1579
|
if (!rectangle.ownerIndex) {
|
|
482
1580
|
continue
|
|
483
1581
|
}
|
|
@@ -488,14 +1586,58 @@ export class SchematicSvgRenderer {
|
|
|
488
1586
|
rectangle
|
|
489
1587
|
),
|
|
490
1588
|
typeOrder: 1,
|
|
491
|
-
markup:
|
|
492
|
-
|
|
493
|
-
|
|
1589
|
+
markup: SchematicSvgRenderer.#appendSvgAttributes(
|
|
1590
|
+
SchematicShapeRenderer.buildRectangleMarkup(
|
|
1591
|
+
rectangle,
|
|
1592
|
+
sheetHeight
|
|
1593
|
+
),
|
|
1594
|
+
SchematicSvgRenderer.#semanticAttributes(
|
|
1595
|
+
'rectangle',
|
|
1596
|
+
rectangle,
|
|
1597
|
+
SchematicSvgRenderer.#primitiveIndex(
|
|
1598
|
+
semanticContext,
|
|
1599
|
+
'rectangles',
|
|
1600
|
+
rectangle,
|
|
1601
|
+
index
|
|
1602
|
+
),
|
|
1603
|
+
semanticContext
|
|
1604
|
+
)
|
|
1605
|
+
)
|
|
1606
|
+
})
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
for (const [index, rectangle] of roundedRectangles.entries()) {
|
|
1610
|
+
if (!rectangle.ownerIndex) {
|
|
1611
|
+
continue
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
items.push({
|
|
1615
|
+
renderOrder:
|
|
1616
|
+
SchematicSvgRenderer.#resolvePrimitiveRenderOrder(
|
|
1617
|
+
rectangle
|
|
1618
|
+
),
|
|
1619
|
+
typeOrder: 1.5,
|
|
1620
|
+
markup: SchematicSvgRenderer.#appendSvgAttributes(
|
|
1621
|
+
SchematicShapeRenderer.buildRoundedRectangleMarkup(
|
|
1622
|
+
rectangle,
|
|
1623
|
+
sheetHeight
|
|
1624
|
+
),
|
|
1625
|
+
SchematicSvgRenderer.#semanticAttributes(
|
|
1626
|
+
'rounded-rectangle',
|
|
1627
|
+
rectangle,
|
|
1628
|
+
SchematicSvgRenderer.#primitiveIndex(
|
|
1629
|
+
semanticContext,
|
|
1630
|
+
'roundedRectangles',
|
|
1631
|
+
rectangle,
|
|
1632
|
+
index
|
|
1633
|
+
),
|
|
1634
|
+
semanticContext
|
|
1635
|
+
)
|
|
494
1636
|
)
|
|
495
1637
|
})
|
|
496
1638
|
}
|
|
497
1639
|
|
|
498
|
-
for (const ellipse of ellipses) {
|
|
1640
|
+
for (const [index, ellipse] of ellipses.entries()) {
|
|
499
1641
|
if (!ellipse.ownerIndex) {
|
|
500
1642
|
continue
|
|
501
1643
|
}
|
|
@@ -504,14 +1646,27 @@ export class SchematicSvgRenderer {
|
|
|
504
1646
|
renderOrder:
|
|
505
1647
|
SchematicSvgRenderer.#resolvePrimitiveRenderOrder(ellipse),
|
|
506
1648
|
typeOrder: 2,
|
|
507
|
-
markup:
|
|
508
|
-
|
|
509
|
-
|
|
1649
|
+
markup: SchematicSvgRenderer.#appendSvgAttributes(
|
|
1650
|
+
SchematicShapeRenderer.buildEllipseMarkup(
|
|
1651
|
+
ellipse,
|
|
1652
|
+
sheetHeight
|
|
1653
|
+
),
|
|
1654
|
+
SchematicSvgRenderer.#semanticAttributes(
|
|
1655
|
+
'ellipse',
|
|
1656
|
+
ellipse,
|
|
1657
|
+
SchematicSvgRenderer.#primitiveIndex(
|
|
1658
|
+
semanticContext,
|
|
1659
|
+
'ellipses',
|
|
1660
|
+
ellipse,
|
|
1661
|
+
index
|
|
1662
|
+
),
|
|
1663
|
+
semanticContext
|
|
1664
|
+
)
|
|
510
1665
|
)
|
|
511
1666
|
})
|
|
512
1667
|
}
|
|
513
1668
|
|
|
514
|
-
for (const line of lines) {
|
|
1669
|
+
for (const [index, line] of lines.entries()) {
|
|
515
1670
|
if (!line.ownerIndex) {
|
|
516
1671
|
continue
|
|
517
1672
|
}
|
|
@@ -522,12 +1677,19 @@ export class SchematicSvgRenderer {
|
|
|
522
1677
|
typeOrder: 3,
|
|
523
1678
|
markup: SchematicSvgRenderer.#buildSchematicLineMarkup(
|
|
524
1679
|
line,
|
|
525
|
-
sheetHeight
|
|
1680
|
+
sheetHeight,
|
|
1681
|
+
SchematicSvgRenderer.#primitiveIndex(
|
|
1682
|
+
semanticContext,
|
|
1683
|
+
'lines',
|
|
1684
|
+
line,
|
|
1685
|
+
index
|
|
1686
|
+
),
|
|
1687
|
+
semanticContext
|
|
526
1688
|
)
|
|
527
1689
|
})
|
|
528
1690
|
}
|
|
529
1691
|
|
|
530
|
-
for (const arc of arcs) {
|
|
1692
|
+
for (const [index, arc] of arcs.entries()) {
|
|
531
1693
|
if (!arc.ownerIndex) {
|
|
532
1694
|
continue
|
|
533
1695
|
}
|
|
@@ -536,7 +1698,104 @@ export class SchematicSvgRenderer {
|
|
|
536
1698
|
renderOrder:
|
|
537
1699
|
SchematicSvgRenderer.#resolvePrimitiveRenderOrder(arc),
|
|
538
1700
|
typeOrder: 4,
|
|
539
|
-
markup:
|
|
1701
|
+
markup: SchematicSvgRenderer.#appendSvgAttributes(
|
|
1702
|
+
SchematicShapeRenderer.buildArcMarkup(arc, sheetHeight),
|
|
1703
|
+
SchematicSvgRenderer.#semanticAttributes(
|
|
1704
|
+
'arc',
|
|
1705
|
+
arc,
|
|
1706
|
+
SchematicSvgRenderer.#primitiveIndex(
|
|
1707
|
+
semanticContext,
|
|
1708
|
+
'arcs',
|
|
1709
|
+
arc,
|
|
1710
|
+
index
|
|
1711
|
+
),
|
|
1712
|
+
semanticContext
|
|
1713
|
+
)
|
|
1714
|
+
)
|
|
1715
|
+
})
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
for (const [index, bezier] of beziers.entries()) {
|
|
1719
|
+
if (!bezier.ownerIndex) {
|
|
1720
|
+
continue
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
items.push({
|
|
1724
|
+
renderOrder:
|
|
1725
|
+
SchematicSvgRenderer.#resolvePrimitiveRenderOrder(bezier),
|
|
1726
|
+
typeOrder: 5,
|
|
1727
|
+
markup: SchematicSvgRenderer.#appendSvgAttributes(
|
|
1728
|
+
SchematicShapeRenderer.buildBezierMarkup(
|
|
1729
|
+
bezier,
|
|
1730
|
+
sheetHeight
|
|
1731
|
+
),
|
|
1732
|
+
SchematicSvgRenderer.#semanticAttributes(
|
|
1733
|
+
'bezier',
|
|
1734
|
+
bezier,
|
|
1735
|
+
SchematicSvgRenderer.#primitiveIndex(
|
|
1736
|
+
semanticContext,
|
|
1737
|
+
'beziers',
|
|
1738
|
+
bezier,
|
|
1739
|
+
index
|
|
1740
|
+
),
|
|
1741
|
+
semanticContext
|
|
1742
|
+
)
|
|
1743
|
+
)
|
|
1744
|
+
})
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
for (const [index, pie] of pies.entries()) {
|
|
1748
|
+
if (!pie.ownerIndex) {
|
|
1749
|
+
continue
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
items.push({
|
|
1753
|
+
renderOrder:
|
|
1754
|
+
SchematicSvgRenderer.#resolvePrimitiveRenderOrder(pie),
|
|
1755
|
+
typeOrder: 6,
|
|
1756
|
+
markup: SchematicSvgRenderer.#appendSvgAttributes(
|
|
1757
|
+
SchematicShapeRenderer.buildPieMarkup(pie, sheetHeight),
|
|
1758
|
+
SchematicSvgRenderer.#semanticAttributes(
|
|
1759
|
+
'pie',
|
|
1760
|
+
pie,
|
|
1761
|
+
SchematicSvgRenderer.#primitiveIndex(
|
|
1762
|
+
semanticContext,
|
|
1763
|
+
'pies',
|
|
1764
|
+
pie,
|
|
1765
|
+
index
|
|
1766
|
+
),
|
|
1767
|
+
semanticContext
|
|
1768
|
+
)
|
|
1769
|
+
)
|
|
1770
|
+
})
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
for (const [index, symbol] of ieeeSymbols.entries()) {
|
|
1774
|
+
if (!symbol.ownerIndex) {
|
|
1775
|
+
continue
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
items.push({
|
|
1779
|
+
renderOrder:
|
|
1780
|
+
SchematicSvgRenderer.#resolvePrimitiveRenderOrder(symbol),
|
|
1781
|
+
typeOrder: 7,
|
|
1782
|
+
markup: SchematicSvgRenderer.#appendSvgAttributes(
|
|
1783
|
+
SchematicShapeRenderer.buildIeeeSymbolMarkup(
|
|
1784
|
+
symbol,
|
|
1785
|
+
sheetHeight
|
|
1786
|
+
),
|
|
1787
|
+
SchematicSvgRenderer.#semanticAttributes(
|
|
1788
|
+
'ieee-symbol',
|
|
1789
|
+
symbol,
|
|
1790
|
+
SchematicSvgRenderer.#primitiveIndex(
|
|
1791
|
+
semanticContext,
|
|
1792
|
+
'ieeeSymbols',
|
|
1793
|
+
symbol,
|
|
1794
|
+
index
|
|
1795
|
+
),
|
|
1796
|
+
semanticContext
|
|
1797
|
+
)
|
|
1798
|
+
)
|
|
540
1799
|
})
|
|
541
1800
|
}
|
|
542
1801
|
|
|
@@ -575,9 +1834,16 @@ export class SchematicSvgRenderer {
|
|
|
575
1834
|
* the source primitive requests them.
|
|
576
1835
|
* @param {{ x1: number, y1: number, x2: number, y2: number, color: string, width: number, lineStyle?: number, isBus?: boolean, recordType?: string }} line
|
|
577
1836
|
* @param {number} sheetHeight
|
|
1837
|
+
* @param {number} index Stable primitive index.
|
|
1838
|
+
* @param {object | undefined} semanticContext Semantic lookup context.
|
|
578
1839
|
* @returns {string}
|
|
579
1840
|
*/
|
|
580
|
-
static #buildSchematicLineMarkup(
|
|
1841
|
+
static #buildSchematicLineMarkup(
|
|
1842
|
+
line,
|
|
1843
|
+
sheetHeight,
|
|
1844
|
+
index = 0,
|
|
1845
|
+
semanticContext = undefined
|
|
1846
|
+
) {
|
|
581
1847
|
return (
|
|
582
1848
|
'<line x1="' +
|
|
583
1849
|
formatNumber(line.x1) +
|
|
@@ -600,6 +1866,13 @@ export class SchematicSvgRenderer {
|
|
|
600
1866
|
) +
|
|
601
1867
|
'"' +
|
|
602
1868
|
SchematicSvgRenderer.#buildSchematicLineStyleAttributes(line) +
|
|
1869
|
+
SchematicSvgRenderer.#buildSchematicLineMarkerAttributes(line) +
|
|
1870
|
+
SchematicSvgRenderer.#semanticAttributes(
|
|
1871
|
+
'line',
|
|
1872
|
+
line,
|
|
1873
|
+
index,
|
|
1874
|
+
semanticContext
|
|
1875
|
+
) +
|
|
603
1876
|
' />'
|
|
604
1877
|
)
|
|
605
1878
|
}
|
|
@@ -643,6 +1916,34 @@ export class SchematicSvgRenderer {
|
|
|
643
1916
|
)
|
|
644
1917
|
}
|
|
645
1918
|
|
|
1919
|
+
/**
|
|
1920
|
+
* Builds SVG marker attributes for one schematic line.
|
|
1921
|
+
* @param {{ startMarker?: object, endMarker?: object }} line Line primitive.
|
|
1922
|
+
* @returns {string}
|
|
1923
|
+
*/
|
|
1924
|
+
static #buildSchematicLineMarkerAttributes(line) {
|
|
1925
|
+
return (
|
|
1926
|
+
(line.startMarker
|
|
1927
|
+
? ' marker-start="url(#' +
|
|
1928
|
+
escapeHtml(
|
|
1929
|
+
SchematicSvgRenderer.#schematicLineMarkerId(
|
|
1930
|
+
line.startMarker
|
|
1931
|
+
)
|
|
1932
|
+
) +
|
|
1933
|
+
')"'
|
|
1934
|
+
: '') +
|
|
1935
|
+
(line.endMarker
|
|
1936
|
+
? ' marker-end="url(#' +
|
|
1937
|
+
escapeHtml(
|
|
1938
|
+
SchematicSvgRenderer.#schematicLineMarkerId(
|
|
1939
|
+
line.endMarker
|
|
1940
|
+
)
|
|
1941
|
+
) +
|
|
1942
|
+
')"'
|
|
1943
|
+
: '')
|
|
1944
|
+
)
|
|
1945
|
+
}
|
|
1946
|
+
|
|
646
1947
|
/**
|
|
647
1948
|
* Builds page border and title-block chrome from sheet metadata.
|
|
648
1949
|
* @param {number} width
|
|
@@ -666,9 +1967,18 @@ export class SchematicSvgRenderer {
|
|
|
666
1967
|
* @param {number} sheetHeight
|
|
667
1968
|
* @param {{ x1: number, y1: number, x2: number, y2: number }[]} lines
|
|
668
1969
|
* @param {{ x: number, y: number, length: number, name?: string, ownerIndex?: string, orientation: 'left' | 'right' | 'top' | 'bottom' }[]} pins
|
|
1970
|
+
* @param {number} index Stable primitive index.
|
|
1971
|
+
* @param {object | undefined} semanticContext Semantic lookup context.
|
|
669
1972
|
* @returns {string}
|
|
670
1973
|
*/
|
|
671
|
-
static #buildSchematicTextMarkup(
|
|
1974
|
+
static #buildSchematicTextMarkup(
|
|
1975
|
+
text,
|
|
1976
|
+
sheetHeight,
|
|
1977
|
+
lines,
|
|
1978
|
+
pins,
|
|
1979
|
+
index = 0,
|
|
1980
|
+
semanticContext = undefined
|
|
1981
|
+
) {
|
|
672
1982
|
const matchedOwnerPin =
|
|
673
1983
|
SchematicOwnerPinLabelLayout.findExplicitOwnerPinLabelMatch(
|
|
674
1984
|
text,
|
|
@@ -676,16 +1986,32 @@ export class SchematicSvgRenderer {
|
|
|
676
1986
|
)
|
|
677
1987
|
|
|
678
1988
|
if (text.recordType === '17') {
|
|
679
|
-
return
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
1989
|
+
return SchematicSvgRenderer.#appendSvgAttributes(
|
|
1990
|
+
SchematicPowerPortRenderer.buildMarkup(
|
|
1991
|
+
text,
|
|
1992
|
+
lines,
|
|
1993
|
+
pins,
|
|
1994
|
+
sheetHeight
|
|
1995
|
+
),
|
|
1996
|
+
SchematicSvgRenderer.#semanticAttributes(
|
|
1997
|
+
'text',
|
|
1998
|
+
text,
|
|
1999
|
+
index,
|
|
2000
|
+
semanticContext
|
|
2001
|
+
)
|
|
684
2002
|
)
|
|
685
2003
|
}
|
|
686
2004
|
|
|
687
2005
|
if (text.recordType === '209' || text.recordType === '28') {
|
|
688
|
-
return
|
|
2006
|
+
return SchematicSvgRenderer.#appendSvgAttributes(
|
|
2007
|
+
SchematicNoteRenderer.buildMarkup(text, sheetHeight),
|
|
2008
|
+
SchematicSvgRenderer.#semanticAttributes(
|
|
2009
|
+
'text',
|
|
2010
|
+
text,
|
|
2011
|
+
index,
|
|
2012
|
+
semanticContext
|
|
2013
|
+
)
|
|
2014
|
+
)
|
|
689
2015
|
}
|
|
690
2016
|
const placement = SchematicSvgRenderer.#resolveSchematicTextPlacement(
|
|
691
2017
|
text,
|
|
@@ -694,21 +2020,29 @@ export class SchematicSvgRenderer {
|
|
|
694
2020
|
matchedOwnerPin
|
|
695
2021
|
)
|
|
696
2022
|
|
|
697
|
-
return
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
2023
|
+
return SchematicSvgRenderer.#appendSvgAttributes(
|
|
2024
|
+
createSvgText(
|
|
2025
|
+
'schematic-label',
|
|
2026
|
+
placement.x,
|
|
2027
|
+
placement.y,
|
|
2028
|
+
text.resolvedText ?? text.text,
|
|
2029
|
+
SchematicColorResolver.resolveColor(
|
|
2030
|
+
text.color,
|
|
2031
|
+
'--schematic-text-color'
|
|
2032
|
+
),
|
|
2033
|
+
SchematicOwnerPinLabelLayout.resolveSchematicTextAnchor(
|
|
2034
|
+
text,
|
|
2035
|
+
placement.anchor,
|
|
2036
|
+
matchedOwnerPin
|
|
2037
|
+
),
|
|
2038
|
+
SchematicTypography.buildSchematicTextRenderOptions(text)
|
|
705
2039
|
),
|
|
706
|
-
|
|
2040
|
+
SchematicSvgRenderer.#semanticAttributes(
|
|
2041
|
+
'text',
|
|
707
2042
|
text,
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
)
|
|
711
|
-
SchematicTypography.buildSchematicTextRenderOptions(text)
|
|
2043
|
+
index,
|
|
2044
|
+
semanticContext
|
|
2045
|
+
)
|
|
712
2046
|
)
|
|
713
2047
|
}
|
|
714
2048
|
|
|
@@ -1029,17 +2363,33 @@ export class SchematicSvgRenderer {
|
|
|
1029
2363
|
* @param {{ x: number, y: number, designator?: string }} component
|
|
1030
2364
|
* @param {number} sheetHeight
|
|
1031
2365
|
* @param {{ fonts?: Record<string, { size: number, family: string, bold: boolean }> }} sheet
|
|
2366
|
+
* @param {number} index Stable primitive index.
|
|
2367
|
+
* @param {object | undefined} semanticContext Semantic lookup context.
|
|
1032
2368
|
* @returns {string}
|
|
1033
2369
|
*/
|
|
1034
|
-
static #buildFallbackComponentMarkup(
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
2370
|
+
static #buildFallbackComponentMarkup(
|
|
2371
|
+
component,
|
|
2372
|
+
sheetHeight,
|
|
2373
|
+
sheet,
|
|
2374
|
+
index = 0,
|
|
2375
|
+
semanticContext = undefined
|
|
2376
|
+
) {
|
|
2377
|
+
return SchematicSvgRenderer.#appendSvgAttributes(
|
|
2378
|
+
createSvgText(
|
|
2379
|
+
'schematic-designator',
|
|
2380
|
+
component.x + 8,
|
|
2381
|
+
projectSchematicY(sheetHeight, component.y) - 8,
|
|
2382
|
+
component.designator || '',
|
|
2383
|
+
'var(--schematic-default-ink-color)',
|
|
2384
|
+
'start',
|
|
2385
|
+
SchematicTypography.buildViewerSchematicFontOptions(sheet)
|
|
2386
|
+
),
|
|
2387
|
+
SchematicSvgRenderer.#semanticAttributes(
|
|
2388
|
+
'component',
|
|
2389
|
+
component,
|
|
2390
|
+
index,
|
|
2391
|
+
semanticContext
|
|
2392
|
+
)
|
|
1043
2393
|
)
|
|
1044
2394
|
}
|
|
1045
2395
|
|