smithkit 0.1.1 → 0.3.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 +291 -42
- package/dist/lib/MarkerMeasurements.d.ts +6 -0
- package/dist/lib/MarkerMeasurements.js +22 -0
- package/dist/lib/Smith.d.ts +18 -35
- package/dist/lib/Smith.js +107 -275
- package/dist/lib/SmithFormatter.d.ts +10 -0
- package/dist/lib/SmithFormatter.js +19 -0
- package/dist/lib/appearance/SvgTheme.d.ts +6 -0
- package/dist/lib/appearance/SvgTheme.js +21 -0
- package/dist/lib/appearance/Theme.d.ts +8 -0
- package/dist/lib/appearance/Theme.js +120 -0
- package/dist/lib/appearance/types.d.ts +55 -0
- package/dist/lib/grid/CircleLayerControl.d.ts +15 -0
- package/dist/lib/grid/CircleLayerControl.js +40 -0
- package/dist/lib/{draw → grid}/ConstCircles.d.ts +4 -9
- package/dist/lib/{draw → grid}/ConstCircles.js +8 -22
- package/dist/lib/{draw → grid}/ConstQCircles.d.ts +2 -2
- package/dist/lib/{draw → grid}/ConstQCircles.js +4 -4
- package/dist/lib/{draw → grid}/ConstSwrCircles.d.ts +2 -2
- package/dist/lib/{draw → grid}/ConstSwrCircles.js +4 -4
- package/dist/lib/grid/GridDefinitions.d.ts +18 -0
- package/dist/lib/grid/GridDefinitions.js +167 -0
- package/dist/lib/grid/GridGeometry.d.ts +26 -0
- package/dist/lib/grid/GridGeometry.js +58 -0
- package/dist/lib/grid/GridLabels.d.ts +8 -0
- package/dist/lib/grid/GridLabels.js +45 -0
- package/dist/lib/grid/GridLayerControl.d.ts +13 -0
- package/dist/lib/grid/GridLayerControl.js +48 -0
- package/dist/lib/grid/LayerControl.d.ts +6 -0
- package/dist/lib/grid/LayerControl.js +16 -0
- package/dist/lib/grid/SmithGridLayer.d.ts +26 -0
- package/dist/lib/grid/SmithGridLayer.js +95 -0
- package/dist/lib/grid/types.d.ts +1 -1
- package/dist/lib/index.d.ts +11 -11
- package/dist/lib/index.js +5 -7
- package/dist/lib/{draw → interaction}/SmithCursor.d.ts +3 -20
- package/dist/lib/{draw → interaction}/SmithCursor.js +12 -20
- package/dist/lib/io/Touchstone.d.ts +10 -0
- package/dist/lib/io/Touchstone.js +75 -0
- package/dist/lib/layers.d.ts +7 -9
- package/dist/lib/layers.js +1 -73
- package/dist/lib/{complex → math}/Complex.d.ts +1 -0
- package/dist/lib/{complex → math}/Complex.js +1 -1
- package/dist/lib/math/geometry.d.ts +14 -0
- package/dist/lib/measurements.d.ts +2 -4
- package/dist/lib/measurements.js +1 -19
- package/dist/lib/rendering/SvgChartRenderer.d.ts +66 -0
- package/dist/lib/rendering/SvgChartRenderer.js +342 -0
- package/dist/lib/rf/RfCalculations.d.ts +65 -0
- package/dist/lib/rf/RfCalculations.js +161 -0
- package/dist/lib/{SmithConstantCircle.d.ts → rf/SmithConstantCircle.d.ts} +2 -3
- package/dist/lib/{SmithConstantCircle.js → rf/SmithConstantCircle.js} +3 -4
- package/dist/lib/scales/PeripheralScaleDefinitions.d.ts +16 -0
- package/dist/lib/scales/PeripheralScaleDefinitions.js +54 -0
- package/dist/lib/scales/RadialScaleDefinitions.d.ts +15 -0
- package/dist/lib/scales/RadialScaleDefinitions.js +112 -0
- package/dist/lib/scales/SmithPeripheralScales.d.ts +4 -2
- package/dist/lib/scales/SmithPeripheralScales.js +55 -35
- package/dist/lib/scales/SmithScales.d.ts +11 -2
- package/dist/lib/scales/SmithScales.js +78 -38
- package/dist/lib/svg/ImageExporter.d.ts +18 -0
- package/dist/lib/svg/ImageExporter.js +211 -0
- package/dist/lib/svg/LabelLayout.d.ts +17 -0
- package/dist/lib/svg/LabelLayout.js +145 -0
- package/dist/lib/svg/MarkerLegend.d.ts +12 -0
- package/dist/lib/svg/MarkerLegend.js +63 -0
- package/dist/lib/{draw → svg}/SmithArc.d.ts +1 -1
- package/dist/lib/{draw → svg}/SmithCircle.d.ts +1 -1
- package/dist/lib/{draw → svg}/SmithLine.d.ts +1 -1
- package/dist/lib/{draw → svg}/SmithScaler.d.ts +1 -4
- package/dist/lib/{draw → svg}/SmithText.d.ts +1 -1
- package/dist/lib/svg/SvgExporter.d.ts +19 -0
- package/dist/lib/svg/SvgExporter.js +182 -0
- package/dist/lib/svg/export.d.ts +36 -0
- package/dist/lib/{draw → traces}/SmithData.d.ts +24 -35
- package/dist/lib/traces/SmithData.js +195 -0
- package/dist/lib/{draw → traces}/SmithMarker.d.ts +14 -3
- package/dist/lib/traces/SmithMarker.js +240 -0
- package/dist/lib/traces/TraceModel.d.ts +31 -0
- package/dist/lib/traces/TraceModel.js +123 -0
- package/dist/lib/traces/TraceRenderer.d.ts +33 -0
- package/dist/lib/traces/TraceRenderer.js +152 -0
- package/package.json +4 -1
- package/dist/lib/SmithArcsDefs.d.ts +0 -10
- package/dist/lib/SmithArcsDefs.js +0 -10
- package/dist/lib/components.d.ts +0 -14
- package/dist/lib/components.js +0 -26
- package/dist/lib/draw/SmithData.js +0 -366
- package/dist/lib/draw/SmithGridLayer.d.ts +0 -19
- package/dist/lib/draw/SmithGridLayer.js +0 -48
- package/dist/lib/draw/SmithMarker.js +0 -122
- package/dist/lib/formatting.d.ts +0 -7
- package/dist/lib/formatting.js +0 -16
- package/dist/lib/grid/definitions.d.ts +0 -3
- package/dist/lib/grid/definitions.js +0 -123
- package/dist/lib/grid/geometry.d.ts +0 -20
- package/dist/lib/grid/geometry.js +0 -55
- package/dist/lib/grid/labels.d.ts +0 -3
- package/dist/lib/grid/labels.js +0 -41
- package/dist/lib/io/touchstone.d.ts +0 -7
- package/dist/lib/io/touchstone.js +0 -72
- package/dist/lib/math/complexRatio.d.ts +0 -3
- package/dist/lib/math/complexRatio.js +0 -30
- package/dist/lib/renormalization.d.ts +0 -10
- package/dist/lib/renormalization.js +0 -62
- package/dist/lib/rf.d.ts +0 -35
- package/dist/lib/rf.js +0 -74
- package/dist/lib/scales/peripheralScales.d.ts +0 -12
- package/dist/lib/scales/peripheralScales.js +0 -49
- package/dist/lib/scales/radialScales.d.ts +0 -12
- package/dist/lib/scales/radialScales.js +0 -109
- package/dist/lib/shapes/Arc.d.ts +0 -6
- package/dist/lib/shapes/Circle.d.ts +0 -5
- package/dist/lib/shapes/Line.d.ts +0 -5
- package/dist/lib/shapes/Point.d.ts +0 -1
- package/dist/lib/shapes/Point.js +0 -1
- /package/dist/lib/{draw/SmithDrawOptions.js → appearance/types.js} +0 -0
- /package/dist/lib/{draw → grid}/ConstConductance.d.ts +0 -0
- /package/dist/lib/{draw → grid}/ConstConductance.js +0 -0
- /package/dist/lib/{draw → grid}/ConstReactance.d.ts +0 -0
- /package/dist/lib/{draw → grid}/ConstReactance.js +0 -0
- /package/dist/lib/{draw → grid}/ConstResistance.d.ts +0 -0
- /package/dist/lib/{draw → grid}/ConstResistance.js +0 -0
- /package/dist/lib/{draw → grid}/ConstSusceptance.d.ts +0 -0
- /package/dist/lib/{draw → grid}/ConstSusceptance.js +0 -0
- /package/dist/lib/{arcs → grid}/Tick.d.ts +0 -0
- /package/dist/lib/{arcs → grid}/Tick.js +0 -0
- /package/dist/lib/{draw → interaction}/MouseGesture.d.ts +0 -0
- /package/dist/lib/{draw → interaction}/MouseGesture.js +0 -0
- /package/dist/lib/{shapes/Arc.js → math/geometry.js} +0 -0
- /package/dist/lib/{draw → svg}/SmithArc.js +0 -0
- /package/dist/lib/{draw → svg}/SmithCircle.js +0 -0
- /package/dist/lib/{draw → svg}/SmithDrawOptions.d.ts +0 -0
- /package/dist/lib/{shapes/Circle.js → svg/SmithDrawOptions.js} +0 -0
- /package/dist/lib/{draw → svg}/SmithGroup.d.ts +0 -0
- /package/dist/lib/{draw → svg}/SmithGroup.js +0 -0
- /package/dist/lib/{draw → svg}/SmithLine.js +0 -0
- /package/dist/lib/{draw → svg}/SmithPath.d.ts +0 -0
- /package/dist/lib/{draw → svg}/SmithPath.js +0 -0
- /package/dist/lib/{draw → svg}/SmithScaler.js +0 -0
- /package/dist/lib/{draw → svg}/SmithShape.d.ts +0 -0
- /package/dist/lib/{draw → svg}/SmithShape.js +0 -0
- /package/dist/lib/{draw → svg}/SmithSvg.d.ts +0 -0
- /package/dist/lib/{draw → svg}/SmithSvg.js +0 -0
- /package/dist/lib/{draw → svg}/SmithText.js +0 -0
- /package/dist/lib/{shapes/Line.js → svg/export.js} +0 -0
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Complex } from './complex/Complex.js';
|
|
1
|
+
import { Complex } from '../math/Complex.js';
|
|
3
2
|
export class SmithConstantCircle {
|
|
4
3
|
Z0;
|
|
5
4
|
constructor(Z0 = 50) {
|
|
@@ -18,8 +17,8 @@ export class SmithConstantCircle {
|
|
|
18
17
|
return this.transform(c, 1 - c.re, -c.im, 1 + c.re, c.im, -2);
|
|
19
18
|
}
|
|
20
19
|
transform(value, nr, ni, dr, di, determinant) {
|
|
21
|
-
const quotient =
|
|
22
|
-
if (!quotient) {
|
|
20
|
+
const quotient = Complex.div(Complex.from(nr, ni), Complex.from(dr, di));
|
|
21
|
+
if (!Number.isFinite(quotient.re) || !Number.isFinite(quotient.im)) {
|
|
23
22
|
return;
|
|
24
23
|
}
|
|
25
24
|
// The analytic imaginary numerator avoids cancellation of nearly equal products.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Complex } from '../math/Complex.js';
|
|
2
|
+
export interface PeripheralScale {
|
|
3
|
+
id: string;
|
|
4
|
+
title: string;
|
|
5
|
+
unit: '°' | 'λ';
|
|
6
|
+
ticks: number[];
|
|
7
|
+
major: (value: number) => boolean;
|
|
8
|
+
angle: (value: number) => number;
|
|
9
|
+
read: (gamma: Complex) => number | null;
|
|
10
|
+
}
|
|
11
|
+
export declare class PeripheralScaleDefinitions {
|
|
12
|
+
private constructor();
|
|
13
|
+
private static phase;
|
|
14
|
+
/** A full reflection-coefficient rotation represents half a wavelength along the line. */
|
|
15
|
+
static create(): PeripheralScale[];
|
|
16
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export class PeripheralScaleDefinitions {
|
|
2
|
+
constructor() { }
|
|
3
|
+
static phase(gamma) {
|
|
4
|
+
return gamma.re === 0 && gamma.im === 0
|
|
5
|
+
? null
|
|
6
|
+
: (Math.atan2(gamma.im, gamma.re) * 180) / Math.PI;
|
|
7
|
+
}
|
|
8
|
+
/** A full reflection-coefficient rotation represents half a wavelength along the line. */
|
|
9
|
+
static create() {
|
|
10
|
+
const wavelengths = (gamma, direction) => {
|
|
11
|
+
const degrees = PeripheralScaleDefinitions.phase(gamma);
|
|
12
|
+
return degrees === null ? null : (0.25 + (direction * degrees) / 720 + 0.5) % 0.5;
|
|
13
|
+
};
|
|
14
|
+
return [
|
|
15
|
+
{
|
|
16
|
+
id: 'transmission-phase',
|
|
17
|
+
title: 'Transmission phase · °',
|
|
18
|
+
unit: '°',
|
|
19
|
+
ticks: Array.from({ length: 179 }, (_, i) => -89 + i),
|
|
20
|
+
major: (v) => v % 5 === 0,
|
|
21
|
+
// Project from Γ = −1 to the perimeter, not from the chart center.
|
|
22
|
+
angle: (v) => 2 * v,
|
|
23
|
+
read: (gamma) => PeripheralScaleDefinitions.phase({ re: 1 + gamma.re, im: gamma.im }),
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 'reflection-phase',
|
|
27
|
+
title: 'Reflection phase · °',
|
|
28
|
+
unit: '°',
|
|
29
|
+
ticks: Array.from({ length: 180 }, (_, i) => -178 + i * 2),
|
|
30
|
+
major: (v) => v % 10 === 0,
|
|
31
|
+
angle: (v) => v,
|
|
32
|
+
read: PeripheralScaleDefinitions.phase,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: 'wavelengths-load',
|
|
36
|
+
title: 'Wavelengths → load',
|
|
37
|
+
unit: 'λ',
|
|
38
|
+
ticks: Array.from({ length: 250 }, (_, i) => i / 500),
|
|
39
|
+
major: (v) => Math.round(v * 500) % 5 === 0,
|
|
40
|
+
angle: (v) => 720 * v - 180,
|
|
41
|
+
read: (gamma) => wavelengths(gamma, 1),
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: 'wavelengths-generator',
|
|
45
|
+
title: 'Wavelengths → generator',
|
|
46
|
+
unit: 'λ',
|
|
47
|
+
ticks: Array.from({ length: 250 }, (_, i) => i / 500),
|
|
48
|
+
major: (v) => Math.round(v * 500) % 5 === 0,
|
|
49
|
+
angle: (v) => 180 - 720 * v,
|
|
50
|
+
read: (gamma) => wavelengths(gamma, -1),
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Complex } from '../math/Complex.js';
|
|
2
|
+
export interface RadialScale {
|
|
3
|
+
id: string;
|
|
4
|
+
title: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
unit: 'dB' | 'ratio';
|
|
7
|
+
read: (gamma: Complex) => number;
|
|
8
|
+
values: number[];
|
|
9
|
+
position: (value: number) => number;
|
|
10
|
+
}
|
|
11
|
+
export declare class RadialScaleDefinitions {
|
|
12
|
+
private constructor();
|
|
13
|
+
/** Normalized positions along each scale, from zero to one. */
|
|
14
|
+
static create(): RadialScale[];
|
|
15
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { SmithConstantCircle } from '../rf/SmithConstantCircle.js';
|
|
2
|
+
export class RadialScaleDefinitions {
|
|
3
|
+
constructor() { }
|
|
4
|
+
/** Normalized positions along each scale, from zero to one. */
|
|
5
|
+
static create() {
|
|
6
|
+
const calcs = new SmithConstantCircle();
|
|
7
|
+
return [
|
|
8
|
+
{
|
|
9
|
+
id: 'vswr',
|
|
10
|
+
unit: 'ratio',
|
|
11
|
+
read: (gamma) => calcs.rflCoeffToSwr(gamma),
|
|
12
|
+
title: 'VSWR',
|
|
13
|
+
values: [1, 1.5, 2, 3, 5, 10, Infinity],
|
|
14
|
+
position: (v) => (v === Infinity ? 1 : calcs.swrToRflCoeffEOrI(v)),
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: 'vswr-db',
|
|
18
|
+
unit: 'dB',
|
|
19
|
+
read: (gamma) => calcs.rflCoeffToDBS(gamma),
|
|
20
|
+
title: 'Standing-wave ratio · dB',
|
|
21
|
+
values: [0, 3, 6, 10, 15, 20, Infinity],
|
|
22
|
+
position: (v) => (v === Infinity ? 1 : calcs.dBSToAbsRflCoeff(v)),
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: 'return-loss',
|
|
26
|
+
unit: 'dB',
|
|
27
|
+
read: (gamma) => calcs.rflCoeffToReturnLoss(gamma),
|
|
28
|
+
title: 'Return loss · dB',
|
|
29
|
+
values: [Infinity, 20, 14, 10, 6, 3, 0],
|
|
30
|
+
position: (v) => calcs.returnLossToRflCoeffEOrI(v),
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: 'mismatch-loss',
|
|
34
|
+
unit: 'dB',
|
|
35
|
+
read: (gamma) => calcs.rflCoeffToMismatchLoss(gamma),
|
|
36
|
+
title: 'Reflection loss · dB',
|
|
37
|
+
description: 'Reflection loss (mismatch loss): −10 log10(1 − |Γ|²), in dB.',
|
|
38
|
+
values: [0, 0.1, 0.5, 1, 2, 4, Infinity],
|
|
39
|
+
position: (v) => calcs.mismatchLossToRflCoeffEOrI(v),
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: 'reflected-power',
|
|
43
|
+
unit: 'ratio',
|
|
44
|
+
read: (gamma) => calcs.rflCoeffP(gamma),
|
|
45
|
+
title: 'Power reflection coefficient',
|
|
46
|
+
description: 'Reflected power divided by incident power: |Γ|² (dimensionless).',
|
|
47
|
+
values: [0, 0.01, 0.1, 0.25, 0.5, 0.75, 1],
|
|
48
|
+
position: (v) => calcs.rflCoeffPToEOrI(v),
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: 'transmitted-power',
|
|
52
|
+
unit: 'ratio',
|
|
53
|
+
read: (gamma) => calcs.rflCoeffToTransmCoeffP(gamma),
|
|
54
|
+
title: 'Power transmission coefficient',
|
|
55
|
+
description: 'Net power delivered to the load divided by incident power: 1 − |Γ|² (dimensionless).',
|
|
56
|
+
values: [1, 0.99, 0.9, 0.75, 0.5, 0.25, 0],
|
|
57
|
+
position: (v) => calcs.transmCoeffPToRflCoeffEOrI(v),
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: 'standing-wave-loss',
|
|
61
|
+
unit: 'ratio',
|
|
62
|
+
read: (gamma) => calcs.rflCoeffToSwLossCoeff(gamma),
|
|
63
|
+
title: 'Standing-wave loss coefficient',
|
|
64
|
+
values: [1, 1.1, 1.5, 2, 3, 5, Infinity],
|
|
65
|
+
position: (v) => (v === Infinity ? 1 : calcs.swLossCoeffToRflCoeffEOrI(v)),
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: 'standing-wave-peak',
|
|
69
|
+
unit: 'ratio',
|
|
70
|
+
read: (gamma) => calcs.rflCoeffToSwPeakConstP(gamma),
|
|
71
|
+
title: 'Standing-wave peak · constant power',
|
|
72
|
+
values: [1, 1.2, 1.5, 2, 3, Infinity],
|
|
73
|
+
position: (v) => (v === Infinity ? 1 : calcs.swPeakConstPToRflCoeffEOrI(v)),
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: 'reflection-coefficient',
|
|
77
|
+
unit: 'ratio',
|
|
78
|
+
read: (gamma) => calcs.rflCoeffEOrI(gamma),
|
|
79
|
+
title: 'Reflection coefficient · |Γ|',
|
|
80
|
+
values: [0, 0.2, 0.4, 0.6, 0.8, 1],
|
|
81
|
+
position: (v) => v,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
id: 'attenuation',
|
|
85
|
+
unit: 'dB',
|
|
86
|
+
read: (gamma) => calcs.rflCoeffToAttenuation(gamma),
|
|
87
|
+
title: 'Attenuation · dB',
|
|
88
|
+
description: 'One-way attenuation of a matched line or attenuator terminated in an open or short circuit. Not insertion loss of an arbitrary load.',
|
|
89
|
+
values: [Infinity, 10, 7, 5, 3, 1, 0],
|
|
90
|
+
position: (v) => calcs.attenuationToRflCoeff(v),
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: 'voltage-transmission',
|
|
94
|
+
unit: 'ratio',
|
|
95
|
+
read: (gamma) => calcs.rflCoeffToTransmCoeffEOrI(gamma),
|
|
96
|
+
title: 'Voltage transmission coefficient',
|
|
97
|
+
description: 'Voltage transmission magnitude: |1 + Γ| (dimensionless).',
|
|
98
|
+
values: [0, 0.4, 0.8, 1.2, 1.6, 2],
|
|
99
|
+
position: (v) => v / 2,
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: 'current-transmission',
|
|
103
|
+
unit: 'ratio',
|
|
104
|
+
read: (gamma) => calcs.rflCoeffToCurrentTransmission(gamma),
|
|
105
|
+
title: 'Current transmission coefficient',
|
|
106
|
+
description: 'Current transmission magnitude: |1 − Γ|, using the voltage reflection coefficient Γ (dimensionless).',
|
|
107
|
+
values: [0, 0.4, 0.8, 1.2, 1.6, 2],
|
|
108
|
+
position: (v) => v / 2,
|
|
109
|
+
},
|
|
110
|
+
];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import type { Complex } from '../
|
|
2
|
-
import { SmithGroup } from '../
|
|
1
|
+
import type { Complex } from '../math/Complex.js';
|
|
2
|
+
import { SmithGroup } from '../svg/SmithGroup.js';
|
|
3
3
|
/** Peripheral rulers in the chart's 500-unit coordinate system. */
|
|
4
4
|
export declare class SmithPeripheralScales extends SmithGroup {
|
|
5
|
+
private static point;
|
|
6
|
+
private static rulerRadius;
|
|
5
7
|
readonly labels: SmithGroup;
|
|
6
8
|
private readonly scales;
|
|
7
9
|
private readonly axes;
|
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { select, format } from 'd3';
|
|
2
|
-
import { SmithGroup } from '../
|
|
3
|
-
import {
|
|
2
|
+
import { SmithGroup } from '../svg/SmithGroup.js';
|
|
3
|
+
import { PeripheralScaleDefinitions } from './PeripheralScaleDefinitions.js';
|
|
4
4
|
let nextScaleId = 0;
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
function point(radius, degrees) {
|
|
8
|
-
const angle = (degrees * Math.PI) / 180;
|
|
9
|
-
return [250 + radius * Math.cos(angle), 250 - radius * Math.sin(angle)];
|
|
10
|
-
}
|
|
5
|
+
const chartRadius = 250;
|
|
6
|
+
const groupWidth = 40;
|
|
11
7
|
/** Peripheral rulers in the chart's 500-unit coordinate system. */
|
|
12
8
|
export class SmithPeripheralScales extends SmithGroup {
|
|
9
|
+
static point(radius, degrees) {
|
|
10
|
+
const angle = (degrees * Math.PI) / 180;
|
|
11
|
+
return [250 + radius * Math.cos(angle), 250 - radius * Math.sin(angle)];
|
|
12
|
+
}
|
|
13
|
+
static rulerRadius(index) {
|
|
14
|
+
return chartRadius + groupWidth / 2 + Math.floor(index / 2) * groupWidth;
|
|
15
|
+
}
|
|
13
16
|
labels = new SmithGroup()
|
|
14
17
|
.attr('data-label-layer', 'peripheral-scales')
|
|
15
18
|
.attr('pointer-events', 'none');
|
|
16
|
-
scales =
|
|
19
|
+
scales = PeripheralScaleDefinitions.create();
|
|
17
20
|
axes = this.Element.attr('data-layer', 'peripheral-scales')
|
|
18
21
|
.attr('pointer-events', 'none')
|
|
19
22
|
.selectAll('g')
|
|
@@ -29,38 +32,55 @@ export class SmithPeripheralScales extends SmithGroup {
|
|
|
29
32
|
.join('g')
|
|
30
33
|
.attr('data-label-scale', (scale) => scale.id)
|
|
31
34
|
.nodes();
|
|
35
|
+
// Two paired rulers: transmission/reflection phase, then electrical length
|
|
36
|
+
// toward load/generator. Strong boundaries enclose each pair.
|
|
37
|
+
this.Element.selectAll('circle.peripheral-boundary')
|
|
38
|
+
.data([chartRadius, chartRadius + groupWidth, chartRadius + 2 * groupWidth])
|
|
39
|
+
.join('circle')
|
|
40
|
+
.attr('class', 'peripheral-boundary')
|
|
41
|
+
.attr('cx', 250)
|
|
42
|
+
.attr('cy', 250)
|
|
43
|
+
.attr('r', (radius) => radius)
|
|
44
|
+
.attr('fill', 'none')
|
|
45
|
+
.attr('stroke', 'var(--smithkit-scales-boundaryColor)')
|
|
46
|
+
.attr('stroke-width', 1.1)
|
|
47
|
+
.attr('vector-effect', 'non-scaling-stroke');
|
|
32
48
|
this.axes.each(function (scale, index) {
|
|
33
49
|
const labelAxis = select(labelAxes[index]);
|
|
34
50
|
const axis = select(this);
|
|
35
|
-
const radius =
|
|
51
|
+
const radius = SmithPeripheralScales.rulerRadius(index);
|
|
52
|
+
const side = index % 2 === 0 ? -1 : 1;
|
|
36
53
|
const captionAngle = scale.unit === 'λ' ? 180 : 0;
|
|
37
54
|
axis.append('title').text(scale.title);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
55
|
+
if (index % 2 === 0) {
|
|
56
|
+
axis
|
|
57
|
+
.append('circle')
|
|
58
|
+
.attr('class', 'peripheral-ruler')
|
|
59
|
+
.attr('cx', 250)
|
|
60
|
+
.attr('cy', 250)
|
|
61
|
+
.attr('r', radius)
|
|
62
|
+
.attr('fill', 'none')
|
|
63
|
+
.attr('stroke', 'var(--smithkit-scales-stroke)')
|
|
64
|
+
.attr('stroke-width', 0.6)
|
|
65
|
+
.attr('vector-effect', 'non-scaling-stroke');
|
|
66
|
+
}
|
|
47
67
|
for (const value of scale.ticks) {
|
|
48
68
|
const angle = scale.angle(value);
|
|
49
|
-
const [x1, y1] = point(radius, angle);
|
|
69
|
+
const [x1, y1] = SmithPeripheralScales.point(radius, angle);
|
|
50
70
|
const major = scale.major(value);
|
|
51
|
-
const [x2, y2] = point(radius
|
|
71
|
+
const [x2, y2] = SmithPeripheralScales.point(radius + side * (major ? 4 : 2), angle);
|
|
52
72
|
axis
|
|
53
73
|
.append('line')
|
|
54
74
|
.attr('x1', x1)
|
|
55
75
|
.attr('y1', y1)
|
|
56
76
|
.attr('x2', x2)
|
|
57
77
|
.attr('y2', y2)
|
|
58
|
-
.attr('stroke', '
|
|
78
|
+
.attr('stroke', 'var(--smithkit-scales-stroke)')
|
|
59
79
|
.attr('stroke-width', major ? 0.8 : 0.5)
|
|
60
80
|
.attr('vector-effect', 'non-scaling-stroke');
|
|
61
81
|
const captionDistance = Math.abs(((angle - captionAngle + 540) % 360) - 180);
|
|
62
82
|
if (major && captionDistance > 25) {
|
|
63
|
-
const [x, y] = point(radius +
|
|
83
|
+
const [x, y] = SmithPeripheralScales.point(radius + side * 11, angle);
|
|
64
84
|
const rotation = angle >= 0 ? 90 - angle : -90 - angle;
|
|
65
85
|
labelAxis
|
|
66
86
|
.append('text')
|
|
@@ -69,16 +89,16 @@ export class SmithPeripheralScales extends SmithGroup {
|
|
|
69
89
|
.attr('transform', `rotate(${rotation},${x},${y})`)
|
|
70
90
|
.attr('text-anchor', 'middle')
|
|
71
91
|
.attr('dominant-baseline', 'central')
|
|
72
|
-
.attr('font-family', '
|
|
73
|
-
.attr('font-size',
|
|
74
|
-
.attr('fill', '
|
|
92
|
+
.attr('font-family', 'var(--smithkit-fontFamily)')
|
|
93
|
+
.attr('font-size', 'var(--smithkit-scales-peripheralFontSize)')
|
|
94
|
+
.attr('fill', 'var(--smithkit-scales-textColor)')
|
|
75
95
|
.text(scale.unit === 'λ' ? value.toFixed(2) : String(value));
|
|
76
96
|
}
|
|
77
97
|
}
|
|
78
98
|
// Reserve a label sector for the curved caption without masking the ruler or its ticks.
|
|
79
|
-
const captionRadius = radius + 5;
|
|
80
|
-
const start = point(captionRadius, captionAngle + 45);
|
|
81
|
-
const end = point(captionRadius, captionAngle - 45);
|
|
99
|
+
const captionRadius = radius + (side < 0 ? -15 : 5);
|
|
100
|
+
const start = SmithPeripheralScales.point(captionRadius, captionAngle + 45);
|
|
101
|
+
const end = SmithPeripheralScales.point(captionRadius, captionAngle - 45);
|
|
82
102
|
const pathId = `smithkit-peripheral-caption-${nextScaleId++}`;
|
|
83
103
|
axis
|
|
84
104
|
.append('defs')
|
|
@@ -94,11 +114,11 @@ export class SmithPeripheralScales extends SmithGroup {
|
|
|
94
114
|
.append('text')
|
|
95
115
|
.attr('class', 'peripheral-caption')
|
|
96
116
|
.attr('text-anchor', 'middle')
|
|
97
|
-
.attr('font-family', '
|
|
98
|
-
.attr('font-size',
|
|
117
|
+
.attr('font-family', 'var(--smithkit-fontFamily)')
|
|
118
|
+
.attr('font-size', 'var(--smithkit-scales-peripheralFontSize)')
|
|
99
119
|
.attr('font-weight', 500)
|
|
100
120
|
.attr('letter-spacing', 0.3)
|
|
101
|
-
.attr('fill', '
|
|
121
|
+
.attr('fill', 'var(--smithkit-scales-textColor)')
|
|
102
122
|
.append('textPath')
|
|
103
123
|
.attr('href', `#${pathId}`)
|
|
104
124
|
.attr('startOffset', '50%')
|
|
@@ -108,8 +128,8 @@ export class SmithPeripheralScales extends SmithGroup {
|
|
|
108
128
|
.attr('class', 'peripheral-indicator')
|
|
109
129
|
.attr('r', 3)
|
|
110
130
|
.attr('visibility', 'hidden')
|
|
111
|
-
.attr('fill', '
|
|
112
|
-
.attr('stroke', '
|
|
131
|
+
.attr('fill', 'var(--smithkit-scales-indicatorColor)')
|
|
132
|
+
.attr('stroke', 'var(--smithkit-scales-indicatorOutline)')
|
|
113
133
|
.attr('stroke-width', 1);
|
|
114
134
|
});
|
|
115
135
|
}
|
|
@@ -134,7 +154,7 @@ export class SmithPeripheralScales extends SmithGroup {
|
|
|
134
154
|
dot.attr('visibility', 'hidden').attr('data-value', null);
|
|
135
155
|
return;
|
|
136
156
|
}
|
|
137
|
-
const [x, y] = point(
|
|
157
|
+
const [x, y] = SmithPeripheralScales.point(SmithPeripheralScales.rulerRadius(index), scale.angle(value));
|
|
138
158
|
dot.attr('cx', x).attr('cy', y).attr('data-value', value).attr('visibility', null);
|
|
139
159
|
});
|
|
140
160
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ScaleImageExportOptions } from '../svg/export.js';
|
|
2
|
+
import type { SmithAppearance } from '../appearance/types.js';
|
|
3
|
+
import type { Complex } from '../math/Complex.js';
|
|
2
4
|
/** Independent parameter scales. Connect update() to a chart's cursor events. */
|
|
3
5
|
export declare class SmithScales {
|
|
4
6
|
private readonly scales;
|
|
@@ -7,10 +9,17 @@ export declare class SmithScales {
|
|
|
7
9
|
private readonly start;
|
|
8
10
|
private readonly length;
|
|
9
11
|
private readonly axes;
|
|
10
|
-
constructor();
|
|
12
|
+
constructor(appearance?: SmithAppearance);
|
|
13
|
+
/** Replace the preset and overrides without changing readings or layout. */
|
|
14
|
+
setAppearance(appearance: SmithAppearance): void;
|
|
11
15
|
draw(target: string | HTMLElement): void;
|
|
12
16
|
/** Show a passive-load reflection coefficient, or hide indicators with null. */
|
|
13
17
|
update(gamma: Complex | null): void;
|
|
18
|
+
private updateAxis;
|
|
19
|
+
/** Export mounted scales, optionally with a separate reading and its description. */
|
|
20
|
+
toSvg(options?: ScaleImageExportOptions): string;
|
|
21
|
+
/** Export mounted scales as PNG without changing their live readings. */
|
|
22
|
+
toPng(options?: ScaleImageExportOptions): Promise<Blob>;
|
|
14
23
|
destroy(): void;
|
|
15
24
|
private assertAlive;
|
|
16
25
|
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import { ImageExporter } from '../svg/ImageExporter.js';
|
|
2
|
+
import { Theme } from '../appearance/Theme.js';
|
|
3
|
+
import { SvgTheme } from '../appearance/SvgTheme.js';
|
|
1
4
|
import { select, format } from 'd3';
|
|
2
|
-
import {
|
|
5
|
+
import { RadialScaleDefinitions } from './RadialScaleDefinitions.js';
|
|
6
|
+
import { SvgExporter } from '../svg/SvgExporter.js';
|
|
3
7
|
/** Independent parameter scales. Connect update() to a chart's cursor events. */
|
|
4
8
|
export class SmithScales {
|
|
5
|
-
scales =
|
|
9
|
+
scales = RadialScaleDefinitions.create();
|
|
6
10
|
container = select(document.createElement('div'))
|
|
7
11
|
.attr('class', 'radial-scales')
|
|
8
12
|
.style('display', 'grid')
|
|
@@ -23,25 +27,27 @@ export class SmithScales {
|
|
|
23
27
|
.style('display', 'block')
|
|
24
28
|
.style('width', '100%')
|
|
25
29
|
.style('height', 'auto');
|
|
26
|
-
constructor() {
|
|
30
|
+
constructor(appearance = {}) {
|
|
31
|
+
this.setAppearance(appearance);
|
|
27
32
|
const labels = this.axes.append('g').attr('data-role', 'labels').attr('pointer-events', 'none');
|
|
28
33
|
labels
|
|
29
34
|
.append('text')
|
|
30
35
|
.attr('x', this.start)
|
|
31
36
|
.attr('y', 12)
|
|
32
|
-
.attr('font-family', '
|
|
33
|
-
.attr('font-size',
|
|
34
|
-
.attr('fill', '
|
|
37
|
+
.attr('font-family', 'var(--smithkit-fontFamily)')
|
|
38
|
+
.attr('font-size', 'var(--smithkit-scales-titleFontSize)')
|
|
39
|
+
.attr('fill', 'var(--smithkit-scales-textColor)')
|
|
35
40
|
.text((scale) => scale.title);
|
|
36
41
|
labels
|
|
37
42
|
.append('text')
|
|
38
43
|
.attr('class', 'scale-value')
|
|
44
|
+
.attr('data-role', 'scale-value')
|
|
39
45
|
.attr('x', this.start)
|
|
40
46
|
.attr('y', 25)
|
|
41
|
-
.attr('font-family', '
|
|
42
|
-
.attr('font-size',
|
|
47
|
+
.attr('font-family', 'var(--smithkit-fontFamily)')
|
|
48
|
+
.attr('font-size', 'var(--smithkit-scales-valueFontSize)')
|
|
43
49
|
.attr('font-weight', 600)
|
|
44
|
-
.attr('fill', '
|
|
50
|
+
.attr('fill', 'var(--smithkit-scales-valueColor)')
|
|
45
51
|
.text('—');
|
|
46
52
|
this.axes
|
|
47
53
|
.append('line')
|
|
@@ -50,7 +56,7 @@ export class SmithScales {
|
|
|
50
56
|
.attr('x2', this.start + this.length)
|
|
51
57
|
.attr('y1', 36)
|
|
52
58
|
.attr('y2', 36)
|
|
53
|
-
.attr('stroke', '
|
|
59
|
+
.attr('stroke', 'var(--smithkit-scales-stroke)')
|
|
54
60
|
.attr('stroke-width', 1);
|
|
55
61
|
const start = this.start;
|
|
56
62
|
const length = this.length;
|
|
@@ -61,7 +67,11 @@ export class SmithScales {
|
|
|
61
67
|
.join('g')
|
|
62
68
|
.attr('class', 'scale-tick')
|
|
63
69
|
.attr('transform', (value) => `translate(${start + length * scale.position(value)},36)`);
|
|
64
|
-
ticks
|
|
70
|
+
ticks
|
|
71
|
+
.append('line')
|
|
72
|
+
.attr('y2', 5)
|
|
73
|
+
.attr('stroke', 'var(--smithkit-scales-stroke)')
|
|
74
|
+
.attr('stroke-width', 1);
|
|
65
75
|
select(this)
|
|
66
76
|
.select('[data-role=labels]')
|
|
67
77
|
.selectAll('g')
|
|
@@ -70,9 +80,9 @@ export class SmithScales {
|
|
|
70
80
|
.attr('transform', (value) => `translate(${start + length * scale.position(value)},36)`)
|
|
71
81
|
.append('text')
|
|
72
82
|
.attr('y', 17)
|
|
73
|
-
.attr('font-size',
|
|
74
|
-
.attr('font-family', '
|
|
75
|
-
.attr('fill', '
|
|
83
|
+
.attr('font-size', 'var(--smithkit-scales-fontSize)')
|
|
84
|
+
.attr('font-family', 'var(--smithkit-fontFamily)')
|
|
85
|
+
.attr('fill', 'var(--smithkit-scales-textColor)')
|
|
76
86
|
.attr('text-anchor', (_, i) => i === 0 ? 'start' : i === scale.values.length - 1 ? 'end' : 'middle')
|
|
77
87
|
.text((value) => (value === Infinity ? '∞' : String(value)));
|
|
78
88
|
});
|
|
@@ -83,12 +93,17 @@ export class SmithScales {
|
|
|
83
93
|
.attr('cx', this.start)
|
|
84
94
|
.attr('cy', 36)
|
|
85
95
|
.attr('r', 4)
|
|
86
|
-
.attr('fill', '
|
|
87
|
-
.attr('stroke', '
|
|
96
|
+
.attr('fill', 'var(--smithkit-scales-indicatorColor)')
|
|
97
|
+
.attr('stroke', 'var(--smithkit-scales-indicatorOutline)')
|
|
88
98
|
.attr('stroke-width', 1);
|
|
89
99
|
labels.raise();
|
|
90
100
|
this.axes.append('title').text((scale) => scale.description ?? scale.title);
|
|
91
101
|
}
|
|
102
|
+
/** Replace the preset and overrides without changing readings or layout. */
|
|
103
|
+
setAppearance(appearance) {
|
|
104
|
+
this.assertAlive();
|
|
105
|
+
SvgTheme.apply(this.container.node(), Theme.resolve(appearance));
|
|
106
|
+
}
|
|
92
107
|
draw(target) {
|
|
93
108
|
this.assertAlive();
|
|
94
109
|
const host = typeof target === 'string' ? document.querySelector(target) : target;
|
|
@@ -100,32 +115,57 @@ export class SmithScales {
|
|
|
100
115
|
/** Show a passive-load reflection coefficient, or hide indicators with null. */
|
|
101
116
|
update(gamma) {
|
|
102
117
|
this.assertAlive();
|
|
118
|
+
this.axes.each((scale, index, nodes) => this.updateAxis(nodes[index], scale, gamma));
|
|
119
|
+
}
|
|
120
|
+
updateAxis(node, scale, gamma) {
|
|
121
|
+
const axis = select(node);
|
|
103
122
|
const magnitude = gamma === null ? NaN : Math.hypot(gamma.re, gamma.im);
|
|
104
123
|
if (!gamma || !Number.isFinite(magnitude) || magnitude > 1) {
|
|
105
|
-
this.axes.select('circle').attr('visibility', 'hidden').attr('data-position', null);
|
|
106
|
-
this.axes.select('.scale-value').text('—');
|
|
107
|
-
this.axes.attr('aria-label', (scale) => `${scale.title}. ${scale.description ?? ''}`.trim());
|
|
108
|
-
this.axes.select('title').text((scale) => scale.description ?? scale.title);
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
const start = this.start;
|
|
112
|
-
const length = this.length;
|
|
113
|
-
this.axes.each(function (scale) {
|
|
114
|
-
const value = scale.read(gamma);
|
|
115
|
-
const position = Math.max(0, Math.min(1, scale.position(value)));
|
|
116
|
-
const axis = select(this);
|
|
117
|
-
const number = value === Infinity ? '∞' : format('.4~g')(value);
|
|
118
|
-
const suffix = scale.unit === 'dB' ? ' dB' : scale.id === 'vswr' ? ' : 1' : '';
|
|
119
|
-
const readout = number + suffix;
|
|
120
|
-
const label = `${scale.title}: ${readout}${scale.description ? `. ${scale.description}` : ''}`;
|
|
121
|
-
axis.select('.scale-value').text(readout);
|
|
122
|
-
axis.attr('aria-label', label).select('title').text(label);
|
|
123
124
|
axis
|
|
124
125
|
.select('circle')
|
|
125
|
-
.attr('
|
|
126
|
-
.
|
|
127
|
-
.attr('
|
|
128
|
-
|
|
126
|
+
.attr('visibility', 'hidden')
|
|
127
|
+
.style('visibility', 'hidden')
|
|
128
|
+
.attr('data-position', null);
|
|
129
|
+
axis.select('[data-role="scale-value"]').text('—');
|
|
130
|
+
axis.attr('aria-label', `${scale.title}. ${scale.description ?? ''}`.trim());
|
|
131
|
+
axis.select('title').text(scale.description ?? scale.title);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const value = scale.read(gamma);
|
|
135
|
+
const position = Math.max(0, Math.min(1, scale.position(value)));
|
|
136
|
+
const number = value === Infinity ? '∞' : format('.4~g')(value);
|
|
137
|
+
const suffix = scale.unit === 'dB' ? ' dB' : scale.id === 'vswr' ? ' : 1' : '';
|
|
138
|
+
const readout = number + suffix;
|
|
139
|
+
const label = `${scale.title}: ${readout}${scale.description ? `. ${scale.description}` : ''}`;
|
|
140
|
+
axis.select('[data-role="scale-value"]').text(readout);
|
|
141
|
+
axis.attr('aria-label', label).select('title').text(label);
|
|
142
|
+
axis
|
|
143
|
+
.select('circle')
|
|
144
|
+
.attr('cx', this.start + position * this.length)
|
|
145
|
+
.attr('data-position', position)
|
|
146
|
+
.attr('visibility', null)
|
|
147
|
+
.style('visibility', 'visible');
|
|
148
|
+
}
|
|
149
|
+
/** Export mounted scales, optionally with a separate reading and its description. */
|
|
150
|
+
toSvg(options) {
|
|
151
|
+
this.assertAlive();
|
|
152
|
+
const container = this.container.node();
|
|
153
|
+
const style = getComputedStyle(container);
|
|
154
|
+
const readout = options?.readout;
|
|
155
|
+
const source = SvgExporter.scales(container, this.axes.nodes(), readout
|
|
156
|
+
? (copy, index) => this.updateAxis(copy, this.scales[index], readout.reflectionCoefficient)
|
|
157
|
+
: undefined, readout?.label
|
|
158
|
+
? {
|
|
159
|
+
text: readout.label,
|
|
160
|
+
color: style.getPropertyValue('--smithkit-scales-textColor').trim(),
|
|
161
|
+
fontFamily: style.getPropertyValue('--smithkit-fontFamily').trim(),
|
|
162
|
+
}
|
|
163
|
+
: undefined);
|
|
164
|
+
return options === undefined ? source : ImageExporter.svg([source], options);
|
|
165
|
+
}
|
|
166
|
+
/** Export mounted scales as PNG without changing their live readings. */
|
|
167
|
+
async toPng(options = {}) {
|
|
168
|
+
return ImageExporter.png([this.toSvg({ readout: options.readout })], options);
|
|
129
169
|
}
|
|
130
170
|
destroy() {
|
|
131
171
|
if (this.destroyed) {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ImageExportOptions } from './export.js';
|
|
2
|
+
interface Legend {
|
|
3
|
+
entries: {
|
|
4
|
+
name: string;
|
|
5
|
+
color: string;
|
|
6
|
+
}[];
|
|
7
|
+
textColor: string;
|
|
8
|
+
fontFamily: string;
|
|
9
|
+
}
|
|
10
|
+
/** Compose SVG snapshots and rasterize PNG reports without changing live chart nodes. */
|
|
11
|
+
export declare class ImageExporter {
|
|
12
|
+
private constructor();
|
|
13
|
+
private static layout;
|
|
14
|
+
static png(sources: string[], options: ImageExportOptions, legend?: Legend): Promise<Blob>;
|
|
15
|
+
static svg(sources: string[], options: ImageExportOptions, legend?: Legend): string;
|
|
16
|
+
private static wrap;
|
|
17
|
+
}
|
|
18
|
+
export {};
|