smithkit 0.1.0 → 0.2.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 (132) hide show
  1. package/README.md +124 -59
  2. package/dist/lib/MarkerMeasurements.d.ts +6 -0
  3. package/dist/lib/MarkerMeasurements.js +22 -0
  4. package/dist/lib/Smith.d.ts +9 -2
  5. package/dist/lib/Smith.js +78 -35
  6. package/dist/lib/SmithFormatter.d.ts +10 -0
  7. package/dist/lib/SmithFormatter.js +19 -0
  8. package/dist/lib/grid/CircleLayerControl.d.ts +15 -0
  9. package/dist/lib/grid/CircleLayerControl.js +40 -0
  10. package/dist/lib/{draw → grid}/ConstCircles.d.ts +6 -6
  11. package/dist/lib/{draw → grid}/ConstCircles.js +12 -5
  12. package/dist/lib/{draw → grid}/ConstQCircles.d.ts +2 -2
  13. package/dist/lib/{draw → grid}/ConstQCircles.js +3 -3
  14. package/dist/lib/{draw → grid}/ConstSwrCircles.d.ts +2 -2
  15. package/dist/lib/{draw → grid}/ConstSwrCircles.js +3 -3
  16. package/dist/lib/grid/GridDefinitions.d.ts +12 -0
  17. package/dist/lib/grid/GridDefinitions.js +138 -0
  18. package/dist/lib/grid/GridGeometry.d.ts +26 -0
  19. package/dist/lib/grid/GridGeometry.js +58 -0
  20. package/dist/lib/grid/GridLabels.d.ts +8 -0
  21. package/dist/lib/grid/GridLabels.js +45 -0
  22. package/dist/lib/grid/GridLayerControl.d.ts +12 -0
  23. package/dist/lib/grid/GridLayerControl.js +39 -0
  24. package/dist/lib/grid/LayerControl.d.ts +6 -0
  25. package/dist/lib/grid/LayerControl.js +16 -0
  26. package/dist/lib/{draw → grid}/SmithGridLayer.d.ts +5 -4
  27. package/dist/lib/{draw → grid}/SmithGridLayer.js +26 -9
  28. package/dist/lib/grid/types.d.ts +1 -1
  29. package/dist/lib/index.d.ts +8 -10
  30. package/dist/lib/index.js +5 -7
  31. package/dist/lib/{draw → interaction}/SmithCursor.d.ts +3 -3
  32. package/dist/lib/{draw → interaction}/SmithCursor.js +6 -6
  33. package/dist/lib/io/Touchstone.d.ts +10 -0
  34. package/dist/lib/io/Touchstone.js +75 -0
  35. package/dist/lib/layers.d.ts +2 -8
  36. package/dist/lib/layers.js +1 -73
  37. package/dist/lib/{complex → math}/Complex.d.ts +1 -0
  38. package/dist/lib/{complex → math}/Complex.js +1 -1
  39. package/dist/lib/math/geometry.d.ts +14 -0
  40. package/dist/lib/measurements.d.ts +2 -4
  41. package/dist/lib/measurements.js +1 -19
  42. package/dist/lib/rf/RfCalculations.d.ts +65 -0
  43. package/dist/lib/rf/RfCalculations.js +161 -0
  44. package/dist/lib/{SmithConstantCircle.d.ts → rf/SmithConstantCircle.d.ts} +2 -3
  45. package/dist/lib/{SmithConstantCircle.js → rf/SmithConstantCircle.js} +3 -4
  46. package/dist/lib/scales/PeripheralScaleDefinitions.d.ts +16 -0
  47. package/dist/lib/scales/PeripheralScaleDefinitions.js +54 -0
  48. package/dist/lib/scales/RadialScaleDefinitions.d.ts +15 -0
  49. package/dist/lib/scales/RadialScaleDefinitions.js +112 -0
  50. package/dist/lib/scales/SmithPeripheralScales.d.ts +7 -2
  51. package/dist/lib/scales/SmithPeripheralScales.js +65 -28
  52. package/dist/lib/scales/SmithScales.d.ts +3 -1
  53. package/dist/lib/scales/SmithScales.js +20 -6
  54. package/dist/lib/svg/LabelLayout.d.ts +17 -0
  55. package/dist/lib/svg/LabelLayout.js +145 -0
  56. package/dist/lib/{draw → svg}/SmithArc.d.ts +1 -1
  57. package/dist/lib/{draw → svg}/SmithCircle.d.ts +1 -1
  58. package/dist/lib/{draw → svg}/SmithLine.d.ts +1 -1
  59. package/dist/lib/{draw → svg}/SmithScaler.d.ts +1 -4
  60. package/dist/lib/{draw → svg}/SmithText.d.ts +1 -1
  61. package/dist/lib/svg/SvgExporter.d.ts +14 -0
  62. package/dist/lib/svg/SvgExporter.js +137 -0
  63. package/dist/lib/{draw → traces}/SmithData.d.ts +24 -35
  64. package/dist/lib/traces/SmithData.js +195 -0
  65. package/dist/lib/{draw → traces}/SmithMarker.d.ts +14 -3
  66. package/dist/lib/traces/SmithMarker.js +237 -0
  67. package/dist/lib/traces/TraceModel.d.ts +31 -0
  68. package/dist/lib/traces/TraceModel.js +123 -0
  69. package/dist/lib/traces/TraceRenderer.d.ts +33 -0
  70. package/dist/lib/traces/TraceRenderer.js +152 -0
  71. package/package.json +4 -1
  72. package/dist/lib/SmithArcsDefs.d.ts +0 -10
  73. package/dist/lib/SmithArcsDefs.js +0 -10
  74. package/dist/lib/components.d.ts +0 -14
  75. package/dist/lib/components.js +0 -26
  76. package/dist/lib/draw/SmithData.js +0 -366
  77. package/dist/lib/draw/SmithMarker.js +0 -122
  78. package/dist/lib/formatting.d.ts +0 -7
  79. package/dist/lib/formatting.js +0 -16
  80. package/dist/lib/grid/definitions.d.ts +0 -3
  81. package/dist/lib/grid/definitions.js +0 -123
  82. package/dist/lib/grid/geometry.d.ts +0 -20
  83. package/dist/lib/grid/geometry.js +0 -55
  84. package/dist/lib/grid/labels.d.ts +0 -3
  85. package/dist/lib/grid/labels.js +0 -41
  86. package/dist/lib/io/touchstone.d.ts +0 -7
  87. package/dist/lib/io/touchstone.js +0 -72
  88. package/dist/lib/math/complexRatio.d.ts +0 -3
  89. package/dist/lib/math/complexRatio.js +0 -30
  90. package/dist/lib/renormalization.d.ts +0 -10
  91. package/dist/lib/renormalization.js +0 -62
  92. package/dist/lib/rf.d.ts +0 -35
  93. package/dist/lib/rf.js +0 -74
  94. package/dist/lib/scales/peripheralScales.d.ts +0 -12
  95. package/dist/lib/scales/peripheralScales.js +0 -49
  96. package/dist/lib/scales/radialScales.d.ts +0 -12
  97. package/dist/lib/scales/radialScales.js +0 -109
  98. package/dist/lib/shapes/Arc.d.ts +0 -6
  99. package/dist/lib/shapes/Circle.d.ts +0 -5
  100. package/dist/lib/shapes/Circle.js +0 -1
  101. package/dist/lib/shapes/Line.d.ts +0 -5
  102. package/dist/lib/shapes/Line.js +0 -1
  103. package/dist/lib/shapes/Point.d.ts +0 -1
  104. package/dist/lib/shapes/Point.js +0 -1
  105. /package/dist/lib/{draw → grid}/ConstConductance.d.ts +0 -0
  106. /package/dist/lib/{draw → grid}/ConstConductance.js +0 -0
  107. /package/dist/lib/{draw → grid}/ConstReactance.d.ts +0 -0
  108. /package/dist/lib/{draw → grid}/ConstReactance.js +0 -0
  109. /package/dist/lib/{draw → grid}/ConstResistance.d.ts +0 -0
  110. /package/dist/lib/{draw → grid}/ConstResistance.js +0 -0
  111. /package/dist/lib/{draw → grid}/ConstSusceptance.d.ts +0 -0
  112. /package/dist/lib/{draw → grid}/ConstSusceptance.js +0 -0
  113. /package/dist/lib/{arcs → grid}/Tick.d.ts +0 -0
  114. /package/dist/lib/{arcs → grid}/Tick.js +0 -0
  115. /package/dist/lib/{draw → interaction}/MouseGesture.d.ts +0 -0
  116. /package/dist/lib/{draw → interaction}/MouseGesture.js +0 -0
  117. /package/dist/lib/{draw/SmithDrawOptions.js → math/geometry.js} +0 -0
  118. /package/dist/lib/{draw → svg}/SmithArc.js +0 -0
  119. /package/dist/lib/{draw → svg}/SmithCircle.js +0 -0
  120. /package/dist/lib/{draw → svg}/SmithDrawOptions.d.ts +0 -0
  121. /package/dist/lib/{shapes/Arc.js → svg/SmithDrawOptions.js} +0 -0
  122. /package/dist/lib/{draw → svg}/SmithGroup.d.ts +0 -0
  123. /package/dist/lib/{draw → svg}/SmithGroup.js +0 -0
  124. /package/dist/lib/{draw → svg}/SmithLine.js +0 -0
  125. /package/dist/lib/{draw → svg}/SmithPath.d.ts +0 -0
  126. /package/dist/lib/{draw → svg}/SmithPath.js +0 -0
  127. /package/dist/lib/{draw → svg}/SmithScaler.js +0 -0
  128. /package/dist/lib/{draw → svg}/SmithShape.d.ts +0 -0
  129. /package/dist/lib/{draw → svg}/SmithShape.js +0 -0
  130. /package/dist/lib/{draw → svg}/SmithSvg.d.ts +0 -0
  131. /package/dist/lib/{draw → svg}/SmithSvg.js +0 -0
  132. /package/dist/lib/{draw → svg}/SmithText.js +0 -0
package/dist/lib/Smith.js CHANGED
@@ -1,29 +1,32 @@
1
1
  import * as d3 from 'd3';
2
- import { MouseGesture } from './draw/MouseGesture.js';
3
- import { SmithSvg } from './draw/SmithSvg.js';
4
- import { SmithGroup } from './draw/SmithGroup.js';
5
- import { SmithCircle } from './draw/SmithCircle.js';
6
- import { SmithData } from './draw/SmithData.js';
7
- import { SmithCursor } from './draw/SmithCursor.js';
8
- import { ConstResistance } from './draw/ConstResistance.js';
9
- import { ConstReactance } from './draw/ConstReactance.js';
10
- import { ConstConductance } from './draw/ConstConductance.js';
11
- import { ConstSusceptance } from './draw/ConstSusceptance.js';
12
- import { ConstQCircles } from './draw/ConstQCircles.js';
13
- import { ConstSwrCircles } from './draw/ConstSwrCircles.js';
14
- import { SmithScaler } from './draw/SmithScaler.js';
15
- import { SmithConstantCircle } from './SmithConstantCircle.js';
16
- import { SmithArcsDefs } from './SmithArcsDefs.js';
17
- import { Complex } from './complex/Complex.js';
2
+ import { MouseGesture } from './interaction/MouseGesture.js';
3
+ import { SmithSvg } from './svg/SmithSvg.js';
4
+ import { LabelLayout } from './svg/LabelLayout.js';
5
+ import { SvgExporter } from './svg/SvgExporter.js';
6
+ import { SmithGroup } from './svg/SmithGroup.js';
7
+ import { SmithCircle } from './svg/SmithCircle.js';
8
+ import { SmithData } from './traces/SmithData.js';
9
+ import { SmithCursor } from './interaction/SmithCursor.js';
10
+ import { ConstResistance } from './grid/ConstResistance.js';
11
+ import { ConstReactance } from './grid/ConstReactance.js';
12
+ import { ConstConductance } from './grid/ConstConductance.js';
13
+ import { ConstSusceptance } from './grid/ConstSusceptance.js';
14
+ import { ConstQCircles } from './grid/ConstQCircles.js';
15
+ import { ConstSwrCircles } from './grid/ConstSwrCircles.js';
16
+ import { SmithScaler } from './svg/SmithScaler.js';
17
+ import { SmithConstantCircle } from './rf/SmithConstantCircle.js';
18
+ import { GridDefinitions } from './grid/GridDefinitions.js';
19
+ import { Complex } from './math/Complex.js';
18
20
  import { SmithPeripheralScales } from './scales/SmithPeripheralScales.js';
19
- import { gridLayer, circleLayer } from './layers.js';
20
- import { renormalizeSamples } from './renormalization.js';
21
- import { readReflection } from './rf.js';
22
- import { compareMarkerReadings } from './measurements.js';
21
+ import { GridLayerControl } from './grid/GridLayerControl.js';
22
+ import { CircleLayerControl } from './grid/CircleLayerControl.js';
23
+ import { RfCalculations } from './rf/RfCalculations.js';
24
+ import { MarkerMeasurements } from './MarkerMeasurements.js';
23
25
  export var SmithEventType;
24
26
  (function (SmithEventType) {
25
27
  SmithEventType["Cursor"] = "cursor";
26
28
  SmithEventType["Marker"] = "marker";
29
+ SmithEventType["MarkerSelect"] = "marker-select";
27
30
  SmithEventType["MarkerDragStart"] = "marker-drag-start";
28
31
  SmithEventType["MarkerDragEnd"] = "marker-drag-end";
29
32
  })(SmithEventType || (SmithEventType = {}));
@@ -31,12 +34,14 @@ export class Smith {
31
34
  referenceOhms;
32
35
  calcs = new SmithConstantCircle();
33
36
  scalers;
37
+ defaultTransform = d3.zoomIdentity.translate(62.5, 62.5).scale(0.75);
34
38
  transform = d3.zoomIdentity;
35
39
  zoomEnabled = true;
36
40
  applyingView = false;
37
41
  zoomBehavior = d3.zoom();
38
42
  svg;
39
43
  resizeObserver;
44
+ labelLayout;
40
45
  container;
41
46
  dataContainer;
42
47
  markerContainer;
@@ -68,7 +73,7 @@ export class Smith {
68
73
  throw new RangeError('Reference impedance must be positive and finite.');
69
74
  }
70
75
  const viewBoxSize = 500;
71
- const gridData = SmithArcsDefs.getData();
76
+ const gridData = GridDefinitions.create();
72
77
  this.scalers = this.createScalers(viewBoxSize);
73
78
  this.svg = new SmithSvg(viewBoxSize);
74
79
  this.container = new SmithGroup();
@@ -122,15 +127,27 @@ export class Smith {
122
127
  this.container.append(this.reactanceAxis);
123
128
  this.container.append(cursorContainer);
124
129
  this.container.append(this.dataContainer);
130
+ const labels = new SmithGroup().attr('data-layer', 'labels').attr('pointer-events', 'none');
131
+ for (const [name, layer] of [
132
+ ['conductance', this.constConductance],
133
+ ['susceptance', this.constSusceptance],
134
+ ['resistance', this.constResistance],
135
+ ['reactance', this.constReactance],
136
+ ]) {
137
+ labels.append(layer.labels.attr('data-label-layer', name));
138
+ }
139
+ labels.append(this.peripheralScaleRenderer.labels);
140
+ this.container.append(labels);
125
141
  this.container.append(this.markerContainer);
142
+ this.labelLayout = new LabelLayout(this.svg.Node, labels.Node, this.defaultTransform);
126
143
  const assertAlive = () => this.assertAlive();
127
144
  this.layers = {
128
- resistance: gridLayer(this.constResistance, assertAlive),
129
- reactance: gridLayer(this.constReactance, assertAlive),
130
- conductance: gridLayer(this.constConductance, assertAlive),
131
- susceptance: gridLayer(this.constSusceptance, assertAlive),
132
- q: circleLayer(this.constQCircles, 0, assertAlive),
133
- vswr: circleLayer(this.constSwrCircles, 1, assertAlive),
145
+ resistance: new GridLayerControl(this.constResistance, assertAlive, () => this.labelLayout.update(true)),
146
+ reactance: new GridLayerControl(this.constReactance, assertAlive, () => this.labelLayout.update(true)),
147
+ conductance: new GridLayerControl(this.constConductance, assertAlive, () => this.labelLayout.update(true)),
148
+ susceptance: new GridLayerControl(this.constSusceptance, assertAlive, () => this.labelLayout.update(true)),
149
+ q: new CircleLayerControl(this.constQCircles, 0, assertAlive),
150
+ vswr: new CircleLayerControl(this.constSwrCircles, 1, assertAlive),
134
151
  };
135
152
  this.peripheralScales = {
136
153
  setVisible: (visible) => {
@@ -141,6 +158,7 @@ export class Smith {
141
158
  else {
142
159
  this.peripheralScaleRenderer.hide();
143
160
  }
161
+ this.labelLayout.update(true);
144
162
  },
145
163
  update: (gamma) => {
146
164
  assertAlive();
@@ -160,6 +178,11 @@ export class Smith {
160
178
  host.appendChild(this.svg.Node);
161
179
  this.updateViewportScale();
162
180
  }
181
+ /** Export the current mounted view as standalone SVG with a transparent background. */
182
+ toSvg() {
183
+ this.assertAlive();
184
+ return SvgExporter.chart(this.svg.Node);
185
+ }
163
186
  /** Remove this chart and release its event handlers. Safe to call more than once. */
164
187
  destroy() {
165
188
  if (this.destroyed) {
@@ -185,6 +208,7 @@ export class Smith {
185
208
  const scale = Math.hypot(matrix.a, matrix.b);
186
209
  this.data.forEach((trace) => trace.setViewportScale(scale));
187
210
  }
211
+ this.labelLayout.update();
188
212
  }
189
213
  assertAlive() {
190
214
  if (this.destroyed) {
@@ -241,19 +265,22 @@ export class Smith {
241
265
  }
242
266
  /** Last cursor position. Use cursor events to detect pointer leave. */
243
267
  get cursorReading() {
244
- return readReflection(this.cursor.Position, this.referenceImpedanceOhms);
268
+ return RfCalculations.readReflection(this.cursor.Position, this.referenceImpedanceOhms);
245
269
  }
246
270
  initializeZoom() {
247
271
  const zoom = this.zoomBehavior
248
272
  .scaleExtent([0.6, 1000])
249
- .filter((event) => this.zoomEnabled && (!event.ctrlKey || event.type === 'wheel') && !event.button)
273
+ .filter((event) => this.zoomEnabled &&
274
+ this.draggedMarkers.size === 0 &&
275
+ (!event.ctrlKey || event.type === 'wheel') &&
276
+ !event.button)
250
277
  .on('start', (event) => {
251
278
  if (event.sourceEvent) {
252
279
  this.mouseGesture.capture(event.sourceEvent, 'zoom');
253
280
  }
254
281
  })
255
282
  .on('zoom', (event) => {
256
- if (!this.zoomEnabled && !this.applyingView) {
283
+ if ((!this.zoomEnabled || this.draggedMarkers.size > 0) && !this.applyingView) {
257
284
  // An already active mouse/touch gesture still needs its normal end event.
258
285
  // Restore D3's view through its public API while that gesture finishes.
259
286
  this.applyView(this.transform);
@@ -289,7 +316,7 @@ export class Smith {
289
316
  }
290
317
  resetView() {
291
318
  this.assertAlive();
292
- const transform = d3.zoomIdentity.translate(62.5, 62.5).scale(0.75);
319
+ const transform = this.defaultTransform;
293
320
  this.applyView(transform);
294
321
  }
295
322
  onZoom(transform) {
@@ -400,6 +427,7 @@ export class Smith {
400
427
  data.setStyle(options);
401
428
  if (options.name !== undefined) {
402
429
  this.traceMetadata.get(data).name = options.name.trim();
430
+ data.setName(options.name.trim());
403
431
  }
404
432
  if (options.color !== undefined) {
405
433
  data.setColor(options.color);
@@ -448,6 +476,14 @@ export class Smith {
448
476
  const location = this.findMarker(id);
449
477
  return location ? this.data[location.datasetNo].removeMarker(location.markerNo) : false;
450
478
  }
479
+ /** Focus a mounted, visible marker for keyboard interaction. */
480
+ focusMarker(id) {
481
+ this.assertAlive();
482
+ const location = this.findMarker(id);
483
+ return location
484
+ ? this.data[location.datasetNo].Markers[location.markerNo].marker.focus()
485
+ : false;
486
+ }
451
487
  setMarkerSample(id, sampleIndex) {
452
488
  this.assertAlive();
453
489
  const location = this.findMarker(id);
@@ -471,7 +507,7 @@ export class Smith {
471
507
  const data = this.data[location.datasetNo];
472
508
  const marker = data.Markers[location.markerNo];
473
509
  return {
474
- ...readReflection(Complex.from(...marker.selectedPoint.reflectionCoefficient), this.referenceImpedanceOhms),
510
+ ...RfCalculations.readReflection(Complex.from(...marker.selectedPoint.reflectionCoefficient), this.referenceImpedanceOhms),
475
511
  frequencyHz: marker.selectedPoint.frequencyHz,
476
512
  traceId: this.traceMetadata.get(data).id,
477
513
  markerId: id,
@@ -483,7 +519,7 @@ export class Smith {
483
519
  compareMarkers(a, b) {
484
520
  const first = this.getMarker(a);
485
521
  const second = this.getMarker(b);
486
- return first && second ? compareMarkerReadings(first, second) : undefined;
522
+ return first && second ? MarkerMeasurements.compare(first, second) : undefined;
487
523
  }
488
524
  /** Current positive real reference impedance, in ohms. */
489
525
  get referenceImpedanceOhms() {
@@ -493,11 +529,11 @@ export class Smith {
493
529
  renormalize(referenceImpedanceOhms) {
494
530
  this.assertAlive();
495
531
  // Validate even with no traces, and prepare every result before changing any state.
496
- renormalizeSamples([], this.referenceOhms, referenceImpedanceOhms);
532
+ RfCalculations.renormalizeSamples([], this.referenceOhms, referenceImpedanceOhms);
497
533
  if (referenceImpedanceOhms === this.referenceOhms) {
498
534
  return;
499
535
  }
500
- const samples = this.data.map((data) => renormalizeSamples(data.Samples, this.referenceOhms, referenceImpedanceOhms));
536
+ const samples = this.data.map((data) => RfCalculations.renormalizeSamples(data.Samples, this.referenceOhms, referenceImpedanceOhms));
501
537
  this.data.forEach((data) => data.Markers.forEach((entry) => entry.marker.cancelDrag()));
502
538
  this.referenceOhms = referenceImpedanceOhms;
503
539
  this.data.forEach((data, index) => data.update(samples[index], 'sample-index'));
@@ -523,6 +559,13 @@ export class Smith {
523
559
  }, this.markerContainer, options);
524
560
  const number = this.nextTraceId++;
525
561
  this.traceMetadata.set(data, { id: `trace-${number}`, name: `Trace ${number}` });
562
+ data.setName(`Trace ${number}`);
563
+ data.setMarkerSelectHandler((marker) => {
564
+ const snapshot = this.getMarker(this.markerId(marker));
565
+ if (snapshot) {
566
+ this.emit({ type: SmithEventType.MarkerSelect, data: snapshot });
567
+ }
568
+ });
526
569
  data.setMarkerMoveHandler((index) => {
527
570
  const marker = data.Markers[index];
528
571
  const snapshot = marker && this.getMarker(this.markerId(marker.marker));
@@ -0,0 +1,10 @@
1
+ import type { Complex } from './math/Complex.js';
2
+ export declare class SmithFormatter {
3
+ private constructor();
4
+ /** Format with three significant digits and an SI prefix; includes spacing before the unit. */
5
+ static number(value: number): string;
6
+ /** Format Cartesian components to decimalPlaces fractional digits. */
7
+ static complex(value: Complex, unit?: string, decimalPlaces?: number): string;
8
+ /** Format magnitude and phase in degrees to decimalPlaces fractional digits. */
9
+ static polar(value: Complex, unit?: string, decimalPlaces?: number): string;
10
+ }
@@ -0,0 +1,19 @@
1
+ import { format } from 'd3';
2
+ export class SmithFormatter {
3
+ constructor() { }
4
+ /** Format with three significant digits and an SI prefix; includes spacing before the unit. */
5
+ static number(value) {
6
+ const formatted = format('.3~s')(value);
7
+ return Number.isFinite(value) && /[a-zA-Zµ]$/.test(formatted)
8
+ ? formatted.replace(/([a-zA-Zµ])$/, ' $1')
9
+ : formatted + ' ';
10
+ }
11
+ /** Format Cartesian components to decimalPlaces fractional digits. */
12
+ static complex(value, unit = '', decimalPlaces = 3) {
13
+ return `${value.toString(decimalPlaces)} ${unit === '' ? '' : `[${unit}]`}`;
14
+ }
15
+ /** Format magnitude and phase in degrees to decimalPlaces fractional digits. */
16
+ static polar(value, unit = '', decimalPlaces = 3) {
17
+ return `${value.abs().toFixed(decimalPlaces)} ${unit} ∠${((value.arg() * 180) / Math.PI).toFixed(decimalPlaces)}°`;
18
+ }
19
+ }
@@ -0,0 +1,15 @@
1
+ import type { ConstQCircles } from './ConstQCircles.js';
2
+ import type { ConstSwrCircles } from './ConstSwrCircles.js';
3
+ import type { CircleLayer, CircleStyle } from '../layers.js';
4
+ import { LayerControl } from './LayerControl.js';
5
+ /** Exposes Q/VSWR circle controls without exposing the SVG renderer. */
6
+ export declare class CircleLayerControl extends LayerControl implements CircleLayer {
7
+ private readonly layer;
8
+ private readonly minimum;
9
+ constructor(layer: ConstQCircles | ConstSwrCircles, minimum: number, assertAlive: () => void);
10
+ setVisible(visible: boolean): void;
11
+ setStyle(style: Partial<CircleStyle>): void;
12
+ addValue(value: number): void;
13
+ removeValue(value: number): void;
14
+ private validateValue;
15
+ }
@@ -0,0 +1,40 @@
1
+ import { LayerControl } from './LayerControl.js';
2
+ /** Exposes Q/VSWR circle controls without exposing the SVG renderer. */
3
+ export class CircleLayerControl extends LayerControl {
4
+ layer;
5
+ minimum;
6
+ constructor(layer, minimum, assertAlive) {
7
+ super(assertAlive);
8
+ this.layer = layer;
9
+ this.minimum = minimum;
10
+ }
11
+ setVisible(visible) {
12
+ this.assertAlive();
13
+ this.layer.visibility(visible);
14
+ }
15
+ setStyle(style) {
16
+ this.assertAlive();
17
+ this.validateLengths(style, ['strokeWidth']);
18
+ if (style.stroke !== undefined) {
19
+ this.layer.Stroke = style.stroke;
20
+ }
21
+ if (style.strokeWidth !== undefined) {
22
+ this.layer.StrokeWidth = String(style.strokeWidth);
23
+ }
24
+ }
25
+ addValue(value) {
26
+ this.assertAlive();
27
+ this.validateValue(value);
28
+ this.layer.append(value);
29
+ }
30
+ removeValue(value) {
31
+ this.assertAlive();
32
+ this.validateValue(value);
33
+ this.layer.remove(value);
34
+ }
35
+ validateValue(value) {
36
+ if (!Number.isFinite(value) || value < this.minimum || value <= 0) {
37
+ throw new RangeError(`Circle value must be finite, positive, and at least ${this.minimum}.`);
38
+ }
39
+ }
40
+ }
@@ -1,10 +1,8 @@
1
1
  import type * as d3 from 'd3';
2
- import { SmithGroup } from './SmithGroup.js';
3
- import { SmithScaler } from './SmithScaler.js';
4
- import { Line } from '../shapes/Line.js';
5
- import { Circle } from '../shapes/Circle.js';
6
- import { Point } from '../shapes/Point.js';
7
- import { SmithConstantCircle } from '../SmithConstantCircle.js';
2
+ import { SmithGroup } from '../svg/SmithGroup.js';
3
+ import { SmithScaler } from '../svg/SmithScaler.js';
4
+ import type { Line, Circle, Point } from '../math/geometry.js';
5
+ import { SmithConstantCircle } from '../rf/SmithConstantCircle.js';
8
6
  export interface ArcData {
9
7
  p1: Point;
10
8
  p2: Point;
@@ -41,6 +39,8 @@ export declare abstract class ConstCircles {
41
39
  showMinor(): void;
42
40
  hideMinor(): void;
43
41
  protected build(): SmithGroup;
42
+ /** Mount labels separately so every grid line is painted before any label. */
43
+ get labels(): SmithGroup;
44
44
  draw(): SmithGroup;
45
45
  setDrawOptions(opts: ConstCirclesDrawOptions): void;
46
46
  set Stroke(stroke: string);
@@ -1,5 +1,5 @@
1
- import { SmithGroup } from './SmithGroup.js';
2
- import { SmithConstantCircle } from '../SmithConstantCircle.js';
1
+ import { SmithGroup } from '../svg/SmithGroup.js';
2
+ import { SmithConstantCircle } from '../rf/SmithConstantCircle.js';
3
3
  export class ConstCircles {
4
4
  scaler;
5
5
  calcs = new SmithConstantCircle();
@@ -12,17 +12,19 @@ export class ConstCircles {
12
12
  }
13
13
  visibility(visible) {
14
14
  if (visible) {
15
- this.container.show();
15
+ this.show();
16
16
  }
17
17
  else {
18
- this.container.hide();
18
+ this.hide();
19
19
  }
20
20
  }
21
21
  show() {
22
22
  this.container.show();
23
+ this.texts.show();
23
24
  }
24
25
  hide() {
25
26
  this.container.hide();
27
+ this.texts.hide();
26
28
  }
27
29
  displayMinor(display) {
28
30
  if (display) {
@@ -39,7 +41,12 @@ export class ConstCircles {
39
41
  this.minor.hide();
40
42
  }
41
43
  build() {
42
- return this.container.append(this.minor).append(this.major).append(this.texts).hide();
44
+ this.texts.hide();
45
+ return this.container.append(this.minor).append(this.major).hide();
46
+ }
47
+ /** Mount labels separately so every grid line is painted before any label. */
48
+ get labels() {
49
+ return this.texts;
43
50
  }
44
51
  draw() {
45
52
  return this.container;
@@ -1,5 +1,5 @@
1
- import { SmithGroup } from './SmithGroup.js';
2
- import { SmithScaler } from './SmithScaler.js';
1
+ import { SmithGroup } from '../svg/SmithGroup.js';
2
+ import { SmithScaler } from '../svg/SmithScaler.js';
3
3
  interface ConstQDrawOptions {
4
4
  stroke: string;
5
5
  strokeWidth: string;
@@ -1,6 +1,6 @@
1
- import { SmithGroup } from './SmithGroup.js';
2
- import { SmithArc } from './SmithArc.js';
3
- import { SmithConstantCircle } from '../SmithConstantCircle.js';
1
+ import { SmithGroup } from '../svg/SmithGroup.js';
2
+ import { SmithArc } from '../svg/SmithArc.js';
3
+ import { SmithConstantCircle } from '../rf/SmithConstantCircle.js';
4
4
  export class ConstQCircles {
5
5
  scaler;
6
6
  calcs = new SmithConstantCircle();
@@ -1,5 +1,5 @@
1
- import { SmithGroup } from './SmithGroup.js';
2
- import { SmithScaler } from './SmithScaler.js';
1
+ import { SmithGroup } from '../svg/SmithGroup.js';
2
+ import { SmithScaler } from '../svg/SmithScaler.js';
3
3
  interface ConstSwrDrawOptions {
4
4
  stroke: string;
5
5
  strokeWidth: string;
@@ -1,6 +1,6 @@
1
- import { SmithGroup } from './SmithGroup.js';
2
- import { SmithCircle } from './SmithCircle.js';
3
- import { SmithConstantCircle } from '../SmithConstantCircle.js';
1
+ import { SmithGroup } from '../svg/SmithGroup.js';
2
+ import { SmithCircle } from '../svg/SmithCircle.js';
3
+ import { SmithConstantCircle } from '../rf/SmithConstantCircle.js';
4
4
  export class ConstSwrCircles {
5
5
  scaler;
6
6
  calcs = new SmithConstantCircle();
@@ -0,0 +1,12 @@
1
+ import type { SmithTicksData } from './types.js';
2
+ export declare class GridDefinitions {
3
+ private constructor();
4
+ private static readonly resistanceMajor;
5
+ private static readonly resistanceMinor;
6
+ private static readonly reactanceMajor;
7
+ private static readonly reactanceMinor;
8
+ private static resistanceArcs;
9
+ private static reactanceArcs;
10
+ /** Build fresh definitions so callers cannot mutate another chart's grid. */
11
+ static create(): SmithTicksData;
12
+ }
@@ -0,0 +1,138 @@
1
+ export class GridDefinitions {
2
+ constructor() { }
3
+ static resistanceMajor = [
4
+ [[0.05, 0.15], 0.2, -0.2, false],
5
+ [[0.1, 0.3, 0.5, 0.7, 0.9], 2, -2, true],
6
+ [[0.2, 0.4, 0.6, 0.8, 1.2, 1.4, 1.6, 1.8], 5, -5, true],
7
+ [[1, 3, 5], 10, -10, true],
8
+ [[2, 4], 20, -20, true],
9
+ [[20], 50, -50, true],
10
+ ];
11
+ static resistanceMinor = [
12
+ [[0.02, 0.04, 0.06, 0.08, 0.12, 0.14, 0.16, 0.18], 0.2, 0.5, false],
13
+ [[0.02, 0.04, 0.06, 0.08, 0.12, 0.14, 0.16, 0.18], -0.2, -0.5, false],
14
+ [[0.05, 0.15, 0.25, 0.35, 0.45], 0.5, 1, false],
15
+ [[0.05, 0.15, 0.25, 0.35, 0.45], -0.5, -1, false],
16
+ [
17
+ [
18
+ 0.01, 0.02, 0.03, 0.04, 0.06, 0.07, 0.08, 0.09, 0.11, 0.12, 0.13, 0.14, 0.16, 0.17, 0.18,
19
+ 0.19,
20
+ ],
21
+ 0.2,
22
+ -0.2,
23
+ false,
24
+ ],
25
+ [[0.22, 0.24, 0.26, 0.28, 0.32, 0.34, 0.36, 0.38, 0.42, 0.44, 0.46, 0.48], 0.5, -0.5, false],
26
+ [[0.55, 0.65, 0.75, 0.85, 0.95], 2, -2, true],
27
+ [[1.1, 1.3, 1.5, 1.7, 1.9], 2, -2, false],
28
+ [[2.2, 2.4, 2.6, 2.8, 3.2, 3.4, 3.6, 3.8], 5, -5, true],
29
+ [[4.2, 4.4, 4.6, 4.8], 5, -5, false],
30
+ [[6, 8, 12, 14, 16, 18], 20, -20, true],
31
+ [[7, 9], 10, -10, true],
32
+ [[30, 40], 50, -50, true],
33
+ ];
34
+ static reactanceMajor = [
35
+ [[0.05, 0.15], 0.2, 0],
36
+ [[0.1, 0.3, 0.5, 0.7, 0.9], 2, 0],
37
+ [[0.2, 0.4, 0.6, 0.8, 1.2, 1.4, 1.6, 1.8], 5, 0],
38
+ [[1, 3, 5], 10, 0],
39
+ [[2, 4, 10], 20, 0],
40
+ [[20], 50, 0],
41
+ ];
42
+ static reactanceMinor = [
43
+ [
44
+ [
45
+ 0.01, 0.02, 0.03, 0.04, 0.06, 0.07, 0.08, 0.09, 0.11, 0.12, 0.13, 0.14, 0.16, 0.17, 0.18,
46
+ 0.19,
47
+ ],
48
+ 0.2,
49
+ 0,
50
+ ],
51
+ [[0.02, 0.04, 0.06, 0.08, 0.12, 0.14, 0.16, 0.18], 0.5, 0.2],
52
+ [[0.22, 0.24, 0.26, 0.28, 0.32, 0.34, 0.36, 0.38, 0.42, 0.44, 0.46, 0.48], 0.5, 0],
53
+ [[0.05, 0.15, 0.25, 0.35, 0.45], 1, 0.5],
54
+ [[0.55, 0.65, 0.75, 0.85, 0.95], 1, 0],
55
+ [[1.1, 1.3, 1.5, 1.7, 1.9], 2, 0],
56
+ [[2.2, 2.4, 2.6, 2.8, 3.2, 3.4, 3.6, 3.8, 4.2, 4.4, 4.6, 4.8], 5, 0],
57
+ [[6, 8, 14, 16, 18], 20, 0],
58
+ [[7, 9], 10, 0],
59
+ [[12, 30, 40], 50, 0],
60
+ ];
61
+ static resistanceArcs(bands) {
62
+ return bands.flatMap(([values, from, to, largeArc]) => values.map((value) => [
63
+ value,
64
+ [
65
+ [from, from < 0 ? 1 : 0],
66
+ [to, to < 0 ? 1 : 0],
67
+ ],
68
+ [largeArc, from > to],
69
+ ]));
70
+ }
71
+ static reactanceArcs(bands) {
72
+ return bands.flatMap(([values, outer, inner]) => values.flatMap((value) => [
73
+ [
74
+ value,
75
+ [
76
+ [outer, 1],
77
+ [inner, 1],
78
+ ],
79
+ [false, false],
80
+ ],
81
+ [
82
+ -value,
83
+ [
84
+ [outer, 0],
85
+ [inner, 0],
86
+ ],
87
+ [false, true],
88
+ ],
89
+ ]));
90
+ }
91
+ /** Build fresh definitions so callers cannot mutate another chart's grid. */
92
+ static create() {
93
+ return {
94
+ resistance: {
95
+ major: {
96
+ lines: [],
97
+ circles: [10, 50],
98
+ arcs: GridDefinitions.resistanceArcs(GridDefinitions.resistanceMajor),
99
+ },
100
+ minor: {
101
+ lines: [],
102
+ circles: [],
103
+ arcs: GridDefinitions.resistanceArcs(GridDefinitions.resistanceMinor),
104
+ },
105
+ },
106
+ reactance: {
107
+ major: {
108
+ lines: [{ p1: [-1, 0], p2: [1, 0] }],
109
+ circles: [],
110
+ arcs: [
111
+ ...GridDefinitions.reactanceArcs(GridDefinitions.reactanceMajor),
112
+ [
113
+ 50,
114
+ [
115
+ [0, 0],
116
+ [0, 1],
117
+ ],
118
+ [false, false],
119
+ ],
120
+ [
121
+ -50,
122
+ [
123
+ [0, 1],
124
+ [0, 0],
125
+ ],
126
+ [false, true],
127
+ ],
128
+ ],
129
+ },
130
+ minor: {
131
+ lines: [],
132
+ circles: [],
133
+ arcs: GridDefinitions.reactanceArcs(GridDefinitions.reactanceMinor),
134
+ },
135
+ },
136
+ };
137
+ }
138
+ }
@@ -0,0 +1,26 @@
1
+ import type { TickDefRequired } from './Tick.js';
2
+ import type { SmithTicksShapes } from './types.js';
3
+ import type { Point } from '../math/geometry.js';
4
+ export type GridKind = 'resistance' | 'reactance' | 'conductance' | 'susceptance';
5
+ export type GridArc = [Point, Point, number, boolean, boolean];
6
+ export declare class GridGeometry {
7
+ private constructor();
8
+ private static readonly calcs;
9
+ private static readonly circles;
10
+ private static readonly clipping;
11
+ /** Normalized geometry only: no DOM, D3, or pixel coordinates. */
12
+ static shapes(kind: GridKind, data: SmithTicksShapes): {
13
+ lines: import("../math/geometry.js").Line[];
14
+ circles: import("../math/geometry.js").Circle[];
15
+ arcs: GridArc[];
16
+ };
17
+ static label(kind: GridKind, definition: TickDefRequired): {
18
+ point: [number, number];
19
+ text: string;
20
+ rotate: number;
21
+ dx: number;
22
+ dy: number;
23
+ textAnchor: string;
24
+ dominantBaseline: string;
25
+ };
26
+ }