altium-toolkit 1.1.41 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +113 -19
- package/docs/api.md +224 -18
- package/docs/capabilities.md +65 -0
- package/docs/migration/legacy-001.md +309 -0
- package/docs/migration/legacy-002.md +309 -0
- package/docs/migration/legacy-003.md +309 -0
- package/docs/migration/legacy-004.md +309 -0
- package/docs/migration/legacy-005.md +111 -0
- package/docs/migration.md +20 -0
- package/docs/model-format.md +28 -3
- package/docs/release-notes-v1.2.0.md +147 -0
- package/docs/testing.md +43 -1
- package/examples/arduino-uno/PcbThreeSceneRenderer.mjs +1 -1
- package/examples/arduino-uno/example.mjs +1 -1
- package/examples/cli-utils.mjs +1 -1
- package/examples/corpus-smoke.mjs +1 -1
- package/examples/inspect-board.mjs +1 -1
- package/examples/library-catalog.mjs +1 -1
- package/examples/validate-library.mjs +1 -1
- package/package.json +20 -5
- package/spec/api-baseline-v1.1.41.json +1 -0
- package/spec/asset-baseline-v1.1.41.json +1 -0
- package/spec/feature-preservation.json +1 -0
- package/spec/library-scope.md +14 -4
- package/spec/native-source-manifest-v1.1.41.json +1 -0
- package/src/capabilities.mjs +4 -0
- package/src/convergence/AltiumCircuitJsonProjection.mjs +230 -0
- package/src/convergence/AltiumDocumentBuilder.mjs +167 -0
- package/src/convergence/AltiumExtensionResolver.mjs +98 -0
- package/src/convergence/AltiumProjectDocumentResolver.mjs +324 -0
- package/src/convergence/AltiumSchematicCoordinateProjection.mjs +130 -0
- package/src/convergence/AltiumWorkerClient.mjs +95 -0
- package/src/convergence/Parser.mjs +285 -0
- package/src/convergence/ParserInput.mjs +282 -0
- package/src/convergence/ProjectLoader.mjs +916 -0
- package/src/convergence/SchematicSvgRenderer.mjs +126 -0
- package/src/convergence/ToolkitCapabilities.mjs +49 -0
- package/src/core/circuit-json/CircuitJsonSchematicDocumentGraphicBuilder.mjs +785 -0
- package/src/core/circuit-json/CircuitJsonSchematicGraphicBuilder.mjs +909 -0
- package/src/core/circuit-json/CircuitJsonSchematicImageProjection.mjs +277 -0
- package/src/core/circuit-json/CircuitJsonSchematicStrokeStyle.mjs +47 -0
- package/src/extensions.mjs +11 -0
- package/src/index.mjs +20 -4
- package/src/interaction.mjs +7 -0
- package/src/legacy-netlist-query.mjs +11 -0
- package/src/legacy-parser.mjs +141 -0
- package/src/legacy-renderers.mjs +25 -0
- package/src/legacy-scene3d.mjs +10 -0
- package/src/manufacturing.mjs +4 -0
- package/src/parser.mjs +12 -138
- package/src/project.mjs +11 -0
- package/src/query.mjs +4 -0
- package/src/renderers.mjs +4 -22
- package/src/scene3d.mjs +5 -8
- package/src/simulation.mjs +4 -0
- package/src/styles/renderers.css +27 -0
- package/src/testing.mjs +8 -0
- package/src/workers/parser.worker.mjs +37 -0
|
@@ -0,0 +1,909 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 André Fiedler
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
|
|
5
|
+
import { SchematicComponentOwnerTextResolver } from '../altium/SchematicComponentOwnerTextResolver.mjs'
|
|
6
|
+
import { CircuitJsonModelAdapterPrimitives } from './CircuitJsonModelAdapterPrimitives.mjs'
|
|
7
|
+
import { CircuitJsonSchematicDocumentGraphicBuilder } from './CircuitJsonSchematicDocumentGraphicBuilder.mjs'
|
|
8
|
+
import { CircuitJsonSchematicStrokeStyle } from './CircuitJsonSchematicStrokeStyle.mjs'
|
|
9
|
+
|
|
10
|
+
const Primitives = CircuitJsonModelAdapterPrimitives
|
|
11
|
+
const ELLIPSE_SEGMENTS = 48
|
|
12
|
+
const BEZIER_SEGMENTS = 24
|
|
13
|
+
const FAMILY_ORDER = new Map(
|
|
14
|
+
[
|
|
15
|
+
'line',
|
|
16
|
+
'rectangle',
|
|
17
|
+
'rounded_rectangle',
|
|
18
|
+
'ellipse',
|
|
19
|
+
'arc',
|
|
20
|
+
'pie',
|
|
21
|
+
'bezier',
|
|
22
|
+
'polygon',
|
|
23
|
+
'text_box',
|
|
24
|
+
'table',
|
|
25
|
+
'sheet',
|
|
26
|
+
'text'
|
|
27
|
+
].map((name, index) => [name, index])
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Projects normalized Altium schematic graphics onto shared CircuitJSON rows.
|
|
32
|
+
*/
|
|
33
|
+
export class CircuitJsonSchematicGraphicBuilder {
|
|
34
|
+
/**
|
|
35
|
+
* Appends all common schematic graphics in native render order.
|
|
36
|
+
* @param {object[]} circuitJson Destination CircuitJSON model.
|
|
37
|
+
* @param {Record<string, unknown>} schematic Native schematic read model.
|
|
38
|
+
* @param {string} idScope Deterministic document id scope.
|
|
39
|
+
* @param {Map<object, string>} schematicComponentIds Component id lookup.
|
|
40
|
+
* @param {Map<string, string>} netIds Source net id lookup.
|
|
41
|
+
* @returns {void}
|
|
42
|
+
*/
|
|
43
|
+
static append(
|
|
44
|
+
circuitJson,
|
|
45
|
+
schematic,
|
|
46
|
+
idScope,
|
|
47
|
+
schematicComponentIds,
|
|
48
|
+
netIds
|
|
49
|
+
) {
|
|
50
|
+
const groups = []
|
|
51
|
+
const ownerIds = CircuitJsonSchematicGraphicBuilder.#componentOwnerIds(
|
|
52
|
+
schematic,
|
|
53
|
+
schematicComponentIds
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
CircuitJsonSchematicGraphicBuilder.#appendLines(
|
|
57
|
+
groups,
|
|
58
|
+
schematic,
|
|
59
|
+
idScope,
|
|
60
|
+
ownerIds,
|
|
61
|
+
netIds
|
|
62
|
+
)
|
|
63
|
+
CircuitJsonSchematicGraphicBuilder.#appendRectangles(
|
|
64
|
+
groups,
|
|
65
|
+
schematic,
|
|
66
|
+
idScope,
|
|
67
|
+
ownerIds
|
|
68
|
+
)
|
|
69
|
+
CircuitJsonSchematicGraphicBuilder.#appendRoundedRectangles(
|
|
70
|
+
groups,
|
|
71
|
+
schematic,
|
|
72
|
+
idScope,
|
|
73
|
+
ownerIds
|
|
74
|
+
)
|
|
75
|
+
CircuitJsonSchematicGraphicBuilder.#appendEllipses(
|
|
76
|
+
groups,
|
|
77
|
+
schematic,
|
|
78
|
+
idScope,
|
|
79
|
+
ownerIds
|
|
80
|
+
)
|
|
81
|
+
CircuitJsonSchematicGraphicBuilder.#appendArcs(
|
|
82
|
+
groups,
|
|
83
|
+
schematic,
|
|
84
|
+
idScope,
|
|
85
|
+
ownerIds
|
|
86
|
+
)
|
|
87
|
+
CircuitJsonSchematicGraphicBuilder.#appendPies(
|
|
88
|
+
groups,
|
|
89
|
+
schematic,
|
|
90
|
+
idScope,
|
|
91
|
+
ownerIds
|
|
92
|
+
)
|
|
93
|
+
CircuitJsonSchematicGraphicBuilder.#appendBeziers(
|
|
94
|
+
groups,
|
|
95
|
+
schematic,
|
|
96
|
+
idScope,
|
|
97
|
+
ownerIds
|
|
98
|
+
)
|
|
99
|
+
CircuitJsonSchematicGraphicBuilder.#appendPolygons(
|
|
100
|
+
groups,
|
|
101
|
+
schematic,
|
|
102
|
+
idScope,
|
|
103
|
+
ownerIds
|
|
104
|
+
)
|
|
105
|
+
CircuitJsonSchematicDocumentGraphicBuilder.append(
|
|
106
|
+
groups,
|
|
107
|
+
schematic,
|
|
108
|
+
idScope,
|
|
109
|
+
ownerIds
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
groups
|
|
113
|
+
.sort(
|
|
114
|
+
(left, right) =>
|
|
115
|
+
left.order - right.order ||
|
|
116
|
+
left.familyOrder - right.familyOrder ||
|
|
117
|
+
left.index - right.index
|
|
118
|
+
)
|
|
119
|
+
.forEach((group) => circuitJson.push(...group.rows))
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Appends authored line and wire rows.
|
|
124
|
+
* @param {object[]} groups Render groups.
|
|
125
|
+
* @param {Record<string, unknown>} schematic Native schematic.
|
|
126
|
+
* @param {string} idScope Document id scope.
|
|
127
|
+
* @param {Map<string, string>} ownerIds Owner-to-component lookup.
|
|
128
|
+
* @param {Map<string, string>} netIds Source net lookup.
|
|
129
|
+
* @returns {void}
|
|
130
|
+
*/
|
|
131
|
+
static #appendLines(groups, schematic, idScope, ownerIds, netIds) {
|
|
132
|
+
for (const [index, line] of Primitives.array(
|
|
133
|
+
schematic.lines
|
|
134
|
+
).entries()) {
|
|
135
|
+
if (String(line?.recordType || '') === '7') continue
|
|
136
|
+
const rows = []
|
|
137
|
+
const lineId = Primitives.id(idScope, [
|
|
138
|
+
'schematic_line',
|
|
139
|
+
CircuitJsonSchematicGraphicBuilder.#identity(line, index)
|
|
140
|
+
])
|
|
141
|
+
rows.push({
|
|
142
|
+
type: 'schematic_line',
|
|
143
|
+
schematic_line_id: lineId,
|
|
144
|
+
x1: Primitives.number(line?.x1, 0),
|
|
145
|
+
y1: Primitives.number(line?.y1, 0),
|
|
146
|
+
x2: Primitives.number(line?.x2, 0),
|
|
147
|
+
y2: Primitives.number(line?.y2, 0),
|
|
148
|
+
stroke_width: Primitives.number(line?.width, 1),
|
|
149
|
+
...CircuitJsonSchematicStrokeStyle.fields(line, line?.width),
|
|
150
|
+
color: Primitives.string(line?.color, '#000000'),
|
|
151
|
+
...CircuitJsonSchematicGraphicBuilder.#ownerField(
|
|
152
|
+
line,
|
|
153
|
+
ownerIds
|
|
154
|
+
)
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
if (String(line?.sourceType || '').toLowerCase() === 'wire') {
|
|
158
|
+
const netKey = String(line.netName || '')
|
|
159
|
+
const sourceNetId =
|
|
160
|
+
netIds.get(netKey) ||
|
|
161
|
+
Primitives.sourceNetId(
|
|
162
|
+
idScope,
|
|
163
|
+
line.netName || line.netIndex || index
|
|
164
|
+
)
|
|
165
|
+
const sourceTraceId = Primitives.id(idScope, [
|
|
166
|
+
'source_trace',
|
|
167
|
+
line.netName || line.netIndex || index,
|
|
168
|
+
index
|
|
169
|
+
])
|
|
170
|
+
rows.push(
|
|
171
|
+
{
|
|
172
|
+
type: 'source_trace',
|
|
173
|
+
source_trace_id: sourceTraceId,
|
|
174
|
+
connected_source_port_ids: [],
|
|
175
|
+
connected_source_net_ids: sourceNetId
|
|
176
|
+
? [sourceNetId]
|
|
177
|
+
: []
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
type: 'schematic_trace',
|
|
181
|
+
schematic_trace_id: Primitives.id(idScope, [
|
|
182
|
+
'schematic_trace',
|
|
183
|
+
index
|
|
184
|
+
]),
|
|
185
|
+
source_trace_id: sourceTraceId,
|
|
186
|
+
junctions: [],
|
|
187
|
+
edges: [
|
|
188
|
+
{
|
|
189
|
+
from: {
|
|
190
|
+
x: Primitives.number(line?.x1, 0),
|
|
191
|
+
y: Primitives.number(line?.y1, 0)
|
|
192
|
+
},
|
|
193
|
+
to: {
|
|
194
|
+
x: Primitives.number(line?.x2, 0),
|
|
195
|
+
y: Primitives.number(line?.y2, 0)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
]
|
|
199
|
+
}
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
CircuitJsonSchematicGraphicBuilder.#group(
|
|
203
|
+
groups,
|
|
204
|
+
line,
|
|
205
|
+
'line',
|
|
206
|
+
index,
|
|
207
|
+
rows
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Appends rectangular schematic primitives.
|
|
214
|
+
* @param {object[]} groups Render groups.
|
|
215
|
+
* @param {Record<string, unknown>} schematic Native schematic.
|
|
216
|
+
* @param {string} idScope Document id scope.
|
|
217
|
+
* @param {Map<string, string>} ownerIds Owner-to-component lookup.
|
|
218
|
+
* @returns {void}
|
|
219
|
+
*/
|
|
220
|
+
static #appendRectangles(groups, schematic, idScope, ownerIds) {
|
|
221
|
+
const families = [
|
|
222
|
+
...Primitives.array(schematic.rectangles),
|
|
223
|
+
...Primitives.array(schematic.regions)
|
|
224
|
+
]
|
|
225
|
+
for (const [index, rectangle] of families.entries()) {
|
|
226
|
+
const width = Math.abs(Primitives.number(rectangle?.width, 0) || 0)
|
|
227
|
+
const height = Math.abs(
|
|
228
|
+
Primitives.number(rectangle?.height, 0) || 0
|
|
229
|
+
)
|
|
230
|
+
const x = Primitives.number(rectangle?.x, 0) || 0
|
|
231
|
+
const y = Primitives.number(rectangle?.y, 0) || 0
|
|
232
|
+
const row = {
|
|
233
|
+
type: 'schematic_rect',
|
|
234
|
+
schematic_rect_id: Primitives.id(idScope, [
|
|
235
|
+
'schematic_rect',
|
|
236
|
+
CircuitJsonSchematicGraphicBuilder.#identity(
|
|
237
|
+
rectangle,
|
|
238
|
+
index
|
|
239
|
+
)
|
|
240
|
+
]),
|
|
241
|
+
center: Primitives.point(x + width / 2, y + height / 2),
|
|
242
|
+
width,
|
|
243
|
+
height,
|
|
244
|
+
...CircuitJsonSchematicGraphicBuilder.#closedStyle(
|
|
245
|
+
rectangle,
|
|
246
|
+
false
|
|
247
|
+
),
|
|
248
|
+
...CircuitJsonSchematicGraphicBuilder.#ownerField(
|
|
249
|
+
rectangle,
|
|
250
|
+
ownerIds
|
|
251
|
+
)
|
|
252
|
+
}
|
|
253
|
+
CircuitJsonSchematicGraphicBuilder.#group(
|
|
254
|
+
groups,
|
|
255
|
+
rectangle,
|
|
256
|
+
'rectangle',
|
|
257
|
+
index,
|
|
258
|
+
[row]
|
|
259
|
+
)
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Appends rounded rectangles as exact sampled paths.
|
|
265
|
+
* @param {object[]} groups Render groups.
|
|
266
|
+
* @param {Record<string, unknown>} schematic Native schematic.
|
|
267
|
+
* @param {string} idScope Document id scope.
|
|
268
|
+
* @param {Map<string, string>} ownerIds Owner-to-component lookup.
|
|
269
|
+
* @returns {void}
|
|
270
|
+
*/
|
|
271
|
+
static #appendRoundedRectangles(groups, schematic, idScope, ownerIds) {
|
|
272
|
+
for (const [index, rectangle] of Primitives.array(
|
|
273
|
+
schematic.roundedRectangles
|
|
274
|
+
).entries()) {
|
|
275
|
+
const row = {
|
|
276
|
+
type: 'schematic_path',
|
|
277
|
+
schematic_path_id: Primitives.id(idScope, [
|
|
278
|
+
'schematic_path',
|
|
279
|
+
'rounded_rectangle',
|
|
280
|
+
CircuitJsonSchematicGraphicBuilder.#identity(
|
|
281
|
+
rectangle,
|
|
282
|
+
index
|
|
283
|
+
)
|
|
284
|
+
]),
|
|
285
|
+
points: CircuitJsonSchematicGraphicBuilder.#roundedRectanglePoints(
|
|
286
|
+
rectangle
|
|
287
|
+
),
|
|
288
|
+
...CircuitJsonSchematicGraphicBuilder.#pathStyle(rectangle),
|
|
289
|
+
...CircuitJsonSchematicGraphicBuilder.#ownerField(
|
|
290
|
+
rectangle,
|
|
291
|
+
ownerIds
|
|
292
|
+
)
|
|
293
|
+
}
|
|
294
|
+
CircuitJsonSchematicGraphicBuilder.#group(
|
|
295
|
+
groups,
|
|
296
|
+
rectangle,
|
|
297
|
+
'rounded_rectangle',
|
|
298
|
+
index,
|
|
299
|
+
[row]
|
|
300
|
+
)
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Appends circles and sampled unequal ellipses.
|
|
306
|
+
* @param {object[]} groups Render groups.
|
|
307
|
+
* @param {Record<string, unknown>} schematic Native schematic.
|
|
308
|
+
* @param {string} idScope Document id scope.
|
|
309
|
+
* @param {Map<string, string>} ownerIds Owner-to-component lookup.
|
|
310
|
+
* @returns {void}
|
|
311
|
+
*/
|
|
312
|
+
static #appendEllipses(groups, schematic, idScope, ownerIds) {
|
|
313
|
+
for (const [index, ellipse] of Primitives.array(
|
|
314
|
+
schematic.ellipses
|
|
315
|
+
).entries()) {
|
|
316
|
+
const radiusX = Math.abs(
|
|
317
|
+
Primitives.number(ellipse?.radiusX, 0) || 0
|
|
318
|
+
)
|
|
319
|
+
const radiusY = Math.abs(
|
|
320
|
+
Primitives.number(ellipse?.radiusY, 0) || 0
|
|
321
|
+
)
|
|
322
|
+
const identity = CircuitJsonSchematicGraphicBuilder.#identity(
|
|
323
|
+
ellipse,
|
|
324
|
+
index
|
|
325
|
+
)
|
|
326
|
+
const owner = CircuitJsonSchematicGraphicBuilder.#ownerField(
|
|
327
|
+
ellipse,
|
|
328
|
+
ownerIds
|
|
329
|
+
)
|
|
330
|
+
const row =
|
|
331
|
+
Math.abs(radiusX - radiusY) <= 0.000001
|
|
332
|
+
? {
|
|
333
|
+
type: 'schematic_circle',
|
|
334
|
+
schematic_circle_id: Primitives.id(idScope, [
|
|
335
|
+
'schematic_circle',
|
|
336
|
+
identity
|
|
337
|
+
]),
|
|
338
|
+
center: Primitives.point(ellipse?.x, ellipse?.y),
|
|
339
|
+
radius: radiusX,
|
|
340
|
+
...CircuitJsonSchematicGraphicBuilder.#closedStyle(
|
|
341
|
+
ellipse,
|
|
342
|
+
false
|
|
343
|
+
),
|
|
344
|
+
...owner
|
|
345
|
+
}
|
|
346
|
+
: {
|
|
347
|
+
type: 'schematic_path',
|
|
348
|
+
schematic_path_id: Primitives.id(idScope, [
|
|
349
|
+
'schematic_path',
|
|
350
|
+
'ellipse',
|
|
351
|
+
identity
|
|
352
|
+
]),
|
|
353
|
+
points: CircuitJsonSchematicGraphicBuilder.#ellipsePoints(
|
|
354
|
+
ellipse,
|
|
355
|
+
ELLIPSE_SEGMENTS
|
|
356
|
+
),
|
|
357
|
+
...CircuitJsonSchematicGraphicBuilder.#pathStyle(
|
|
358
|
+
ellipse
|
|
359
|
+
),
|
|
360
|
+
...owner
|
|
361
|
+
}
|
|
362
|
+
CircuitJsonSchematicGraphicBuilder.#group(
|
|
363
|
+
groups,
|
|
364
|
+
ellipse,
|
|
365
|
+
'ellipse',
|
|
366
|
+
index,
|
|
367
|
+
[row]
|
|
368
|
+
)
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Appends circular arcs and sampled elliptical arcs.
|
|
374
|
+
* @param {object[]} groups Render groups.
|
|
375
|
+
* @param {Record<string, unknown>} schematic Native schematic.
|
|
376
|
+
* @param {string} idScope Document id scope.
|
|
377
|
+
* @param {Map<string, string>} ownerIds Owner-to-component lookup.
|
|
378
|
+
* @returns {void}
|
|
379
|
+
*/
|
|
380
|
+
static #appendArcs(groups, schematic, idScope, ownerIds) {
|
|
381
|
+
for (const [index, arc] of Primitives.array(schematic.arcs).entries()) {
|
|
382
|
+
const radius = Math.abs(Primitives.number(arc?.radius, 0) || 0)
|
|
383
|
+
const radiusY = Math.abs(
|
|
384
|
+
Primitives.number(arc?.radiusY, radius) || radius
|
|
385
|
+
)
|
|
386
|
+
const start = Primitives.number(arc?.startAngle, 0) || 0
|
|
387
|
+
const end = Primitives.number(arc?.endAngle, 360) || 0
|
|
388
|
+
const identity = CircuitJsonSchematicGraphicBuilder.#identity(
|
|
389
|
+
arc,
|
|
390
|
+
index
|
|
391
|
+
)
|
|
392
|
+
const owner = CircuitJsonSchematicGraphicBuilder.#ownerField(
|
|
393
|
+
arc,
|
|
394
|
+
ownerIds
|
|
395
|
+
)
|
|
396
|
+
const row =
|
|
397
|
+
Math.abs(radius - radiusY) <= 0.000001
|
|
398
|
+
? {
|
|
399
|
+
type: 'schematic_arc',
|
|
400
|
+
schematic_arc_id: Primitives.id(idScope, [
|
|
401
|
+
'schematic_arc',
|
|
402
|
+
identity
|
|
403
|
+
]),
|
|
404
|
+
center: Primitives.point(arc?.x, arc?.y),
|
|
405
|
+
radius,
|
|
406
|
+
start_angle_degrees: start,
|
|
407
|
+
end_angle_degrees: end,
|
|
408
|
+
direction:
|
|
409
|
+
CircuitJsonSchematicGraphicBuilder.#arcDelta(
|
|
410
|
+
start,
|
|
411
|
+
end
|
|
412
|
+
) >= 0
|
|
413
|
+
? 'clockwise'
|
|
414
|
+
: 'counterclockwise',
|
|
415
|
+
stroke_width: Primitives.number(arc?.width, 1),
|
|
416
|
+
color: Primitives.string(arc?.color, '#000000'),
|
|
417
|
+
...CircuitJsonSchematicStrokeStyle.fields(
|
|
418
|
+
arc,
|
|
419
|
+
arc?.width
|
|
420
|
+
),
|
|
421
|
+
...owner
|
|
422
|
+
}
|
|
423
|
+
: {
|
|
424
|
+
type: 'schematic_path',
|
|
425
|
+
schematic_path_id: Primitives.id(idScope, [
|
|
426
|
+
'schematic_path',
|
|
427
|
+
'elliptical_arc',
|
|
428
|
+
identity
|
|
429
|
+
]),
|
|
430
|
+
points: CircuitJsonSchematicGraphicBuilder.#ellipticalArcPoints(
|
|
431
|
+
arc,
|
|
432
|
+
radius,
|
|
433
|
+
radiusY,
|
|
434
|
+
start,
|
|
435
|
+
end
|
|
436
|
+
),
|
|
437
|
+
stroke_color: Primitives.string(
|
|
438
|
+
arc?.color,
|
|
439
|
+
'#000000'
|
|
440
|
+
),
|
|
441
|
+
stroke_width: Primitives.number(arc?.width, 1),
|
|
442
|
+
...CircuitJsonSchematicStrokeStyle.fields(
|
|
443
|
+
arc,
|
|
444
|
+
arc?.width
|
|
445
|
+
),
|
|
446
|
+
is_filled: false,
|
|
447
|
+
...owner
|
|
448
|
+
}
|
|
449
|
+
CircuitJsonSchematicGraphicBuilder.#group(
|
|
450
|
+
groups,
|
|
451
|
+
arc,
|
|
452
|
+
'arc',
|
|
453
|
+
index,
|
|
454
|
+
[row]
|
|
455
|
+
)
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Appends filled pie and wedge primitives as closed common paths.
|
|
461
|
+
* @param {object[]} groups Render groups.
|
|
462
|
+
* @param {Record<string, unknown>} schematic Native schematic.
|
|
463
|
+
* @param {string} idScope Document id scope.
|
|
464
|
+
* @param {Map<string, string>} ownerIds Owner-to-component lookup.
|
|
465
|
+
* @returns {void}
|
|
466
|
+
*/
|
|
467
|
+
static #appendPies(groups, schematic, idScope, ownerIds) {
|
|
468
|
+
for (const [index, pie] of Primitives.array(schematic.pies).entries()) {
|
|
469
|
+
const radius = Math.abs(
|
|
470
|
+
Primitives.number(pie?.radius ?? pie?.radiusX, 0) || 0
|
|
471
|
+
)
|
|
472
|
+
const radiusY = Math.abs(
|
|
473
|
+
Primitives.number(pie?.radiusY, radius) || 0
|
|
474
|
+
)
|
|
475
|
+
if (!(radius > 0 && radiusY > 0)) continue
|
|
476
|
+
const start = Primitives.number(pie?.startAngle, 0) || 0
|
|
477
|
+
const end = Primitives.number(pie?.endAngle, 360) || 0
|
|
478
|
+
const row = {
|
|
479
|
+
type: 'schematic_path',
|
|
480
|
+
schematic_path_id: Primitives.id(idScope, [
|
|
481
|
+
'schematic_path',
|
|
482
|
+
'pie',
|
|
483
|
+
CircuitJsonSchematicGraphicBuilder.#identity(pie, index)
|
|
484
|
+
]),
|
|
485
|
+
points: [
|
|
486
|
+
Primitives.point(pie?.x, pie?.y),
|
|
487
|
+
...CircuitJsonSchematicGraphicBuilder.#ellipticalArcPoints(
|
|
488
|
+
pie,
|
|
489
|
+
radius,
|
|
490
|
+
radiusY,
|
|
491
|
+
start,
|
|
492
|
+
end
|
|
493
|
+
)
|
|
494
|
+
],
|
|
495
|
+
...CircuitJsonSchematicGraphicBuilder.#pathStyle(pie),
|
|
496
|
+
...CircuitJsonSchematicGraphicBuilder.#ownerField(pie, ownerIds)
|
|
497
|
+
}
|
|
498
|
+
CircuitJsonSchematicGraphicBuilder.#group(
|
|
499
|
+
groups,
|
|
500
|
+
pie,
|
|
501
|
+
'pie',
|
|
502
|
+
index,
|
|
503
|
+
[row]
|
|
504
|
+
)
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Appends deterministic cubic-Bezier samples.
|
|
510
|
+
* @param {object[]} groups Render groups.
|
|
511
|
+
* @param {Record<string, unknown>} schematic Native schematic.
|
|
512
|
+
* @param {string} idScope Document id scope.
|
|
513
|
+
* @param {Map<string, string>} ownerIds Owner-to-component lookup.
|
|
514
|
+
* @returns {void}
|
|
515
|
+
*/
|
|
516
|
+
static #appendBeziers(groups, schematic, idScope, ownerIds) {
|
|
517
|
+
for (const [index, bezier] of Primitives.array(
|
|
518
|
+
schematic.beziers
|
|
519
|
+
).entries()) {
|
|
520
|
+
const points =
|
|
521
|
+
CircuitJsonSchematicGraphicBuilder.#bezierPoints(bezier)
|
|
522
|
+
if (points.length < 2) continue
|
|
523
|
+
const row = {
|
|
524
|
+
type: 'schematic_path',
|
|
525
|
+
schematic_path_id: Primitives.id(idScope, [
|
|
526
|
+
'schematic_path',
|
|
527
|
+
'bezier',
|
|
528
|
+
CircuitJsonSchematicGraphicBuilder.#identity(bezier, index)
|
|
529
|
+
]),
|
|
530
|
+
points,
|
|
531
|
+
stroke_color: Primitives.string(bezier?.color, '#000000'),
|
|
532
|
+
stroke_width: Primitives.number(bezier?.width, 1),
|
|
533
|
+
...CircuitJsonSchematicStrokeStyle.fields(
|
|
534
|
+
bezier,
|
|
535
|
+
bezier?.width
|
|
536
|
+
),
|
|
537
|
+
is_filled: false,
|
|
538
|
+
...CircuitJsonSchematicGraphicBuilder.#ownerField(
|
|
539
|
+
bezier,
|
|
540
|
+
ownerIds
|
|
541
|
+
)
|
|
542
|
+
}
|
|
543
|
+
CircuitJsonSchematicGraphicBuilder.#group(
|
|
544
|
+
groups,
|
|
545
|
+
bezier,
|
|
546
|
+
'bezier',
|
|
547
|
+
index,
|
|
548
|
+
[row]
|
|
549
|
+
)
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Appends exact polygon point lists.
|
|
555
|
+
* @param {object[]} groups Render groups.
|
|
556
|
+
* @param {Record<string, unknown>} schematic Native schematic.
|
|
557
|
+
* @param {string} idScope Document id scope.
|
|
558
|
+
* @param {Map<string, string>} ownerIds Owner-to-component lookup.
|
|
559
|
+
* @returns {void}
|
|
560
|
+
*/
|
|
561
|
+
static #appendPolygons(groups, schematic, idScope, ownerIds) {
|
|
562
|
+
for (const [index, polygon] of Primitives.array(
|
|
563
|
+
schematic.polygons
|
|
564
|
+
).entries()) {
|
|
565
|
+
const points = Primitives.array(polygon?.points).map((point) =>
|
|
566
|
+
Primitives.point(point?.x, point?.y)
|
|
567
|
+
)
|
|
568
|
+
if (points.length < 2) continue
|
|
569
|
+
const row = {
|
|
570
|
+
type: 'schematic_path',
|
|
571
|
+
schematic_path_id: Primitives.id(idScope, [
|
|
572
|
+
'schematic_path',
|
|
573
|
+
'polygon',
|
|
574
|
+
CircuitJsonSchematicGraphicBuilder.#identity(polygon, index)
|
|
575
|
+
]),
|
|
576
|
+
points,
|
|
577
|
+
...CircuitJsonSchematicGraphicBuilder.#pathStyle(polygon),
|
|
578
|
+
...CircuitJsonSchematicGraphicBuilder.#ownerField(
|
|
579
|
+
polygon,
|
|
580
|
+
ownerIds
|
|
581
|
+
)
|
|
582
|
+
}
|
|
583
|
+
CircuitJsonSchematicGraphicBuilder.#group(
|
|
584
|
+
groups,
|
|
585
|
+
polygon,
|
|
586
|
+
'polygon',
|
|
587
|
+
index,
|
|
588
|
+
[row]
|
|
589
|
+
)
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Resolves native owner ids to canonical schematic component ids.
|
|
595
|
+
* @param {Record<string, unknown>} schematic Native schematic.
|
|
596
|
+
* @param {Map<object, string>} componentIds Component id lookup.
|
|
597
|
+
* @returns {Map<string, string>}
|
|
598
|
+
*/
|
|
599
|
+
static #componentOwnerIds(schematic, componentIds) {
|
|
600
|
+
const ownerIds = new Map()
|
|
601
|
+
const components = Primitives.array(schematic.components)
|
|
602
|
+
const records = Primitives.array(schematic.ownership?.records)
|
|
603
|
+
const componentRecords = records.filter((record) =>
|
|
604
|
+
['1', '45'].includes(String(record?.recordType || ''))
|
|
605
|
+
)
|
|
606
|
+
|
|
607
|
+
for (const [index, component] of components.entries()) {
|
|
608
|
+
const componentId = componentIds.get(component)
|
|
609
|
+
if (!componentId) continue
|
|
610
|
+
for (const ownerIndex of [
|
|
611
|
+
component?.ownerIndex,
|
|
612
|
+
...Primitives.array(component?.ownerIndexes)
|
|
613
|
+
]) {
|
|
614
|
+
CircuitJsonSchematicGraphicBuilder.#setOwner(
|
|
615
|
+
ownerIds,
|
|
616
|
+
ownerIndex,
|
|
617
|
+
componentId
|
|
618
|
+
)
|
|
619
|
+
}
|
|
620
|
+
const uniqueId = String(component?.uniqueId || '').trim()
|
|
621
|
+
const componentRecord =
|
|
622
|
+
componentRecords.find(
|
|
623
|
+
(record) =>
|
|
624
|
+
uniqueId &&
|
|
625
|
+
String(record?.uniqueId || '').trim() === uniqueId
|
|
626
|
+
) || componentRecords[index]
|
|
627
|
+
if (componentRecord) {
|
|
628
|
+
for (const ownerIndex of SchematicComponentOwnerTextResolver.resolveOwnerIndexes(
|
|
629
|
+
componentRecord,
|
|
630
|
+
records
|
|
631
|
+
)) {
|
|
632
|
+
CircuitJsonSchematicGraphicBuilder.#setOwner(
|
|
633
|
+
ownerIds,
|
|
634
|
+
ownerIndex,
|
|
635
|
+
componentId
|
|
636
|
+
)
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
const designator = String(component?.designator || '').trim()
|
|
641
|
+
for (const record of records) {
|
|
642
|
+
if (
|
|
643
|
+
designator &&
|
|
644
|
+
String(record?.name || '').toLowerCase() === 'designator' &&
|
|
645
|
+
String(record?.text || '').trim() === designator
|
|
646
|
+
) {
|
|
647
|
+
CircuitJsonSchematicGraphicBuilder.#setOwner(
|
|
648
|
+
ownerIds,
|
|
649
|
+
record?.ownerIndex,
|
|
650
|
+
componentId
|
|
651
|
+
)
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
return ownerIds
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Builds rounded rectangle perimeter samples.
|
|
660
|
+
* @param {Record<string, unknown>} rectangle Native rounded rectangle.
|
|
661
|
+
* @returns {{ x: number, y: number }[]}
|
|
662
|
+
*/
|
|
663
|
+
static #roundedRectanglePoints(rectangle) {
|
|
664
|
+
const x = Primitives.number(rectangle?.x, 0) || 0
|
|
665
|
+
const y = Primitives.number(rectangle?.y, 0) || 0
|
|
666
|
+
const width = Math.abs(Primitives.number(rectangle?.width, 0) || 0)
|
|
667
|
+
const height = Math.abs(Primitives.number(rectangle?.height, 0) || 0)
|
|
668
|
+
const radius = Math.max(
|
|
669
|
+
Math.min(
|
|
670
|
+
Math.abs(Primitives.number(rectangle?.radius, 0) || 0),
|
|
671
|
+
width / 2,
|
|
672
|
+
height / 2
|
|
673
|
+
),
|
|
674
|
+
0
|
|
675
|
+
)
|
|
676
|
+
if (radius === 0) {
|
|
677
|
+
return [
|
|
678
|
+
Primitives.point(x, y),
|
|
679
|
+
Primitives.point(x + width, y),
|
|
680
|
+
Primitives.point(x + width, y + height),
|
|
681
|
+
Primitives.point(x, y + height)
|
|
682
|
+
]
|
|
683
|
+
}
|
|
684
|
+
const points = []
|
|
685
|
+
const corners = [
|
|
686
|
+
{ x: x + width - radius, y: y + radius, start: -90 },
|
|
687
|
+
{ x: x + width - radius, y: y + height - radius, start: 0 },
|
|
688
|
+
{ x: x + radius, y: y + height - radius, start: 90 },
|
|
689
|
+
{ x: x + radius, y: y + radius, start: 180 }
|
|
690
|
+
]
|
|
691
|
+
for (const [cornerIndex, corner] of corners.entries()) {
|
|
692
|
+
for (let step = 0; step <= 8; step += 1) {
|
|
693
|
+
if (cornerIndex > 0 && step === 0) continue
|
|
694
|
+
const angle = ((corner.start + (step / 8) * 90) * Math.PI) / 180
|
|
695
|
+
points.push(
|
|
696
|
+
Primitives.point(
|
|
697
|
+
corner.x + radius * Math.cos(angle),
|
|
698
|
+
corner.y + radius * Math.sin(angle)
|
|
699
|
+
)
|
|
700
|
+
)
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
return points
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* Builds closed ellipse samples.
|
|
708
|
+
* @param {Record<string, unknown>} ellipse Native ellipse.
|
|
709
|
+
* @param {number} segments Segment count.
|
|
710
|
+
* @returns {{ x: number, y: number }[]}
|
|
711
|
+
*/
|
|
712
|
+
static #ellipsePoints(ellipse, segments) {
|
|
713
|
+
const centerX = Primitives.number(ellipse?.x, 0) || 0
|
|
714
|
+
const centerY = Primitives.number(ellipse?.y, 0) || 0
|
|
715
|
+
const radiusX = Math.abs(Primitives.number(ellipse?.radiusX, 0) || 0)
|
|
716
|
+
const radiusY = Math.abs(Primitives.number(ellipse?.radiusY, 0) || 0)
|
|
717
|
+
return Array.from({ length: segments }, (_, index) => {
|
|
718
|
+
const angle = (index / segments) * Math.PI * 2
|
|
719
|
+
return Primitives.point(
|
|
720
|
+
centerX + radiusX * Math.cos(angle),
|
|
721
|
+
centerY + radiusY * Math.sin(angle)
|
|
722
|
+
)
|
|
723
|
+
})
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* Builds samples for one elliptical arc.
|
|
728
|
+
* @param {Record<string, unknown>} arc Native arc.
|
|
729
|
+
* @param {number} radiusX Horizontal radius.
|
|
730
|
+
* @param {number} radiusY Vertical radius.
|
|
731
|
+
* @param {number} start Start angle.
|
|
732
|
+
* @param {number} end End angle.
|
|
733
|
+
* @returns {{ x: number, y: number }[]}
|
|
734
|
+
*/
|
|
735
|
+
static #ellipticalArcPoints(arc, radiusX, radiusY, start, end) {
|
|
736
|
+
const delta = CircuitJsonSchematicGraphicBuilder.#arcDelta(start, end)
|
|
737
|
+
const segmentCount = Math.max(2, Math.ceil(Math.abs(delta) / 7.5))
|
|
738
|
+
const centerX = Primitives.number(arc?.x, 0) || 0
|
|
739
|
+
const centerY = Primitives.number(arc?.y, 0) || 0
|
|
740
|
+
return Array.from({ length: segmentCount + 1 }, (_, index) => {
|
|
741
|
+
const angle =
|
|
742
|
+
((start + (delta * index) / segmentCount) * Math.PI) / 180
|
|
743
|
+
return Primitives.point(
|
|
744
|
+
centerX + radiusX * Math.cos(angle),
|
|
745
|
+
centerY + radiusY * Math.sin(angle)
|
|
746
|
+
)
|
|
747
|
+
})
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* Builds deterministic samples for every cubic Bezier span.
|
|
752
|
+
* @param {Record<string, unknown>} bezier Native Bezier.
|
|
753
|
+
* @returns {{ x: number, y: number }[]}
|
|
754
|
+
*/
|
|
755
|
+
static #bezierPoints(bezier) {
|
|
756
|
+
const points = []
|
|
757
|
+
for (const [segmentIndex, segment] of Primitives.array(
|
|
758
|
+
bezier?.segments
|
|
759
|
+
).entries()) {
|
|
760
|
+
for (let step = 0; step <= BEZIER_SEGMENTS; step += 1) {
|
|
761
|
+
if (segmentIndex > 0 && step === 0) continue
|
|
762
|
+
const t = step / BEZIER_SEGMENTS
|
|
763
|
+
const inverse = 1 - t
|
|
764
|
+
const x =
|
|
765
|
+
inverse ** 3 *
|
|
766
|
+
(Primitives.number(segment?.start?.x, 0) || 0) +
|
|
767
|
+
3 *
|
|
768
|
+
inverse ** 2 *
|
|
769
|
+
t *
|
|
770
|
+
(Primitives.number(segment?.control1?.x, 0) || 0) +
|
|
771
|
+
3 *
|
|
772
|
+
inverse *
|
|
773
|
+
t ** 2 *
|
|
774
|
+
(Primitives.number(segment?.control2?.x, 0) || 0) +
|
|
775
|
+
t ** 3 * (Primitives.number(segment?.end?.x, 0) || 0)
|
|
776
|
+
const y =
|
|
777
|
+
inverse ** 3 *
|
|
778
|
+
(Primitives.number(segment?.start?.y, 0) || 0) +
|
|
779
|
+
3 *
|
|
780
|
+
inverse ** 2 *
|
|
781
|
+
t *
|
|
782
|
+
(Primitives.number(segment?.control1?.y, 0) || 0) +
|
|
783
|
+
3 *
|
|
784
|
+
inverse *
|
|
785
|
+
t ** 2 *
|
|
786
|
+
(Primitives.number(segment?.control2?.y, 0) || 0) +
|
|
787
|
+
t ** 3 * (Primitives.number(segment?.end?.y, 0) || 0)
|
|
788
|
+
points.push(Primitives.point(x, y))
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
return points
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
/**
|
|
795
|
+
* Builds style fields for one closed primitive.
|
|
796
|
+
* @param {Record<string, unknown>} primitive Native primitive.
|
|
797
|
+
* @param {boolean} path Whether path color naming is required.
|
|
798
|
+
* @returns {Record<string, unknown>}
|
|
799
|
+
*/
|
|
800
|
+
static #closedStyle(primitive, path) {
|
|
801
|
+
return {
|
|
802
|
+
stroke_width: Primitives.number(
|
|
803
|
+
primitive?.lineWidth ?? primitive?.width,
|
|
804
|
+
1
|
|
805
|
+
),
|
|
806
|
+
...(path
|
|
807
|
+
? {
|
|
808
|
+
stroke_color: Primitives.string(
|
|
809
|
+
primitive?.color,
|
|
810
|
+
'#000000'
|
|
811
|
+
)
|
|
812
|
+
}
|
|
813
|
+
: {
|
|
814
|
+
color: Primitives.string(primitive?.color, '#000000')
|
|
815
|
+
}),
|
|
816
|
+
fill_color: Primitives.string(primitive?.fill, '#ffffff'),
|
|
817
|
+
...CircuitJsonSchematicStrokeStyle.fields(
|
|
818
|
+
primitive,
|
|
819
|
+
primitive?.lineWidth ?? primitive?.width
|
|
820
|
+
),
|
|
821
|
+
is_filled:
|
|
822
|
+
primitive?.isSolid === true && primitive?.transparent !== true
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* Builds style fields for one path primitive.
|
|
828
|
+
* @param {Record<string, unknown>} primitive Native primitive.
|
|
829
|
+
* @returns {Record<string, unknown>}
|
|
830
|
+
*/
|
|
831
|
+
static #pathStyle(primitive) {
|
|
832
|
+
return CircuitJsonSchematicGraphicBuilder.#closedStyle(primitive, true)
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* Returns optional ownership for one primitive.
|
|
837
|
+
* @param {Record<string, unknown>} primitive Native primitive.
|
|
838
|
+
* @param {Map<string, string>} ownerIds Owner lookup.
|
|
839
|
+
* @returns {{ schematic_component_id?: string }}
|
|
840
|
+
*/
|
|
841
|
+
static #ownerField(primitive, ownerIds) {
|
|
842
|
+
const ownerId = ownerIds.get(String(primitive?.ownerIndex || ''))
|
|
843
|
+
return ownerId ? { schematic_component_id: ownerId } : {}
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
/**
|
|
847
|
+
* Adds one stable native owner mapping.
|
|
848
|
+
* @param {Map<string, string>} ownerIds Owner map.
|
|
849
|
+
* @param {unknown} ownerIndex Native owner id.
|
|
850
|
+
* @param {string} componentId Canonical component id.
|
|
851
|
+
* @returns {void}
|
|
852
|
+
*/
|
|
853
|
+
static #setOwner(ownerIds, ownerIndex, componentId) {
|
|
854
|
+
const key = String(ownerIndex || '').trim()
|
|
855
|
+
if (key && !ownerIds.has(key)) ownerIds.set(key, componentId)
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
/**
|
|
859
|
+
* Appends one sortable group descriptor.
|
|
860
|
+
* @param {object[]} groups Group list.
|
|
861
|
+
* @param {Record<string, unknown>} source Native source row.
|
|
862
|
+
* @param {string} family Graphic family.
|
|
863
|
+
* @param {number} index Family-local index.
|
|
864
|
+
* @param {object[]} rows Canonical rows.
|
|
865
|
+
* @returns {void}
|
|
866
|
+
*/
|
|
867
|
+
static #group(groups, source, family, index, rows) {
|
|
868
|
+
const renderOrder = Primitives.number(source?.renderOrder, index)
|
|
869
|
+
groups.push({
|
|
870
|
+
order: Number.isFinite(renderOrder) ? renderOrder : index,
|
|
871
|
+
familyOrder: FAMILY_ORDER.get(family) ?? FAMILY_ORDER.size,
|
|
872
|
+
index,
|
|
873
|
+
rows
|
|
874
|
+
})
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
/**
|
|
878
|
+
* Returns a deterministic primitive identity.
|
|
879
|
+
* @param {Record<string, unknown>} source Native source row.
|
|
880
|
+
* @param {number} index Family-local index.
|
|
881
|
+
* @returns {unknown}
|
|
882
|
+
*/
|
|
883
|
+
static #identity(source, index) {
|
|
884
|
+
return (
|
|
885
|
+
source?.recordId ||
|
|
886
|
+
source?.uniqueId ||
|
|
887
|
+
source?.uuid ||
|
|
888
|
+
source?.id ||
|
|
889
|
+
source?.renderOrder ||
|
|
890
|
+
index
|
|
891
|
+
)
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
/**
|
|
895
|
+
* Normalizes a native arc delta without discarding full turns.
|
|
896
|
+
* @param {number} start Start angle.
|
|
897
|
+
* @param {number} end End angle.
|
|
898
|
+
* @returns {number}
|
|
899
|
+
*/
|
|
900
|
+
static #arcDelta(start, end) {
|
|
901
|
+
let delta = Number(end) - Number(start)
|
|
902
|
+
while (delta < -360) delta += 360
|
|
903
|
+
while (delta > 360) delta -= 360
|
|
904
|
+
return delta
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
Object.freeze(CircuitJsonSchematicGraphicBuilder.prototype)
|
|
909
|
+
Object.freeze(CircuitJsonSchematicGraphicBuilder)
|