smithkit 0.4.0 → 0.6.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 (39) hide show
  1. package/README.md +47 -774
  2. package/dist/lib/OptionsValidation.js +7 -3
  3. package/dist/lib/Smith.d.ts +7 -1
  4. package/dist/lib/Smith.js +17 -9
  5. package/dist/lib/grid/SmithGridLayer.js +3 -1
  6. package/dist/lib/index.d.ts +2 -2
  7. package/dist/lib/io/Touchstone.d.ts +13 -7
  8. package/dist/lib/io/Touchstone.js +146 -46
  9. package/dist/lib/io/types.d.ts +17 -0
  10. package/dist/lib/io/types.js +1 -0
  11. package/dist/lib/layers.d.ts +4 -0
  12. package/dist/lib/math/Complex.d.ts +1 -1
  13. package/dist/lib/math/Complex.js +2 -2
  14. package/dist/lib/measurements.d.ts +2 -0
  15. package/dist/lib/options.d.ts +4 -0
  16. package/dist/lib/rendering/SvgChartRenderer.d.ts +3 -3
  17. package/dist/lib/rendering/SvgChartRenderer.js +28 -3
  18. package/dist/lib/rf/RfCalculations.d.ts +3 -1
  19. package/dist/lib/rf/RfCalculations.js +23 -6
  20. package/dist/lib/samples.d.ts +5 -1
  21. package/dist/lib/scales/SmithPeripheralScales.d.ts +5 -0
  22. package/dist/lib/scales/SmithPeripheralScales.js +20 -1
  23. package/dist/lib/scales/SmithScales.js +2 -2
  24. package/dist/lib/svg/LabelLayout.js +9 -2
  25. package/dist/lib/svg/SvgExporter.d.ts +1 -1
  26. package/dist/lib/svg/SvgExporter.js +4 -3
  27. package/dist/lib/svg/export.d.ts +2 -0
  28. package/dist/lib/traces/SmithData.d.ts +10 -4
  29. package/dist/lib/traces/SmithData.js +14 -4
  30. package/dist/lib/traces/SmithMarker.js +1 -0
  31. package/dist/lib/traces/TraceBuffer.d.ts +19 -0
  32. package/dist/lib/traces/TraceBuffer.js +176 -0
  33. package/dist/lib/traces/TraceLineDetail.d.ts +8 -0
  34. package/dist/lib/traces/TraceLineDetail.js +52 -0
  35. package/dist/lib/traces/TraceModel.d.ts +6 -8
  36. package/dist/lib/traces/TraceModel.js +24 -49
  37. package/dist/lib/traces/TraceRenderer.d.ts +13 -3
  38. package/dist/lib/traces/TraceRenderer.js +83 -23
  39. package/package.json +25 -6
package/README.md CHANGED
@@ -3,27 +3,13 @@
3
3
  **`smithkit`** is a framework-independent JavaScript and TypeScript library for
4
4
  interactive SVG Smith charts, RF calculations, and S11 measurement exploration.
5
5
 
6
- [Try the interactive demo](https://rosek86.github.io/smithkit/) or browse the
6
+ [![Smithkit in dark mode: a Smith chart with one orange trace, a marker, and active cursor readings on the parameter scales.](https://raw.githubusercontent.com/rosek86/smithkit/main/docs/images/smithkit-preview.png)](https://rosek86.github.io/smithkit/)
7
+
8
+ [API documentation](https://rosek86.github.io/smithkit/api/) · [Try the interactive demo](https://rosek86.github.io/smithkit/) or browse the
7
9
  [integration examples](https://rosek86.github.io/smithkit/examples/index.html):
8
- static charts, marker controls, themes, report exports, and lifecycle cleanup.
10
+ static charts, marker controls, a 100,000-sample trace, themes, report exports, React/Angular components, and lifecycle cleanup.
9
11
  Each example includes its TypeScript source.
10
12
 
11
- Upgrading from 0.3.x? Version 0.4.0 introduces grouped constructor options and
12
- `setOptions()`, static chart defaults, and explicit marker creation. See the
13
- [0.4 migration guide](https://github.com/rosek86/smithkit/blob/v0.4.0/docs/migration-0.4.md).
14
-
15
- Upgrading from 0.2.x? Version 0.3.0 replaces `GridLayer.setMinorVisible()` with
16
- `setDetail('basic' | 'standard' | 'detailed')`. Replace `false` with `'standard'`
17
- and `true` with `'detailed'`; `'basic'` shows only the principal circles.
18
-
19
- Upgrading from 0.1.x? Version 0.2.0 groups calculation, parsing, formatting, and
20
- comparison functions into classes. See the [migration guide](https://github.com/rosek86/smithkit/blob/v0.2.0/docs/migration-0.2.md)
21
- for the changed imports.
22
-
23
- The [API review and compatibility policy](https://github.com/rosek86/smithkit/blob/main/docs/api-review.md)
24
- define the public contract and the compatibility scope intended for 1.0. During
25
- 0.x development, breaking changes are documented in minor releases.
26
-
27
13
  ## Features
28
14
 
29
15
  - Labeled resistance, reactance, conductance, and susceptance grids.
@@ -34,7 +20,7 @@ define the public contract and the compatibility scope intended for 1.0. During
34
20
  - Physical impedance/admittance conversions and DOM-independent RF readings.
35
21
  - Twelve parameter scales mounted independently of chart zoom.
36
22
  - Four peripheral rulers for phase and electrical length.
37
- - One-port Touchstone 1.x import with RI, MA, and DB representations.
23
+ - Touchstone 1.x `.s1p` / `.s2p` reflection import (RI/MA/DB), packed output, and `.s1p` export.
38
24
  - Standalone SVG and configurable PNG export of the chart and radial scales.
39
25
  - ESM modules and TypeScript declarations.
40
26
 
@@ -47,16 +33,25 @@ npm install smithkit
47
33
  ```
48
34
 
49
35
  Browser applications need SVG, ES2022, and an ESM-compatible build tool.
36
+ The checked TypeScript minimum is 5.7.3. See the
37
+ [browser and framework compatibility](https://github.com/rosek86/smithkit/blob/main/docs/compatibility.md)
38
+ for tested environments and build targets.
50
39
  No library stylesheet is required. To build the library from source, see
51
40
  [Contributing](https://github.com/rosek86/smithkit/blob/main/CONTRIBUTING.md).
52
41
 
53
42
  For a complete application using the installed package, see the
54
43
  [standalone integration example](https://github.com/rosek86/smithkit/tree/main/examples/basic).
55
44
  It demonstrates mounting, replacing samples, event subscriptions, resizing, and cleanup.
45
+ Copyable [React](https://rosek86.github.io/smithkit/examples/react/index.html) and
46
+ [Angular](https://rosek86.github.io/smithkit/examples/angular/index.html) components
47
+ cover the same lifecycle through framework props/inputs.
48
+
49
+ For upgrades from earlier versions, see the
50
+ [migration guide](https://github.com/rosek86/smithkit/blob/main/docs/migrations.md).
56
51
 
57
52
  ## Quick start
58
53
 
59
- Give the container an explicit size and run the code after it is mounted:
54
+ Mount a container with a non-zero size:
60
55
 
61
56
  ```html
62
57
  <div id="smith" style="width: 100%; max-width: 700px; aspect-ratio: 1;"></div>
@@ -65,776 +60,54 @@ Give the container an explicit size and run the code after it is mounted:
65
60
  ```ts
66
61
  import { Smith } from 'smithkit';
67
62
 
68
- const chart = new Smith({ referenceImpedanceOhms: 50 }); // Reference impedance in ohms; defaults to 50.
63
+ const chart = new Smith({ referenceImpedanceOhms: 50 });
69
64
  chart.draw('#smith');
70
65
  const traceId = chart.addTrace(
71
66
  [
72
- { frequencyHz: 1e9, reflectionCoefficient: [0.5, -0.5] },
73
- { frequencyHz: 1.5e9, reflectionCoefficient: [0, 0] },
74
- { frequencyHz: 2e9, reflectionCoefficient: [0.5, 0.5] },
75
- ],
76
- { name: 'Antenna', color: '#2563eb' },
77
- );
78
- ```
79
-
80
- Each `TraceSample` contains `frequencyHz` and the dimensionless voltage reflection
81
- coefficient Γ as `[real, imaginary]`. Samples are copied and validated; readonly tuples and arrays (`as const`) are accepted. Frequencies
82
- must be finite and non-negative; both coordinates must be finite. A trace must
83
- contain at least one sample.
84
-
85
- Traces start without markers. Call `chart.addMarker(traceId)` to add one at the
86
- first sample, or pass a sample index to choose its initial position. Drag markers to select
87
- samples with a mouse or touch. Enable zoom to scroll to zoom and drag the chart to pan. Markers
88
- and their focus indicators stay above grid labels and sample points. `chart.resetView()` restores the initial view, including peripheral rulers.
89
-
90
- Construct `Smith` only in a browser, such as your framework's mount hook. Importing
91
- the package and using its calculation or parsing methods does not require a DOM.
92
-
93
- The default chart is a static presentation: standard impedance grid with labels,
94
- no peripheral scales, and zoom and cursor tracking disabled. Markers are optional
95
- and remain interactive when explicitly added.
96
-
97
- Enable interactions through constructor options or the corresponding setters:
98
-
99
- ```ts
100
- import { Smith } from 'smithkit';
101
-
102
- const chart = new Smith({ interaction: { zoom: true, cursor: true } });
103
- chart.draw('#smith');
104
-
105
- chart.setZoomEnabled(false); // Freeze the current view; page scrolling is unaffected.
106
- chart.setCursorEnabled(false); // Hide cursor geometry and cancel pending readings.
107
- ```
108
-
109
- Zoom controls wheel/double-click zoom and mouse/touch panning and pinch gestures;
110
- it is independent of cursor tracking and marker interaction. `resetView()` remains
111
- available with zoom disabled. Disabling during an active gesture freezes the view
112
- while that gesture finishes normally.
113
-
114
- Cursor tracking controls the cursor overlay and `Cursor` events. Disabling it
115
- clears the current readout with one `Cursor` event carrying `undefined`, cancels
116
- queued readings, and suppresses further cursor events. Enabling waits for a new
117
- pointer movement. The last `cursorReading` remains available; marker controls and
118
- events work independently.
119
-
120
- `draw(selector | HTMLElement)` moves the existing SVG when called again. A missing
121
- container throws. Call `chart.destroy()` on unmount; it removes the SVG, releases
122
- event handlers, and cancels queued notifications. Repeated destruction is safe.
123
-
124
- ## Constructor configuration
125
-
126
- `new Smith(options?)` accepts a typed `SmithOptions` object. Every field is optional:
127
-
128
- ```ts
129
- import { Smith } from 'smithkit';
130
-
131
- const chart = new Smith({
132
- referenceImpedanceOhms: 75,
133
- appearance: { theme: 'dark' },
134
- interaction: { zoom: false },
135
- peripheralScales: { visible: false },
136
- grid: {
137
- detail: 'basic',
138
- labelsVisible: true,
139
- style: { majorWidth: 1.5 },
140
- layers: {
141
- resistance: { style: { stroke: '#60a5fa' } },
142
- reactance: { labelsVisible: false },
143
- conductance: { visible: true, detail: 'standard' },
144
- },
145
- },
146
- circles: {
147
- q: { visible: true, values: [1, 2, 5] },
148
- vswr: { visible: true, values: [2, 3], style: { stroke: '#fbbf24' } },
149
- },
150
- });
151
- chart.draw('#smith');
152
-
153
- // Update all four grids together, including hidden admittance layers.
154
- chart.setGridDetail('standard');
155
- // Individual overrides remain available.
156
- chart.layers.reactance.setDetail('basic');
157
- ```
158
-
159
- Configuration is grouped by responsibility:
160
-
161
- | Group | Purpose | Runtime API |
162
- | ------------------------ | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
163
- | `referenceImpedanceOhms` | Physical reference impedance | `renormalize()` |
164
- | `appearance` | Theme, fonts, colors, marker/cursor styling | `setAppearance()` |
165
- | `interaction` | `zoom` and `cursor` tracking switches | `setZoomEnabled()`, `setCursorEnabled()` |
166
- | `grid` | Shared detail, labels, styles; `layers` overrides for resistance/reactance/conductance/susceptance | `setGridDetail()`, `layers.resistance` and the other grid controls |
167
- | `circles` | Independent Q and VSWR values, visibility, styles | `layers.q`, `layers.vswr` |
168
- | `peripheralScales` | Visibility of all peripheral rulers together | `peripheralScales.setVisible()` |
169
-
170
- Defaults are 50 Ω, the light theme, zoom and cursor disabled, peripheral scales hidden,
171
- and standard grids with labels. Resistance/reactance are visible;
172
- conductance/susceptance and Q/VSWR circles are hidden. Q defaults to
173
- `[0.5, 1, 2, 5, 10]`; VSWR defaults to `[1.2, 1.5, 2, 3, 5, 10]`.
174
-
175
- Shared `grid` settings apply to all four grids; `grid.layers` overrides individual
176
- fields afterward. Styles merge by property. Explicit layer styles override theme
177
- colors and remain in effect when `setAppearance()` changes the theme.
178
- Options and circle arrays are copied; changing the original object does not update
179
- the chart. Use the existing chart/layer methods for later changes. Unknown constructor groups
180
- or structural option names throw `TypeError`; omitted fields use defaults.
181
-
182
- Export configuration belongs to `toSvg()`/`toPng()` calls, independently of chart
183
- construction. `scaleReadout` requires `scales`; otherwise export throws/rejects with `TypeError`.
184
-
185
- The constructor configures presentation and interaction. Add measurement data with
186
- `addTrace()` and optional markers with `addMarker()`; no markers are created implicitly.
187
- Radial parameter scales remain independently mounted `SmithScales` instances.
188
-
189
- ### Updating configuration
190
-
191
- `chart.setOptions(options)` accepts the same `SmithOptions` type as the constructor:
192
-
193
- ```ts
194
- chart.setOptions({
195
- interaction: { zoom: true },
196
- grid: {
197
- detail: 'basic',
198
- layers: { reactance: { labelsVisible: false } },
199
- },
200
- circles: { vswr: { visible: true, values: [2, 3] } },
201
- });
202
- ```
203
-
204
- This is a patch: omitted and `undefined` fields retain their current values;
205
- `{}` groups do nothing. Shared grid settings apply to all four grids first, then
206
- per-layer overrides in the same call. A later shared setting also replaces that
207
- field on previously customized layers. Style fields merge; circle value lists
208
- replace. `appearance`, when provided, replaces the preset/overrides exactly like
209
- `setAppearance()` (it is not recursively merged with the previous appearance).
210
-
211
- The complete patch is validated before application. Invalid settings or impossible
212
- reference conversions leave the chart unchanged. `referenceImpedanceOhms` invokes
213
- renormalization of existing traces and retains physical impedance and marker IDs.
214
- Other configuration updates preserve measurements, markers, selection, and view.
215
- Options are not retained; subsequent edits to the input object have no effect.
216
- Individual setters remain available for focused changes.
217
-
218
- ### Keyboard and touch markers
219
-
220
- Use Tab/Shift+Tab to focus a marker. Arrow keys move through samples in input order;
221
- Right/Up advances and Left/Down goes back. Shift+Arrow or Page Up/Down moves ten
222
- samples, and Home/End selects the first/last sample. Navigation clamps at the ends
223
- and also works when chart zoom is disabled. A single-sample trace remains focusable
224
- for reading, with its slider marked disabled.
225
-
226
- A focused marker has a contrasting outline and exposes its trace name, marker
227
- number, sample position, and frequency to assistive technology. Hidden traces are
228
- excluded from keyboard focus. `chart.focusMarker(id)` allows a separate marker
229
- selector to focus the matching chart control. The demo provides a **Focus on chart**
230
- button beside its marker selector.
231
-
232
- Markers have a 44 CSS px circular hit area and an 18 CSS px triangle, kept constant
233
- through zoom and container resizing. A marker touch gesture does not pan the chart;
234
- cancelling it releases the drag state. Touch target areas may overlap for nearby
235
- markers; use keyboard focus or the demo selector to reach an obscured marker.
236
-
237
- ## Traces and markers
238
-
239
- Trace and marker IDs are opaque strings, unique within a chart and never reused
240
- there. They survive renaming, hiding, updating samples, and removing other items.
241
- They are not persistent across chart instances or page reloads.
242
-
243
- ```ts
244
- const markerA = chart.addMarker(traceId)!; // First sample.
245
- const markerB = chart.addMarker(traceId, 2)!; // Zero-based sample index.
246
-
247
- chart.setTraceOptions(traceId, { name: 'Tuned antenna', visible: true });
248
- chart.setMarkerSample(markerB, 1);
249
- console.log(chart.getMarker(markerB)?.frequencyHz); // 1.5e9
250
-
251
- const difference = chart.compareMarkers(markerA, markerB);
252
- console.log(difference?.frequencyDeltaHz); // B − A, in Hz.
253
- console.log(difference?.impedanceDeltaOhms?.toVector());
254
- console.log(difference?.phaseDeltaDegrees);
255
-
256
- chart.removeMarker(markerB);
257
- ```
258
-
259
- | Method | Result / behavior |
260
- | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
261
- | `addTrace(samples, options?)` | Returns a trace ID, without creating markers. Options: nonblank `name`, solid CSS `color`, and `visible`. |
262
- | `getTraces()` | Detached `TraceInfo` snapshots: ID, name, color, visibility, sample count, and marker metadata. |
263
- | `setTraceOptions(id, options)` | Updates the supplied options only. Invalid options throw before any change. |
264
- | `updateTrace(id, samples, options?)` | Replaces samples and preserves identity, appearance, and markers. Empty/invalid input throws without changing the trace. |
265
- | `removeTrace(id)` | Removes the trace and its markers. |
266
- | `clearTraces()` | Removes all traces and their markers. |
267
- | `addMarker(traceId, sampleIndex = 0)` | Returns a marker ID, or `undefined` if the trace is missing. |
268
- | `getMarker(id)` | Returns a `MarkerSnapshot`, or `undefined` if missing. |
269
- | `focusMarker(id)` | Moves keyboard focus to a mounted, visible marker; returns `false` if missing or not focusable. |
270
- | `setMarkerSample(id, sampleIndex)` | Selects an existing sample and queues a marker notification. |
271
- | `removeMarker(id)` | Removes one marker without renumbering the remaining markers. |
272
- | `compareMarkers(a, b)` | Returns B − A, or `undefined` if either marker is missing. |
273
- | `referenceImpedanceOhms` | Read-only reference impedance. Must be positive and finite. |
274
-
275
- Update/remove methods return a boolean: `false` means the ID was not found.
276
- Sample indices must be zero-based integers within the trace; invalid indices throw
277
- `RangeError`. Sample replacement follows the nearest measured frequency by default.
278
- Pass `{ markerSelection: 'sample-index' }` to retain indices (clamped to the new last
279
- sample if the trace shrinks), or `{ markerSelection: 'reflection' }` to select the
280
- nearest Γ. Frequency and Γ ties select the earlier sample in input order, including
281
- unsorted sweeps and duplicate frequencies. Selection uses the actual selected sample,
282
- not a previous requested frequency. No interpolation is performed.
283
-
284
- ```ts
285
- chart.updateTrace(
286
- traceId,
287
- [
288
- { frequencyHz: 1e9, reflectionCoefficient: [0.2, 0] },
289
- { frequencyHz: 2e9, reflectionCoefficient: [0.3, 0] },
67
+ [1e9, 0.5, -0.5],
68
+ [1.5e9, 0, 0],
69
+ [2e9, 0.5, 0.5],
290
70
  ],
291
- { markerSelection: 'frequency' },
71
+ { name: 'Antenna', color: '#2563eb', mode: 'line' },
292
72
  );
293
- ```
294
-
295
- Invalid strategies throw `TypeError`; empty or invalid samples throw `RangeError`
296
- before mutation. Unknown trace IDs return `false` without validating input.
297
- Renormalization always preserves sample indices because it transforms the existing
298
- sweep rather than replacing it.
299
73
 
300
- Trace snapshots contain `markers: { id, number, sampleIndex }[]`. `number` is the
301
- stable display number within that trace. A `MarkerSnapshot` includes the common
302
- RF reading fields below plus `traceId`, `markerId`, `markerNumber`, `sampleIndex`,
303
- and `frequencyHz`.
74
+ // Optional: enable interaction and add a marker explicitly.
75
+ chart.setOptions({ interaction: { zoom: true, cursor: true } });
76
+ chart.addMarker(traceId, 1);
304
77
 
305
- Hiding a trace hides its points and markers while retaining measurements for
306
- comparisons. Hiding or deleting a dragged marker/trace ends the drag. Mutations
307
- after destruction throw; trace/marker lookups return empty or missing results.
308
-
309
- ## Trace appearance
310
-
311
- Pass `mode: 'points' | 'line' | 'both'`, `lineWidth`, and `pointRadius` to
312
- `addTrace` or `setTraceOptions`. Defaults are points, 2 px line width, and 2 px point radius. Both sizes are
313
- CSS pixels and remain constant under chart zoom and container resize. Widths and radii must be positive and finite.
314
- `getTraces()` includes the current style.
315
-
316
- Lines connect all samples in input order, without smoothing or frequency sorting.
317
- A line-only trace creates one SVG path and no sample circles, suitable for dense
318
- sweeps. Points/both render every sample for traces up to 5,000 samples. Larger traces
319
- render one representative per point-radius-sized display cell and skip points
320
- outside the viewport; representatives are recalculated during zoom/pan.
321
- This only simplifies the image: all original samples remain available to markers,
322
- snapping, readouts, and comparisons. Lines still connect every input sample.
323
- Styles do not change marker identity or layer ordering. Hidden traces retain their style after sample replacement.
324
- The demo exposes the same controls per trace.
325
-
326
- ## Events and readings
327
-
328
- `onEvent(listener)` supports multiple independent subscriptions and returns an
329
- unsubscribe function. `SmithEvent` is a discriminated union: checking `type`
330
- narrows `data` without a cast or a property-presence test.
331
-
332
- ```ts
333
- import { SmithEventType } from 'smithkit';
334
-
335
- const unsubscribe = chart.onEvent((event) => {
336
- if (event.type === SmithEventType.Cursor) {
337
- // undefined when the pointer leaves or a marker drag starts.
338
- console.log(event.data?.impedanceOhms?.toVector());
339
- return;
340
- }
341
- // Marker, MarkerSelect, MarkerDragStart, and MarkerDragEnd carry a MarkerSnapshot.
342
- console.log(event.data.markerId, event.data.frequencyHz);
343
- });
344
-
345
- // On component unmount:
346
- unsubscribe();
347
- chart.destroy();
78
+ // On component unmount: chart.destroy();
348
79
  ```
349
80
 
350
- The event types are `Cursor` (`'cursor'`), `Marker` (`'marker'`),
351
- `MarkerSelect` (`'marker-select'`),
352
- `MarkerDragStart` (`'marker-drag-start'`), and `MarkerDragEnd` (`'marker-drag-end'`).
353
- `MarkerSelect` fires synchronously when a marker receives focus or is engaged by
354
- keyboard navigation or a pointer press. Use it to select the corresponding
355
- readout; it does not indicate a position change. Keyboard changes use the normal
356
- queued `Marker` event and do not emit drag lifecycle events.
357
- Cursor movement is suspended during marker dragging. Drag start/end events fire
358
- synchronously; marker position notifications are queued and report the latest
359
- snapshot at delivery. Before delivery, repeated updates to the same marker coalesce
360
- into one notification; different markers have independent notifications. `addMarker()`
361
- queues an initial marker notification too; `addTrace()` does not. Removed markers cannot deliver queued
362
- position events.
363
- Trace metadata changes do not emit marker events; refresh metadata after mutations.
364
- Destruction suppresses application callbacks.
365
-
366
- `chart.cursorReading` returns the last cursor position. Cursor and marker readings
367
- share `SmithReading`; `RfCalculations.readReflection(gamma, referenceImpedanceOhms = 50)` produces
368
- the same reading without constructing a chart.
369
-
370
- | Fields | Units / meaning |
371
- | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------- |
372
- | `reflectionCoefficient` | Complex, dimensionless voltage Γ. |
373
- | `impedanceOhms`, `admittanceSiemens` | Physical complex Z in Ω and Y in S. Multiply Y by 1000 for mS display. |
374
- | `phaseDegrees` | Phase of Γ in degrees; undefined at Γ = 0. |
375
- | `vswr`, `q` | Dimensionless voltage standing-wave ratio and \|X/R\|. |
376
- | `returnLossDb`, `reflectionLossDb` | dB. Reflection loss is also called mismatch loss. |
377
- | `standingWaveRatioDb` | 20 log₁₀(VSWR), in dB. |
378
- | `attenuationDb` | Half the return loss; interpretation described below. |
379
- | `powerReflectionCoefficient`, `powerTransmissionCoefficient` | \|Γ\|² and 1 − \|Γ\|². |
380
- | `voltageReflectionCoefficientMagnitude` | \|Γ\|. |
381
- | `voltageTransmissionCoefficientMagnitude`, `currentTransmissionCoefficientMagnitude` | \|1 + Γ\| and \|1 − Γ\|, respectively. |
382
- | `standingWavePeak`, `standingWaveLossCoefficient` | √VSWR and (1 + \|Γ\|²)/(1 − \|Γ\|²). |
383
-
384
- Singular Z at Γ = +1 and Y at Γ = −1 are `undefined`. Q is undefined at an open
385
- or short, and infinite for a nonzero purely reactive impedance. Finite points near
386
- these limits retain finite readings within the supported numeric range. An exact
387
- infinite scalar limit is preserved: perfect match gives infinite return loss;
388
- \|Γ\| = 1 gives infinite VSWR and reflection loss.
389
-
390
- For active loads (\|Γ\| > 1), VSWR, standing-wave quantities, reflection loss,
391
- attenuation, and power transmission are `undefined`. Other quantities remain
392
- available. Non-finite complex inputs or invalid reference impedances throw
393
- `RangeError` in the public RF methods. Unrepresentable complex conversion
394
- results are `undefined`.
395
-
396
- ## Selecting a marker by frequency
397
-
398
- `chart.setMarkerFrequency(markerId, frequencyHz)` selects the nearest measured
399
- frequency and returns `true`, or `false` if the marker does not exist. It accepts
400
- finite, non-negative Hz values; other values throw `RangeError`. It works with
401
- unsorted sweeps and duplicate frequencies. Equal-distance ties choose the earliest
402
- sample in input order. Requests outside the sweep select the nearest endpoint.
403
- No interpolation or sample reordering is performed.
404
-
405
- Read `chart.getMarker(markerId).frequencyHz` for the actual selected frequency.
406
- The same queued `Marker` event is emitted as for sample-index selection, and
407
- comparisons update accordingly. The demo accepts MHz and shows the selected
408
- measurement frequency; dragging and sample selection keep that field synchronized.
409
-
410
- ## Calculations without a chart
411
-
412
- ```ts
413
- import { Complex, RfCalculations } from 'smithkit';
414
-
415
- const gamma = RfCalculations.impedanceToReflection(Complex.from(75, 25), 50)!;
416
- const reading = RfCalculations.readReflection(gamma, 50);
417
- console.log(reading.impedanceOhms?.toVector()); // Approximately [75, 25].
418
- console.log(reading.admittanceSiemens?.toVector());
419
- ```
81
+ Each tuple is `[frequencyHz, realGamma, imaginaryGamma]`. Object samples and
82
+ packed `Float64Array` triples are also supported. Inputs are validated and copied.
420
83
 
421
- | Static method | Input and output |
422
- | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
423
- | `RfCalculations.reflectionToImpedance(gamma, referenceImpedanceOhms = 50)` | Γ → physical Z in Ω. |
424
- | `RfCalculations.impedanceToReflection(impedanceOhms, referenceImpedanceOhms = 50)` | Physical Z in Ω → Γ. |
425
- | `RfCalculations.reflectionToAdmittance(gamma, referenceImpedanceOhms = 50)` | Γ → physical Y in S. |
426
- | `RfCalculations.admittanceToReflection(admittanceSiemens, referenceImpedanceOhms = 50)` | Physical Y in S → Γ. |
427
- | `RfCalculations.readReflection(gamma, referenceImpedanceOhms = 50)` | Complete `SmithReading`. |
428
- | `MarkerMeasurements.compare(a, b)` | Compare readings without a chart. Each input needs `frequencyHz`, `reflectionCoefficient`, and `impedanceOhms`. |
84
+ By default, the chart shows a labeled standard impedance grid with zoom, cursor
85
+ tracking, and peripheral scales disabled. No markers are added automatically.
86
+ The constructor and `setOptions()` share the same grouped configuration schema.
429
87
 
430
- Comparison results contain `frequencyDeltaHz`, `impedanceDeltaOhms`, and
431
- `phaseDeltaDegrees`. Phase differences use B − A wrapped to **[−180°, 180°)**:
432
- 179° → −179° gives +2°; an exact half turn gives −180°. Phase is undefined if
433
- either Γ is zero or non-finite. Impedance differences are undefined if either Z
434
- is singular or non-finite. Frequencies must be finite and non-negative. This is
435
- a point comparison, not sweep phase unwrapping or group delay.
88
+ Create charts in the browser after mounting the host; call `destroy()` on unmount.
89
+ Calculation, formatting, and Touchstone classes also work without a DOM.
436
90
 
437
- `Complex.from(re, im)` constructs complex numbers. Read components through `re` and `im`. Arithmetic (`add`, `sub`,
438
- `mul`, `div`, `pow`) returns new values. The class also provides `abs`, `arg`
439
- (radians), and static methods such as `Complex.conj`, `Complex.sqrt`,
440
- `Complex.exp`, `Complex.log`, and trigonometric/hyperbolic functions and inverses. `toVector()` returns `[real, imaginary]`; `toString(dp)` formats
441
- a Cartesian value. See the exported declarations for all operations.
91
+ ## Documentation
442
92
 
443
- The `SmithFormatter` class provides static methods: `SmithFormatter.number(value)` (three significant
444
- digits and SI prefixes), `SmithFormatter.complex(value, unit?, decimalPlaces?)`, and
445
- `SmithFormatter.polar(value, unit?, decimalPlaces?)` (phase in degrees). They work
446
- without a chart or DOM. Complex formatters default to three fractional digits.
447
- `SmithFormatter.number` includes spacing so you can append a unit, e.g. `SmithFormatter.number(1e9) + 'Hz'`.
93
+ The [searchable API documentation](https://rosek86.github.io/smithkit/api/) combines
94
+ usage guides with generated reference pages for every public class, method, and type.
95
+ The hosted site follows `main`; consult the installed package's declarations when
96
+ using an older release.
448
97
 
449
- `RfCalculations.reactanceToComponent(reactanceOhms, frequencyHz)` calculates an ideal equivalent
450
- series component without formatting or constructing a chart:
451
-
452
- ```ts
453
- import { RfCalculations, SmithFormatter } from 'smithkit';
454
-
455
- const component = RfCalculations.reactanceToComponent(-50, 1e9);
456
- if (component?.kind === 'capacitor') {
457
- console.log(SmithFormatter.number(component.capacitanceFarads) + 'F'); // 3.18 pF
458
- } else if (component?.kind === 'inductor') {
459
- console.log(SmithFormatter.number(component.inductanceHenries) + 'H');
460
- }
461
- ```
462
-
463
- Use `reading.impedanceOhms.im` for the reactance when impedance is defined.
464
- Zero frequency, zero reactance, and unrepresentable results return `undefined`.
465
- Non-finite reactance or negative/non-finite frequency throws `RangeError`.
466
- This is a single-frequency equivalence, not a fitted circuit model.
467
-
468
- ## Appearance and themes
469
-
470
- `Smith` and `SmithScales` accept the same appearance configuration. The default
471
- is the light preset with a transparent background. The dark preset includes a
472
- solid dark background so its labels remain readable when embedded or exported.
473
-
474
- ```ts
475
- import { Smith, SmithScales } from 'smithkit';
476
- import type { SmithAppearance } from 'smithkit';
477
-
478
- const appearance: SmithAppearance = {
479
- theme: 'dark',
480
- overrides: {
481
- fontFamily: 'Arial, sans-serif',
482
- grid: { majorWidth: 1.2 },
483
- cursor: { impedanceColor: '#fbbf24' },
484
- marker: { focusColor: '#38bdf8' },
485
- traceColors: ['#38bdf8', '#fb923c', '#a78bfa'],
486
- },
487
- };
488
- const chart = new Smith({ referenceImpedanceOhms: 50, appearance });
489
- const scales = new SmithScales(appearance);
490
- chart.draw('#smith');
491
- scales.draw('#scales');
492
-
493
- // Update both independent components using the same configuration.
494
- chart.setAppearance({ theme: 'light' });
495
- scales.setAppearance({ theme: 'light' });
496
- ```
497
-
498
- Each `setAppearance` call replaces the previous preset and overrides; omitted
499
- settings return to the selected preset, or light when `theme` is omitted.
500
- Nested overrides are partial. Appearance is scoped to each component and does not
501
- change the surrounding application's CSS or other chart instances.
502
-
503
- | Override | Settings |
504
- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
505
- | `background`, `fontFamily` | Component background and shared font family. |
506
- | `grid` | `stroke`, `textColor`, `majorWidth`, `minorWidth`, `fontSize`. |
507
- | `boundary` | `stroke`, `width` for the Smith chart boundary. |
508
- | `circles` | `q`, `vswr` colors and shared `width`. |
509
- | `scales` | `stroke`, `boundaryColor`, `textColor`, `valueColor`, `indicatorColor`, `indicatorOutline`; `fontSize` for radial ticks, `titleFontSize`, `valueFontSize`, and `peripheralFontSize`. |
510
- | `cursor` | `pointColor`, `impedanceColor`, `admittanceColor`, `pointSize` (dot diameter), `lineWidth`. |
511
- | `marker` | `outlineColor`, `textColor`, `focusColor`, `focusHaloColor`. Marker fill follows its trace color. |
512
- | `traceColors` | Non-empty palette for automatically colored traces. |
513
-
514
- Colors must be solid CSS colors accepted by the library's color parser, including
515
- hex, RGB, HSL, named colors, and `transparent`; CSS variable references are not
516
- accepted in the configuration. Numeric values must be positive and finite.
517
- Line widths and cursor point size are CSS pixels. Font sizes are SVG chart/scale
518
- units and follow the existing layout; grid labels retain their adaptive minimum
519
- size. Marker geometry and the 44 px touch target stay unchanged.
520
-
521
- Appearance updates retain zoom, samples, marker selection/focus, layer visibility,
522
- and scale readings. They recolor existing and future automatically colored traces.
523
- Explicit colors passed through trace options and explicit layer styles take
524
- precedence and survive theme changes. Applying a new appearance does not reset
525
- those individual overrides. Invalid appearance settings throw before making changes.
526
-
527
- SVG exports resolve the appearance without requiring theme CSS. A configured
528
- background is included in the exported image; `transparent` preserves transparency.
529
- The **Theme** selector in the demo's **Chart settings** dialog updates both library components and the demo UI.
530
-
531
- ## Grid layers
532
-
533
- Resistance and reactance are initially visible. Conductance, susceptance, Q,
534
- and VSWR layers start hidden. All controls are under `chart.layers`:
535
-
536
- ```ts
537
- chart.layers.resistance.setVisible(false);
538
- chart.layers.reactance.setVisible(false);
539
- chart.layers.conductance.setVisible(true);
540
- chart.layers.susceptance.setVisible(true);
541
- chart.layers.conductance.setDetail('standard');
542
- chart.layers.conductance.setLabelsVisible(false);
543
- chart.layers.susceptance.setLabelsVisible(false);
544
- chart.layers.susceptance.setStyle({ stroke: '#64748b', textColor: '#2a7f62' });
545
- chart.layers.vswr.addValue(4);
546
- chart.layers.vswr.setVisible(true);
547
- ```
548
-
549
- The four grid layers support `setVisible`, `setLabelsVisible`, `setDetail`, and
550
- `setStyle(Partial<GridStyle>)`. Style fields: `stroke`, `majorWidth`, `minorWidth`,
551
- `textColor`, `textFontFamily`, `textFontSize`. Widths are positive finite numbers
552
- in CSS pixels. Font size is a positive finite base size in chart units; adaptive
553
- layout raises it when needed to keep the font size at least 9 CSS pixels in the
554
- default view. Partial updates retain other settings. Invalid numeric styles throw
555
- before any property is changed.
556
-
557
- Use `chart.setGridDetail('basic' | 'standard' | 'detailed')` for all four grid layers,
558
- or `layer.setDetail(...)` to override one layer:
559
-
560
- - **Basic:** complete circles and arcs for normalized values 0.2, 0.5, 1, 2, and 5,
561
- with matching axis/rim labels and the zero label. Intended for small or static charts.
562
- - **Standard** (default): the full major grid and its labels, without minor lines.
563
- - **Detailed:** the full major and minor grid.
564
-
565
- Changing detail preserves layer visibility, styles, and the current view. Label
566
- collision handling still applies at every level. Peripheral scales and Q/VSWR
567
- circles are independent. The demo's **Chart settings → Grid detail** selector updates all four grid layers.
568
-
569
- Use `setLabelsVisible(false)` to hide a grid layer's labels while keeping its lines.
570
- Labels default to visible. The setting persists through detail, style, theme, and
571
- whole-layer visibility changes and is included in SVG exports. Calling
572
- `setLabelsVisible(true)` restores labels allowed by the current detail and collision
573
- layout; it does not show a hidden layer. The **Grid labels** checkbox in the demo’s **Chart settings** dialog controls all four grid layers together. Peripheral scale labels are unaffected.
574
-
575
- `layers.q` and `layers.vswr` support `setVisible`,
576
- `setStyle({ stroke?, strokeWidth? })`, `setValues`, `addValue`, and `removeValue`.
577
- `setValues([1, 2, 5])` replaces the entire list; `setValues([])` clears it.
578
- Input arrays are copied, duplicate values are ignored, and invalid input leaves
579
- the previous values unchanged.
580
- Circle stroke widths are positive finite numbers in CSS pixels.
581
- Q values must be positive and finite; VSWR values must be finite and at least 1.
582
- Duplicate additions and removal of absent values have no effect.
583
-
584
- Grid labels use normalized values; readings use physical units. Strokes default
585
- to slate gray with 1 px major and 0.6 px minor lines. Stroke widths stay constant
586
- under zoom. Layer controls do not expose drawing objects or SVG nodes.
587
-
588
- Grid labels adapt automatically to the chart's rendered size,
589
- including static charts with zoom disabled. The layout prioritizes key values,
590
- hides labels that overlap or extend beyond the default view, and prioritizes zero
591
- and the highest labeled axis value. It recalculates on resize and layer visibility
592
- or style changes. Zoom and pan preserve the chosen labels and scale them smoothly
593
- with the grid. This changes only labels; grid lines and ticks remain unchanged.
594
- SVG exports preserve the current label layout.
595
-
596
- For small static charts, hide minor grid lines and, when unnecessary, peripheral
597
- scales using the controls above. Very small charts necessarily show fewer values;
598
- use a larger container for detailed readings. Peripheral labels retain their full
599
- wording and scale with the chart. Their rulers form two outlined bands: phase
600
- (transmission/reflection) and electrical length (toward load/generator), with a
601
- shared tick circle inside each band.
602
- The independent `SmithScales` panel uses its own responsive layout.
603
-
604
- ## Parameter scales
605
-
606
- Mount `SmithScales` independently so its twelve axes stay outside chart zoom:
607
-
608
- ```ts
609
- import { SmithScales, SmithEventType } from 'smithkit';
610
-
611
- const scales = new SmithScales();
612
- scales.draw('#smith-scales'); // Provide a separate mounted container.
613
- const stopScales = chart.onEvent((event) => {
614
- if (event.type === SmithEventType.Cursor || event.type === SmithEventType.Marker) {
615
- const gamma = event.data?.reflectionCoefficient ?? null;
616
- scales.update(gamma);
617
- chart.peripheralScales.update(gamma);
618
- }
619
- });
620
-
621
- // On unmount: stopScales(); scales.destroy(); chart.destroy();
622
- ```
623
-
624
- `update(Complex | null)` synchronizes indicators, values, tooltips, and accessible
625
- labels. Null, non-finite, or active-load inputs hide indicators. `draw` accepts a
626
- selector or `HTMLElement`; `destroy` is idempotent. Scales adapt to container width.
627
-
628
- Labels follow the complete Smith chart terminology: **Reflection loss**,
629
- **Power reflection coefficient**, **Power transmission coefficient**, and separate
630
- **Voltage transmission coefficient** and **Current transmission coefficient**.
631
- Ratios are dimensionless; loss/standing-wave dB axes are in dB. Transmission
632
- indicators depend on Γ phase as well as magnitude.
633
-
634
- Attenuation is −10 log₁₀\|Γ\|, half the return loss. Its one-way loss interpretation
635
- assumes a matched line or attenuator terminated in an open or short: the reflected
636
- wave traverses it twice. It does not measure insertion loss of an arbitrary load
637
- from S11 alone. At \|Γ\| = 0.1 it reads 10 dB, while return loss reads 20 dB.
638
-
639
- Peripheral rulers are hidden by default. Enable them with
640
- `{ peripheralScales: { visible: true } }` or `chart.peripheralScales.setVisible(true)`.
641
- `chart.peripheralScales` supports `setVisible(boolean)` and `update(Complex | null)`.
642
- Its four rulers zoom with the chart: reflection phase, voltage transmission phase,
643
- wavelengths toward generator, and wavelengths toward load. Wavelength rulers start
644
- at the negative real axis, with 0.5 λ per revolution (generator clockwise, load
645
- counterclockwise). These are electrical-length coordinates modulo 0.5 λ.
646
- Reflection phase and wavelength coordinates are undefined at perfect match;
647
- transmission phase is undefined at Γ = −1.
648
-
649
- Scale conventions follow the [complete Smith chart reference](https://www.uiyinc.com/assets/The-Complete-Smith-Chart-Black-Magic-Design.jpg)
650
- and [CERN's Smith chart introduction](https://arxiv.org/abs/1201.4068).
651
-
652
- ## Reference impedance and renormalization
653
-
654
- `chart.renormalize(referenceImpedanceOhms)` changes the positive real reference
655
- impedance and transforms every loaded Γ to preserve physical Z. Trace/marker IDs,
656
- names, colors, visibility, frequencies, and selected sample indices are retained.
657
- Results for all traces are validated before any change; a singular/unrepresentable
658
- result rejects the operation without changing Z₀ or data. Active marker drags end,
659
- cursor indicators are cleared, and marker readings are queued with the new values.
660
- Changing to the current Z₀ has no effect. Mutations after destruction throw.
661
-
662
- Two DOM-independent methods are available on `RfCalculations`:
663
-
664
- - `RfCalculations.renormalizeReflection(gamma, fromOhms, toOhms)` returns the new complex Γ or
665
- `undefined` for a singular/unrepresentable result. Exact open and short limits
666
- remain Γ = +1 and −1. References must be positive, finite, real ohm values.
667
- - `RfCalculations.renormalizeSamples(samples, fromOhms, toOhms)` returns a new sample array in
668
- the same order, preserving frequencies. Invalid samples or a singular result
669
- throw `RangeError`; the input is never modified.
670
-
671
- For example, a matched 75 Ω load has Γ = 0 at 75 Ω and Γ = 0.2 at 50 Ω.
672
- Renormalization changes Γ, VSWR, and loss readings; physical impedance stays 75 Ω.
673
- Simply relabeling the same Γ with a new Z₀ would describe a different load.
674
-
675
- The demo exposes chart Z₀ and explicitly renormalizes existing traces when applied.
676
- Import renormalization is enabled by a labeled checkbox. Disable it to reject
677
- files whose reference differs from the chart. Complex reference impedances and
678
- multiport renormalization are outside the supported scope.
679
-
680
- ## Touchstone import
681
-
682
- `Touchstone.parse(text)` returns `{ samples, referenceImpedanceOhms }`. It accepts
683
- one-port Touchstone 1.x, converts RI/MA/DB into Cartesian Γ and all frequencies
684
- into Hz, and throws for unsupported or invalid input.
685
-
686
- ```ts
687
- import { Smith, Touchstone } from 'smithkit';
688
-
689
- async function showMeasurement(file: File): Promise<Smith> {
690
- const { samples, referenceImpedanceOhms } = Touchstone.parse(await file.text());
691
- const chart = new Smith({ referenceImpedanceOhms });
692
- chart.draw('#smith');
693
- chart.addTrace(samples, { name: file.name });
694
- return chart;
695
- }
696
- ```
697
-
698
- Omitted options use Touchstone defaults: GHz, S, MA, 50 Ω. For an existing chart,
699
- use `RfCalculations.renormalizeSamples` when the file reference differs from
700
- `chart.referenceImpedanceOhms`. The parser itself does not renormalize data.
701
- Multiport data and Touchstone 2.x are not supported.
702
-
703
- ## PNG and report export
704
-
705
- `chart.toPng(options?)` and `scales.toPng(options?)` return `Promise<Blob>` with MIME
706
- type `image/png`. Call them in a browser after mounting the components at non-zero
707
- sizes, as with SVG export. The current view, labels, colors, markers, and scale
708
- readings are captured without modifying the live components.
709
-
710
- ```ts
711
- import { SmithScales } from 'smithkit';
712
-
713
- const scales = new SmithScales();
714
- scales.draw('#scales');
715
- const png = await chart.toPng({ width: 3200, background: 'white', legend: true });
716
- // Use the Blob with your application's download, preview, or upload flow.
717
- const report = await chart.toPng({ width: 2400, scales, legend: true });
718
- const scalePng = await scales.toPng({ width: 1200, background: 'transparent' });
719
- ```
720
-
721
- - `width` / `height`: output pixels. Omit both to use the snapshot's natural size;
722
- specify one to preserve proportions. With both set, the complete image is
723
- centered and fitted without distortion or cropping. Padding uses the chosen
724
- background, or transparency when no background is specified.
725
- - `background`: omit to keep component backgrounds, or pass a concrete CSS color
726
- (for example `'#fff'`) or `'transparent'` to replace them. Chart colors stay as
727
- configured; choose a suitable chart theme for your report background.
728
- - `legend` (chart only): append the names and colors of visible traces. Long names
729
- wrap; hidden traces are omitted. Default: `false`.
730
- - `markerLegend` (chart only): independently append marker descriptions below the chart/scales.
731
- Default: `false`. `true` includes every marker of visible traces, with trace name,
732
- marker number, frequency, and impedance. Use an options object to select `markerIds`
733
- and `fields` (`frequency`, `impedance`, `admittance`, `reflectionCoefficient`, `vswr`,
734
- `returnLoss`). Fields use Hz with SI prefixes, Ω, mS, dimensionless Γ, VSWR ratios,
735
- and dB respectively. Empty `markerIds` selects none; empty `fields` keeps only names
736
- and marker numbers. Hidden traces are always omitted, duplicate selections appear
737
- once, and unknown IDs/fields throw. Long descriptions wrap. Readings are captured at
738
- export invocation, without moving markers or changing the live view.
739
- - `scales` (chart only): a mounted `SmithScales` instance to place centered below the chart.
740
- Its current responsive layout and readings are preserved. Use `scaleReadout` to
741
- export a specific reading independently of the live view.
742
-
743
- ```ts
744
- // Works with toSvg() as well. Trace and marker legends are independent.
745
- const png = await chart.toPng({
746
- legend: false,
747
- markerLegend: {
748
- fields: ['frequency', 'impedance', 'vswr'],
749
- // Omit markerIds to include all markers of visible traces.
750
- },
751
- });
752
- ```
753
-
754
- Dimensions must be positive integers, at most 8192 px per side and 32 megapixels
755
- in total. Invalid options and unavailable/destroyed components reject the promise.
756
- PNG export uses browser SVG decoding and Canvas 2D and releases temporary image URLs.
757
- Fonts must be available to the browser; external font files are not embedded.
758
- Export size increases resolution; it does not change the current label density.
759
-
760
- The demo's **Export** dialog supports chart, scales, or a combined report,
761
- custom dimensions/background, and independent trace and marker legends. **Include marker legend** reveals field and marker checkboxes; it is disabled for scales-only exports. The default output width is 3200 px. Choose PNG or SVG in the same dialog; both formats support these report options. The demo starts in dark mode. For exports containing parameter scales, **Scale readings** explicitly selects a marker or **None**, independently of the Cursor/Marker tab. Marker exports include the trace name, marker number, and frequency; cursor readings are not exported.
762
-
763
- To export a specific scale reading without changing the live view, pass `readout` to
764
- `scales.toSvg()` / `scales.toPng()`, or `scaleReadout` to a combined chart export:
765
-
766
- ```ts
767
- import { Complex, SmithScales } from 'smithkit';
768
-
769
- const scales = new SmithScales();
770
- scales.draw('#scales');
771
- const report = await chart.toPng({
772
- scales,
773
- scaleReadout: {
774
- reflectionCoefficient: Complex.from(0.2, 0.3),
775
- label: 'Antenna · Marker 1 · Frequency: 1 GHz',
776
- },
777
- });
778
- ```
779
-
780
- Use `{ reflectionCoefficient: null }` for scales without readings. Omit the override to
781
- preserve the current readings. Labels wrap to fit the exported scales.
782
-
783
- ## SVG export
784
-
785
- `chart.toSvg(options?)` and `scales.toSvg(options?)` return SVG strings synchronously.
786
- They accept the same `SmithImageExportOptions` / `ImageExportOptions` as PNG export,
787
- including size, background, legend, and scales. Omitting options preserves the
788
- original standalone SVG snapshot. Call them after `draw()`
789
- while the component is mounted and has a non-zero rendered size; exporting before
790
- mounting, from a `display: none` container, or after destruction throws an error.
791
-
792
- ```ts
793
- const svg = chart.toSvg();
794
- const blob = new Blob([svg], { type: 'image/svg+xml;charset=utf-8' });
795
- const url = URL.createObjectURL(blob);
796
- const link = document.createElement('a');
797
- link.href = url;
798
- link.download = 'smith-chart.svg';
799
- document.body.appendChild(link);
800
- link.click();
801
- link.remove();
802
- setTimeout(() => URL.revokeObjectURL(url), 1000);
803
- ```
804
-
805
- The chart export preserves the current zoom/pan, layer visibility, labels, traces,
806
- markers, and visible indicators. It captures the rendered points, including dense
807
- trace point reduction; it is an image export, not a measurement-data export.
808
- The scale export combines all twelve axes into one SVG using their current layout
809
- and displayed readings. To export a marker reading, call `scales.update()` with
810
- that marker's reflection coefficient before `scales.toSvg()`.
811
-
812
- Files have explicit pixel dimensions and preserve the configured component background
813
- (transparent by default). Presentation
814
- styles are inlined so the application's stylesheet is not required. Text remains
815
- editable, and curved captions retain their internal path references. Font files
816
- and surrounding HTML controls/backgrounds are not embedded. Downloading is the
817
- consumer's responsibility; the library does not create files or modify live nodes.
818
- In the demo, choose **Export → Format → SVG**, then select chart, scales, or the combined report.
98
+ | Topic | Guide |
99
+ | -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
100
+ | Mounting, resizing, cleanup | [Getting started](https://rosek86.github.io/smithkit/api/documents/Getting_started.html) |
101
+ | Constructor options, runtime patches, themes, layers | [Configuration](https://rosek86.github.io/smithkit/api/documents/Configuration.html) |
102
+ | Data formats, trace styling, markers, keyboard and touch | [Traces and markers](https://rosek86.github.io/smithkit/api/documents/Traces_and_markers.html) |
103
+ | Subscriptions, event timing, readouts and units | [Events and readings](https://rosek86.github.io/smithkit/api/documents/Events_and_readings.html) |
104
+ | Radial and peripheral scales | [Scales](https://rosek86.github.io/smithkit/api/documents/Scales.html) |
105
+ | Complex arithmetic, conversions and renormalization | [RF calculations](https://rosek86.github.io/smithkit/api/documents/RF_calculations.html) |
106
+ | S11/S22 import, packed parsing, one-port export | [Touchstone](https://rosek86.github.io/smithkit/api/documents/Touchstone.html) |
107
+ | SVG/PNG, dimensions, backgrounds, scales and legends | [Report export](https://rosek86.github.io/smithkit/api/documents/SVG_and_PNG_export.html) |
108
+ | React and Angular lifecycle integration | [Framework integration](https://rosek86.github.io/smithkit/api/documents/Framework_integration.html) |
819
109
 
820
110
  ## Development and license
821
111
 
822
112
  See [CONTRIBUTING.md](CONTRIBUTING.md) for development, testing, release, and demo
823
113
  hosting instructions. Licensed under [MIT](LICENSE).
824
-
825
- ## Numerical limits
826
-
827
- `Complex` uses JavaScript binary64 numbers. Multiplication and division scale
828
- intermediate products to avoid premature overflow and underflow; logarithms,
829
- square roots, and reciprocals also handle extreme finite components. Results still
830
- round to binary64: unrepresentable magnitudes become infinity and tiny results
831
- can underflow to signed zero. Division by zero and reciprocals at zero return
832
- `[NaN, NaN]`. These operations reject non-finite operands with NaN components.
833
- The principal logarithm of zero retains its `-Infinity` real limit, and signed
834
- zeros select the side of square-root and logarithm branch cuts. `pow(z, 0)` is one,
835
- including zero to the zeroth power; negative powers of zero are undefined.
836
-
837
- RF helpers validate finite inputs and use `undefined` for unrepresentable complex
838
- readings, including an overflowing impedance difference between markers. These
839
- conventions do not imply arbitrary precision or numerical certification of every
840
- transcendental operation.