altium-toolkit 1.1.3 → 1.1.22
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 +37 -0
- package/docs/model-format.md +18 -0
- package/docs/schemas/altium_toolkit/normalized_model_a1.schema.json +2 -2
- package/docs/testing.md +5 -0
- package/package.json +1 -1
- package/spec/library-scope.md +5 -0
- package/src/core/altium/AltiumLibraryBatchExporter.mjs +206 -0
- package/src/core/altium/AltiumLibraryRecordBuilder.mjs +293 -0
- package/src/core/altium/AltiumParser.mjs +5 -2
- package/src/core/altium/AltiumPcbLibExporter.mjs +101 -0
- package/src/core/altium/AltiumSchLibExporter.mjs +57 -0
- package/src/core/altium/AsciiRecordParser.mjs +43 -11
- package/src/core/altium/PcbComponentKindPolicy.mjs +9 -9
- package/src/core/altium/PcbEmbeddedModelExtractor.mjs +22 -3
- package/src/core/altium/PcbOutlineRecovery.mjs +94 -0
- package/src/core/altium/SchematicDirectiveParser.mjs +5 -17
- package/src/core/altium/SchematicNoErcSymbolResolver.mjs +36 -0
- package/src/core/altium/SchematicPinParser.mjs +87 -20
- package/src/core/altium/SchematicPrimitiveParser.mjs +116 -8
- package/src/core/altium/SchematicStreamExtractor.mjs +62 -15
- package/src/core/altium/SourceBundleExporter.mjs +156 -0
- package/src/core/altium/SourceComponentBundleNormalizer.mjs +295 -0
- package/src/core/altium/SourceComponentClient.mjs +239 -0
- package/src/core/ole/OleCompoundDocumentWriter.mjs +449 -0
- package/src/parser.mjs +8 -0
- package/src/styles/altium-renderers.css +6 -6
- package/src/ui/PcbArcUtils.mjs +19 -2
- package/src/ui/PcbScene3dBuilder.mjs +202 -20
- package/src/ui/PcbScene3dModelRegistry.mjs +28 -18
- package/src/ui/PcbScene3dPlacementSideResolver.mjs +48 -6
- package/src/ui/SchematicColorResolver.mjs +185 -0
- package/src/ui/SchematicDirectiveRenderer.mjs +133 -22
- package/src/ui/SchematicLineColorResolver.mjs +88 -0
- package/src/ui/SchematicNoteRenderer.mjs +5 -1
- package/src/ui/SchematicOwnerPinLabelLayout.mjs +269 -8
- package/src/ui/SchematicOwnerPinMarkerLineThemer.mjs +155 -0
- package/src/ui/SchematicPinSvgRenderer.mjs +229 -62
- package/src/ui/SchematicShapeRenderer.mjs +37 -11
- package/src/ui/SchematicSvgRenderer.mjs +944 -51
|
@@ -24,6 +24,7 @@ export class SchematicPinSvgRenderer {
|
|
|
24
24
|
* @param {Map<string, number>} explicitOwnerPinLabelOffsets
|
|
25
25
|
* @param {Map<string, 'left' | 'right'>} compactExternalNumberLabelSides
|
|
26
26
|
* @param {Map<string, { left: number, right: number }>} internalNumberLabelBoxes
|
|
27
|
+
* @param {Set<string>} overlappingExternalNumberLabelKeys
|
|
27
28
|
* @returns {string}
|
|
28
29
|
*/
|
|
29
30
|
static buildMarkup(
|
|
@@ -34,7 +35,8 @@ export class SchematicPinSvgRenderer {
|
|
|
34
35
|
explicitOwnerPinNameLabels,
|
|
35
36
|
explicitOwnerPinLabelOffsets,
|
|
36
37
|
compactExternalNumberLabelSides = new Map(),
|
|
37
|
-
internalNumberLabelBoxes = new Map()
|
|
38
|
+
internalNumberLabelBoxes = new Map(),
|
|
39
|
+
overlappingExternalNumberLabelKeys = new Set()
|
|
38
40
|
) {
|
|
39
41
|
const geometry =
|
|
40
42
|
SchematicPinSvgRenderer.#projectSchematicPinGeometry(pin)
|
|
@@ -53,6 +55,11 @@ export class SchematicPinSvgRenderer {
|
|
|
53
55
|
const labelMode = pin.labelMode || 'name-and-number'
|
|
54
56
|
const outerMarkerStyle =
|
|
55
57
|
SchematicPinSvgRenderer.#resolveSchematicOuterPinMarkerStyle(pin)
|
|
58
|
+
const markerStyle =
|
|
59
|
+
outerMarkerStyle ||
|
|
60
|
+
SchematicPinSvgRenderer.#resolveSchematicElectricalPinMarkerStyle(
|
|
61
|
+
pin
|
|
62
|
+
)
|
|
56
63
|
const rotateTopNumber =
|
|
57
64
|
pin.orientation === 'top' &&
|
|
58
65
|
rotatedVerticalNumberOwners.has(String(pin.ownerIndex || ''))
|
|
@@ -69,6 +76,13 @@ export class SchematicPinSvgRenderer {
|
|
|
69
76
|
null
|
|
70
77
|
const internalNumberLabelBox =
|
|
71
78
|
internalNumberLabelBoxes.get(String(pin.ownerIndex || '')) || null
|
|
79
|
+
const shouldRenderExternalNumber =
|
|
80
|
+
!overlappingExternalNumberLabelKeys.has(
|
|
81
|
+
SchematicOwnerPinLabelLayout.buildOwnerPinLabelKey(
|
|
82
|
+
pin.ownerIndex,
|
|
83
|
+
pin.designator
|
|
84
|
+
)
|
|
85
|
+
)
|
|
72
86
|
|
|
73
87
|
if (pin.orientation === 'left') {
|
|
74
88
|
if (labelMode !== 'hidden' && labelMode !== 'name-only') {
|
|
@@ -82,7 +96,7 @@ export class SchematicPinSvgRenderer {
|
|
|
82
96
|
SchematicPinSvgRenderer.#buildPinNameTextMarkup(
|
|
83
97
|
'schematic-pin-name',
|
|
84
98
|
internalNumberPlacement.x,
|
|
85
|
-
projectedY
|
|
99
|
+
projectedY + 3,
|
|
86
100
|
pin,
|
|
87
101
|
labelColor,
|
|
88
102
|
internalNumberPlacement.anchor,
|
|
@@ -91,16 +105,14 @@ export class SchematicPinSvgRenderer {
|
|
|
91
105
|
)
|
|
92
106
|
}
|
|
93
107
|
|
|
94
|
-
const defaultNumberX =
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
pin
|
|
103
|
-
)
|
|
108
|
+
const defaultNumberX =
|
|
109
|
+
SchematicPinSvgRenderer.#resolveExternalHorizontalNumberX(
|
|
110
|
+
pin,
|
|
111
|
+
geometry,
|
|
112
|
+
markerStyle,
|
|
113
|
+
compactExternalNumberLabelSide,
|
|
114
|
+
internalNumberLabelBox
|
|
115
|
+
)
|
|
104
116
|
const numberX = hasExplicitOwnerPinName
|
|
105
117
|
? SchematicOwnerPinLabelLayout.resolveExplicitOwnerPinNumberX(
|
|
106
118
|
pin,
|
|
@@ -108,25 +120,31 @@ export class SchematicPinSvgRenderer {
|
|
|
108
120
|
explicitOwnerPinLabelOffsets
|
|
109
121
|
)
|
|
110
122
|
: defaultNumberX
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
123
|
+
if (shouldRenderExternalNumber) {
|
|
124
|
+
texts.push(
|
|
125
|
+
createSvgText(
|
|
126
|
+
'schematic-pin-number',
|
|
127
|
+
numberX,
|
|
128
|
+
projectedY - 1,
|
|
129
|
+
pin.designator,
|
|
130
|
+
labelColor,
|
|
131
|
+
SchematicPinSvgRenderer.#resolveExternalHorizontalNumberAnchor(
|
|
132
|
+
pin,
|
|
133
|
+
markerStyle
|
|
134
|
+
),
|
|
135
|
+
textOptions
|
|
136
|
+
)
|
|
120
137
|
)
|
|
121
|
-
|
|
138
|
+
}
|
|
122
139
|
}
|
|
123
140
|
|
|
124
141
|
if (
|
|
125
142
|
labelMode !== 'hidden' &&
|
|
126
143
|
labelMode !== 'number-only' &&
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
144
|
+
SchematicPinSvgRenderer.#shouldRenderHorizontalPinName(
|
|
145
|
+
pin,
|
|
146
|
+
hasExplicitOwnerPinName
|
|
147
|
+
)
|
|
130
148
|
) {
|
|
131
149
|
texts.push(
|
|
132
150
|
SchematicPinSvgRenderer.#buildPinNameTextMarkup(
|
|
@@ -158,7 +176,7 @@ export class SchematicPinSvgRenderer {
|
|
|
158
176
|
SchematicPinSvgRenderer.#buildPinNameTextMarkup(
|
|
159
177
|
'schematic-pin-name',
|
|
160
178
|
internalNumberPlacement.x,
|
|
161
|
-
projectedY
|
|
179
|
+
projectedY + 3,
|
|
162
180
|
pin,
|
|
163
181
|
labelColor,
|
|
164
182
|
internalNumberPlacement.anchor,
|
|
@@ -167,16 +185,14 @@ export class SchematicPinSvgRenderer {
|
|
|
167
185
|
)
|
|
168
186
|
}
|
|
169
187
|
|
|
170
|
-
const defaultNumberX =
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
pin
|
|
179
|
-
)
|
|
188
|
+
const defaultNumberX =
|
|
189
|
+
SchematicPinSvgRenderer.#resolveExternalHorizontalNumberX(
|
|
190
|
+
pin,
|
|
191
|
+
geometry,
|
|
192
|
+
markerStyle,
|
|
193
|
+
compactExternalNumberLabelSide,
|
|
194
|
+
internalNumberLabelBox
|
|
195
|
+
)
|
|
180
196
|
const numberX = hasExplicitOwnerPinName
|
|
181
197
|
? SchematicOwnerPinLabelLayout.resolveExplicitOwnerPinNumberX(
|
|
182
198
|
pin,
|
|
@@ -184,25 +200,31 @@ export class SchematicPinSvgRenderer {
|
|
|
184
200
|
explicitOwnerPinLabelOffsets
|
|
185
201
|
)
|
|
186
202
|
: defaultNumberX
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
203
|
+
if (shouldRenderExternalNumber) {
|
|
204
|
+
texts.push(
|
|
205
|
+
createSvgText(
|
|
206
|
+
'schematic-pin-number',
|
|
207
|
+
numberX,
|
|
208
|
+
projectedY - 1,
|
|
209
|
+
pin.designator,
|
|
210
|
+
labelColor,
|
|
211
|
+
SchematicPinSvgRenderer.#resolveExternalHorizontalNumberAnchor(
|
|
212
|
+
pin,
|
|
213
|
+
markerStyle
|
|
214
|
+
),
|
|
215
|
+
textOptions
|
|
216
|
+
)
|
|
196
217
|
)
|
|
197
|
-
|
|
218
|
+
}
|
|
198
219
|
}
|
|
199
220
|
|
|
200
221
|
if (
|
|
201
222
|
labelMode !== 'hidden' &&
|
|
202
223
|
labelMode !== 'number-only' &&
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
224
|
+
SchematicPinSvgRenderer.#shouldRenderHorizontalPinName(
|
|
225
|
+
pin,
|
|
226
|
+
hasExplicitOwnerPinName
|
|
227
|
+
)
|
|
206
228
|
) {
|
|
207
229
|
texts.push(
|
|
208
230
|
SchematicPinSvgRenderer.#buildPinNameTextMarkup(
|
|
@@ -290,12 +312,7 @@ export class SchematicPinSvgRenderer {
|
|
|
290
312
|
'" y2="' +
|
|
291
313
|
formatNumber(projectedOuterY) +
|
|
292
314
|
'" stroke="' +
|
|
293
|
-
escapeHtml(
|
|
294
|
-
SchematicColorResolver.resolveColor(
|
|
295
|
-
pin.color,
|
|
296
|
-
'--schematic-accent-ink-color'
|
|
297
|
-
)
|
|
298
|
-
) +
|
|
315
|
+
escapeHtml(SchematicPinSvgRenderer.#resolvePinStrokeColor(pin)) +
|
|
299
316
|
'" />' +
|
|
300
317
|
markerMarkup +
|
|
301
318
|
texts.join('') +
|
|
@@ -322,6 +339,19 @@ export class SchematicPinSvgRenderer {
|
|
|
322
339
|
)
|
|
323
340
|
}
|
|
324
341
|
|
|
342
|
+
const electricalMarkerStyle =
|
|
343
|
+
SchematicPinSvgRenderer.#resolveSchematicElectricalPinMarkerStyle(
|
|
344
|
+
pin
|
|
345
|
+
)
|
|
346
|
+
if (electricalMarkerStyle) {
|
|
347
|
+
return SchematicPinSvgRenderer.#buildOuterPinMarkerMarkup(
|
|
348
|
+
pin,
|
|
349
|
+
geometry,
|
|
350
|
+
sheetHeight,
|
|
351
|
+
electricalMarkerStyle
|
|
352
|
+
)
|
|
353
|
+
}
|
|
354
|
+
|
|
325
355
|
if (Number(pin.electrical || 0) !== 1) {
|
|
326
356
|
return ''
|
|
327
357
|
}
|
|
@@ -444,23 +474,50 @@ export class SchematicPinSvgRenderer {
|
|
|
444
474
|
/**
|
|
445
475
|
* Returns the horizontal pin-number clearance needed by the pin geometry.
|
|
446
476
|
* @param {'single-in' | 'single-out' | 'double' | 'cross' | null} markerStyle
|
|
447
|
-
* @param {{ length?: number }} pin
|
|
477
|
+
* @param {{ length?: number, electrical?: number }} pin
|
|
448
478
|
* @returns {number}
|
|
449
479
|
*/
|
|
450
480
|
static #resolveHorizontalPinNumberClearance(markerStyle, pin) {
|
|
451
481
|
switch (markerStyle) {
|
|
452
482
|
case 'double':
|
|
453
|
-
return
|
|
483
|
+
return 21
|
|
454
484
|
case 'cross':
|
|
455
|
-
return
|
|
485
|
+
return 9
|
|
456
486
|
case 'single-in':
|
|
457
487
|
case 'single-out':
|
|
458
488
|
return 8
|
|
459
489
|
default:
|
|
490
|
+
if (Number(pin?.electrical || 0) === 1) {
|
|
491
|
+
return 16
|
|
492
|
+
}
|
|
493
|
+
|
|
460
494
|
return SchematicPinSvgRenderer.#resolveLongPinInset(pin, 2)
|
|
461
495
|
}
|
|
462
496
|
}
|
|
463
497
|
|
|
498
|
+
/**
|
|
499
|
+
* Returns whether a horizontal pin name should be rendered inside the body.
|
|
500
|
+
* Numeric name/designator matches are duplicate pin numbers; nonnumeric
|
|
501
|
+
* matches such as PAD are valid body labels.
|
|
502
|
+
* @param {{ name?: string, designator?: string }} pin Pin primitive.
|
|
503
|
+
* @param {boolean} hasExplicitOwnerPinName Whether an explicit owner text already draws the label.
|
|
504
|
+
* @returns {boolean}
|
|
505
|
+
*/
|
|
506
|
+
static #shouldRenderHorizontalPinName(pin, hasExplicitOwnerPinName) {
|
|
507
|
+
const name = String(pin?.name || '').trim()
|
|
508
|
+
const designator = String(pin?.designator || '').trim()
|
|
509
|
+
|
|
510
|
+
if (!name || hasExplicitOwnerPinName) {
|
|
511
|
+
return false
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (name !== designator) {
|
|
515
|
+
return true
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
return !/^\d+$/u.test(name)
|
|
519
|
+
}
|
|
520
|
+
|
|
464
521
|
/**
|
|
465
522
|
* Returns the horizontal pin-name inset used inside the symbol body.
|
|
466
523
|
* @param {{ length?: number }} pin
|
|
@@ -472,7 +529,7 @@ export class SchematicPinSvgRenderer {
|
|
|
472
529
|
return 10
|
|
473
530
|
}
|
|
474
531
|
|
|
475
|
-
return SchematicPinSvgRenderer.#resolveLongPinInset(pin,
|
|
532
|
+
return SchematicPinSvgRenderer.#resolveLongPinInset(pin, 7)
|
|
476
533
|
}
|
|
477
534
|
|
|
478
535
|
/**
|
|
@@ -491,6 +548,70 @@ export class SchematicPinSvgRenderer {
|
|
|
491
548
|
return fallback === 2 ? 10 : 8
|
|
492
549
|
}
|
|
493
550
|
|
|
551
|
+
/**
|
|
552
|
+
* Places synthetic horizontal pin numbers outside the symbol body. Owner
|
|
553
|
+
* bodies with internal numeric labels need a stub-side offset so the
|
|
554
|
+
* outside contact number does not collapse onto the inside duplicate.
|
|
555
|
+
* @param {{ length?: number, orientation: 'left' | 'right' | 'top' | 'bottom', electrical?: number }} pin
|
|
556
|
+
* @param {{ bodyX: number }} geometry
|
|
557
|
+
* @param {'single-in' | 'single-out' | 'double' | 'cross' | null} markerStyle
|
|
558
|
+
* @param {'left' | 'right' | null} compactExternalNumberLabelSide
|
|
559
|
+
* @param {{ left: number, right: number } | null} internalNumberLabelBox
|
|
560
|
+
* @returns {number}
|
|
561
|
+
*/
|
|
562
|
+
static #resolveExternalHorizontalNumberX(
|
|
563
|
+
pin,
|
|
564
|
+
geometry,
|
|
565
|
+
markerStyle,
|
|
566
|
+
compactExternalNumberLabelSide,
|
|
567
|
+
internalNumberLabelBox
|
|
568
|
+
) {
|
|
569
|
+
const clearance =
|
|
570
|
+
SchematicPinSvgRenderer.#resolveHorizontalPinNumberClearance(
|
|
571
|
+
markerStyle,
|
|
572
|
+
pin
|
|
573
|
+
)
|
|
574
|
+
const offset =
|
|
575
|
+
compactExternalNumberLabelSide || internalNumberLabelBox
|
|
576
|
+
? Math.max(
|
|
577
|
+
clearance,
|
|
578
|
+
SchematicPinSvgRenderer.#resolveCompactExternalHorizontalNumberOffset(
|
|
579
|
+
pin
|
|
580
|
+
)
|
|
581
|
+
)
|
|
582
|
+
: clearance
|
|
583
|
+
|
|
584
|
+
if (pin.orientation === 'left') {
|
|
585
|
+
return geometry.bodyX - offset
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
if (pin.orientation === 'right') {
|
|
589
|
+
return geometry.bodyX + offset
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
return geometry.bodyX
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Resolves the text edge used for external horizontal pin numbers.
|
|
597
|
+
* Double/electrical marker labels are authored from the route side back
|
|
598
|
+
* toward the marker, while ordinary pins extend away from the body.
|
|
599
|
+
* @param {{ orientation: 'left' | 'right' | 'top' | 'bottom', electrical?: number }} pin Pin primitive.
|
|
600
|
+
* @param {'single-in' | 'single-out' | 'double' | 'cross' | null} markerStyle Marker style.
|
|
601
|
+
* @returns {'start' | 'end'}
|
|
602
|
+
*/
|
|
603
|
+
static #resolveExternalHorizontalNumberAnchor(pin, markerStyle) {
|
|
604
|
+
const routeFacing =
|
|
605
|
+
markerStyle === 'double' ||
|
|
606
|
+
(!markerStyle && Number(pin?.electrical || 0) === 1)
|
|
607
|
+
|
|
608
|
+
if (routeFacing) {
|
|
609
|
+
return pin.orientation === 'left' ? 'start' : 'end'
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
return pin.orientation === 'left' ? 'end' : 'start'
|
|
613
|
+
}
|
|
614
|
+
|
|
494
615
|
/**
|
|
495
616
|
* Places numeric-only labels just inside a rectangular owner body.
|
|
496
617
|
* @param {{ name?: string, orientation: 'left' | 'right' | 'top' | 'bottom' }} pin
|
|
@@ -517,7 +638,7 @@ export class SchematicPinSvgRenderer {
|
|
|
517
638
|
return null
|
|
518
639
|
}
|
|
519
640
|
|
|
520
|
-
const inset = Math.min(
|
|
641
|
+
const inset = Math.min(10, Math.max(6, (right - left) / 6))
|
|
521
642
|
|
|
522
643
|
return {
|
|
523
644
|
x: pin.orientation === 'left' ? left + inset : right - inset,
|
|
@@ -532,8 +653,10 @@ export class SchematicPinSvgRenderer {
|
|
|
532
653
|
* @returns {number}
|
|
533
654
|
*/
|
|
534
655
|
static #resolveCompactExternalHorizontalNumberX(pin, geometry) {
|
|
535
|
-
const
|
|
536
|
-
|
|
656
|
+
const offset =
|
|
657
|
+
SchematicPinSvgRenderer.#resolveCompactExternalHorizontalNumberOffset(
|
|
658
|
+
pin
|
|
659
|
+
)
|
|
537
660
|
|
|
538
661
|
if (pin.orientation === 'left') {
|
|
539
662
|
return geometry.bodyX - offset
|
|
@@ -546,6 +669,17 @@ export class SchematicPinSvgRenderer {
|
|
|
546
669
|
return geometry.bodyX
|
|
547
670
|
}
|
|
548
671
|
|
|
672
|
+
/**
|
|
673
|
+
* Resolves a stub-side offset for compact external horizontal numbers.
|
|
674
|
+
* @param {{ length?: number }} pin
|
|
675
|
+
* @returns {number}
|
|
676
|
+
*/
|
|
677
|
+
static #resolveCompactExternalHorizontalNumberOffset(pin) {
|
|
678
|
+
const length = Math.abs(Number(pin.length || 0))
|
|
679
|
+
|
|
680
|
+
return Math.max(8, Math.min(length - 6, 12))
|
|
681
|
+
}
|
|
682
|
+
|
|
549
683
|
/**
|
|
550
684
|
* Places compact owner-drawn vertical pin numbers on the same side as the
|
|
551
685
|
* horizontal terminal contacts.
|
|
@@ -672,6 +806,39 @@ export class SchematicPinSvgRenderer {
|
|
|
672
806
|
}
|
|
673
807
|
}
|
|
674
808
|
|
|
809
|
+
/**
|
|
810
|
+
* Resolves marker styles derived from the pin electrical type. The
|
|
811
|
+
* bidirectional type keeps its legacy two-triangle geometry path.
|
|
812
|
+
* @param {{ electrical?: number, orientation: 'left' | 'right' | 'top' | 'bottom' }} pin
|
|
813
|
+
* @returns {'single-in' | 'single-out' | null}
|
|
814
|
+
*/
|
|
815
|
+
static #resolveSchematicElectricalPinMarkerStyle(pin) {
|
|
816
|
+
if (pin.orientation !== 'left' && pin.orientation !== 'right') {
|
|
817
|
+
return null
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
switch (Number(pin.electrical)) {
|
|
821
|
+
case 0:
|
|
822
|
+
return 'single-in'
|
|
823
|
+
case 2:
|
|
824
|
+
return 'single-out'
|
|
825
|
+
default:
|
|
826
|
+
return null
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
/**
|
|
831
|
+
* Resolves the visible stroke for a schematic pin stub.
|
|
832
|
+
* @param {{ color?: string }} pin
|
|
833
|
+
* @returns {string}
|
|
834
|
+
*/
|
|
835
|
+
static #resolvePinStrokeColor(pin) {
|
|
836
|
+
return SchematicColorResolver.resolveNonTextColor(
|
|
837
|
+
pin?.color,
|
|
838
|
+
'--schematic-accent-ink-color'
|
|
839
|
+
)
|
|
840
|
+
}
|
|
841
|
+
|
|
675
842
|
/**
|
|
676
843
|
* Builds one pin-name text element, including overline tspans when needed.
|
|
677
844
|
* @param {string} className
|
|
@@ -231,10 +231,12 @@ export class SchematicShapeRenderer {
|
|
|
231
231
|
* @returns {string}
|
|
232
232
|
*/
|
|
233
233
|
static buildRectangleMarkup(rectangle, sheetHeight) {
|
|
234
|
-
const fill =
|
|
235
|
-
SchematicShapeRenderer.#resolveSchematicRectangleFill(rectangle)
|
|
236
234
|
const preserveSourceColors =
|
|
237
235
|
SchematicShapeRenderer.#isOwnerColorStrip(rectangle)
|
|
236
|
+
const fill = SchematicShapeRenderer.#resolveSchematicRectangleFill(
|
|
237
|
+
rectangle,
|
|
238
|
+
preserveSourceColors
|
|
239
|
+
)
|
|
238
240
|
|
|
239
241
|
return (
|
|
240
242
|
'<rect class="schematic-rectangle" x="' +
|
|
@@ -250,7 +252,7 @@ export class SchematicShapeRenderer {
|
|
|
250
252
|
'" fill="' +
|
|
251
253
|
escapeHtml(
|
|
252
254
|
preserveSourceColors
|
|
253
|
-
? SchematicColorResolver.
|
|
255
|
+
? SchematicColorResolver.resolveMutedSourceFill(
|
|
254
256
|
fill,
|
|
255
257
|
'--schematic-fill-color'
|
|
256
258
|
)
|
|
@@ -262,7 +264,7 @@ export class SchematicShapeRenderer {
|
|
|
262
264
|
'" stroke="' +
|
|
263
265
|
escapeHtml(
|
|
264
266
|
preserveSourceColors
|
|
265
|
-
? SchematicColorResolver.
|
|
267
|
+
? SchematicColorResolver.resolveMutedSourceColor(
|
|
266
268
|
rectangle.color,
|
|
267
269
|
'--schematic-default-ink-color'
|
|
268
270
|
)
|
|
@@ -290,10 +292,12 @@ export class SchematicShapeRenderer {
|
|
|
290
292
|
*/
|
|
291
293
|
static buildRoundedRectangleMarkup(rectangle, sheetHeight) {
|
|
292
294
|
const radius = Math.max(Number(rectangle.radius || 0), 0)
|
|
293
|
-
const fill =
|
|
294
|
-
SchematicShapeRenderer.#resolveSchematicRectangleFill(rectangle)
|
|
295
295
|
const preserveSourceColors =
|
|
296
296
|
SchematicShapeRenderer.#isOwnerColorStrip(rectangle)
|
|
297
|
+
const fill = SchematicShapeRenderer.#resolveSchematicRectangleFill(
|
|
298
|
+
rectangle,
|
|
299
|
+
preserveSourceColors
|
|
300
|
+
)
|
|
297
301
|
|
|
298
302
|
return (
|
|
299
303
|
'<rect class="schematic-rounded-rectangle" x="' +
|
|
@@ -313,7 +317,7 @@ export class SchematicShapeRenderer {
|
|
|
313
317
|
'" fill="' +
|
|
314
318
|
escapeHtml(
|
|
315
319
|
preserveSourceColors
|
|
316
|
-
? SchematicColorResolver.
|
|
320
|
+
? SchematicColorResolver.resolveMutedSourceFill(
|
|
317
321
|
fill,
|
|
318
322
|
'--schematic-fill-color'
|
|
319
323
|
)
|
|
@@ -325,7 +329,7 @@ export class SchematicShapeRenderer {
|
|
|
325
329
|
'" stroke="' +
|
|
326
330
|
escapeHtml(
|
|
327
331
|
preserveSourceColors
|
|
328
|
-
? SchematicColorResolver.
|
|
332
|
+
? SchematicColorResolver.resolveMutedSourceColor(
|
|
329
333
|
rectangle.color,
|
|
330
334
|
'--schematic-default-ink-color'
|
|
331
335
|
)
|
|
@@ -470,19 +474,41 @@ export class SchematicShapeRenderer {
|
|
|
470
474
|
/**
|
|
471
475
|
* Resolves the visible fill for one schematic rectangle primitive.
|
|
472
476
|
* @param {{ fill: string, isSolid: boolean, transparent: boolean }} rectangle
|
|
477
|
+
* @param {boolean} preserveSourceFill
|
|
473
478
|
* @returns {string}
|
|
474
479
|
*/
|
|
475
|
-
static #resolveSchematicRectangleFill(rectangle) {
|
|
480
|
+
static #resolveSchematicRectangleFill(rectangle, preserveSourceFill) {
|
|
476
481
|
if (rectangle.transparent || !rectangle.isSolid) {
|
|
482
|
+
if (
|
|
483
|
+
preserveSourceFill &&
|
|
484
|
+
SchematicShapeRenderer.#hasVisibleSourceFill(rectangle)
|
|
485
|
+
) {
|
|
486
|
+
return rectangle.fill
|
|
487
|
+
}
|
|
488
|
+
|
|
477
489
|
return 'none'
|
|
478
490
|
}
|
|
479
491
|
|
|
480
492
|
return rectangle.fill || 'none'
|
|
481
493
|
}
|
|
482
494
|
|
|
495
|
+
/**
|
|
496
|
+
* Returns true when a transparent owner color strip still carries an
|
|
497
|
+
* authored fill color that encodes symbol-side rail intent.
|
|
498
|
+
* @param {{ fill?: string }} rectangle
|
|
499
|
+
* @returns {boolean}
|
|
500
|
+
*/
|
|
501
|
+
static #hasVisibleSourceFill(rectangle) {
|
|
502
|
+
const fill = String(rectangle?.fill || '')
|
|
503
|
+
.trim()
|
|
504
|
+
.toLowerCase()
|
|
505
|
+
|
|
506
|
+
return Boolean(fill) && fill !== 'none' && fill !== 'transparent'
|
|
507
|
+
}
|
|
508
|
+
|
|
483
509
|
/**
|
|
484
510
|
* Returns true for narrow symbol-owned color swatches such as per-section
|
|
485
|
-
* strips that carry
|
|
511
|
+
* strips that carry source palette meaning.
|
|
486
512
|
* @param {{ width: number, height: number, ownerIndex?: string }} rectangle
|
|
487
513
|
* @returns {boolean}
|
|
488
514
|
*/
|
|
@@ -496,7 +522,7 @@ export class SchematicShapeRenderer {
|
|
|
496
522
|
const shortSide = Math.min(width, height)
|
|
497
523
|
const longSide = Math.max(width, height)
|
|
498
524
|
|
|
499
|
-
return shortSide > 0 && shortSide <= 8 && longSide >=
|
|
525
|
+
return shortSide > 0 && shortSide <= 8 && longSide >= 20
|
|
500
526
|
}
|
|
501
527
|
|
|
502
528
|
/**
|