smithkit 0.2.0 → 0.4.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 +318 -25
- package/dist/lib/OptionsValidation.d.ts +16 -0
- package/dist/lib/OptionsValidation.js +168 -0
- package/dist/lib/Smith.d.ts +24 -39
- package/dist/lib/Smith.js +175 -287
- 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 +125 -0
- package/dist/lib/appearance/types.d.ts +55 -0
- package/dist/lib/appearance/types.js +1 -0
- package/dist/lib/grid/CircleLayerControl.d.ts +1 -1
- package/dist/lib/grid/CircleLayerControl.js +10 -8
- package/dist/lib/grid/ConstCircles.d.ts +0 -3
- package/dist/lib/grid/ConstCircles.js +6 -20
- package/dist/lib/grid/ConstQCircles.d.ts +1 -0
- package/dist/lib/grid/ConstQCircles.js +6 -1
- package/dist/lib/grid/ConstSwrCircles.d.ts +1 -0
- package/dist/lib/grid/ConstSwrCircles.js +6 -1
- package/dist/lib/grid/GridDefinitions.d.ts +7 -1
- package/dist/lib/grid/GridDefinitions.js +29 -0
- package/dist/lib/grid/GridLayerControl.d.ts +5 -4
- package/dist/lib/grid/GridLayerControl.js +13 -3
- package/dist/lib/grid/LayerControl.d.ts +1 -2
- package/dist/lib/grid/LayerControl.js +1 -10
- package/dist/lib/grid/SmithGridLayer.d.ts +7 -1
- package/dist/lib/grid/SmithGridLayer.js +35 -5
- package/dist/lib/index.d.ts +4 -1
- package/dist/lib/interaction/SmithCursor.d.ts +0 -17
- package/dist/lib/interaction/SmithCursor.js +6 -14
- package/dist/lib/layers.d.ts +7 -1
- package/dist/lib/options.d.ts +54 -0
- package/dist/lib/options.js +1 -0
- package/dist/lib/rendering/SvgChartRenderer.d.ts +69 -0
- package/dist/lib/rendering/SvgChartRenderer.js +364 -0
- package/dist/lib/scales/SmithPeripheralScales.js +11 -11
- package/dist/lib/scales/SmithScales.d.ts +10 -3
- package/dist/lib/scales/SmithScales.js +73 -39
- package/dist/lib/svg/ImageExporter.d.ts +18 -0
- package/dist/lib/svg/ImageExporter.js +211 -0
- package/dist/lib/svg/MarkerLegend.d.ts +12 -0
- package/dist/lib/svg/MarkerLegend.js +63 -0
- package/dist/lib/svg/SvgExporter.d.ts +6 -1
- package/dist/lib/svg/SvgExporter.js +50 -5
- package/dist/lib/svg/export.d.ts +36 -0
- package/dist/lib/svg/export.js +1 -0
- package/dist/lib/traces/SmithMarker.js +8 -5
- package/package.json +1 -1
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { color } from 'd3';
|
|
2
|
+
/** Resolve and validate a detached theme before touching any chart state or DOM. */
|
|
3
|
+
export class Theme {
|
|
4
|
+
constructor() { }
|
|
5
|
+
static resolve(appearance = {}) {
|
|
6
|
+
if (!appearance || typeof appearance !== 'object' || Array.isArray(appearance)) {
|
|
7
|
+
throw new TypeError('Appearance must be an object.');
|
|
8
|
+
}
|
|
9
|
+
for (const key of Object.keys(appearance)) {
|
|
10
|
+
if (key !== 'theme' && key !== 'overrides') {
|
|
11
|
+
throw new TypeError(`Unknown appearance option: ${key}.`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const name = appearance.theme === undefined ? 'light' : appearance.theme;
|
|
15
|
+
if (name !== 'light' && name !== 'dark') {
|
|
16
|
+
throw new TypeError('Theme must be light or dark.');
|
|
17
|
+
}
|
|
18
|
+
const dark = name === 'dark';
|
|
19
|
+
const theme = {
|
|
20
|
+
background: dark ? '#0f172a' : 'transparent',
|
|
21
|
+
fontFamily: 'Verdana, sans-serif',
|
|
22
|
+
grid: {
|
|
23
|
+
stroke: dark ? '#94a3b8' : '#64748b',
|
|
24
|
+
textColor: dark ? '#e2e8f0' : '#111827',
|
|
25
|
+
majorWidth: 1,
|
|
26
|
+
minorWidth: 0.6,
|
|
27
|
+
fontSize: 7,
|
|
28
|
+
},
|
|
29
|
+
boundary: { stroke: dark ? '#cbd5e1' : '#334155', width: 1 },
|
|
30
|
+
circles: { q: dark ? '#93c5fd' : 'blue', vswr: dark ? '#fbbf24' : 'orange', width: 1 },
|
|
31
|
+
scales: {
|
|
32
|
+
stroke: dark ? '#94a3b8' : '#64748b',
|
|
33
|
+
boundaryColor: dark ? '#cbd5e1' : '#334155',
|
|
34
|
+
textColor: dark ? '#e2e8f0' : '#334155',
|
|
35
|
+
valueColor: dark ? '#fca5a5' : '#b42318',
|
|
36
|
+
indicatorColor: dark ? '#f87171' : '#dc2626',
|
|
37
|
+
indicatorOutline: dark ? '#0f172a' : 'white',
|
|
38
|
+
fontSize: 10,
|
|
39
|
+
titleFontSize: 12,
|
|
40
|
+
valueFontSize: 11,
|
|
41
|
+
peripheralFontSize: 10.5,
|
|
42
|
+
},
|
|
43
|
+
cursor: {
|
|
44
|
+
pointColor: dark ? '#fca5a5' : 'red',
|
|
45
|
+
impedanceColor: dark ? '#fca5a5' : 'red',
|
|
46
|
+
admittanceColor: dark ? '#86efac' : 'green',
|
|
47
|
+
pointSize: 5,
|
|
48
|
+
lineWidth: 1,
|
|
49
|
+
},
|
|
50
|
+
marker: {
|
|
51
|
+
outlineColor: dark ? '#e2e8f0' : 'gray',
|
|
52
|
+
textColor: 'white',
|
|
53
|
+
focusColor: dark ? '#93c5fd' : '#1d4ed8',
|
|
54
|
+
focusHaloColor: dark ? '#0f172a' : 'white',
|
|
55
|
+
},
|
|
56
|
+
traceColors: dark
|
|
57
|
+
? [
|
|
58
|
+
'#fb923c',
|
|
59
|
+
'#4ade80',
|
|
60
|
+
'#f87171',
|
|
61
|
+
'#c084fc',
|
|
62
|
+
'#d6a681',
|
|
63
|
+
'#f472b6',
|
|
64
|
+
'#cbd5e1',
|
|
65
|
+
'#d4d45c',
|
|
66
|
+
'#22d3ee',
|
|
67
|
+
'#60a5fa',
|
|
68
|
+
]
|
|
69
|
+
: [
|
|
70
|
+
'#ff7f0e',
|
|
71
|
+
'#2ca02c',
|
|
72
|
+
'#d62728',
|
|
73
|
+
'#9467bd',
|
|
74
|
+
'#8c564b',
|
|
75
|
+
'#e377c2',
|
|
76
|
+
'#7f7f7f',
|
|
77
|
+
'#bcbd22',
|
|
78
|
+
'#17becf',
|
|
79
|
+
'#1f77b4',
|
|
80
|
+
],
|
|
81
|
+
};
|
|
82
|
+
if (appearance.overrides !== undefined) {
|
|
83
|
+
Theme.merge(theme, appearance.overrides);
|
|
84
|
+
}
|
|
85
|
+
return theme;
|
|
86
|
+
}
|
|
87
|
+
static merge(target, source, path = 'appearance') {
|
|
88
|
+
if (!source || typeof source !== 'object' || Array.isArray(source)) {
|
|
89
|
+
throw new TypeError(`${path} overrides must be an object.`);
|
|
90
|
+
}
|
|
91
|
+
for (const [key, value] of Object.entries(source)) {
|
|
92
|
+
if (!Object.hasOwn(target, key)) {
|
|
93
|
+
throw new TypeError(`Unknown appearance setting: ${path}.${key}.`);
|
|
94
|
+
}
|
|
95
|
+
if (value === undefined) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const previous = target[key];
|
|
99
|
+
if (Array.isArray(previous)) {
|
|
100
|
+
if (!Array.isArray(value) || value.length === 0 || !value.every(Theme.isColor)) {
|
|
101
|
+
throw new TypeError('Trace colors must be a non-empty array of solid CSS colors.');
|
|
102
|
+
}
|
|
103
|
+
target[key] = [...value];
|
|
104
|
+
}
|
|
105
|
+
else if (typeof previous === 'object') {
|
|
106
|
+
Theme.merge(previous, value, `${path}.${key}`);
|
|
107
|
+
}
|
|
108
|
+
else if (typeof previous === 'number') {
|
|
109
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) {
|
|
110
|
+
throw new RangeError(`${path}.${key} must be positive and finite.`);
|
|
111
|
+
}
|
|
112
|
+
target[key] = value;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
if (key === 'fontFamily' ? typeof value !== 'string' || !value.trim() : !Theme.isColor(value)) {
|
|
116
|
+
throw new TypeError(`${path}.${key} must be ${key === 'fontFamily' ? 'a non-empty font family' : 'a solid CSS color'}.`);
|
|
117
|
+
}
|
|
118
|
+
target[key] = value;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
static isColor(value) {
|
|
123
|
+
return typeof value === 'string' && color(value) !== null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** Resolved appearance shared by charts and independent scales. Lengths are positive. */
|
|
2
|
+
export interface SmithTheme {
|
|
3
|
+
background: string;
|
|
4
|
+
fontFamily: string;
|
|
5
|
+
grid: {
|
|
6
|
+
stroke: string;
|
|
7
|
+
textColor: string;
|
|
8
|
+
majorWidth: number;
|
|
9
|
+
minorWidth: number;
|
|
10
|
+
fontSize: number;
|
|
11
|
+
};
|
|
12
|
+
boundary: {
|
|
13
|
+
stroke: string;
|
|
14
|
+
width: number;
|
|
15
|
+
};
|
|
16
|
+
circles: {
|
|
17
|
+
q: string;
|
|
18
|
+
vswr: string;
|
|
19
|
+
width: number;
|
|
20
|
+
};
|
|
21
|
+
scales: {
|
|
22
|
+
stroke: string;
|
|
23
|
+
boundaryColor: string;
|
|
24
|
+
textColor: string;
|
|
25
|
+
valueColor: string;
|
|
26
|
+
indicatorColor: string;
|
|
27
|
+
indicatorOutline: string;
|
|
28
|
+
fontSize: number;
|
|
29
|
+
titleFontSize: number;
|
|
30
|
+
valueFontSize: number;
|
|
31
|
+
peripheralFontSize: number;
|
|
32
|
+
};
|
|
33
|
+
cursor: {
|
|
34
|
+
pointColor: string;
|
|
35
|
+
impedanceColor: string;
|
|
36
|
+
admittanceColor: string;
|
|
37
|
+
pointSize: number;
|
|
38
|
+
lineWidth: number;
|
|
39
|
+
};
|
|
40
|
+
marker: {
|
|
41
|
+
outlineColor: string;
|
|
42
|
+
textColor: string;
|
|
43
|
+
focusColor: string;
|
|
44
|
+
focusHaloColor: string;
|
|
45
|
+
};
|
|
46
|
+
traceColors: readonly string[];
|
|
47
|
+
}
|
|
48
|
+
export type SmithThemeOverrides = {
|
|
49
|
+
[K in keyof SmithTheme]?: SmithTheme[K] extends readonly string[] ? readonly string[] : SmithTheme[K] extends object ? Partial<SmithTheme[K]> : SmithTheme[K];
|
|
50
|
+
};
|
|
51
|
+
/** Each application replaces the previous appearance, starting from the named preset. */
|
|
52
|
+
export interface SmithAppearance {
|
|
53
|
+
theme?: 'light' | 'dark';
|
|
54
|
+
overrides?: SmithThemeOverrides;
|
|
55
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -9,7 +9,7 @@ export declare class CircleLayerControl extends LayerControl implements CircleLa
|
|
|
9
9
|
constructor(layer: ConstQCircles | ConstSwrCircles, minimum: number, assertAlive: () => void);
|
|
10
10
|
setVisible(visible: boolean): void;
|
|
11
11
|
setStyle(style: Partial<CircleStyle>): void;
|
|
12
|
+
setValues(values: readonly number[]): void;
|
|
12
13
|
addValue(value: number): void;
|
|
13
14
|
removeValue(value: number): void;
|
|
14
|
-
private validateValue;
|
|
15
15
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { OptionsValidation } from '../OptionsValidation.js';
|
|
1
2
|
import { LayerControl } from './LayerControl.js';
|
|
2
3
|
/** Exposes Q/VSWR circle controls without exposing the SVG renderer. */
|
|
3
4
|
export class CircleLayerControl extends LayerControl {
|
|
@@ -10,11 +11,12 @@ export class CircleLayerControl extends LayerControl {
|
|
|
10
11
|
}
|
|
11
12
|
setVisible(visible) {
|
|
12
13
|
this.assertAlive();
|
|
14
|
+
OptionsValidation.boolean(visible, 'Circle visibility');
|
|
13
15
|
this.layer.visibility(visible);
|
|
14
16
|
}
|
|
15
17
|
setStyle(style) {
|
|
16
18
|
this.assertAlive();
|
|
17
|
-
|
|
19
|
+
OptionsValidation.circleStyle(style);
|
|
18
20
|
if (style.stroke !== undefined) {
|
|
19
21
|
this.layer.Stroke = style.stroke;
|
|
20
22
|
}
|
|
@@ -22,19 +24,19 @@ export class CircleLayerControl extends LayerControl {
|
|
|
22
24
|
this.layer.StrokeWidth = String(style.strokeWidth);
|
|
23
25
|
}
|
|
24
26
|
}
|
|
27
|
+
setValues(values) {
|
|
28
|
+
this.assertAlive();
|
|
29
|
+
OptionsValidation.circleValues(values, this.minimum);
|
|
30
|
+
this.layer.setValues(values);
|
|
31
|
+
}
|
|
25
32
|
addValue(value) {
|
|
26
33
|
this.assertAlive();
|
|
27
|
-
|
|
34
|
+
OptionsValidation.circleValue(value, this.minimum);
|
|
28
35
|
this.layer.append(value);
|
|
29
36
|
}
|
|
30
37
|
removeValue(value) {
|
|
31
38
|
this.assertAlive();
|
|
32
|
-
|
|
39
|
+
OptionsValidation.circleValue(value, this.minimum);
|
|
33
40
|
this.layer.remove(value);
|
|
34
41
|
}
|
|
35
|
-
validateValue(value) {
|
|
36
|
-
if (!Number.isFinite(value) || value < this.minimum || value <= 0) {
|
|
37
|
-
throw new RangeError(`Circle value must be finite, positive, and at least ${this.minimum}.`);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
42
|
}
|
|
@@ -35,9 +35,6 @@ export declare abstract class ConstCircles {
|
|
|
35
35
|
visibility(visible: boolean): void;
|
|
36
36
|
show(): void;
|
|
37
37
|
hide(): void;
|
|
38
|
-
displayMinor(display: boolean): void;
|
|
39
|
-
showMinor(): void;
|
|
40
|
-
hideMinor(): void;
|
|
41
38
|
protected build(): SmithGroup;
|
|
42
39
|
/** Mount labels separately so every grid line is painted before any label. */
|
|
43
40
|
get labels(): SmithGroup;
|
|
@@ -26,20 +26,6 @@ export class ConstCircles {
|
|
|
26
26
|
this.container.hide();
|
|
27
27
|
this.texts.hide();
|
|
28
28
|
}
|
|
29
|
-
displayMinor(display) {
|
|
30
|
-
if (display) {
|
|
31
|
-
this.minor.show();
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
this.minor.hide();
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
showMinor() {
|
|
38
|
-
this.minor.show();
|
|
39
|
-
}
|
|
40
|
-
hideMinor() {
|
|
41
|
-
this.minor.hide();
|
|
42
|
-
}
|
|
43
29
|
build() {
|
|
44
30
|
this.texts.hide();
|
|
45
31
|
return this.container.append(this.minor).append(this.major).hide();
|
|
@@ -108,12 +94,12 @@ export class ConstCircles {
|
|
|
108
94
|
defaultDrawingOptions() {
|
|
109
95
|
return {
|
|
110
96
|
// Keep curved grid strokes visible at normal browser zoom and low pixel density.
|
|
111
|
-
stroke: '
|
|
112
|
-
majorWidth: '
|
|
113
|
-
minorWidth: '
|
|
114
|
-
textColor: '
|
|
115
|
-
textFontFamily: '
|
|
116
|
-
textFontSize: '
|
|
97
|
+
stroke: 'var(--smithkit-grid-stroke)',
|
|
98
|
+
majorWidth: 'var(--smithkit-grid-majorWidth)',
|
|
99
|
+
minorWidth: 'var(--smithkit-grid-minorWidth)',
|
|
100
|
+
textColor: 'var(--smithkit-grid-textColor)',
|
|
101
|
+
textFontFamily: 'var(--smithkit-fontFamily)',
|
|
102
|
+
textFontSize: 'var(--smithkit-grid-fontSize)',
|
|
117
103
|
};
|
|
118
104
|
}
|
|
119
105
|
scaleArc(scaler, d) {
|
|
@@ -28,7 +28,7 @@ export class ConstQCircles {
|
|
|
28
28
|
this.container.append(a2);
|
|
29
29
|
}
|
|
30
30
|
getDefaultDrawOptions() {
|
|
31
|
-
return { stroke: '
|
|
31
|
+
return { stroke: 'var(--smithkit-circles-q)', strokeWidth: 'var(--smithkit-circles-width)' };
|
|
32
32
|
}
|
|
33
33
|
setDrawOptions(opts) {
|
|
34
34
|
this.opts = opts;
|
|
@@ -74,6 +74,11 @@ export class ConstQCircles {
|
|
|
74
74
|
this.circles.push(Q);
|
|
75
75
|
this.drawConstQCircle(Q);
|
|
76
76
|
}
|
|
77
|
+
setValues(values) {
|
|
78
|
+
this.circles = [...new Set(values)];
|
|
79
|
+
this.container.Element.selectAll('*').remove();
|
|
80
|
+
this.drawConstQCircles(this.circles);
|
|
81
|
+
}
|
|
77
82
|
remove(Q) {
|
|
78
83
|
const index = this.circles.indexOf(Q);
|
|
79
84
|
if (index === -1) {
|
|
@@ -27,7 +27,7 @@ export class ConstSwrCircles {
|
|
|
27
27
|
this.container.append(circle);
|
|
28
28
|
}
|
|
29
29
|
getDefaultDrawOptions() {
|
|
30
|
-
return { stroke: '
|
|
30
|
+
return { stroke: 'var(--smithkit-circles-vswr)', strokeWidth: 'var(--smithkit-circles-width)' };
|
|
31
31
|
}
|
|
32
32
|
setDrawOptions(opts) {
|
|
33
33
|
this.opts = opts;
|
|
@@ -73,6 +73,11 @@ export class ConstSwrCircles {
|
|
|
73
73
|
this.circles.push(swr);
|
|
74
74
|
this.drawConstSwrCircle(swr);
|
|
75
75
|
}
|
|
76
|
+
setValues(values) {
|
|
77
|
+
this.circles = [...new Set(values)];
|
|
78
|
+
this.container.Element.selectAll('*').remove();
|
|
79
|
+
this.drawConstSwrCircles(this.circles);
|
|
80
|
+
}
|
|
76
81
|
remove(swr) {
|
|
77
82
|
const index = this.circles.indexOf(swr);
|
|
78
83
|
if (index === -1) {
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import type { SmithTicksData } from './types.js';
|
|
1
|
+
import type { SmithTicksData, SmithTicksShapes } from './types.js';
|
|
2
2
|
export declare class GridDefinitions {
|
|
3
3
|
private constructor();
|
|
4
|
+
static readonly basicValues: readonly number[];
|
|
5
|
+
/** Complete principal circles and boundary-to-axis arcs for a sparse grid. */
|
|
6
|
+
static basic(): {
|
|
7
|
+
resistance: SmithTicksShapes;
|
|
8
|
+
reactance: SmithTicksShapes;
|
|
9
|
+
};
|
|
4
10
|
private static readonly resistanceMajor;
|
|
5
11
|
private static readonly resistanceMinor;
|
|
6
12
|
private static readonly reactanceMajor;
|
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
export class GridDefinitions {
|
|
2
2
|
constructor() { }
|
|
3
|
+
static basicValues = Object.freeze([0.2, 0.5, 1, 2, 5]);
|
|
4
|
+
/** Complete principal circles and boundary-to-axis arcs for a sparse grid. */
|
|
5
|
+
static basic() {
|
|
6
|
+
return {
|
|
7
|
+
resistance: { lines: [], circles: [...GridDefinitions.basicValues], arcs: [] },
|
|
8
|
+
reactance: {
|
|
9
|
+
lines: [{ p1: [-1, 0], p2: [1, 0] }],
|
|
10
|
+
circles: [],
|
|
11
|
+
arcs: GridDefinitions.basicValues.flatMap((value) => [
|
|
12
|
+
[
|
|
13
|
+
value,
|
|
14
|
+
[
|
|
15
|
+
[0, 0],
|
|
16
|
+
[0, 1],
|
|
17
|
+
],
|
|
18
|
+
[false, false],
|
|
19
|
+
],
|
|
20
|
+
[
|
|
21
|
+
-value,
|
|
22
|
+
[
|
|
23
|
+
[0, 1],
|
|
24
|
+
[0, 0],
|
|
25
|
+
],
|
|
26
|
+
[false, true],
|
|
27
|
+
],
|
|
28
|
+
]),
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
3
32
|
static resistanceMajor = [
|
|
4
33
|
[[0.05, 0.15], 0.2, -0.2, false],
|
|
5
34
|
[[0.1, 0.3, 0.5, 0.7, 0.9], 2, -2, true],
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { GridLayer, GridStyle } from '../layers.js';
|
|
1
|
+
import type { SmithGridLayer } from './SmithGridLayer.js';
|
|
2
|
+
import type { GridDetail, GridLayer, GridStyle } from '../layers.js';
|
|
3
3
|
import { LayerControl } from './LayerControl.js';
|
|
4
4
|
/** Exposes grid controls without exposing the SVG renderer. */
|
|
5
5
|
export declare class GridLayerControl extends LayerControl implements GridLayer {
|
|
6
6
|
private readonly layer;
|
|
7
7
|
private readonly changed;
|
|
8
|
-
constructor(layer:
|
|
8
|
+
constructor(layer: SmithGridLayer, assertAlive: () => void, changed: () => void);
|
|
9
9
|
setVisible(visible: boolean): void;
|
|
10
|
-
|
|
10
|
+
setLabelsVisible(visible: boolean): void;
|
|
11
|
+
setDetail(detail: GridDetail): void;
|
|
11
12
|
setStyle(style: Partial<GridStyle>): void;
|
|
12
13
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { OptionsValidation } from '../OptionsValidation.js';
|
|
1
2
|
import { LayerControl } from './LayerControl.js';
|
|
2
3
|
/** Exposes grid controls without exposing the SVG renderer. */
|
|
3
4
|
export class GridLayerControl extends LayerControl {
|
|
@@ -10,16 +11,25 @@ export class GridLayerControl extends LayerControl {
|
|
|
10
11
|
}
|
|
11
12
|
setVisible(visible) {
|
|
12
13
|
this.assertAlive();
|
|
14
|
+
OptionsValidation.boolean(visible, 'Grid visibility');
|
|
13
15
|
this.layer.visibility(visible);
|
|
14
16
|
this.changed();
|
|
15
17
|
}
|
|
16
|
-
|
|
18
|
+
setLabelsVisible(visible) {
|
|
17
19
|
this.assertAlive();
|
|
18
|
-
|
|
20
|
+
OptionsValidation.boolean(visible, 'Grid visibility');
|
|
21
|
+
this.layer.setLabelsVisible(visible);
|
|
22
|
+
this.changed();
|
|
23
|
+
}
|
|
24
|
+
setDetail(detail) {
|
|
25
|
+
this.assertAlive();
|
|
26
|
+
OptionsValidation.detail(detail);
|
|
27
|
+
this.layer.setDetail(detail);
|
|
28
|
+
this.changed();
|
|
19
29
|
}
|
|
20
30
|
setStyle(style) {
|
|
21
31
|
this.assertAlive();
|
|
22
|
-
|
|
32
|
+
OptionsValidation.gridStyle(style);
|
|
23
33
|
const properties = {
|
|
24
34
|
stroke: 'Stroke',
|
|
25
35
|
majorWidth: 'MajorWidth',
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
/** Shared lifecycle
|
|
1
|
+
/** Shared lifecycle guard for chart layer adapters. */
|
|
2
2
|
export declare abstract class LayerControl {
|
|
3
3
|
protected readonly assertAlive: () => void;
|
|
4
4
|
protected constructor(assertAlive: () => void);
|
|
5
|
-
protected validateLengths<T>(style: T, keys: readonly (keyof T)[]): void;
|
|
6
5
|
}
|
|
@@ -1,16 +1,7 @@
|
|
|
1
|
-
/** Shared lifecycle
|
|
1
|
+
/** Shared lifecycle guard for chart layer adapters. */
|
|
2
2
|
export class LayerControl {
|
|
3
3
|
assertAlive;
|
|
4
4
|
constructor(assertAlive) {
|
|
5
5
|
this.assertAlive = assertAlive;
|
|
6
6
|
}
|
|
7
|
-
validateLengths(style, keys) {
|
|
8
|
-
for (const key of keys) {
|
|
9
|
-
const value = style[key];
|
|
10
|
-
if (value !== undefined &&
|
|
11
|
-
(typeof value !== 'number' || !Number.isFinite(value) || value <= 0)) {
|
|
12
|
-
throw new RangeError(`${String(key)} must be a positive finite number.`);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
7
|
}
|
|
@@ -3,9 +3,10 @@ import { SmithGroup } from '../svg/SmithGroup.js';
|
|
|
3
3
|
import type { SmithScaler } from '../svg/SmithScaler.js';
|
|
4
4
|
import type { SmithTicksData } from './types.js';
|
|
5
5
|
import { type GridKind } from './GridGeometry.js';
|
|
6
|
+
import type { GridDetail } from '../layers.js';
|
|
6
7
|
export interface GridLayerParams {
|
|
7
8
|
scaler: SmithScaler;
|
|
8
|
-
|
|
9
|
+
detail: GridDetail;
|
|
9
10
|
data: SmithTicksData;
|
|
10
11
|
}
|
|
11
12
|
/** SVG adapter shared by the four constant impedance/admittance layers. */
|
|
@@ -14,7 +15,12 @@ export declare class SmithGridLayer extends ConstCircles {
|
|
|
14
15
|
protected major: SmithGroup;
|
|
15
16
|
protected minor: SmithGroup;
|
|
16
17
|
protected texts: SmithGroup;
|
|
18
|
+
private readonly standardDefinitions;
|
|
19
|
+
private readonly basicDefinitions;
|
|
20
|
+
private detail;
|
|
17
21
|
constructor(params: GridLayerParams, kind: GridKind);
|
|
22
|
+
setLabelsVisible(visible: boolean): void;
|
|
23
|
+
setDetail(detail: GridDetail): void;
|
|
18
24
|
private static labelPriority;
|
|
19
25
|
private drawGrid;
|
|
20
26
|
}
|
|
@@ -2,6 +2,7 @@ import { ConstCircles } from './ConstCircles.js';
|
|
|
2
2
|
import { SmithGroup } from '../svg/SmithGroup.js';
|
|
3
3
|
import { SmithText } from '../svg/SmithText.js';
|
|
4
4
|
import { GridGeometry } from './GridGeometry.js';
|
|
5
|
+
import { GridDefinitions } from './GridDefinitions.js';
|
|
5
6
|
import { GridLabels } from './GridLabels.js';
|
|
6
7
|
/** SVG adapter shared by the four constant impedance/admittance layers. */
|
|
7
8
|
export class SmithGridLayer extends ConstCircles {
|
|
@@ -9,17 +10,23 @@ export class SmithGridLayer extends ConstCircles {
|
|
|
9
10
|
major;
|
|
10
11
|
minor;
|
|
11
12
|
texts;
|
|
13
|
+
standardDefinitions;
|
|
14
|
+
basicDefinitions;
|
|
15
|
+
detail = 'detailed';
|
|
12
16
|
constructor(params, kind) {
|
|
13
17
|
super(params.scaler);
|
|
14
18
|
this.kind = kind;
|
|
15
19
|
const real = kind === 'resistance' || kind === 'conductance';
|
|
16
20
|
const definitions = params.data[real ? 'resistance' : 'reactance'];
|
|
21
|
+
this.standardDefinitions = definitions.major;
|
|
22
|
+
this.basicDefinitions = GridDefinitions.basic()[real ? 'resistance' : 'reactance'];
|
|
17
23
|
this.major = this.drawGrid(definitions.major, this.opts.majorWidth);
|
|
18
24
|
this.minor = this.drawGrid(definitions.minor, this.opts.minorWidth);
|
|
19
25
|
this.texts = new SmithGroup()
|
|
20
26
|
.attr('stroke', 'none')
|
|
21
|
-
.attr('font-size',
|
|
22
|
-
.attr('font-family',
|
|
27
|
+
.attr('font-size', this.opts.textFontSize)
|
|
28
|
+
.attr('font-family', this.opts.textFontFamily)
|
|
29
|
+
.attr('fill', this.opts.textColor);
|
|
23
30
|
for (const tick of real ? GridLabels.resistance() : GridLabels.reactance()) {
|
|
24
31
|
const label = GridGeometry.label(kind, tick.definition);
|
|
25
32
|
const text = new SmithText(this.scaler.point(label.point), label.text, {
|
|
@@ -30,12 +37,36 @@ export class SmithGridLayer extends ConstCircles {
|
|
|
30
37
|
dominantBaseline: label.dominantBaseline,
|
|
31
38
|
});
|
|
32
39
|
text.Element.attr('data-label-priority', SmithGridLayer.labelPriority(tick.definition.point.r, tick.definition.point.i));
|
|
40
|
+
const { r, i } = tick.definition.point;
|
|
41
|
+
const basic = real
|
|
42
|
+
? i === 0 && (r === 0 || GridDefinitions.basicValues.includes(r))
|
|
43
|
+
: r === 0 && GridDefinitions.basicValues.includes(Math.abs(i));
|
|
44
|
+
text.Element.attr('data-basic-label', String(basic));
|
|
33
45
|
this.texts.append(text);
|
|
34
46
|
}
|
|
35
47
|
this.build();
|
|
36
|
-
|
|
48
|
+
this.setDetail(params.detail);
|
|
49
|
+
}
|
|
50
|
+
setLabelsVisible(visible) {
|
|
51
|
+
// Keep this separate from whole-layer opacity and detail-specific label filtering.
|
|
52
|
+
this.texts.attr('display', visible ? null : 'none');
|
|
53
|
+
}
|
|
54
|
+
setDetail(detail) {
|
|
55
|
+
if (detail === this.detail) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if ((detail === 'basic') !== (this.detail === 'basic')) {
|
|
59
|
+
this.major.Element.selectAll('*').remove();
|
|
60
|
+
this.drawGrid(detail === 'basic' ? this.basicDefinitions : this.standardDefinitions, this.opts.majorWidth, this.major);
|
|
61
|
+
}
|
|
62
|
+
if (detail === 'detailed') {
|
|
63
|
+
this.minor.show();
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
37
66
|
this.minor.hide();
|
|
38
67
|
}
|
|
68
|
+
this.texts.Element.selectAll('[data-basic-label="false"]').attr('display', detail === 'basic' ? 'none' : null);
|
|
69
|
+
this.detail = detail;
|
|
39
70
|
}
|
|
40
71
|
static labelPriority(real, imaginary) {
|
|
41
72
|
const value = Math.abs(imaginary === 0 ? real : imaginary);
|
|
@@ -52,9 +83,8 @@ export class SmithGridLayer extends ConstCircles {
|
|
|
52
83
|
: 40;
|
|
53
84
|
return importance + (imaginary === 0 ? 20 : real === 0 ? 10 : 0);
|
|
54
85
|
}
|
|
55
|
-
drawGrid(definitions, width) {
|
|
86
|
+
drawGrid(definitions, width, group = new SmithGroup()) {
|
|
56
87
|
const geometry = GridGeometry.shapes(this.kind, definitions);
|
|
57
|
-
const group = new SmithGroup();
|
|
58
88
|
this.drawShapes(group.Element, this.opts.stroke, width, {
|
|
59
89
|
lines: geometry.lines.map((line) => this.scaler.line(line)),
|
|
60
90
|
circles: geometry.circles.map((circle) => this.scaler.circle(circle)),
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -9,6 +9,9 @@ export { MarkerMeasurements } from './MarkerMeasurements.js';
|
|
|
9
9
|
export type { TraceOptions, TraceUpdateOptions, MarkerSelectionStrategy, TraceStyle, TraceRenderMode, TraceInfo, MarkerInfo, MarkerSnapshot, MarkerComparison, } from './measurements.js';
|
|
10
10
|
export { RfCalculations } from './rf/RfCalculations.js';
|
|
11
11
|
export type { SmithReading } from './rf/RfCalculations.js';
|
|
12
|
-
export type { GridStyle, CircleStyle, GridLayer, CircleLayer, ChartLayers, PeripheralScales, } from './layers.js';
|
|
12
|
+
export type { GridStyle, CircleStyle, GridLayer, GridDetail, CircleLayer, ChartLayers, PeripheralScales, } from './layers.js';
|
|
13
13
|
export { SmithFormatter } from './SmithFormatter.js';
|
|
14
14
|
export type { ReactiveComponent } from './rf/RfCalculations.js';
|
|
15
|
+
export type { SmithAppearance, SmithTheme, SmithThemeOverrides } from './appearance/types.js';
|
|
16
|
+
export type { MarkerLegendField, MarkerLegendOptions, ImageExportOptions, SmithImageExportOptions, ScaleImageExportOptions, ScaleExportReadout, } from './svg/export.js';
|
|
17
|
+
export type { SmithOptions, GridOptions, GridLayerOptions, CircleLayerOptions, CircleOptions, InteractionOptions, PeripheralScalesOptions, } from './options.js';
|
|
@@ -1,25 +1,10 @@
|
|
|
1
1
|
import { SmithGroup } from '../svg/SmithGroup.js';
|
|
2
2
|
import { SmithScaler } from '../svg/SmithScaler.js';
|
|
3
3
|
import { Complex } from '../math/Complex.js';
|
|
4
|
-
interface DrawOptions {
|
|
5
|
-
point: {
|
|
6
|
-
radius: number;
|
|
7
|
-
color: string;
|
|
8
|
-
};
|
|
9
|
-
impedance: {
|
|
10
|
-
width: number;
|
|
11
|
-
color: string;
|
|
12
|
-
};
|
|
13
|
-
admittance: {
|
|
14
|
-
width: number;
|
|
15
|
-
color: string;
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
4
|
export declare class SmithCursor {
|
|
19
5
|
private scaler;
|
|
20
6
|
private epsilon;
|
|
21
7
|
private pendingMove;
|
|
22
|
-
private drawingOpts;
|
|
23
8
|
private calcs;
|
|
24
9
|
private zClipCircle;
|
|
25
10
|
private yClipCircle;
|
|
@@ -44,6 +29,4 @@ export declare class SmithCursor {
|
|
|
44
29
|
hide(): void;
|
|
45
30
|
private cancelPendingMove;
|
|
46
31
|
setMoveHandler(handler: ((rc: Complex) => void) | null): void;
|
|
47
|
-
setDrawOptions(opts: DrawOptions): void;
|
|
48
32
|
}
|
|
49
|
-
export {};
|