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
@@ -1,8 +1,9 @@
1
1
  import { select, format } from 'd3';
2
- import { radialScales } from './radialScales.js';
2
+ import { RadialScaleDefinitions } from './RadialScaleDefinitions.js';
3
+ import { SvgExporter } from '../svg/SvgExporter.js';
3
4
  /** Independent parameter scales. Connect update() to a chart's cursor events. */
4
5
  export class SmithScales {
5
- scales = radialScales();
6
+ scales = RadialScaleDefinitions.create();
6
7
  container = select(document.createElement('div'))
7
8
  .attr('class', 'radial-scales')
8
9
  .style('display', 'grid')
@@ -24,7 +25,8 @@ export class SmithScales {
24
25
  .style('width', '100%')
25
26
  .style('height', 'auto');
26
27
  constructor() {
27
- this.axes
28
+ const labels = this.axes.append('g').attr('data-role', 'labels').attr('pointer-events', 'none');
29
+ labels
28
30
  .append('text')
29
31
  .attr('x', this.start)
30
32
  .attr('y', 12)
@@ -32,7 +34,7 @@ export class SmithScales {
32
34
  .attr('font-size', 12)
33
35
  .attr('fill', '#334155')
34
36
  .text((scale) => scale.title);
35
- this.axes
37
+ labels
36
38
  .append('text')
37
39
  .attr('class', 'scale-value')
38
40
  .attr('x', this.start)
@@ -55,12 +57,18 @@ export class SmithScales {
55
57
  const length = this.length;
56
58
  this.axes.each(function (scale) {
57
59
  const ticks = select(this)
58
- .selectAll('g')
60
+ .selectAll('g.scale-tick')
59
61
  .data(scale.values)
60
62
  .join('g')
63
+ .attr('class', 'scale-tick')
61
64
  .attr('transform', (value) => `translate(${start + length * scale.position(value)},36)`);
62
65
  ticks.append('line').attr('y2', 5).attr('stroke', '#64748b').attr('stroke-width', 1);
63
- ticks
66
+ select(this)
67
+ .select('[data-role=labels]')
68
+ .selectAll('g')
69
+ .data(scale.values)
70
+ .join('g')
71
+ .attr('transform', (value) => `translate(${start + length * scale.position(value)},36)`)
64
72
  .append('text')
65
73
  .attr('y', 17)
66
74
  .attr('font-size', 10)
@@ -79,6 +87,7 @@ export class SmithScales {
79
87
  .attr('fill', '#dc2626')
80
88
  .attr('stroke', 'white')
81
89
  .attr('stroke-width', 1);
90
+ labels.raise();
82
91
  this.axes.append('title').text((scale) => scale.description ?? scale.title);
83
92
  }
84
93
  draw(target) {
@@ -119,6 +128,11 @@ export class SmithScales {
119
128
  .attr('visibility', null);
120
129
  });
121
130
  }
131
+ /** Export all mounted scales in their current layout as a standalone SVG. */
132
+ toSvg() {
133
+ this.assertAlive();
134
+ return SvgExporter.scales(this.container.node(), this.axes.nodes());
135
+ }
122
136
  destroy() {
123
137
  if (this.destroyed) {
124
138
  return;
@@ -0,0 +1,17 @@
1
+ /** Responsive label placement measured in the default view, independent of user zoom. */
2
+ export declare class LabelLayout {
3
+ private readonly viewport;
4
+ private readonly container;
5
+ private readonly baseView;
6
+ private width;
7
+ private height;
8
+ private readonly resizedFonts;
9
+ constructor(viewport: SVGSVGElement, container: SVGGElement, baseView: {
10
+ x: number;
11
+ y: number;
12
+ k: number;
13
+ });
14
+ update(force?: boolean): void;
15
+ private static transformBox;
16
+ private static overlaps;
17
+ }
@@ -0,0 +1,145 @@
1
+ /** Responsive label placement measured in the default view, independent of user zoom. */
2
+ export class LabelLayout {
3
+ viewport;
4
+ container;
5
+ baseView;
6
+ width = 0;
7
+ height = 0;
8
+ resizedFonts = new Map();
9
+ constructor(viewport, container, baseView) {
10
+ this.viewport = viewport;
11
+ this.container = container;
12
+ this.baseView = baseView;
13
+ }
14
+ update(force = false) {
15
+ const viewport = this.viewport.getBoundingClientRect();
16
+ if (!this.viewport.isConnected || viewport.width <= 0 || viewport.height <= 0) {
17
+ return;
18
+ }
19
+ if (!force && viewport.width === this.width && viewport.height === this.height) {
20
+ return;
21
+ }
22
+ this.width = viewport.width;
23
+ this.height = viewport.height;
24
+ const rootMatrix = this.viewport.getScreenCTM();
25
+ const containerMatrix = this.container.getScreenCTM();
26
+ if (!rootMatrix || !containerMatrix) {
27
+ return;
28
+ }
29
+ const reference = rootMatrix.translate(this.baseView.x, this.baseView.y).scale(this.baseView.k);
30
+ const normalize = reference.multiply(containerMatrix.inverse());
31
+ // Peripheral rulers retain their complete, fixed-scale lettering. Only grid
32
+ // labels participate in thinning and minimum-font-size adjustments.
33
+ const labels = [...this.container.querySelectorAll('text')].filter((label) => !label.closest('[data-label-layer=peripheral-scales]'));
34
+ const view = this.viewport.ownerDocument.defaultView;
35
+ // Restore only properties owned by this layout before measuring the base styling.
36
+ for (const label of labels) {
37
+ if (label.hasAttribute('data-label-hidden')) {
38
+ label.removeAttribute('visibility');
39
+ label.removeAttribute('data-label-hidden');
40
+ }
41
+ const previousFont = this.resizedFonts.get(label);
42
+ if (previousFont !== undefined) {
43
+ if (previousFont) {
44
+ label.style.fontSize = previousFont;
45
+ }
46
+ else {
47
+ label.style.removeProperty('font-size');
48
+ }
49
+ }
50
+ }
51
+ this.resizedFonts.clear();
52
+ const styles = new Map();
53
+ const style = (element) => {
54
+ let result = styles.get(element);
55
+ if (!result) {
56
+ result = view.getComputedStyle(element);
57
+ styles.set(element, result);
58
+ }
59
+ return result;
60
+ };
61
+ const candidates = labels.flatMap((label, order) => {
62
+ for (let parent = label; parent; parent = parent.parentElement) {
63
+ const computed = style(parent);
64
+ if (computed.display === 'none' ||
65
+ computed.visibility === 'hidden' ||
66
+ Number(computed.opacity) === 0) {
67
+ return [];
68
+ }
69
+ if (parent === this.viewport) {
70
+ break;
71
+ }
72
+ }
73
+ const screenMatrix = label.getScreenCTM();
74
+ const matrix = screenMatrix ? normalize.multiply(screenMatrix) : null;
75
+ const scale = matrix ? Math.hypot(matrix.a, matrix.b) : 0;
76
+ if (scale <= 0) {
77
+ return [];
78
+ }
79
+ return [
80
+ {
81
+ label,
82
+ order,
83
+ scale,
84
+ matrix: matrix,
85
+ baseSize: parseFloat(style(label).fontSize),
86
+ priority: Number(label.dataset.labelPriority ?? 0),
87
+ },
88
+ ];
89
+ });
90
+ // Batch font changes before reading bounds to avoid alternating layout reads/writes.
91
+ for (const { label, scale, baseSize } of candidates) {
92
+ if (Number.isFinite(baseSize) && baseSize * scale < 9) {
93
+ this.resizedFonts.set(label, label.style.fontSize);
94
+ label.style.fontSize = `${9 / scale}px`;
95
+ }
96
+ }
97
+ const measured = candidates.map((entry) => ({
98
+ ...entry,
99
+ box: LabelLayout.transformBox(entry.label.getBBox(), entry.matrix),
100
+ }));
101
+ measured.sort((a, b) => b.priority - a.priority || a.order - b.order);
102
+ const occupied = [];
103
+ const hidden = [];
104
+ for (const { label, box } of measured) {
105
+ const outside = box.right <= box.left ||
106
+ box.bottom <= box.top ||
107
+ box.left < viewport.left + 1 ||
108
+ box.right > viewport.right - 1 ||
109
+ box.top < viewport.top + 1 ||
110
+ box.bottom > viewport.bottom - 1;
111
+ const collision = occupied.some((other) => LabelLayout.overlaps(box, other));
112
+ if (outside || collision) {
113
+ hidden.push(label);
114
+ }
115
+ else {
116
+ occupied.push(box);
117
+ }
118
+ }
119
+ for (const label of hidden) {
120
+ label.setAttribute('visibility', 'hidden');
121
+ label.setAttribute('data-label-hidden', 'true');
122
+ }
123
+ }
124
+ static transformBox(box, matrix) {
125
+ const points = [
126
+ new DOMPoint(box.x, box.y),
127
+ new DOMPoint(box.x + box.width, box.y),
128
+ new DOMPoint(box.x, box.y + box.height),
129
+ new DOMPoint(box.x + box.width, box.y + box.height),
130
+ ].map((point) => point.matrixTransform(matrix));
131
+ return {
132
+ left: Math.min(...points.map((point) => point.x)),
133
+ right: Math.max(...points.map((point) => point.x)),
134
+ top: Math.min(...points.map((point) => point.y)),
135
+ bottom: Math.max(...points.map((point) => point.y)),
136
+ };
137
+ }
138
+ static overlaps(a, b) {
139
+ const gap = 2;
140
+ return (a.left < b.right + gap &&
141
+ a.right + gap > b.left &&
142
+ a.top < b.bottom + gap &&
143
+ a.bottom + gap > b.top);
144
+ }
145
+ }
@@ -1,4 +1,4 @@
1
- import { Point } from '../shapes/Point.js';
1
+ import { Point } from '../math/geometry.js';
2
2
  import { SmithShape } from './SmithShape.js';
3
3
  import { SmithDrawOptions } from './SmithDrawOptions.js';
4
4
  export declare class SmithArc extends SmithShape {
@@ -1,4 +1,4 @@
1
- import { Circle } from '../shapes/Circle.js';
1
+ import { Circle } from '../math/geometry.js';
2
2
  import { SmithShape } from './SmithShape.js';
3
3
  import { SmithDrawOptions } from './SmithDrawOptions.js';
4
4
  export declare class SmithCircle extends SmithShape {
@@ -1,4 +1,4 @@
1
- import { Point } from '../shapes/Point.js';
1
+ import { Point } from '../math/geometry.js';
2
2
  import { SmithShape } from './SmithShape.js';
3
3
  import { SmithDrawOptions } from './SmithDrawOptions.js';
4
4
  export declare class SmithLine extends SmithShape {
@@ -1,8 +1,5 @@
1
1
  import * as d3 from 'd3';
2
- import { Circle } from '../shapes/Circle.js';
3
- import { Point } from '../shapes/Point.js';
4
- import { Line } from '../shapes/Line.js';
5
- import { Arc } from '../shapes/Arc.js';
2
+ import type { Circle, Point, Line, Arc } from '../math/geometry.js';
6
3
  export declare class SmithScaler {
7
4
  readonly x: d3.ScaleLinear<number, number>;
8
5
  readonly y: d3.ScaleLinear<number, number>;
@@ -1,4 +1,4 @@
1
- import { Point } from '../shapes/Point.js';
1
+ import { Point } from '../math/geometry.js';
2
2
  import { SmithShape } from './SmithShape.js';
3
3
  interface TextOptions {
4
4
  dx?: string;
@@ -0,0 +1,14 @@
1
+ /** Captures rendered SVG with presentation styles resolved in its source document. */
2
+ export declare class SvgExporter {
3
+ private constructor();
4
+ private static readonly namespace;
5
+ private static readonly properties;
6
+ private static readonly nonInherited;
7
+ static chart(source: SVGElement): string;
8
+ /** Preserve the current CSS layout of separate scale SVGs in one SVG document. */
9
+ static scales(container: HTMLElement, axes: readonly SVGSVGElement[]): string;
10
+ private static bounds;
11
+ private static copy;
12
+ private static size;
13
+ private static serialize;
14
+ }
@@ -0,0 +1,137 @@
1
+ /** Captures rendered SVG with presentation styles resolved in its source document. */
2
+ export class SvgExporter {
3
+ constructor() { }
4
+ static namespace = 'http://www.w3.org/2000/svg';
5
+ static properties = [
6
+ 'color',
7
+ 'display',
8
+ 'visibility',
9
+ 'opacity',
10
+ 'fill',
11
+ 'fill-opacity',
12
+ 'fill-rule',
13
+ 'stroke',
14
+ 'stroke-width',
15
+ 'stroke-opacity',
16
+ 'stroke-dasharray',
17
+ 'stroke-dashoffset',
18
+ 'stroke-linecap',
19
+ 'stroke-linejoin',
20
+ 'stroke-miterlimit',
21
+ 'vector-effect',
22
+ 'paint-order',
23
+ 'shape-rendering',
24
+ 'text-rendering',
25
+ 'font-family',
26
+ 'font-size',
27
+ 'font-style',
28
+ 'font-weight',
29
+ 'font-variant',
30
+ 'font-stretch',
31
+ 'letter-spacing',
32
+ 'word-spacing',
33
+ 'text-anchor',
34
+ 'dominant-baseline',
35
+ 'alignment-baseline',
36
+ 'baseline-shift',
37
+ 'text-decoration',
38
+ 'white-space',
39
+ ];
40
+ static nonInherited = new Set([
41
+ 'display',
42
+ 'opacity',
43
+ 'vector-effect',
44
+ 'alignment-baseline',
45
+ 'dominant-baseline',
46
+ 'baseline-shift',
47
+ 'text-decoration',
48
+ ]);
49
+ static chart(source) {
50
+ const bounds = SvgExporter.bounds(source);
51
+ const copy = SvgExporter.copy(source);
52
+ SvgExporter.size(copy, bounds.width, bounds.height);
53
+ return SvgExporter.serialize(copy);
54
+ }
55
+ /** Preserve the current CSS layout of separate scale SVGs in one SVG document. */
56
+ static scales(container, axes) {
57
+ const bounds = SvgExporter.bounds(container);
58
+ const root = container.ownerDocument.createElementNS(SvgExporter.namespace, 'svg');
59
+ root.setAttribute('viewBox', `0 0 ${bounds.width} ${bounds.height}`);
60
+ SvgExporter.size(root, bounds.width, bounds.height);
61
+ for (const axis of axes) {
62
+ const position = SvgExporter.bounds(axis);
63
+ const copy = SvgExporter.copy(axis);
64
+ copy.setAttribute('x', String(position.x - bounds.x));
65
+ copy.setAttribute('y', String(position.y - bounds.y));
66
+ SvgExporter.size(copy, position.width, position.height);
67
+ root.appendChild(copy);
68
+ }
69
+ return SvgExporter.serialize(root);
70
+ }
71
+ static bounds(element) {
72
+ const bounds = element.getBoundingClientRect();
73
+ if (!element.isConnected || bounds.width <= 0 || bounds.height <= 0) {
74
+ throw new Error('SVG export requires a mounted component with a visible, non-zero size.');
75
+ }
76
+ return bounds;
77
+ }
78
+ static copy(source) {
79
+ const copy = source.cloneNode(true);
80
+ const originals = [source, ...source.querySelectorAll('*')];
81
+ const copies = [copy, ...copy.querySelectorAll('*')];
82
+ const view = source.ownerDocument.defaultView;
83
+ const styles = new Map(originals.map((element) => [element, view.getComputedStyle(element)]));
84
+ originals.forEach((original, index) => {
85
+ const element = copies[index];
86
+ const style = styles.get(original);
87
+ const parentStyle = original.parentElement ? styles.get(original.parentElement) : undefined;
88
+ // Keep SVG geometry/transform attributes, but replace page-dependent CSS.
89
+ element.removeAttribute('style');
90
+ for (const property of SvgExporter.properties) {
91
+ const value = style.getPropertyValue(property);
92
+ element.removeAttribute(property);
93
+ // Resolve inherited values at their boundary instead of repeating every font and
94
+ // stroke property on thousands of descendants. CSS overrides may replace attributes.
95
+ if (value &&
96
+ (SvgExporter.nonInherited.has(property) ||
97
+ parentStyle?.getPropertyValue(property) !== value)) {
98
+ element.style.setProperty(property, value);
99
+ }
100
+ }
101
+ // An exported image has no event handlers or interactive controls.
102
+ element.removeAttribute('tabindex');
103
+ if (original.getAttribute('data-role') === 'marker') {
104
+ // A static marker is an image, not an operable sample slider.
105
+ element.setAttribute('role', 'img');
106
+ element.setAttribute('aria-label', `${original.getAttribute('aria-label')}; ${original.getAttribute('aria-valuetext')}`);
107
+ for (const attribute of [
108
+ 'aria-valuemin',
109
+ 'aria-valuemax',
110
+ 'aria-valuenow',
111
+ 'aria-valuetext',
112
+ 'aria-orientation',
113
+ 'aria-description',
114
+ 'aria-disabled',
115
+ ]) {
116
+ element.removeAttribute(attribute);
117
+ }
118
+ }
119
+ element.removeAttribute('class');
120
+ for (const attribute of [...element.attributes]) {
121
+ if (attribute.name.startsWith('on')) {
122
+ element.removeAttribute(attribute.name);
123
+ }
124
+ }
125
+ });
126
+ return copy;
127
+ }
128
+ static size(svg, width, height) {
129
+ svg.setAttribute('width', String(width));
130
+ svg.setAttribute('height', String(height));
131
+ svg.style.setProperty('overflow', 'hidden');
132
+ }
133
+ static serialize(svg) {
134
+ svg.setAttribute('xmlns', SvgExporter.namespace);
135
+ return new XMLSerializer().serializeToString(svg);
136
+ }
137
+ }
@@ -1,48 +1,33 @@
1
- import { SmithGroup } from './SmithGroup.js';
1
+ import { SmithGroup } from '../svg/SmithGroup.js';
2
2
  import type { TraceStyle, MarkerSelectionStrategy } from '../measurements.js';
3
3
  import { SmithMarker } from './SmithMarker.js';
4
- import { SmithScaler } from './SmithScaler.js';
5
- import { TraceSamples, TraceSample } from '../samples.js';
6
- interface Marker {
7
- marker: SmithMarker;
8
- selectedPoint: TraceSample;
9
- number: number;
10
- }
11
- interface Transform {
12
- x: number;
13
- y: number;
14
- k: number;
15
- }
4
+ import { SmithScaler } from '../svg/SmithScaler.js';
5
+ import type { TraceSamples, TraceSample } from '../samples.js';
6
+ import { type TraceTransform } from './TraceRenderer.js';
7
+ /** Coordinates trace rendering, draggable markers, and deferred marker events. */
16
8
  export declare class SmithData {
17
- private data;
18
9
  private color;
19
10
  private transform;
20
- private fgContainer;
21
11
  private scaler;
22
12
  private markerDragHandler?;
23
13
  private markerContainer;
24
- private style;
25
- private viewportScale;
14
+ private readonly model;
15
+ private readonly renderer;
16
+ private readonly markers;
17
+ private readonly pendingEvents;
26
18
  private destroyed;
19
+ private viewportScale;
20
+ private name;
21
+ private selectHandler;
27
22
  private visible;
28
- private pendingEvents;
29
- private group;
30
- private markersCount;
31
- private markers;
32
23
  private handler;
33
- constructor(data: TraceSamples, color: string, transform: Transform, fgContainer: SmithGroup, scaler: SmithScaler, markerDragHandler?: ((marker: SmithMarker, dragging: boolean) => void) | undefined, markerContainer?: SmithGroup, style?: Partial<TraceStyle>);
34
- private drawTrace;
35
- private renderPoints;
24
+ constructor(data: TraceSamples, color: string, transform: TraceTransform, container: SmithGroup, scaler: SmithScaler, markerDragHandler?: ((marker: SmithMarker, dragging: boolean) => void) | undefined, markerContainer?: SmithGroup, style?: Partial<TraceStyle>);
36
25
  setViewportScale(scale: number): void;
37
- zoom(transform: Transform): void;
38
- private zoomDataPoints;
39
- private zoomAllMarkers;
26
+ zoom(transform: TraceTransform): void;
40
27
  addMarker(sampleIndex?: number): number;
41
28
  removeMarker(index: number): boolean;
42
29
  setMarkerSample(index: number, sampleIndex: number): boolean;
43
30
  setMarkerFrequency(index: number, frequencyHz: number): boolean;
44
- private nearestFrequencyIndex;
45
- private validateSampleIndex;
46
31
  get Samples(): TraceSamples;
47
32
  get SampleCount(): number;
48
33
  get Color(): string;
@@ -51,16 +36,20 @@ export declare class SmithData {
51
36
  setColor(color: string): void;
52
37
  get Style(): TraceStyle;
53
38
  setStyle(options: Partial<TraceStyle>): void;
54
- private redraw;
55
39
  setVisible(visible: boolean): void;
56
- private copySamples;
40
+ private moveMarker;
41
+ private updateReading;
42
+ setName(name: string): void;
43
+ setMarkerSelectHandler(handler: (marker: SmithMarker) => void): void;
57
44
  private notifyMarker;
58
45
  private cancelEvents;
59
46
  update(values: TraceSamples, strategy?: MarkerSelectionStrategy): void;
60
47
  destroy(): void;
61
- getMarker(index: number): Marker | undefined;
62
- get Markers(): Marker[];
48
+ get Markers(): {
49
+ number: number;
50
+ selectedPoint: TraceSample;
51
+ marker: SmithMarker;
52
+ }[];
63
53
  setMarkerMoveHandler(handler: (marker: number, data: TraceSample) => void): void;
64
- private findClosestPointTo;
54
+ private assertAlive;
65
55
  }
66
- export {};