altium-toolkit 1.0.9 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/api.md +6 -2
- package/docs/model-format.md +29 -4
- package/docs/schemas/altium_toolkit/ci_artifact_bundle_a1.schema.json +80 -0
- package/docs/schemas/altium_toolkit/contract_gate_a1.schema.json +34 -0
- package/docs/schemas/altium_toolkit/draftsman_board_view_cache_a1.schema.json +115 -0
- package/docs/schemas/altium_toolkit/draftsman_digest_a1.schema.json +166 -0
- package/docs/schemas/altium_toolkit/host_capabilities_a1.schema.json +39 -0
- package/docs/schemas/altium_toolkit/library_merge_plan_a1.schema.json +56 -0
- package/docs/schemas/altium_toolkit/library_qa_a1.schema.json +70 -0
- package/docs/schemas/altium_toolkit/netlist_a1.schema.json +6 -0
- package/docs/schemas/altium_toolkit/normalized_model_a1.schema.json +856 -7
- package/docs/schemas/altium_toolkit/parser_compatibility_fuzz_a1.schema.json +25 -0
- package/docs/schemas/altium_toolkit/pcb_bom_profile_a1.schema.json +48 -0
- package/docs/schemas/altium_toolkit/pcb_layer_stack_a1.schema.json +98 -0
- package/docs/schemas/altium_toolkit/pcb_layer_stack_fidelity_a1.schema.json +66 -0
- package/docs/schemas/altium_toolkit/pcb_placed_footprint_extraction_a1.schema.json +31 -0
- package/docs/schemas/altium_toolkit/pcb_review_metadata_a1.schema.json +62 -0
- package/docs/schemas/altium_toolkit/pcb_rigid_flex_topology_a1.schema.json +52 -0
- package/docs/schemas/altium_toolkit/pcb_svg_semantics_a1.schema.json +27 -0
- package/docs/schemas/altium_toolkit/pcblib_parity_a1.schema.json +24 -0
- package/docs/schemas/altium_toolkit/project_bom_pnp_reconciliation_a1.schema.json +63 -0
- package/docs/schemas/altium_toolkit/project_bundle_a1.schema.json +6 -0
- package/docs/schemas/altium_toolkit/project_document_graph_a1.schema.json +33 -0
- package/docs/schemas/altium_toolkit/project_outjob_digest_a1.schema.json +46 -0
- package/docs/schemas/altium_toolkit/project_script_a1.schema.json +50 -0
- package/docs/schemas/altium_toolkit/schematic_render_ops_a1.schema.json +55 -0
- package/docs/schemas/altium_toolkit/schematic_template_extraction_a1.schema.json +37 -0
- package/docs/schemas/altium_toolkit/svg_model_cross_link_a1.schema.json +39 -0
- package/package.json +1 -1
- package/src/core/altium/AltiumParser.mjs +12 -2
- package/src/core/altium/CiArtifactBundleBuilder.mjs +213 -0
- package/src/core/altium/ContractGateReportBuilder.mjs +351 -0
- package/src/core/altium/DraftsmanBoardViewMetadataBuilder.mjs +653 -0
- package/src/core/altium/DraftsmanDigestParser.mjs +928 -0
- package/src/core/altium/DraftsmanImagePayloadManifestBuilder.mjs +178 -0
- package/src/core/altium/HostCapabilityDiagnosticsBuilder.mjs +271 -0
- package/src/core/altium/LibraryQaReportBuilder.mjs +504 -0
- package/src/core/altium/LibraryRenderManifestBuilder.mjs +172 -2
- package/src/core/altium/ParserCompatibilityFuzzer.mjs +192 -0
- package/src/core/altium/PcbBomProfileBuilder.mjs +263 -0
- package/src/core/altium/PcbComponentKindPolicy.mjs +146 -0
- package/src/core/altium/PcbLayerStackFidelityReportBuilder.mjs +141 -0
- package/src/core/altium/PcbLayerStackInterchangeParser.mjs +453 -0
- package/src/core/altium/PcbLayerStackQueryHelper.mjs +195 -0
- package/src/core/altium/PcbLayerStackReadModelBuilder.mjs +906 -0
- package/src/core/altium/PcbLayerStackSourceMetadataParser.mjs +488 -0
- package/src/core/altium/PcbLibModelParser.mjs +2 -0
- package/src/core/altium/PcbLibParityReportBuilder.mjs +242 -0
- package/src/core/altium/PcbModelParser.mjs +211 -22
- package/src/core/altium/PcbPadStackParser.mjs +171 -2
- package/src/core/altium/PcbPickPlacePositionResolver.mjs +11 -1
- package/src/core/altium/PcbPlacedFootprintManifestBuilder.mjs +338 -0
- package/src/core/altium/PcbPolygonRecordParser.mjs +120 -0
- package/src/core/altium/PcbRegionPrimitiveParser.mjs +71 -2
- package/src/core/altium/PcbReviewDrillMetadataBuilder.mjs +301 -0
- package/src/core/altium/PcbReviewMetadataBuilder.mjs +373 -0
- package/src/core/altium/PcbReviewPolygonRealizationBuilder.mjs +269 -0
- package/src/core/altium/PcbReviewRouteHighlightProfileBuilder.mjs +298 -0
- package/src/core/altium/PcbRigidFlexTopologyBuilder.mjs +171 -0
- package/src/core/altium/PcbRouteAnalysisBuilder.mjs +730 -0
- package/src/core/altium/PcbStatisticsBuilder.mjs +9 -0
- package/src/core/altium/PrintableTextDecoder.mjs +70 -6
- package/src/core/altium/PrjPcbModelParser.mjs +69 -2
- package/src/core/altium/PrjScrModelParser.mjs +386 -0
- package/src/core/altium/ProjectBomPnpReconciliationBuilder.mjs +237 -0
- package/src/core/altium/ProjectDesignBundleBuilder.mjs +76 -2
- package/src/core/altium/ProjectDocumentGraphBuilder.mjs +280 -0
- package/src/core/altium/ProjectNetlistExporter.mjs +5 -1
- package/src/core/altium/ProjectOutJobDigestBuilder.mjs +424 -13
- package/src/core/altium/SvgModelCrossLinkValidator.mjs +435 -0
- package/src/core/circuit-json/CircuitJsonModelAdapter.mjs +300 -96
- package/src/core/circuit-json/CircuitJsonModelAdapterPcbElements.mjs +244 -0
- package/src/core/circuit-json/CircuitJsonModelSchema.mjs +1 -1
- package/src/parser.mjs +21 -0
- package/src/ui/PcbFootprintPrimitiveSelector.mjs +13 -1
- package/src/ui/PcbScene3dBuilder.mjs +26 -4
- package/src/ui/PcbSvgRenderer.mjs +65 -0
- package/src/ui/SchematicRenderOpsSidecarBuilder.mjs +554 -0
- package/src/ui/SchematicSvgRenderer.mjs +48 -2
|
@@ -4,8 +4,72 @@
|
|
|
4
4
|
|
|
5
5
|
import { CircuitJsonModelSchema } from './CircuitJsonModelSchema.mjs'
|
|
6
6
|
import { CircuitJsonModelAdapterPrimitives } from './CircuitJsonModelAdapterPrimitives.mjs'
|
|
7
|
+
import { CircuitJsonModelAdapterPcbElements } from './CircuitJsonModelAdapterPcbElements.mjs'
|
|
7
8
|
|
|
8
9
|
const Primitives = CircuitJsonModelAdapterPrimitives
|
|
10
|
+
const PcbElements = CircuitJsonModelAdapterPcbElements
|
|
11
|
+
const ALTIUM_TOOLKIT_SIDECARS = [
|
|
12
|
+
{
|
|
13
|
+
type: 'altium_toolkit_pcb_layer_stack',
|
|
14
|
+
paths: [['pcb', 'layerStackReadModel']],
|
|
15
|
+
schema: 'altium-toolkit.pcb.layer-stack.a1'
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
type: 'altium_toolkit_pcb_rigid_flex_topology',
|
|
19
|
+
paths: [['pcb', 'rigidFlexTopology']],
|
|
20
|
+
schema: 'altium-toolkit.pcb.rigid-flex-topology.a1'
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
type: 'altium_toolkit_pcb_review_metadata',
|
|
24
|
+
paths: [['pcb', 'reviewMetadata']],
|
|
25
|
+
schema: 'altium-toolkit.pcb.review-metadata.a1'
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
type: 'altium_toolkit_pcb_placed_footprint_extraction',
|
|
29
|
+
paths: [['pcb', 'footprintExtractionManifest']],
|
|
30
|
+
schema: 'altium-toolkit.pcb.placed-footprint-extraction.a1'
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
type: 'altium_toolkit_pcblib_parity',
|
|
34
|
+
paths: [['pcbLibrary', 'parityReport']],
|
|
35
|
+
schema: 'altium-toolkit.pcblib.parity.a1'
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
type: 'altium_toolkit_project_outjob_digest',
|
|
39
|
+
paths: [['project', 'outJobDigest']],
|
|
40
|
+
schema: 'altium-toolkit.project.outjob-digest.a1'
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
type: 'altium_toolkit_project_document_graph',
|
|
44
|
+
paths: [['project', 'documentGraph'], ['documentGraph']],
|
|
45
|
+
schema: 'altium-toolkit.project.document-graph.a1'
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
type: 'altium_toolkit_project_bom_pnp_reconciliation',
|
|
49
|
+
paths: [['reconciliation']],
|
|
50
|
+
schema: 'altium-toolkit.project.bom-pnp-reconciliation.a1'
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
type: 'altium_toolkit_draftsman_image_payloads',
|
|
54
|
+
paths: [['draftsman', 'imagePayloads']],
|
|
55
|
+
schema: 'altium-toolkit.draftsman.image-payloads.a1'
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
type: 'altium_toolkit_draftsman_board_view_metadata',
|
|
59
|
+
paths: [['draftsman', 'boardViewMetadata']],
|
|
60
|
+
schema: 'altium-toolkit.draftsman.board-view-cache.a1'
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
type: 'altium_toolkit_contract_gate',
|
|
64
|
+
paths: [['contractGate']],
|
|
65
|
+
schema: 'altium-toolkit.contract-gate.a1'
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
type: 'altium_toolkit_host_capabilities',
|
|
69
|
+
paths: [['hostCapabilities']],
|
|
70
|
+
schema: 'altium-toolkit.host-capabilities.a1'
|
|
71
|
+
}
|
|
72
|
+
]
|
|
9
73
|
|
|
10
74
|
/**
|
|
11
75
|
* Converts between legacy renderer models and Circuit JSON element arrays.
|
|
@@ -53,6 +117,7 @@ export class CircuitJsonModelAdapter {
|
|
|
53
117
|
}
|
|
54
118
|
|
|
55
119
|
CircuitJsonModelAdapter.#appendBom(circuitJson, model, idScope)
|
|
120
|
+
CircuitJsonModelAdapter.#appendSidecars(circuitJson, model, idScope)
|
|
56
121
|
CircuitJsonModelAdapter.#attachCompatibility(circuitJson, model)
|
|
57
122
|
|
|
58
123
|
return CircuitJsonModelSchema.attach(circuitJson)
|
|
@@ -175,7 +240,8 @@ export class CircuitJsonModelAdapter {
|
|
|
175
240
|
width: Primitives.number(component.width, 0),
|
|
176
241
|
height: Primitives.number(component.height, 0)
|
|
177
242
|
},
|
|
178
|
-
rotation: Primitives.number(component.rotation, 0)
|
|
243
|
+
rotation: Primitives.number(component.rotation, 0),
|
|
244
|
+
is_box_with_pins: true
|
|
179
245
|
})
|
|
180
246
|
}
|
|
181
247
|
|
|
@@ -204,9 +270,8 @@ export class CircuitJsonModelAdapter {
|
|
|
204
270
|
pin.name || pin.designator || pinIndex,
|
|
205
271
|
String(pinIndex + 1)
|
|
206
272
|
),
|
|
207
|
-
|
|
208
|
-
pin.pinNumber || pin.designator || pin.name
|
|
209
|
-
String(pinIndex + 1)
|
|
273
|
+
...CircuitJsonModelAdapter.#pinNumberField(
|
|
274
|
+
pin.pinNumber || pin.designator || pin.name
|
|
210
275
|
)
|
|
211
276
|
})
|
|
212
277
|
circuitJson.push({
|
|
@@ -232,7 +297,8 @@ export class CircuitJsonModelAdapter {
|
|
|
232
297
|
circuitJson.push({
|
|
233
298
|
type: 'source_net',
|
|
234
299
|
source_net_id: sourceNetId,
|
|
235
|
-
name: Primitives.string(net.name, `NET_${netIndex + 1}`)
|
|
300
|
+
name: Primitives.string(net.name, `NET_${netIndex + 1}`),
|
|
301
|
+
member_source_group_ids: []
|
|
236
302
|
})
|
|
237
303
|
}
|
|
238
304
|
|
|
@@ -269,7 +335,7 @@ export class CircuitJsonModelAdapter {
|
|
|
269
335
|
*/
|
|
270
336
|
static #appendPcb(circuitJson, model, idScope) {
|
|
271
337
|
const pcb = model.pcb || {}
|
|
272
|
-
const
|
|
338
|
+
const componentRefs = new Map()
|
|
273
339
|
const sourceNetIds = new Map()
|
|
274
340
|
const boardId = Primitives.id(idScope, ['pcb_board'])
|
|
275
341
|
|
|
@@ -295,7 +361,8 @@ export class CircuitJsonModelAdapter {
|
|
|
295
361
|
name: Primitives.string(
|
|
296
362
|
net.name || net.netName,
|
|
297
363
|
`NET_${netIndex + 1}`
|
|
298
|
-
)
|
|
364
|
+
),
|
|
365
|
+
member_source_group_ids: []
|
|
299
366
|
})
|
|
300
367
|
}
|
|
301
368
|
|
|
@@ -310,9 +377,12 @@ export class CircuitJsonModelAdapter {
|
|
|
310
377
|
'pcb_component',
|
|
311
378
|
component.designator || component.name || componentIndex
|
|
312
379
|
])
|
|
313
|
-
|
|
380
|
+
componentRefs.set(
|
|
314
381
|
Primitives.componentKey(component, componentIndex),
|
|
315
|
-
|
|
382
|
+
{
|
|
383
|
+
pcbComponentId,
|
|
384
|
+
sourceComponentId
|
|
385
|
+
}
|
|
316
386
|
)
|
|
317
387
|
circuitJson.push(
|
|
318
388
|
CircuitJsonModelAdapter.#sourceComponent(
|
|
@@ -345,7 +415,7 @@ export class CircuitJsonModelAdapter {
|
|
|
345
415
|
idScope,
|
|
346
416
|
pad,
|
|
347
417
|
padIndex,
|
|
348
|
-
|
|
418
|
+
componentRefs,
|
|
349
419
|
sourceNetIds
|
|
350
420
|
)
|
|
351
421
|
}
|
|
@@ -367,8 +437,7 @@ export class CircuitJsonModelAdapter {
|
|
|
367
437
|
circuitJson,
|
|
368
438
|
idScope,
|
|
369
439
|
via,
|
|
370
|
-
viaIndex
|
|
371
|
-
sourceNetIds
|
|
440
|
+
viaIndex
|
|
372
441
|
)
|
|
373
442
|
}
|
|
374
443
|
}
|
|
@@ -415,7 +484,7 @@ export class CircuitJsonModelAdapter {
|
|
|
415
484
|
* @param {string} idScope
|
|
416
485
|
* @param {Record<string, unknown>} pad
|
|
417
486
|
* @param {number} padIndex
|
|
418
|
-
* @param {Map<string, string>}
|
|
487
|
+
* @param {Map<string, { pcbComponentId: string, sourceComponentId: string }>} componentRefs
|
|
419
488
|
* @param {Map<string, string>} sourceNetIds
|
|
420
489
|
* @returns {void}
|
|
421
490
|
*/
|
|
@@ -424,12 +493,14 @@ export class CircuitJsonModelAdapter {
|
|
|
424
493
|
idScope,
|
|
425
494
|
pad,
|
|
426
495
|
padIndex,
|
|
427
|
-
|
|
496
|
+
componentRefs,
|
|
428
497
|
sourceNetIds
|
|
429
498
|
) {
|
|
499
|
+
const componentRef =
|
|
500
|
+
componentRefs.get(String(pad.componentIndex)) ||
|
|
501
|
+
componentRefs.get('0')
|
|
430
502
|
const sourceComponentId =
|
|
431
|
-
|
|
432
|
-
componentIds.get('0') ||
|
|
503
|
+
componentRef?.sourceComponentId ||
|
|
433
504
|
Primitives.id(idScope, ['source_component', 'unassigned'])
|
|
434
505
|
const sourcePortId = Primitives.sourcePortId(
|
|
435
506
|
idScope,
|
|
@@ -438,69 +509,74 @@ export class CircuitJsonModelAdapter {
|
|
|
438
509
|
sourceComponentId
|
|
439
510
|
)
|
|
440
511
|
const pcbPortId = Primitives.id(idScope, ['pcb_port', sourcePortId])
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
String(padIndex + 1)
|
|
454
|
-
)
|
|
455
|
-
]
|
|
456
|
-
}
|
|
457
|
-
const sourceNetId = Primitives.netIdForPrimitive(
|
|
458
|
-
idScope,
|
|
459
|
-
pad,
|
|
460
|
-
sourceNetIds
|
|
461
|
-
)
|
|
512
|
+
const pcbComponentId =
|
|
513
|
+
componentRef?.pcbComponentId ||
|
|
514
|
+
Primitives.id(idScope, ['pcb_component', 'unassigned'])
|
|
515
|
+
const center = Primitives.milPoint(pad.x, pad.y)
|
|
516
|
+
const layer = Primitives.side(pad.layer)
|
|
517
|
+
const portHints = [
|
|
518
|
+
Primitives.string(
|
|
519
|
+
pad.name || pad.pinName || pad.designator,
|
|
520
|
+
String(padIndex + 1)
|
|
521
|
+
)
|
|
522
|
+
]
|
|
523
|
+
Primitives.netIdForPrimitive(idScope, pad, sourceNetIds)
|
|
462
524
|
|
|
463
525
|
circuitJson.push({
|
|
464
526
|
type: 'source_port',
|
|
465
527
|
source_port_id: sourcePortId,
|
|
466
528
|
source_component_id: sourceComponentId,
|
|
467
|
-
name:
|
|
468
|
-
|
|
529
|
+
name: portHints[0],
|
|
530
|
+
port_hints: portHints,
|
|
531
|
+
...CircuitJsonModelAdapter.#pinNumberField(portHints[0])
|
|
469
532
|
})
|
|
470
533
|
circuitJson.push({
|
|
471
534
|
type: 'pcb_port',
|
|
472
|
-
|
|
473
|
-
|
|
535
|
+
pcb_port_id: pcbPortId,
|
|
536
|
+
source_port_id: sourcePortId,
|
|
537
|
+
pcb_component_id: pcbComponentId,
|
|
538
|
+
x: center.x,
|
|
539
|
+
y: center.y,
|
|
540
|
+
layers: Primitives.isThroughHolePad(pad)
|
|
541
|
+
? ['top', 'bottom']
|
|
542
|
+
: [layer]
|
|
474
543
|
})
|
|
475
544
|
|
|
476
545
|
if (Primitives.isThroughHolePad(pad)) {
|
|
477
|
-
circuitJson.push(
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
546
|
+
circuitJson.push(
|
|
547
|
+
pad.isPlated === false
|
|
548
|
+
? PcbElements.hole(
|
|
549
|
+
idScope,
|
|
550
|
+
pad,
|
|
551
|
+
padIndex,
|
|
552
|
+
pcbComponentId,
|
|
553
|
+
center
|
|
554
|
+
)
|
|
555
|
+
: PcbElements.platedHole(
|
|
556
|
+
idScope,
|
|
557
|
+
pad,
|
|
558
|
+
padIndex,
|
|
559
|
+
pcbComponentId,
|
|
560
|
+
pcbPortId,
|
|
561
|
+
center,
|
|
562
|
+
portHints
|
|
563
|
+
)
|
|
564
|
+
)
|
|
487
565
|
return
|
|
488
566
|
}
|
|
489
567
|
|
|
490
|
-
circuitJson.push(
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
rotation: Primitives.number(pad.rotation || pad.holeRotation, 0)
|
|
503
|
-
})
|
|
568
|
+
circuitJson.push(
|
|
569
|
+
PcbElements.smtPad(
|
|
570
|
+
idScope,
|
|
571
|
+
pad,
|
|
572
|
+
padIndex,
|
|
573
|
+
pcbComponentId,
|
|
574
|
+
pcbPortId,
|
|
575
|
+
center,
|
|
576
|
+
layer,
|
|
577
|
+
portHints
|
|
578
|
+
)
|
|
579
|
+
)
|
|
504
580
|
}
|
|
505
581
|
|
|
506
582
|
/**
|
|
@@ -523,14 +599,16 @@ export class CircuitJsonModelAdapter {
|
|
|
523
599
|
'source_trace',
|
|
524
600
|
track.netName || track.netIndex || trackIndex
|
|
525
601
|
])
|
|
602
|
+
const sourceNetId = Primitives.netIdForPrimitive(
|
|
603
|
+
idScope,
|
|
604
|
+
track,
|
|
605
|
+
sourceNetIds
|
|
606
|
+
)
|
|
526
607
|
circuitJson.push({
|
|
527
608
|
type: 'source_trace',
|
|
528
609
|
source_trace_id: sourceTraceId,
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
track,
|
|
532
|
-
sourceNetIds
|
|
533
|
-
)
|
|
610
|
+
connected_source_port_ids: [],
|
|
611
|
+
connected_source_net_ids: sourceNetId ? [sourceNetId] : []
|
|
534
612
|
})
|
|
535
613
|
circuitJson.push({
|
|
536
614
|
type: 'pcb_trace',
|
|
@@ -561,18 +639,12 @@ export class CircuitJsonModelAdapter {
|
|
|
561
639
|
* @param {string} idScope
|
|
562
640
|
* @param {Record<string, unknown>} via
|
|
563
641
|
* @param {number} viaIndex
|
|
564
|
-
* @param {Map<string, string>} sourceNetIds
|
|
565
642
|
* @returns {void}
|
|
566
643
|
*/
|
|
567
|
-
static #appendPcbVia(circuitJson, idScope, via, viaIndex
|
|
644
|
+
static #appendPcbVia(circuitJson, idScope, via, viaIndex) {
|
|
568
645
|
circuitJson.push({
|
|
569
646
|
type: 'pcb_via',
|
|
570
647
|
pcb_via_id: Primitives.id(idScope, ['pcb_via', viaIndex]),
|
|
571
|
-
source_net_id: Primitives.netIdForPrimitive(
|
|
572
|
-
idScope,
|
|
573
|
-
via,
|
|
574
|
-
sourceNetIds
|
|
575
|
-
),
|
|
576
648
|
x: Primitives.milNumber(via.x, 0),
|
|
577
649
|
y: Primitives.milNumber(via.y, 0),
|
|
578
650
|
outer_diameter: Primitives.milNumber(via.diameter, 0),
|
|
@@ -642,6 +714,107 @@ export class CircuitJsonModelAdapter {
|
|
|
642
714
|
}
|
|
643
715
|
}
|
|
644
716
|
|
|
717
|
+
/**
|
|
718
|
+
* Appends serialized Altium Toolkit sidecar elements.
|
|
719
|
+
* @param {object[]} circuitJson
|
|
720
|
+
* @param {Record<string, unknown>} model
|
|
721
|
+
* @param {string} idScope
|
|
722
|
+
* @returns {void}
|
|
723
|
+
*/
|
|
724
|
+
static #appendSidecars(circuitJson, model, idScope) {
|
|
725
|
+
for (const descriptor of ALTIUM_TOOLKIT_SIDECARS) {
|
|
726
|
+
const payload = CircuitJsonModelAdapter.#sidecarPayload(
|
|
727
|
+
model,
|
|
728
|
+
descriptor.paths
|
|
729
|
+
)
|
|
730
|
+
if (!payload) continue
|
|
731
|
+
|
|
732
|
+
circuitJson.push(
|
|
733
|
+
CircuitJsonModelAdapter.#sidecarElement(
|
|
734
|
+
descriptor,
|
|
735
|
+
payload,
|
|
736
|
+
model,
|
|
737
|
+
idScope
|
|
738
|
+
)
|
|
739
|
+
)
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* Builds one custom Altium Toolkit Circuit JSON sidecar element.
|
|
745
|
+
* @param {{ type: string, schema: string }} descriptor
|
|
746
|
+
* @param {Record<string, unknown>} payload
|
|
747
|
+
* @param {Record<string, unknown>} model
|
|
748
|
+
* @param {string} idScope
|
|
749
|
+
* @returns {object}
|
|
750
|
+
*/
|
|
751
|
+
static #sidecarElement(descriptor, payload, model, idScope) {
|
|
752
|
+
return {
|
|
753
|
+
type: descriptor.type,
|
|
754
|
+
altium_toolkit_sidecar_id: Primitives.id(idScope, [
|
|
755
|
+
'altium_toolkit_sidecar',
|
|
756
|
+
descriptor.type
|
|
757
|
+
]),
|
|
758
|
+
source_document: CircuitJsonModelAdapter.#sourceDocument(model),
|
|
759
|
+
schema: Primitives.string(payload.schema, descriptor.schema),
|
|
760
|
+
payload
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* Returns the first available sidecar payload from candidate model paths.
|
|
766
|
+
* @param {Record<string, unknown>} model
|
|
767
|
+
* @param {string[][]} paths
|
|
768
|
+
* @returns {Record<string, unknown> | null}
|
|
769
|
+
*/
|
|
770
|
+
static #sidecarPayload(model, paths) {
|
|
771
|
+
for (const path of paths) {
|
|
772
|
+
const payload = CircuitJsonModelAdapter.#valueAtPath(model, path)
|
|
773
|
+
if (CircuitJsonModelAdapter.#isSidecarPayload(payload)) {
|
|
774
|
+
return payload
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
return null
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* Returns a nested object value.
|
|
782
|
+
* @param {Record<string, unknown>} value
|
|
783
|
+
* @param {string[]} path
|
|
784
|
+
* @returns {unknown}
|
|
785
|
+
*/
|
|
786
|
+
static #valueAtPath(value, path) {
|
|
787
|
+
return path.reduce(
|
|
788
|
+
(current, key) =>
|
|
789
|
+
current && typeof current === 'object'
|
|
790
|
+
? current[key]
|
|
791
|
+
: undefined,
|
|
792
|
+
value
|
|
793
|
+
)
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* Returns true when a value can be serialized as a sidecar payload.
|
|
798
|
+
* @param {unknown} value
|
|
799
|
+
* @returns {value is Record<string, unknown>}
|
|
800
|
+
*/
|
|
801
|
+
static #isSidecarPayload(value) {
|
|
802
|
+
return !!value && typeof value === 'object' && !Array.isArray(value)
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/**
|
|
806
|
+
* Builds the source document identity for sidecar elements.
|
|
807
|
+
* @param {Record<string, unknown>} model
|
|
808
|
+
* @returns {{ kind: string, file_type: string, file_name: string }}
|
|
809
|
+
*/
|
|
810
|
+
static #sourceDocument(model) {
|
|
811
|
+
return {
|
|
812
|
+
kind: Primitives.string(model.kind, 'document'),
|
|
813
|
+
file_type: Primitives.string(model.fileType, ''),
|
|
814
|
+
file_name: Primitives.string(model.fileName, '')
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
|
|
645
818
|
/**
|
|
646
819
|
* Appends one schematic line or trace.
|
|
647
820
|
* @param {object[]} circuitJson
|
|
@@ -664,33 +837,47 @@ export class CircuitJsonModelAdapter {
|
|
|
664
837
|
x2: Primitives.number(line.x2, 0),
|
|
665
838
|
y2: Primitives.number(line.y2, 0),
|
|
666
839
|
stroke_width: Primitives.number(line.width, 1),
|
|
667
|
-
is_dashed: line.dashed === true
|
|
840
|
+
is_dashed: line.dashed === true,
|
|
841
|
+
color: '#000000'
|
|
668
842
|
}
|
|
669
843
|
circuitJson.push(lineElement)
|
|
670
844
|
|
|
671
845
|
if (line.kind === 'wire' || line.netName || line.netIndex) {
|
|
846
|
+
const sourceNetId =
|
|
847
|
+
netIds.get(String(line.netName)) ||
|
|
848
|
+
Primitives.sourceNetId(
|
|
849
|
+
idScope,
|
|
850
|
+
line.netName || line.netIndex || lineIndex
|
|
851
|
+
)
|
|
852
|
+
const sourceTraceId = Primitives.id(idScope, [
|
|
853
|
+
'source_trace',
|
|
854
|
+
line.netName || line.netIndex || lineIndex,
|
|
855
|
+
lineIndex
|
|
856
|
+
])
|
|
857
|
+
circuitJson.push({
|
|
858
|
+
type: 'source_trace',
|
|
859
|
+
source_trace_id: sourceTraceId,
|
|
860
|
+
connected_source_port_ids: [],
|
|
861
|
+
connected_source_net_ids: sourceNetId ? [sourceNetId] : []
|
|
862
|
+
})
|
|
672
863
|
circuitJson.push({
|
|
673
864
|
type: 'schematic_trace',
|
|
674
865
|
schematic_trace_id: Primitives.id(idScope, [
|
|
675
866
|
'schematic_trace',
|
|
676
867
|
lineIndex
|
|
677
868
|
]),
|
|
678
|
-
source_trace_id:
|
|
679
|
-
|
|
680
|
-
line.netName || line.netIndex || lineIndex
|
|
681
|
-
]),
|
|
682
|
-
source_net_id:
|
|
683
|
-
netIds.get(String(line.netName)) ||
|
|
684
|
-
Primitives.sourceNetId(
|
|
685
|
-
idScope,
|
|
686
|
-
line.netName || line.netIndex || lineIndex
|
|
687
|
-
),
|
|
869
|
+
source_trace_id: sourceTraceId,
|
|
870
|
+
junctions: [],
|
|
688
871
|
edges: [
|
|
689
872
|
{
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
873
|
+
from: {
|
|
874
|
+
x: lineElement.x1,
|
|
875
|
+
y: lineElement.y1
|
|
876
|
+
},
|
|
877
|
+
to: {
|
|
878
|
+
x: lineElement.x2,
|
|
879
|
+
y: lineElement.y2
|
|
880
|
+
}
|
|
694
881
|
}
|
|
695
882
|
]
|
|
696
883
|
})
|
|
@@ -712,11 +899,11 @@ export class CircuitJsonModelAdapter {
|
|
|
712
899
|
)
|
|
713
900
|
const base = {
|
|
714
901
|
text: textValue,
|
|
715
|
-
|
|
716
|
-
anchor_alignment: 'center'
|
|
902
|
+
position: Primitives.point(text.x, text.y)
|
|
717
903
|
}
|
|
718
904
|
|
|
719
905
|
if (Primitives.isNetLabel(text)) {
|
|
906
|
+
const center = Primitives.point(text.x, text.y)
|
|
720
907
|
circuitJson.push({
|
|
721
908
|
type: 'schematic_net_label',
|
|
722
909
|
schematic_net_label_id: Primitives.id(idScope, [
|
|
@@ -727,7 +914,10 @@ export class CircuitJsonModelAdapter {
|
|
|
727
914
|
idScope,
|
|
728
915
|
textValue || textIndex
|
|
729
916
|
),
|
|
730
|
-
|
|
917
|
+
text: textValue,
|
|
918
|
+
center,
|
|
919
|
+
anchor_position: center,
|
|
920
|
+
anchor_side: 'top'
|
|
731
921
|
})
|
|
732
922
|
return
|
|
733
923
|
}
|
|
@@ -738,10 +928,24 @@ export class CircuitJsonModelAdapter {
|
|
|
738
928
|
'schematic_text',
|
|
739
929
|
textIndex
|
|
740
930
|
]),
|
|
741
|
-
...base
|
|
931
|
+
...base,
|
|
932
|
+
font_size: Primitives.number(text.fontSize || text.size, 0.18),
|
|
933
|
+
rotation: Primitives.number(text.rotation, 0),
|
|
934
|
+
anchor: 'center',
|
|
935
|
+
color: '#000000'
|
|
742
936
|
})
|
|
743
937
|
}
|
|
744
938
|
|
|
939
|
+
/**
|
|
940
|
+
* Returns an optional numeric pin number field.
|
|
941
|
+
* @param {unknown} value
|
|
942
|
+
* @returns {{ pin_number?: number }}
|
|
943
|
+
*/
|
|
944
|
+
static #pinNumberField(value) {
|
|
945
|
+
const pinNumber = Primitives.number(value, null)
|
|
946
|
+
return pinNumber === null ? {} : { pin_number: pinNumber }
|
|
947
|
+
}
|
|
948
|
+
|
|
745
949
|
/**
|
|
746
950
|
* Returns a source component id for one schematic pin.
|
|
747
951
|
* @param {Record<string, unknown>} pin
|