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.
Files changed (146) hide show
  1. package/README.md +291 -42
  2. package/dist/lib/MarkerMeasurements.d.ts +6 -0
  3. package/dist/lib/MarkerMeasurements.js +22 -0
  4. package/dist/lib/Smith.d.ts +18 -35
  5. package/dist/lib/Smith.js +107 -275
  6. package/dist/lib/SmithFormatter.d.ts +10 -0
  7. package/dist/lib/SmithFormatter.js +19 -0
  8. package/dist/lib/appearance/SvgTheme.d.ts +6 -0
  9. package/dist/lib/appearance/SvgTheme.js +21 -0
  10. package/dist/lib/appearance/Theme.d.ts +8 -0
  11. package/dist/lib/appearance/Theme.js +120 -0
  12. package/dist/lib/appearance/types.d.ts +55 -0
  13. package/dist/lib/grid/CircleLayerControl.d.ts +15 -0
  14. package/dist/lib/grid/CircleLayerControl.js +40 -0
  15. package/dist/lib/{draw → grid}/ConstCircles.d.ts +4 -9
  16. package/dist/lib/{draw → grid}/ConstCircles.js +8 -22
  17. package/dist/lib/{draw → grid}/ConstQCircles.d.ts +2 -2
  18. package/dist/lib/{draw → grid}/ConstQCircles.js +4 -4
  19. package/dist/lib/{draw → grid}/ConstSwrCircles.d.ts +2 -2
  20. package/dist/lib/{draw → grid}/ConstSwrCircles.js +4 -4
  21. package/dist/lib/grid/GridDefinitions.d.ts +18 -0
  22. package/dist/lib/grid/GridDefinitions.js +167 -0
  23. package/dist/lib/grid/GridGeometry.d.ts +26 -0
  24. package/dist/lib/grid/GridGeometry.js +58 -0
  25. package/dist/lib/grid/GridLabels.d.ts +8 -0
  26. package/dist/lib/grid/GridLabels.js +45 -0
  27. package/dist/lib/grid/GridLayerControl.d.ts +13 -0
  28. package/dist/lib/grid/GridLayerControl.js +48 -0
  29. package/dist/lib/grid/LayerControl.d.ts +6 -0
  30. package/dist/lib/grid/LayerControl.js +16 -0
  31. package/dist/lib/grid/SmithGridLayer.d.ts +26 -0
  32. package/dist/lib/grid/SmithGridLayer.js +95 -0
  33. package/dist/lib/grid/types.d.ts +1 -1
  34. package/dist/lib/index.d.ts +11 -11
  35. package/dist/lib/index.js +5 -7
  36. package/dist/lib/{draw → interaction}/SmithCursor.d.ts +3 -20
  37. package/dist/lib/{draw → interaction}/SmithCursor.js +12 -20
  38. package/dist/lib/io/Touchstone.d.ts +10 -0
  39. package/dist/lib/io/Touchstone.js +75 -0
  40. package/dist/lib/layers.d.ts +7 -9
  41. package/dist/lib/layers.js +1 -73
  42. package/dist/lib/{complex → math}/Complex.d.ts +1 -0
  43. package/dist/lib/{complex → math}/Complex.js +1 -1
  44. package/dist/lib/math/geometry.d.ts +14 -0
  45. package/dist/lib/measurements.d.ts +2 -4
  46. package/dist/lib/measurements.js +1 -19
  47. package/dist/lib/rendering/SvgChartRenderer.d.ts +66 -0
  48. package/dist/lib/rendering/SvgChartRenderer.js +342 -0
  49. package/dist/lib/rf/RfCalculations.d.ts +65 -0
  50. package/dist/lib/rf/RfCalculations.js +161 -0
  51. package/dist/lib/{SmithConstantCircle.d.ts → rf/SmithConstantCircle.d.ts} +2 -3
  52. package/dist/lib/{SmithConstantCircle.js → rf/SmithConstantCircle.js} +3 -4
  53. package/dist/lib/scales/PeripheralScaleDefinitions.d.ts +16 -0
  54. package/dist/lib/scales/PeripheralScaleDefinitions.js +54 -0
  55. package/dist/lib/scales/RadialScaleDefinitions.d.ts +15 -0
  56. package/dist/lib/scales/RadialScaleDefinitions.js +112 -0
  57. package/dist/lib/scales/SmithPeripheralScales.d.ts +4 -2
  58. package/dist/lib/scales/SmithPeripheralScales.js +55 -35
  59. package/dist/lib/scales/SmithScales.d.ts +11 -2
  60. package/dist/lib/scales/SmithScales.js +78 -38
  61. package/dist/lib/svg/ImageExporter.d.ts +18 -0
  62. package/dist/lib/svg/ImageExporter.js +211 -0
  63. package/dist/lib/svg/LabelLayout.d.ts +17 -0
  64. package/dist/lib/svg/LabelLayout.js +145 -0
  65. package/dist/lib/svg/MarkerLegend.d.ts +12 -0
  66. package/dist/lib/svg/MarkerLegend.js +63 -0
  67. package/dist/lib/{draw → svg}/SmithArc.d.ts +1 -1
  68. package/dist/lib/{draw → svg}/SmithCircle.d.ts +1 -1
  69. package/dist/lib/{draw → svg}/SmithLine.d.ts +1 -1
  70. package/dist/lib/{draw → svg}/SmithScaler.d.ts +1 -4
  71. package/dist/lib/{draw → svg}/SmithText.d.ts +1 -1
  72. package/dist/lib/svg/SvgExporter.d.ts +19 -0
  73. package/dist/lib/svg/SvgExporter.js +182 -0
  74. package/dist/lib/svg/export.d.ts +36 -0
  75. package/dist/lib/{draw → traces}/SmithData.d.ts +24 -35
  76. package/dist/lib/traces/SmithData.js +195 -0
  77. package/dist/lib/{draw → traces}/SmithMarker.d.ts +14 -3
  78. package/dist/lib/traces/SmithMarker.js +240 -0
  79. package/dist/lib/traces/TraceModel.d.ts +31 -0
  80. package/dist/lib/traces/TraceModel.js +123 -0
  81. package/dist/lib/traces/TraceRenderer.d.ts +33 -0
  82. package/dist/lib/traces/TraceRenderer.js +152 -0
  83. package/package.json +4 -1
  84. package/dist/lib/SmithArcsDefs.d.ts +0 -10
  85. package/dist/lib/SmithArcsDefs.js +0 -10
  86. package/dist/lib/components.d.ts +0 -14
  87. package/dist/lib/components.js +0 -26
  88. package/dist/lib/draw/SmithData.js +0 -366
  89. package/dist/lib/draw/SmithGridLayer.d.ts +0 -19
  90. package/dist/lib/draw/SmithGridLayer.js +0 -48
  91. package/dist/lib/draw/SmithMarker.js +0 -122
  92. package/dist/lib/formatting.d.ts +0 -7
  93. package/dist/lib/formatting.js +0 -16
  94. package/dist/lib/grid/definitions.d.ts +0 -3
  95. package/dist/lib/grid/definitions.js +0 -123
  96. package/dist/lib/grid/geometry.d.ts +0 -20
  97. package/dist/lib/grid/geometry.js +0 -55
  98. package/dist/lib/grid/labels.d.ts +0 -3
  99. package/dist/lib/grid/labels.js +0 -41
  100. package/dist/lib/io/touchstone.d.ts +0 -7
  101. package/dist/lib/io/touchstone.js +0 -72
  102. package/dist/lib/math/complexRatio.d.ts +0 -3
  103. package/dist/lib/math/complexRatio.js +0 -30
  104. package/dist/lib/renormalization.d.ts +0 -10
  105. package/dist/lib/renormalization.js +0 -62
  106. package/dist/lib/rf.d.ts +0 -35
  107. package/dist/lib/rf.js +0 -74
  108. package/dist/lib/scales/peripheralScales.d.ts +0 -12
  109. package/dist/lib/scales/peripheralScales.js +0 -49
  110. package/dist/lib/scales/radialScales.d.ts +0 -12
  111. package/dist/lib/scales/radialScales.js +0 -109
  112. package/dist/lib/shapes/Arc.d.ts +0 -6
  113. package/dist/lib/shapes/Circle.d.ts +0 -5
  114. package/dist/lib/shapes/Line.d.ts +0 -5
  115. package/dist/lib/shapes/Point.d.ts +0 -1
  116. package/dist/lib/shapes/Point.js +0 -1
  117. /package/dist/lib/{draw/SmithDrawOptions.js → appearance/types.js} +0 -0
  118. /package/dist/lib/{draw → grid}/ConstConductance.d.ts +0 -0
  119. /package/dist/lib/{draw → grid}/ConstConductance.js +0 -0
  120. /package/dist/lib/{draw → grid}/ConstReactance.d.ts +0 -0
  121. /package/dist/lib/{draw → grid}/ConstReactance.js +0 -0
  122. /package/dist/lib/{draw → grid}/ConstResistance.d.ts +0 -0
  123. /package/dist/lib/{draw → grid}/ConstResistance.js +0 -0
  124. /package/dist/lib/{draw → grid}/ConstSusceptance.d.ts +0 -0
  125. /package/dist/lib/{draw → grid}/ConstSusceptance.js +0 -0
  126. /package/dist/lib/{arcs → grid}/Tick.d.ts +0 -0
  127. /package/dist/lib/{arcs → grid}/Tick.js +0 -0
  128. /package/dist/lib/{draw → interaction}/MouseGesture.d.ts +0 -0
  129. /package/dist/lib/{draw → interaction}/MouseGesture.js +0 -0
  130. /package/dist/lib/{shapes/Arc.js → math/geometry.js} +0 -0
  131. /package/dist/lib/{draw → svg}/SmithArc.js +0 -0
  132. /package/dist/lib/{draw → svg}/SmithCircle.js +0 -0
  133. /package/dist/lib/{draw → svg}/SmithDrawOptions.d.ts +0 -0
  134. /package/dist/lib/{shapes/Circle.js → svg/SmithDrawOptions.js} +0 -0
  135. /package/dist/lib/{draw → svg}/SmithGroup.d.ts +0 -0
  136. /package/dist/lib/{draw → svg}/SmithGroup.js +0 -0
  137. /package/dist/lib/{draw → svg}/SmithLine.js +0 -0
  138. /package/dist/lib/{draw → svg}/SmithPath.d.ts +0 -0
  139. /package/dist/lib/{draw → svg}/SmithPath.js +0 -0
  140. /package/dist/lib/{draw → svg}/SmithScaler.js +0 -0
  141. /package/dist/lib/{draw → svg}/SmithShape.d.ts +0 -0
  142. /package/dist/lib/{draw → svg}/SmithShape.js +0 -0
  143. /package/dist/lib/{draw → svg}/SmithSvg.d.ts +0 -0
  144. /package/dist/lib/{draw → svg}/SmithSvg.js +0 -0
  145. /package/dist/lib/{draw → svg}/SmithText.js +0 -0
  146. /package/dist/lib/{shapes/Line.js → svg/export.js} +0 -0
@@ -1,19 +1 @@
1
- /** Compare B against A without a chart or browser. Frequencies must be finite and non-negative. */
2
- export function compareMarkerReadings(a, b) {
3
- if (![a.frequencyHz, b.frequencyHz].every((frequencyHz) => Number.isFinite(frequencyHz) && frequencyHz >= 0)) {
4
- throw new RangeError('Marker frequencies must be finite and non-negative.');
5
- }
6
- const isFinite = (value) => Number.isFinite(value.re) && Number.isFinite(value.im);
7
- const hasPhase = (gamma) => isFinite(gamma) && (gamma.re !== 0 || gamma.im !== 0);
8
- const phaseDelta = hasPhase(a.reflectionCoefficient) && hasPhase(b.reflectionCoefficient)
9
- ? ((b.reflectionCoefficient.arg() - a.reflectionCoefficient.arg()) * 180) / Math.PI
10
- : undefined;
11
- const impedanceDelta = a.impedanceOhms && b.impedanceOhms && isFinite(a.impedanceOhms) && isFinite(b.impedanceOhms)
12
- ? b.impedanceOhms.sub(a.impedanceOhms)
13
- : undefined;
14
- return {
15
- frequencyDeltaHz: b.frequencyHz - a.frequencyHz,
16
- impedanceDeltaOhms: impedanceDelta && isFinite(impedanceDelta) ? impedanceDelta : undefined,
17
- phaseDeltaDegrees: phaseDelta === undefined ? undefined : ((phaseDelta + 540) % 360) - 180,
18
- };
19
- }
1
+ export {};
@@ -0,0 +1,66 @@
1
+ import type { SmithTheme } from '../appearance/types.js';
2
+ import { SmithData } from '../traces/SmithData.js';
3
+ import type { SmithMarker } from '../traces/SmithMarker.js';
4
+ import { TraceSamples } from '../samples.js';
5
+ import { Complex } from '../math/Complex.js';
6
+ import type { ChartLayers, PeripheralScales } from '../layers.js';
7
+ import type { TraceStyle } from '../measurements.js';
8
+ /** Internal SVG backend. Owns chart elements, view transforms, and SVG gestures. */
9
+ export declare class SvgChartRenderer {
10
+ private onCursorChange;
11
+ private calcs;
12
+ private scalers;
13
+ private readonly defaultTransform;
14
+ private transform;
15
+ private zoomEnabled;
16
+ private applyingView;
17
+ private zoomBehavior;
18
+ private svg;
19
+ private resizeObserver;
20
+ private readonly labelLayout;
21
+ private container;
22
+ private dataContainer;
23
+ private markerContainer;
24
+ private reactanceAxis;
25
+ private constResistance;
26
+ private constReactance;
27
+ private constConductance;
28
+ private constSusceptance;
29
+ private constSwrCircles;
30
+ private constQCircles;
31
+ private cursor;
32
+ private peripheralScaleRenderer;
33
+ private readonly traces;
34
+ private destroyed;
35
+ private mouseGesture;
36
+ private markerDragging;
37
+ private cursorBeforeMarkerDrag;
38
+ readonly layers: ChartLayers;
39
+ readonly peripheralScales: PeripheralScales;
40
+ constructor(onCursorChange: ((position: Complex | undefined) => void) | null);
41
+ setTheme(theme: SmithTheme): void;
42
+ draw(target: string | HTMLElement): void;
43
+ /** Export the current mounted view as standalone SVG with its configured background. */
44
+ toSvg(): string;
45
+ /** Remove this chart and release its event handlers. Safe to call more than once. */
46
+ destroy(): void;
47
+ private updateViewportScale;
48
+ private assertAlive;
49
+ private createScalers;
50
+ private cursorMove;
51
+ setMarkerDragging(dragging: boolean): void;
52
+ hideCursor(): void;
53
+ private initCursor;
54
+ get cursorPosition(): Complex;
55
+ private initializeZoom;
56
+ /** Enable or disable wheel, double-click, and mouse/touch zoom/pan. Keeps the current view. */
57
+ setZoomEnabled(enabled: boolean): void;
58
+ private applyView;
59
+ resetView(): void;
60
+ private onZoom;
61
+ private cursorContainer;
62
+ private drawReactanceAxis;
63
+ /** Construct trace rendering without exposing chart containers or scalers to Smith. */
64
+ createTrace(values: TraceSamples, color: string, style: Partial<TraceStyle>, onMarkerDrag: (marker: SmithMarker, dragging: boolean) => void): SmithData;
65
+ removeTrace(trace: SmithData): void;
66
+ }
@@ -0,0 +1,342 @@
1
+ import { SvgTheme } from '../appearance/SvgTheme.js';
2
+ import * as d3 from 'd3';
3
+ import { MouseGesture } from '../interaction/MouseGesture.js';
4
+ import { SmithSvg } from '../svg/SmithSvg.js';
5
+ import { LabelLayout } from '../svg/LabelLayout.js';
6
+ import { SvgExporter } from '../svg/SvgExporter.js';
7
+ import { SmithGroup } from '../svg/SmithGroup.js';
8
+ import { SmithCircle } from '../svg/SmithCircle.js';
9
+ import { SmithData } from '../traces/SmithData.js';
10
+ import { SmithCursor } from '../interaction/SmithCursor.js';
11
+ import { ConstResistance } from '../grid/ConstResistance.js';
12
+ import { ConstReactance } from '../grid/ConstReactance.js';
13
+ import { ConstConductance } from '../grid/ConstConductance.js';
14
+ import { ConstSusceptance } from '../grid/ConstSusceptance.js';
15
+ import { ConstQCircles } from '../grid/ConstQCircles.js';
16
+ import { ConstSwrCircles } from '../grid/ConstSwrCircles.js';
17
+ import { SmithScaler } from '../svg/SmithScaler.js';
18
+ import { SmithConstantCircle } from '../rf/SmithConstantCircle.js';
19
+ import { GridDefinitions } from '../grid/GridDefinitions.js';
20
+ import { Complex } from '../math/Complex.js';
21
+ import { SmithPeripheralScales } from '../scales/SmithPeripheralScales.js';
22
+ import { GridLayerControl } from '../grid/GridLayerControl.js';
23
+ import { CircleLayerControl } from '../grid/CircleLayerControl.js';
24
+ /** Internal SVG backend. Owns chart elements, view transforms, and SVG gestures. */
25
+ export class SvgChartRenderer {
26
+ onCursorChange;
27
+ calcs = new SmithConstantCircle();
28
+ scalers;
29
+ defaultTransform = d3.zoomIdentity.translate(62.5, 62.5).scale(0.75);
30
+ transform = d3.zoomIdentity;
31
+ zoomEnabled = true;
32
+ applyingView = false;
33
+ zoomBehavior = d3.zoom();
34
+ svg;
35
+ resizeObserver;
36
+ labelLayout;
37
+ container;
38
+ dataContainer;
39
+ markerContainer;
40
+ reactanceAxis;
41
+ constResistance;
42
+ constReactance;
43
+ constConductance;
44
+ constSusceptance;
45
+ constSwrCircles;
46
+ constQCircles;
47
+ cursor;
48
+ peripheralScaleRenderer = new SmithPeripheralScales();
49
+ traces = new Set();
50
+ destroyed = false;
51
+ mouseGesture = new MouseGesture();
52
+ markerDragging = false;
53
+ cursorBeforeMarkerDrag = null;
54
+ layers;
55
+ peripheralScales;
56
+ constructor(onCursorChange) {
57
+ this.onCursorChange = onCursorChange;
58
+ const viewBoxSize = 500;
59
+ const gridData = GridDefinitions.create();
60
+ this.scalers = this.createScalers(viewBoxSize);
61
+ this.svg = new SmithSvg(viewBoxSize);
62
+ this.container = new SmithGroup();
63
+ this.constResistance = new ConstResistance({
64
+ data: gridData,
65
+ scaler: this.scalers.default,
66
+ detail: 'detailed',
67
+ });
68
+ this.constResistance.show();
69
+ this.constReactance = new ConstReactance({
70
+ data: gridData,
71
+ scaler: this.scalers.default,
72
+ detail: 'detailed',
73
+ });
74
+ this.constReactance.show();
75
+ this.constConductance = new ConstConductance({
76
+ data: gridData,
77
+ scaler: this.scalers.default,
78
+ detail: 'detailed',
79
+ });
80
+ this.constConductance.hide();
81
+ this.constSusceptance = new ConstSusceptance({
82
+ data: gridData,
83
+ scaler: this.scalers.default,
84
+ detail: 'detailed',
85
+ });
86
+ this.constSusceptance.hide();
87
+ this.constQCircles = new ConstQCircles(this.scalers.default);
88
+ this.constQCircles.hide();
89
+ this.constSwrCircles = new ConstSwrCircles(this.scalers.default);
90
+ this.constSwrCircles.hide();
91
+ this.cursor = this.initCursor();
92
+ const cursorContainer = this.cursorContainer();
93
+ this.reactanceAxis = this.drawReactanceAxis({
94
+ stroke: 'var(--smithkit-boundary-stroke)',
95
+ strokeWidth: 'var(--smithkit-boundary-width)',
96
+ fill: 'none',
97
+ });
98
+ this.dataContainer = new SmithGroup().attr('data-layer', 'samples');
99
+ this.markerContainer = new SmithGroup().attr('data-layer', 'markers');
100
+ // build chart
101
+ this.svg.append(this.container);
102
+ this.container.append(this.constConductance.draw().attr('data-layer', 'conductance'));
103
+ this.container.append(this.constSusceptance.draw().attr('data-layer', 'susceptance'));
104
+ this.container.append(this.constResistance.draw().attr('data-layer', 'resistance'));
105
+ this.container.append(this.constReactance.draw().attr('data-layer', 'reactance'));
106
+ this.container.append(this.constQCircles.draw());
107
+ this.container.append(this.constSwrCircles.draw());
108
+ this.container.append(this.peripheralScaleRenderer);
109
+ this.container.append(this.cursor.Group);
110
+ this.container.append(this.reactanceAxis);
111
+ this.container.append(cursorContainer);
112
+ this.container.append(this.dataContainer);
113
+ const labels = new SmithGroup().attr('data-layer', 'labels').attr('pointer-events', 'none');
114
+ for (const [name, layer] of [
115
+ ['conductance', this.constConductance],
116
+ ['susceptance', this.constSusceptance],
117
+ ['resistance', this.constResistance],
118
+ ['reactance', this.constReactance],
119
+ ]) {
120
+ labels.append(layer.labels.attr('data-label-layer', name));
121
+ }
122
+ labels.append(this.peripheralScaleRenderer.labels);
123
+ this.container.append(labels);
124
+ this.container.append(this.markerContainer);
125
+ this.labelLayout = new LabelLayout(this.svg.Node, labels.Node, this.defaultTransform);
126
+ const assertAlive = () => this.assertAlive();
127
+ this.layers = {
128
+ resistance: new GridLayerControl(this.constResistance, assertAlive, () => this.labelLayout.update(true)),
129
+ reactance: new GridLayerControl(this.constReactance, assertAlive, () => this.labelLayout.update(true)),
130
+ conductance: new GridLayerControl(this.constConductance, assertAlive, () => this.labelLayout.update(true)),
131
+ susceptance: new GridLayerControl(this.constSusceptance, assertAlive, () => this.labelLayout.update(true)),
132
+ q: new CircleLayerControl(this.constQCircles, 0, assertAlive),
133
+ vswr: new CircleLayerControl(this.constSwrCircles, 1, assertAlive),
134
+ };
135
+ this.peripheralScales = {
136
+ setVisible: (visible) => {
137
+ assertAlive();
138
+ if (visible) {
139
+ this.peripheralScaleRenderer.show();
140
+ }
141
+ else {
142
+ this.peripheralScaleRenderer.hide();
143
+ }
144
+ this.labelLayout.update(true);
145
+ },
146
+ update: (gamma) => {
147
+ assertAlive();
148
+ this.peripheralScaleRenderer.update(gamma);
149
+ },
150
+ };
151
+ this.initializeZoom();
152
+ this.resizeObserver = new ResizeObserver(() => this.updateViewportScale());
153
+ this.resizeObserver.observe(this.svg.Node);
154
+ }
155
+ setTheme(theme) {
156
+ this.assertAlive();
157
+ SvgTheme.apply(this.svg.Node, theme);
158
+ this.labelLayout.update(true);
159
+ }
160
+ draw(target) {
161
+ this.assertAlive();
162
+ const host = typeof target === 'string' ? document.querySelector(target) : target;
163
+ if (!host) {
164
+ throw new Error('Chart container was not found.');
165
+ }
166
+ host.appendChild(this.svg.Node);
167
+ this.updateViewportScale();
168
+ }
169
+ /** Export the current mounted view as standalone SVG with its configured background. */
170
+ toSvg() {
171
+ this.assertAlive();
172
+ return SvgExporter.chart(this.svg.Node);
173
+ }
174
+ /** Remove this chart and release its event handlers. Safe to call more than once. */
175
+ destroy() {
176
+ if (this.destroyed) {
177
+ return;
178
+ }
179
+ this.resizeObserver.disconnect();
180
+ this.traces.forEach((trace) => trace.destroy());
181
+ this.traces.clear();
182
+ this.destroyed = true;
183
+ this.cursor.setMoveHandler(null);
184
+ this.onCursorChange = null;
185
+ this.mouseGesture.destroy();
186
+ this.zoomBehavior.on('start', null).on('zoom', null);
187
+ this.svg.Element.interrupt().on('.zoom', null);
188
+ this.svg.Element.selectAll('*').interrupt().on('.smithkit', null).on('.drag', null);
189
+ this.svg.Element.remove();
190
+ }
191
+ updateViewportScale() {
192
+ if (this.destroyed) {
193
+ return;
194
+ }
195
+ const matrix = this.svg.Node.getScreenCTM();
196
+ if (matrix) {
197
+ const scale = Math.hypot(matrix.a, matrix.b);
198
+ this.traces.forEach((trace) => trace.setViewportScale(scale));
199
+ }
200
+ this.labelLayout.update();
201
+ }
202
+ assertAlive() {
203
+ if (this.destroyed) {
204
+ throw new Error('This Smith chart has been destroyed. Create a new instance.');
205
+ }
206
+ }
207
+ createScalers(size) {
208
+ const impedance = new SmithScaler(d3.scaleLinear().domain([-1, 1]).range([0, size]), d3.scaleLinear().domain([1, -1]).range([0, size]), d3
209
+ .scaleLinear()
210
+ .domain([0, 1])
211
+ .range([0, size / 2]));
212
+ const admittance = new SmithScaler(d3.scaleLinear().domain([1, -1]).range([0, size]), d3.scaleLinear().domain([-1, 1]).range([0, size]), d3
213
+ .scaleLinear()
214
+ .domain([0, 1])
215
+ .range([0, size / 2]));
216
+ return { default: impedance, impedance, admittance };
217
+ }
218
+ cursorMove(p) {
219
+ if (this.markerDragging) {
220
+ return;
221
+ }
222
+ this.cursor.Position = Complex.from(this.scalers.default.pointInvert(p));
223
+ }
224
+ setMarkerDragging(dragging) {
225
+ this.markerDragging = dragging;
226
+ if (dragging) {
227
+ this.cursorBeforeMarkerDrag = this.svg.Node.style.getPropertyValue('cursor') || null;
228
+ this.svg.Element.style('cursor', 'grabbing').style('--smithkit-marker-cursor', 'grabbing');
229
+ this.cursor.hide();
230
+ }
231
+ else {
232
+ this.svg.Element.style('cursor', () => this.cursorBeforeMarkerDrag).style('--smithkit-marker-cursor', null);
233
+ this.cursorBeforeMarkerDrag = null;
234
+ }
235
+ }
236
+ hideCursor() {
237
+ this.cursor.hide();
238
+ }
239
+ initCursor() {
240
+ const cursor = new SmithCursor(this.scalers.default);
241
+ cursor.Group.attr('class', 'smith-cursor');
242
+ cursor.setMoveHandler(() => {
243
+ this.onCursorChange?.(this.cursor.Position);
244
+ });
245
+ return cursor;
246
+ }
247
+ get cursorPosition() {
248
+ return this.cursor.Position;
249
+ }
250
+ initializeZoom() {
251
+ const zoom = this.zoomBehavior
252
+ .scaleExtent([0.6, 1000])
253
+ .filter((event) => this.zoomEnabled &&
254
+ !this.markerDragging &&
255
+ (!event.ctrlKey || event.type === 'wheel') &&
256
+ !event.button)
257
+ .on('start', (event) => {
258
+ if (event.sourceEvent) {
259
+ this.mouseGesture.capture(event.sourceEvent, 'zoom');
260
+ }
261
+ })
262
+ .on('zoom', (event) => {
263
+ if ((!this.zoomEnabled || this.markerDragging) && !this.applyingView) {
264
+ // An already active mouse/touch gesture still needs its normal end event.
265
+ // Restore D3's view through its public API while that gesture finishes.
266
+ this.applyView(this.transform);
267
+ return;
268
+ }
269
+ this.onZoom(event.transform);
270
+ });
271
+ this.svg.Element.call(zoom);
272
+ this.resetView();
273
+ }
274
+ /** Enable or disable wheel, double-click, and mouse/touch zoom/pan. Keeps the current view. */
275
+ setZoomEnabled(enabled) {
276
+ this.assertAlive();
277
+ if (typeof enabled !== 'boolean') {
278
+ throw new TypeError('Zoom enabled must be a boolean.');
279
+ }
280
+ if (this.zoomEnabled === enabled) {
281
+ return;
282
+ }
283
+ this.zoomEnabled = enabled;
284
+ if (!enabled) {
285
+ this.svg.Element.interrupt();
286
+ }
287
+ }
288
+ applyView(transform) {
289
+ this.applyingView = true;
290
+ try {
291
+ this.svg.Element.call(this.zoomBehavior.transform, transform);
292
+ }
293
+ finally {
294
+ this.applyingView = false;
295
+ }
296
+ }
297
+ resetView() {
298
+ this.assertAlive();
299
+ const transform = this.defaultTransform;
300
+ this.applyView(transform);
301
+ }
302
+ onZoom(transform) {
303
+ if (this.destroyed) {
304
+ return;
305
+ }
306
+ this.transform = transform;
307
+ this.container.Element.attr('transform', transform.toString());
308
+ this.traces.forEach((d) => d.zoom(transform));
309
+ }
310
+ cursorContainer() {
311
+ const shape = this.drawReactanceAxis({ fill: 'transparent', stroke: 'none' });
312
+ shape.Element.style('pointer-events', 'all')
313
+ .on('pointermove.smithkit', (event) => {
314
+ this.cursorMove(d3.pointer(event));
315
+ })
316
+ .on('pointerleave.smithkit', () => {
317
+ this.cursor.hide();
318
+ this.onCursorChange?.(undefined);
319
+ });
320
+ return shape;
321
+ }
322
+ drawReactanceAxis(opts) {
323
+ const c = this.calcs.resistanceCircle(0);
324
+ c.p[0] = this.scalers.default.x(c.p[0]);
325
+ c.p[1] = this.scalers.default.y(c.p[1]);
326
+ c.r = this.scalers.default.r(c.r);
327
+ return new SmithCircle(c, opts);
328
+ }
329
+ /** Construct trace rendering without exposing chart containers or scalers to Smith. */
330
+ createTrace(values, color, style, onMarkerDrag) {
331
+ this.assertAlive();
332
+ const trace = new SmithData(values, color, this.transform, this.dataContainer, this.scalers.default, onMarkerDrag, this.markerContainer, style);
333
+ this.traces.add(trace);
334
+ this.updateViewportScale();
335
+ return trace;
336
+ }
337
+ removeTrace(trace) {
338
+ // Release gestures while Smith can still resolve the marker's public snapshot.
339
+ trace.destroy();
340
+ this.traces.delete(trace);
341
+ }
342
+ }
@@ -0,0 +1,65 @@
1
+ import type { TraceSamples } from '../samples.js';
2
+ import { Complex } from '../math/Complex.js';
3
+ /** Physical quantities derived from a voltage reflection coefficient Γ. */
4
+ export interface SmithReading {
5
+ readonly reflectionCoefficient: Complex;
6
+ readonly impedanceOhms: Complex | undefined;
7
+ readonly admittanceSiemens: Complex | undefined;
8
+ readonly phaseDegrees: number | undefined;
9
+ readonly vswr: number | undefined;
10
+ readonly standingWaveRatioDb: number | undefined;
11
+ readonly returnLossDb: number;
12
+ readonly reflectionLossDb: number | undefined;
13
+ readonly attenuationDb: number | undefined;
14
+ readonly q: number | undefined;
15
+ readonly powerReflectionCoefficient: number;
16
+ readonly voltageReflectionCoefficientMagnitude: number;
17
+ readonly powerTransmissionCoefficient: number | undefined;
18
+ readonly voltageTransmissionCoefficientMagnitude: number;
19
+ readonly currentTransmissionCoefficientMagnitude: number;
20
+ readonly standingWavePeak: number | undefined;
21
+ readonly standingWaveLossCoefficient: number | undefined;
22
+ }
23
+ /** An ideal series component equivalent to a reactance at one frequency. */
24
+ export type ReactiveComponent = {
25
+ readonly kind: 'inductor';
26
+ readonly inductanceHenries: number;
27
+ } | {
28
+ readonly kind: 'capacitor';
29
+ readonly capacitanceFarads: number;
30
+ };
31
+ export declare class RfCalculations {
32
+ private constructor();
33
+ private static readonly calcs;
34
+ private static validateReference;
35
+ private static validateComplex;
36
+ private static finite;
37
+ /** Convert Γ to physical impedance in ohms; undefined at the open circuit Γ = 1. */
38
+ static reflectionToImpedance(gamma: Complex, referenceImpedanceOhms?: number): Complex | undefined;
39
+ /** Convert physical impedance in ohms to Γ. Undefined for a singular result. */
40
+ static impedanceToReflection(impedanceOhms: Complex, referenceImpedanceOhms?: number): Complex | undefined;
41
+ /** Convert Γ to physical admittance in siemens; undefined at the short circuit Γ = −1. */
42
+ static reflectionToAdmittance(gamma: Complex, referenceImpedanceOhms?: number): Complex | undefined;
43
+ /** Convert physical admittance in siemens to Γ. Undefined for a singular result. */
44
+ static admittanceToReflection(admittanceSiemens: Complex, referenceImpedanceOhms?: number): Complex | undefined;
45
+ /**
46
+ * Read Γ without a chart or DOM. Undefined means a singular or inapplicable quantity.
47
+ * Passive-load quantities are undefined outside |Γ| ≤ 1; exact infinite limits are retained.
48
+ * Attenuation assumes a fully reflecting termination and is half the return loss.
49
+ */
50
+ static readReflection(gamma: Complex, referenceImpedanceOhms?: number): SmithReading;
51
+ /**
52
+ * Change the positive real reference impedance of a voltage reflection coefficient.
53
+ * Preserves physical impedance, including exact open/short limits. A singular or
54
+ * unrepresentable result is undefined; non-finite inputs throw RangeError.
55
+ */
56
+ static renormalizeReflection(gamma: Complex, fromOhms: number, toOhms: number): Complex | undefined;
57
+ /** Copy and renormalize every sample; reject invalid/singular data without modifying the input. */
58
+ static renormalizeSamples(samples: TraceSamples, fromOhms: number, toOhms: number): TraceSamples;
59
+ /**
60
+ * Convert reactance in ohms at a frequency in Hz to an ideal series L or C.
61
+ * Zero frequency, zero reactance, or an unrepresentable component returns undefined.
62
+ * Non-finite reactance and negative/non-finite frequency throw RangeError.
63
+ */
64
+ static reactanceToComponent(reactanceOhms: number, frequencyHz: number): ReactiveComponent | undefined;
65
+ }
@@ -0,0 +1,161 @@
1
+ import { Complex } from '../math/Complex.js';
2
+ import { SmithConstantCircle } from './SmithConstantCircle.js';
3
+ export class RfCalculations {
4
+ constructor() { }
5
+ static calcs = new SmithConstantCircle();
6
+ static validateReference(referenceImpedanceOhms) {
7
+ if (!Number.isFinite(referenceImpedanceOhms) || referenceImpedanceOhms <= 0) {
8
+ throw new RangeError('Reference impedance must be positive and finite.');
9
+ }
10
+ }
11
+ static validateComplex(value) {
12
+ if (!Number.isFinite(value.re) || !Number.isFinite(value.im)) {
13
+ throw new RangeError('Complex components must be finite.');
14
+ }
15
+ }
16
+ static finite(value) {
17
+ return value && Number.isFinite(value.re) && Number.isFinite(value.im) ? value : undefined;
18
+ }
19
+ /** Convert Γ to physical impedance in ohms; undefined at the open circuit Γ = 1. */
20
+ static reflectionToImpedance(gamma, referenceImpedanceOhms = 50) {
21
+ RfCalculations.validateReference(referenceImpedanceOhms);
22
+ RfCalculations.validateComplex(gamma);
23
+ return RfCalculations.finite(RfCalculations.calcs.rflCoeffToImpedance(gamma)?.mul(referenceImpedanceOhms));
24
+ }
25
+ /** Convert physical impedance in ohms to Γ. Undefined for a singular result. */
26
+ static impedanceToReflection(impedanceOhms, referenceImpedanceOhms = 50) {
27
+ RfCalculations.validateReference(referenceImpedanceOhms);
28
+ RfCalculations.validateComplex(impedanceOhms);
29
+ return RfCalculations.finite(RfCalculations.calcs.impedanceToRflCoeff(impedanceOhms.div(referenceImpedanceOhms)));
30
+ }
31
+ /** Convert Γ to physical admittance in siemens; undefined at the short circuit Γ = −1. */
32
+ static reflectionToAdmittance(gamma, referenceImpedanceOhms = 50) {
33
+ RfCalculations.validateReference(referenceImpedanceOhms);
34
+ RfCalculations.validateComplex(gamma);
35
+ return RfCalculations.finite(RfCalculations.calcs.rflCoeffToAdmittance(gamma)?.div(referenceImpedanceOhms));
36
+ }
37
+ /** Convert physical admittance in siemens to Γ. Undefined for a singular result. */
38
+ static admittanceToReflection(admittanceSiemens, referenceImpedanceOhms = 50) {
39
+ RfCalculations.validateReference(referenceImpedanceOhms);
40
+ RfCalculations.validateComplex(admittanceSiemens);
41
+ return RfCalculations.finite(RfCalculations.calcs.admittanceToRflCoeff(admittanceSiemens.mul(referenceImpedanceOhms)));
42
+ }
43
+ /**
44
+ * Read Γ without a chart or DOM. Undefined means a singular or inapplicable quantity.
45
+ * Passive-load quantities are undefined outside |Γ| ≤ 1; exact infinite limits are retained.
46
+ * Attenuation assumes a fully reflecting termination and is half the return loss.
47
+ */
48
+ static readReflection(gamma, referenceImpedanceOhms = 50) {
49
+ const impedanceOhms = RfCalculations.reflectionToImpedance(gamma, referenceImpedanceOhms);
50
+ const admittanceSiemens = RfCalculations.reflectionToAdmittance(gamma, referenceImpedanceOhms);
51
+ const magnitude = gamma.abs();
52
+ const passive = magnitude <= 1;
53
+ const returnLossDb = -20 * Math.log10(magnitude);
54
+ const vswr = passive ? (1 + magnitude) / (1 - magnitude) : undefined;
55
+ return {
56
+ reflectionCoefficient: gamma,
57
+ impedanceOhms,
58
+ admittanceSiemens,
59
+ phaseDegrees: magnitude === 0 ? undefined : (gamma.arg() * 180) / Math.PI,
60
+ vswr,
61
+ standingWaveRatioDb: vswr === undefined ? undefined : 20 * Math.log10(vswr),
62
+ returnLossDb,
63
+ reflectionLossDb: passive ? (-10 / Math.LN10) * Math.log1p(-(magnitude ** 2)) : undefined,
64
+ attenuationDb: passive ? returnLossDb / 2 : undefined,
65
+ q: impedanceOhms && (impedanceOhms.re !== 0 || impedanceOhms.im !== 0)
66
+ ? Math.abs(impedanceOhms.im / impedanceOhms.re)
67
+ : undefined,
68
+ powerReflectionCoefficient: magnitude ** 2,
69
+ voltageReflectionCoefficientMagnitude: magnitude,
70
+ powerTransmissionCoefficient: passive ? 1 - magnitude ** 2 : undefined,
71
+ voltageTransmissionCoefficientMagnitude: gamma.add(1).abs(),
72
+ currentTransmissionCoefficientMagnitude: Complex.one().sub(gamma).abs(),
73
+ standingWavePeak: vswr === undefined ? undefined : Math.sqrt(vswr),
74
+ standingWaveLossCoefficient: passive
75
+ ? (1 + magnitude ** 2) / (1 - magnitude ** 2)
76
+ : undefined,
77
+ };
78
+ }
79
+ /**
80
+ * Change the positive real reference impedance of a voltage reflection coefficient.
81
+ * Preserves physical impedance, including exact open/short limits. A singular or
82
+ * unrepresentable result is undefined; non-finite inputs throw RangeError.
83
+ */
84
+ static renormalizeReflection(gamma, fromOhms, toOhms) {
85
+ RfCalculations.validateReference(fromOhms);
86
+ RfCalculations.validateReference(toOhms);
87
+ if (!Number.isFinite(gamma.re) || !Number.isFinite(gamma.im)) {
88
+ throw new RangeError('Reflection coefficient components must be finite.');
89
+ }
90
+ if (fromOhms === toOhms || (gamma.im === 0 && Math.abs(gamma.re) === 1)) {
91
+ return gamma;
92
+ }
93
+ // Γnew = (Γold + k) / (1 + k Γold). Scale references before summing.
94
+ const referenceScale = Math.max(fromOhms, toOhms);
95
+ const from = fromOhms / referenceScale;
96
+ const to = toOhms / referenceScale;
97
+ const k = (fromOhms - toOhms) / referenceScale / (from + to);
98
+ const scale = Math.max(1, Math.abs(gamma.re), Math.abs(gamma.im));
99
+ const x = gamma.re / scale;
100
+ const y = gamma.im / scale;
101
+ const inverseScale = 1 / scale;
102
+ const nr = x + k * inverseScale;
103
+ const dr = inverseScale + k * x;
104
+ const di = k * y;
105
+ const denominatorScale = Math.max(Math.abs(dr), Math.abs(di));
106
+ if (denominatorScale === 0) {
107
+ return;
108
+ }
109
+ const real = dr / denominatorScale;
110
+ const imaginary = di / denominatorScale;
111
+ const denominator = real ** 2 + imaginary ** 2;
112
+ const re = ((nr / denominatorScale) * real + (y / denominatorScale) * imaginary) / denominator;
113
+ // Analytic imaginary numerator avoids cancellation for very large finite Γ.
114
+ const im = (((1 - k) * (1 + k) * (y / denominatorScale)) / denominator) *
115
+ (inverseScale / denominatorScale);
116
+ return Number.isFinite(re) && Number.isFinite(im) ? Complex.from(re, im) : undefined;
117
+ }
118
+ /** Copy and renormalize every sample; reject invalid/singular data without modifying the input. */
119
+ static renormalizeSamples(samples, fromOhms, toOhms) {
120
+ RfCalculations.validateReference(fromOhms);
121
+ RfCalculations.validateReference(toOhms);
122
+ return samples.map((sample, index) => {
123
+ if (!Number.isFinite(sample.frequencyHz) ||
124
+ sample.frequencyHz < 0 ||
125
+ sample.reflectionCoefficient.length !== 2) {
126
+ throw new RangeError(`Invalid sample at index ${index}.`);
127
+ }
128
+ const gamma = RfCalculations.renormalizeReflection(Complex.from(...sample.reflectionCoefficient), fromOhms, toOhms);
129
+ if (!gamma) {
130
+ throw new RangeError(`Renormalization is singular or outside the numeric range at sample ${index}.`);
131
+ }
132
+ return { frequencyHz: sample.frequencyHz, reflectionCoefficient: gamma.toVector() };
133
+ });
134
+ }
135
+ /**
136
+ * Convert reactance in ohms at a frequency in Hz to an ideal series L or C.
137
+ * Zero frequency, zero reactance, or an unrepresentable component returns undefined.
138
+ * Non-finite reactance and negative/non-finite frequency throw RangeError.
139
+ */
140
+ static reactanceToComponent(reactanceOhms, frequencyHz) {
141
+ if (!Number.isFinite(reactanceOhms)) {
142
+ throw new RangeError('Reactance must be finite.');
143
+ }
144
+ if (!Number.isFinite(frequencyHz) || frequencyHz < 0) {
145
+ throw new RangeError('Frequency must be finite and non-negative.');
146
+ }
147
+ if (frequencyHz === 0 || reactanceOhms === 0) {
148
+ return;
149
+ }
150
+ // Compute in log space so intermediate products/quotients cannot overflow.
151
+ const logReactance = Math.log(Math.abs(reactanceOhms));
152
+ const logAngularFrequency = Math.log(2 * Math.PI) + Math.log(frequencyHz);
153
+ const value = Math.exp((reactanceOhms > 0 ? logReactance : -logReactance) - logAngularFrequency);
154
+ if (!Number.isFinite(value) || value === 0) {
155
+ return;
156
+ }
157
+ return reactanceOhms > 0
158
+ ? { kind: 'inductor', inductanceHenries: value }
159
+ : { kind: 'capacitor', capacitanceFarads: value };
160
+ }
161
+ }
@@ -1,6 +1,5 @@
1
- import { Circle } from './shapes/Circle.js';
2
- import { Point } from './shapes/Point.js';
3
- import { Complex } from './complex/Complex.js';
1
+ import type { Circle, Point } from '../math/geometry.js';
2
+ import { Complex } from '../math/Complex.js';
4
3
  export declare class SmithConstantCircle {
5
4
  Z0: number;
6
5
  constructor(Z0?: number);