smithkit 0.1.1 → 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.
- package/README.md +119 -40
- package/dist/lib/MarkerMeasurements.d.ts +6 -0
- package/dist/lib/MarkerMeasurements.js +22 -0
- package/dist/lib/Smith.d.ts +9 -2
- package/dist/lib/Smith.js +68 -36
- package/dist/lib/SmithFormatter.d.ts +10 -0
- package/dist/lib/SmithFormatter.js +19 -0
- package/dist/lib/grid/CircleLayerControl.d.ts +15 -0
- package/dist/lib/grid/CircleLayerControl.js +40 -0
- package/dist/lib/{draw → grid}/ConstCircles.d.ts +4 -6
- package/dist/lib/{draw → grid}/ConstCircles.js +2 -2
- package/dist/lib/{draw → grid}/ConstQCircles.d.ts +2 -2
- package/dist/lib/{draw → grid}/ConstQCircles.js +3 -3
- package/dist/lib/{draw → grid}/ConstSwrCircles.d.ts +2 -2
- package/dist/lib/{draw → grid}/ConstSwrCircles.js +3 -3
- package/dist/lib/grid/GridDefinitions.d.ts +12 -0
- package/dist/lib/grid/GridDefinitions.js +138 -0
- package/dist/lib/grid/GridGeometry.d.ts +26 -0
- package/dist/lib/grid/GridGeometry.js +58 -0
- package/dist/lib/grid/GridLabels.d.ts +8 -0
- package/dist/lib/grid/GridLabels.js +45 -0
- package/dist/lib/grid/GridLayerControl.d.ts +12 -0
- package/dist/lib/grid/GridLayerControl.js +39 -0
- package/dist/lib/grid/LayerControl.d.ts +6 -0
- package/dist/lib/grid/LayerControl.js +16 -0
- package/dist/lib/{draw → grid}/SmithGridLayer.d.ts +5 -4
- package/dist/lib/{draw → grid}/SmithGridLayer.js +26 -9
- package/dist/lib/grid/types.d.ts +1 -1
- package/dist/lib/index.d.ts +8 -10
- package/dist/lib/index.js +5 -7
- package/dist/lib/{draw → interaction}/SmithCursor.d.ts +3 -3
- package/dist/lib/{draw → interaction}/SmithCursor.js +6 -6
- package/dist/lib/io/Touchstone.d.ts +10 -0
- package/dist/lib/io/Touchstone.js +75 -0
- package/dist/lib/layers.d.ts +2 -8
- package/dist/lib/layers.js +1 -73
- package/dist/lib/{complex → math}/Complex.d.ts +1 -0
- package/dist/lib/{complex → math}/Complex.js +1 -1
- package/dist/lib/math/geometry.d.ts +14 -0
- package/dist/lib/measurements.d.ts +2 -4
- package/dist/lib/measurements.js +1 -19
- package/dist/lib/rf/RfCalculations.d.ts +65 -0
- package/dist/lib/rf/RfCalculations.js +161 -0
- package/dist/lib/{SmithConstantCircle.d.ts → rf/SmithConstantCircle.d.ts} +2 -3
- package/dist/lib/{SmithConstantCircle.js → rf/SmithConstantCircle.js} +3 -4
- package/dist/lib/scales/PeripheralScaleDefinitions.d.ts +16 -0
- package/dist/lib/scales/PeripheralScaleDefinitions.js +54 -0
- package/dist/lib/scales/RadialScaleDefinitions.d.ts +15 -0
- package/dist/lib/scales/RadialScaleDefinitions.js +112 -0
- package/dist/lib/scales/SmithPeripheralScales.d.ts +4 -2
- package/dist/lib/scales/SmithPeripheralScales.js +46 -26
- package/dist/lib/scales/SmithScales.d.ts +3 -1
- package/dist/lib/scales/SmithScales.js +8 -2
- package/dist/lib/svg/LabelLayout.d.ts +17 -0
- package/dist/lib/svg/LabelLayout.js +145 -0
- package/dist/lib/{draw → svg}/SmithArc.d.ts +1 -1
- package/dist/lib/{draw → svg}/SmithCircle.d.ts +1 -1
- package/dist/lib/{draw → svg}/SmithLine.d.ts +1 -1
- package/dist/lib/{draw → svg}/SmithScaler.d.ts +1 -4
- package/dist/lib/{draw → svg}/SmithText.d.ts +1 -1
- package/dist/lib/svg/SvgExporter.d.ts +14 -0
- package/dist/lib/svg/SvgExporter.js +137 -0
- package/dist/lib/{draw → traces}/SmithData.d.ts +24 -35
- package/dist/lib/traces/SmithData.js +195 -0
- package/dist/lib/{draw → traces}/SmithMarker.d.ts +14 -3
- package/dist/lib/traces/SmithMarker.js +237 -0
- package/dist/lib/traces/TraceModel.d.ts +31 -0
- package/dist/lib/traces/TraceModel.js +123 -0
- package/dist/lib/traces/TraceRenderer.d.ts +33 -0
- package/dist/lib/traces/TraceRenderer.js +152 -0
- package/package.json +4 -1
- package/dist/lib/SmithArcsDefs.d.ts +0 -10
- package/dist/lib/SmithArcsDefs.js +0 -10
- package/dist/lib/components.d.ts +0 -14
- package/dist/lib/components.js +0 -26
- package/dist/lib/draw/SmithData.js +0 -366
- package/dist/lib/draw/SmithMarker.js +0 -122
- package/dist/lib/formatting.d.ts +0 -7
- package/dist/lib/formatting.js +0 -16
- package/dist/lib/grid/definitions.d.ts +0 -3
- package/dist/lib/grid/definitions.js +0 -123
- package/dist/lib/grid/geometry.d.ts +0 -20
- package/dist/lib/grid/geometry.js +0 -55
- package/dist/lib/grid/labels.d.ts +0 -3
- package/dist/lib/grid/labels.js +0 -41
- package/dist/lib/io/touchstone.d.ts +0 -7
- package/dist/lib/io/touchstone.js +0 -72
- package/dist/lib/math/complexRatio.d.ts +0 -3
- package/dist/lib/math/complexRatio.js +0 -30
- package/dist/lib/renormalization.d.ts +0 -10
- package/dist/lib/renormalization.js +0 -62
- package/dist/lib/rf.d.ts +0 -35
- package/dist/lib/rf.js +0 -74
- package/dist/lib/scales/peripheralScales.d.ts +0 -12
- package/dist/lib/scales/peripheralScales.js +0 -49
- package/dist/lib/scales/radialScales.d.ts +0 -12
- package/dist/lib/scales/radialScales.js +0 -109
- package/dist/lib/shapes/Arc.d.ts +0 -6
- package/dist/lib/shapes/Circle.d.ts +0 -5
- package/dist/lib/shapes/Circle.js +0 -1
- package/dist/lib/shapes/Line.d.ts +0 -5
- package/dist/lib/shapes/Line.js +0 -1
- package/dist/lib/shapes/Point.d.ts +0 -1
- package/dist/lib/shapes/Point.js +0 -1
- /package/dist/lib/{draw → grid}/ConstConductance.d.ts +0 -0
- /package/dist/lib/{draw → grid}/ConstConductance.js +0 -0
- /package/dist/lib/{draw → grid}/ConstReactance.d.ts +0 -0
- /package/dist/lib/{draw → grid}/ConstReactance.js +0 -0
- /package/dist/lib/{draw → grid}/ConstResistance.d.ts +0 -0
- /package/dist/lib/{draw → grid}/ConstResistance.js +0 -0
- /package/dist/lib/{draw → grid}/ConstSusceptance.d.ts +0 -0
- /package/dist/lib/{draw → grid}/ConstSusceptance.js +0 -0
- /package/dist/lib/{arcs → grid}/Tick.d.ts +0 -0
- /package/dist/lib/{arcs → grid}/Tick.js +0 -0
- /package/dist/lib/{draw → interaction}/MouseGesture.d.ts +0 -0
- /package/dist/lib/{draw → interaction}/MouseGesture.js +0 -0
- /package/dist/lib/{draw/SmithDrawOptions.js → math/geometry.js} +0 -0
- /package/dist/lib/{draw → svg}/SmithArc.js +0 -0
- /package/dist/lib/{draw → svg}/SmithCircle.js +0 -0
- /package/dist/lib/{draw → svg}/SmithDrawOptions.d.ts +0 -0
- /package/dist/lib/{shapes/Arc.js → svg/SmithDrawOptions.js} +0 -0
- /package/dist/lib/{draw → svg}/SmithGroup.d.ts +0 -0
- /package/dist/lib/{draw → svg}/SmithGroup.js +0 -0
- /package/dist/lib/{draw → svg}/SmithLine.js +0 -0
- /package/dist/lib/{draw → svg}/SmithPath.d.ts +0 -0
- /package/dist/lib/{draw → svg}/SmithPath.js +0 -0
- /package/dist/lib/{draw → svg}/SmithScaler.js +0 -0
- /package/dist/lib/{draw → svg}/SmithShape.d.ts +0 -0
- /package/dist/lib/{draw → svg}/SmithShape.js +0 -0
- /package/dist/lib/{draw → svg}/SmithSvg.d.ts +0 -0
- /package/dist/lib/{draw → svg}/SmithSvg.js +0 -0
- /package/dist/lib/{draw → svg}/SmithText.js +0 -0
|
@@ -1,48 +1,33 @@
|
|
|
1
|
-
import { SmithGroup } from '
|
|
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 '
|
|
5
|
-
import { TraceSamples, TraceSample } from '../samples.js';
|
|
6
|
-
|
|
7
|
-
|
|
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
|
|
25
|
-
private
|
|
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:
|
|
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:
|
|
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
|
|
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
|
-
|
|
62
|
-
|
|
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
|
|
54
|
+
private assertAlive;
|
|
65
55
|
}
|
|
66
|
-
export {};
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { SmithMarker } from './SmithMarker.js';
|
|
2
|
+
import { TraceModel } from './TraceModel.js';
|
|
3
|
+
import { TraceRenderer } from './TraceRenderer.js';
|
|
4
|
+
/** Coordinates trace rendering, draggable markers, and deferred marker events. */
|
|
5
|
+
export class SmithData {
|
|
6
|
+
color;
|
|
7
|
+
transform;
|
|
8
|
+
scaler;
|
|
9
|
+
markerDragHandler;
|
|
10
|
+
markerContainer;
|
|
11
|
+
model;
|
|
12
|
+
renderer;
|
|
13
|
+
markers = new Map();
|
|
14
|
+
pendingEvents = new Map();
|
|
15
|
+
destroyed = false;
|
|
16
|
+
viewportScale = 1;
|
|
17
|
+
name = 'Trace';
|
|
18
|
+
selectHandler = null;
|
|
19
|
+
visible = true;
|
|
20
|
+
handler = null;
|
|
21
|
+
constructor(data, color, transform, container, scaler, markerDragHandler, markerContainer = container, style = {}) {
|
|
22
|
+
this.color = color;
|
|
23
|
+
this.transform = transform;
|
|
24
|
+
this.scaler = scaler;
|
|
25
|
+
this.markerDragHandler = markerDragHandler;
|
|
26
|
+
this.markerContainer = markerContainer;
|
|
27
|
+
this.model = new TraceModel(data);
|
|
28
|
+
this.renderer = new TraceRenderer(this.model.Samples, color, transform, container, scaler, style);
|
|
29
|
+
}
|
|
30
|
+
setViewportScale(scale) {
|
|
31
|
+
this.viewportScale = scale;
|
|
32
|
+
this.renderer.setViewportScale(scale);
|
|
33
|
+
this.markers.forEach((marker) => marker.zoom(this.transform.k * scale));
|
|
34
|
+
}
|
|
35
|
+
zoom(transform) {
|
|
36
|
+
if (this.destroyed) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
this.transform = transform;
|
|
40
|
+
this.renderer.zoom(transform);
|
|
41
|
+
this.markers.forEach((marker) => marker.zoom(transform.k * this.viewportScale));
|
|
42
|
+
}
|
|
43
|
+
addMarker(sampleIndex = 0) {
|
|
44
|
+
this.assertAlive();
|
|
45
|
+
const entry = this.model.addMarker(sampleIndex);
|
|
46
|
+
const marker = new SmithMarker(entry.number, this.color, (dragging) => this.markerDragHandler?.(marker, dragging));
|
|
47
|
+
this.markers.set(entry, marker);
|
|
48
|
+
marker.Element.attr('data-role', 'marker');
|
|
49
|
+
marker.setSampleHandler((sampleIndex) => this.setMarkerSample(this.model.markerIndex(entry), sampleIndex));
|
|
50
|
+
marker.setSelectHandler(() => this.selectHandler?.(marker));
|
|
51
|
+
this.markerContainer.append(marker);
|
|
52
|
+
marker.setDragHandler((point) => {
|
|
53
|
+
if (this.model.selectNearestPoint(entry, this.scaler.pointInvert(point))) {
|
|
54
|
+
this.moveMarker(entry);
|
|
55
|
+
marker.zoom(this.transform.k * this.viewportScale);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
marker.zoom(this.transform.k * this.viewportScale);
|
|
59
|
+
marker.show();
|
|
60
|
+
marker.Element.style('display', () => (this.visible ? null : 'none'));
|
|
61
|
+
this.moveMarker(entry);
|
|
62
|
+
return this.model.markerIndex(entry);
|
|
63
|
+
}
|
|
64
|
+
removeMarker(index) {
|
|
65
|
+
const entry = this.model.getMarker(index);
|
|
66
|
+
if (!entry) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
clearTimeout(this.pendingEvents.get(entry));
|
|
70
|
+
this.pendingEvents.delete(entry);
|
|
71
|
+
// Ending a drag must still be able to read the marker snapshot.
|
|
72
|
+
this.markers.get(entry).destroy();
|
|
73
|
+
this.markers.delete(entry);
|
|
74
|
+
this.model.removeMarker(index);
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
setMarkerSample(index, sampleIndex) {
|
|
78
|
+
const entry = this.model.setMarkerSample(index, sampleIndex);
|
|
79
|
+
if (!entry) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
this.moveMarker(entry);
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
setMarkerFrequency(index, frequencyHz) {
|
|
86
|
+
const entry = this.model.setMarkerFrequency(index, frequencyHz);
|
|
87
|
+
if (!entry) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
this.moveMarker(entry);
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
get Samples() {
|
|
94
|
+
return this.model.Samples;
|
|
95
|
+
}
|
|
96
|
+
get SampleCount() {
|
|
97
|
+
return this.model.Samples.length;
|
|
98
|
+
}
|
|
99
|
+
get Color() {
|
|
100
|
+
return this.color;
|
|
101
|
+
}
|
|
102
|
+
get Visible() {
|
|
103
|
+
return this.visible;
|
|
104
|
+
}
|
|
105
|
+
markerSampleIndex(index) {
|
|
106
|
+
return this.model.markerSampleIndex(index);
|
|
107
|
+
}
|
|
108
|
+
setColor(color) {
|
|
109
|
+
this.color = color;
|
|
110
|
+
this.renderer.setColor(color);
|
|
111
|
+
this.markers.forEach((marker) => marker.setColor(color));
|
|
112
|
+
}
|
|
113
|
+
get Style() {
|
|
114
|
+
return this.renderer.Style;
|
|
115
|
+
}
|
|
116
|
+
setStyle(options) {
|
|
117
|
+
this.renderer.setStyle(options);
|
|
118
|
+
}
|
|
119
|
+
setVisible(visible) {
|
|
120
|
+
this.visible = visible;
|
|
121
|
+
this.renderer.setVisible(visible);
|
|
122
|
+
this.markers.forEach((marker) => {
|
|
123
|
+
if (!visible) {
|
|
124
|
+
marker.cancelDrag();
|
|
125
|
+
marker.Node?.blur();
|
|
126
|
+
}
|
|
127
|
+
marker.Element.style('display', () => (visible ? null : 'none'));
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
moveMarker(entry) {
|
|
131
|
+
const marker = this.markers.get(entry);
|
|
132
|
+
marker.move(this.scaler.point(entry.selectedPoint.reflectionCoefficient));
|
|
133
|
+
this.updateReading(entry, marker);
|
|
134
|
+
this.notifyMarker(entry);
|
|
135
|
+
}
|
|
136
|
+
updateReading(entry, marker) {
|
|
137
|
+
marker.setReading(`${this.name}, marker ${entry.number}`, this.model.markerSampleIndex(this.model.markerIndex(entry)), this.SampleCount, entry.selectedPoint.frequencyHz);
|
|
138
|
+
}
|
|
139
|
+
setName(name) {
|
|
140
|
+
this.name = name;
|
|
141
|
+
this.markers.forEach((marker, entry) => this.updateReading(entry, marker));
|
|
142
|
+
}
|
|
143
|
+
setMarkerSelectHandler(handler) {
|
|
144
|
+
this.selectHandler = handler;
|
|
145
|
+
}
|
|
146
|
+
notifyMarker(entry) {
|
|
147
|
+
if (this.pendingEvents.has(entry)) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const timer = setTimeout(() => {
|
|
151
|
+
this.pendingEvents.delete(entry);
|
|
152
|
+
const index = this.model.markerIndex(entry);
|
|
153
|
+
if (!this.destroyed && index >= 0) {
|
|
154
|
+
this.handler?.(index, entry.selectedPoint);
|
|
155
|
+
}
|
|
156
|
+
}, 0);
|
|
157
|
+
this.pendingEvents.set(entry, timer);
|
|
158
|
+
}
|
|
159
|
+
cancelEvents() {
|
|
160
|
+
this.pendingEvents.forEach((timer) => clearTimeout(timer));
|
|
161
|
+
this.pendingEvents.clear();
|
|
162
|
+
}
|
|
163
|
+
update(values, strategy = 'frequency') {
|
|
164
|
+
this.assertAlive();
|
|
165
|
+
this.model.update(values, strategy);
|
|
166
|
+
this.cancelEvents();
|
|
167
|
+
this.renderer.update(this.model.Samples);
|
|
168
|
+
this.model.Markers.forEach((entry) => this.moveMarker(entry));
|
|
169
|
+
}
|
|
170
|
+
destroy() {
|
|
171
|
+
if (this.destroyed) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
this.destroyed = true;
|
|
175
|
+
this.cancelEvents();
|
|
176
|
+
this.handler = null;
|
|
177
|
+
this.selectHandler = null;
|
|
178
|
+
this.markers.forEach((marker) => marker.destroy());
|
|
179
|
+
this.markers.clear();
|
|
180
|
+
this.model.clear();
|
|
181
|
+
this.markerDragHandler = undefined;
|
|
182
|
+
this.renderer.destroy();
|
|
183
|
+
}
|
|
184
|
+
get Markers() {
|
|
185
|
+
return this.model.Markers.map((entry) => ({ ...entry, marker: this.markers.get(entry) }));
|
|
186
|
+
}
|
|
187
|
+
setMarkerMoveHandler(handler) {
|
|
188
|
+
this.handler = handler;
|
|
189
|
+
}
|
|
190
|
+
assertAlive() {
|
|
191
|
+
if (this.destroyed) {
|
|
192
|
+
throw new Error('This dataset has been removed.');
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
@@ -1,25 +1,36 @@
|
|
|
1
|
-
import { SmithShape } from '
|
|
2
|
-
import { Point } from '../
|
|
1
|
+
import { SmithShape } from '../svg/SmithShape.js';
|
|
2
|
+
import { Point } from '../math/geometry.js';
|
|
3
3
|
export declare class SmithMarker extends SmithShape {
|
|
4
4
|
private dragStateHandler;
|
|
5
5
|
private readonly size;
|
|
6
6
|
private mouseGesture;
|
|
7
7
|
private activeDrags;
|
|
8
8
|
private destroyed;
|
|
9
|
+
private sampleIndex;
|
|
10
|
+
private sampleCount;
|
|
11
|
+
private scale;
|
|
9
12
|
private triangle;
|
|
10
13
|
private inner;
|
|
11
14
|
private text;
|
|
15
|
+
private hitArea;
|
|
16
|
+
private focusRing;
|
|
12
17
|
private rc;
|
|
13
18
|
private dragHandler;
|
|
19
|
+
private sampleHandler;
|
|
20
|
+
private selectHandler;
|
|
14
21
|
constructor(marker: number, color: string, dragStateHandler?: ((dragging: boolean) => void) | null);
|
|
22
|
+
focus(): boolean;
|
|
23
|
+
setReading(name: string, index: number, count: number, frequencyHz: number): void;
|
|
24
|
+
private keydown;
|
|
15
25
|
destroy(): void;
|
|
16
26
|
cancelDrag(): void;
|
|
17
27
|
setColor(color: string): void;
|
|
18
|
-
private onDrag;
|
|
19
28
|
get Position(): Point;
|
|
20
29
|
move(p: Point): void;
|
|
21
30
|
zoom(k: number): void;
|
|
22
31
|
show(): void;
|
|
23
32
|
hide(): void;
|
|
24
33
|
setDragHandler(handler: (p: Point) => void): void;
|
|
34
|
+
setSampleHandler(handler: (index: number) => void): void;
|
|
35
|
+
setSelectHandler(handler: () => void): void;
|
|
25
36
|
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import * as d3 from 'd3';
|
|
2
|
+
import { MouseGesture } from '../interaction/MouseGesture.js';
|
|
3
|
+
import { SmithShape } from '../svg/SmithShape.js';
|
|
4
|
+
import { SmithFormatter } from '../SmithFormatter.js';
|
|
5
|
+
export class SmithMarker extends SmithShape {
|
|
6
|
+
dragStateHandler;
|
|
7
|
+
size = 18;
|
|
8
|
+
mouseGesture = new MouseGesture();
|
|
9
|
+
activeDrags = new Set();
|
|
10
|
+
destroyed = false;
|
|
11
|
+
sampleIndex = 0;
|
|
12
|
+
sampleCount = 1;
|
|
13
|
+
scale = 1;
|
|
14
|
+
triangle;
|
|
15
|
+
inner;
|
|
16
|
+
text;
|
|
17
|
+
hitArea;
|
|
18
|
+
focusRing;
|
|
19
|
+
rc = [0, 0];
|
|
20
|
+
dragHandler = null;
|
|
21
|
+
sampleHandler = null;
|
|
22
|
+
selectHandler = null;
|
|
23
|
+
constructor(marker, color, dragStateHandler = null) {
|
|
24
|
+
super(d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'g')));
|
|
25
|
+
this.dragStateHandler = dragStateHandler;
|
|
26
|
+
const g = this.Element.attr('tabindex', 0)
|
|
27
|
+
.attr('role', 'slider')
|
|
28
|
+
.attr('aria-orientation', 'horizontal')
|
|
29
|
+
.attr('aria-description', 'Arrow keys move one sample; Shift+Arrow or Page Up/Down moves ten. Home and End select the first and last sample.')
|
|
30
|
+
.style('outline', 'none')
|
|
31
|
+
.style('touch-action', 'none')
|
|
32
|
+
.style('cursor', 'var(--smithkit-marker-cursor, grab)')
|
|
33
|
+
.on('focus.smithkit', () => {
|
|
34
|
+
this.focusRing.attr('visibility', null);
|
|
35
|
+
if (this.activeDrags.size === 0) {
|
|
36
|
+
this.selectHandler?.();
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
.on('blur.smithkit', () => this.focusRing.attr('visibility', 'hidden'))
|
|
40
|
+
.on('keydown.smithkit', (event) => this.keydown(event))
|
|
41
|
+
.on('touchstart.smithkit', (event) => event.stopPropagation());
|
|
42
|
+
this.hitArea = g
|
|
43
|
+
.append('circle')
|
|
44
|
+
.attr('class', 'marker-hit-area')
|
|
45
|
+
.attr('fill', 'none')
|
|
46
|
+
.attr('pointer-events', 'all')
|
|
47
|
+
.attr('stroke', 'none')
|
|
48
|
+
.attr('aria-hidden', 'true');
|
|
49
|
+
this.focusRing = g
|
|
50
|
+
.append('g')
|
|
51
|
+
.attr('class', 'marker-focus')
|
|
52
|
+
.attr('visibility', 'hidden')
|
|
53
|
+
.attr('pointer-events', 'none')
|
|
54
|
+
.attr('aria-hidden', 'true');
|
|
55
|
+
for (const [stroke, width] of [
|
|
56
|
+
['white', 5],
|
|
57
|
+
['#1d4ed8', 2.5],
|
|
58
|
+
]) {
|
|
59
|
+
this.focusRing
|
|
60
|
+
.append('circle')
|
|
61
|
+
.attr('fill', 'none')
|
|
62
|
+
.attr('stroke', stroke)
|
|
63
|
+
.attr('stroke-width', width)
|
|
64
|
+
.attr('vector-effect', 'non-scaling-stroke');
|
|
65
|
+
}
|
|
66
|
+
this.triangle = g.append('polygon').attr('stroke', 'none').attr('fill', 'gray');
|
|
67
|
+
this.inner = g.append('polygon').attr('stroke', 'none').attr('fill', color);
|
|
68
|
+
this.text = g
|
|
69
|
+
.append('text')
|
|
70
|
+
.attr('pointer-events', 'none')
|
|
71
|
+
.attr('aria-hidden', 'true')
|
|
72
|
+
.attr('font-family', 'Verdana')
|
|
73
|
+
.attr('font-weight', 'normal')
|
|
74
|
+
.attr('text-anchor', 'middle')
|
|
75
|
+
.attr('dominant-baseline', 'middle')
|
|
76
|
+
.attr('fill', 'white')
|
|
77
|
+
.text(marker.toString());
|
|
78
|
+
this.zoom(1);
|
|
79
|
+
const drag = d3
|
|
80
|
+
.drag()
|
|
81
|
+
.container(() => this.Node.parentNode)
|
|
82
|
+
.touchable(() => true)
|
|
83
|
+
.filter((event) => !this.destroyed && !event.ctrlKey && !event.button && this.activeDrags.size === 0)
|
|
84
|
+
.on('start', (event) => {
|
|
85
|
+
if (this.destroyed || this.activeDrags.size > 0) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
this.mouseGesture.capture(event.sourceEvent, 'drag');
|
|
89
|
+
const wasIdle = this.activeDrags.size === 0;
|
|
90
|
+
this.activeDrags.add(event.identifier);
|
|
91
|
+
this.Node.focus({ preventScroll: true });
|
|
92
|
+
if (wasIdle) {
|
|
93
|
+
this.dragStateHandler?.(true);
|
|
94
|
+
this.selectHandler?.();
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
.on('drag', (event) => {
|
|
98
|
+
if (!this.destroyed && this.activeDrags.has(event.identifier)) {
|
|
99
|
+
this.dragHandler?.([event.x, event.y]);
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
.on('end', (event) => {
|
|
103
|
+
if (this.activeDrags.delete(event.identifier) && this.activeDrags.size === 0) {
|
|
104
|
+
this.dragStateHandler?.(false);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
g.call(drag);
|
|
108
|
+
}
|
|
109
|
+
focus() {
|
|
110
|
+
if (this.destroyed || this.Element.style('display') === 'none' || !this.Node.isConnected) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
this.Node.focus({ preventScroll: true });
|
|
114
|
+
return this.Node.ownerDocument.activeElement === this.Node;
|
|
115
|
+
}
|
|
116
|
+
setReading(name, index, count, frequencyHz) {
|
|
117
|
+
this.sampleIndex = index;
|
|
118
|
+
this.sampleCount = count;
|
|
119
|
+
this.Element.attr('aria-label', name)
|
|
120
|
+
.attr('aria-valuemin', 0)
|
|
121
|
+
.attr('aria-valuemax', count - 1)
|
|
122
|
+
.attr('aria-valuenow', index)
|
|
123
|
+
.attr('aria-valuetext', `${SmithFormatter.number(frequencyHz)}Hz, sample ${index + 1} of ${count}`)
|
|
124
|
+
.attr('aria-disabled', count <= 1 ? 'true' : null);
|
|
125
|
+
}
|
|
126
|
+
keydown(event) {
|
|
127
|
+
if (this.destroyed ||
|
|
128
|
+
event.altKey ||
|
|
129
|
+
event.ctrlKey ||
|
|
130
|
+
event.metaKey ||
|
|
131
|
+
this.activeDrags.size > 0) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const step = event.shiftKey ? 10 : 1;
|
|
135
|
+
let next;
|
|
136
|
+
switch (event.key) {
|
|
137
|
+
case 'ArrowRight':
|
|
138
|
+
case 'ArrowUp':
|
|
139
|
+
next = this.sampleIndex + step;
|
|
140
|
+
break;
|
|
141
|
+
case 'ArrowLeft':
|
|
142
|
+
case 'ArrowDown':
|
|
143
|
+
next = this.sampleIndex - step;
|
|
144
|
+
break;
|
|
145
|
+
case 'PageUp':
|
|
146
|
+
next = this.sampleIndex + 10;
|
|
147
|
+
break;
|
|
148
|
+
case 'PageDown':
|
|
149
|
+
next = this.sampleIndex - 10;
|
|
150
|
+
break;
|
|
151
|
+
case 'Home':
|
|
152
|
+
next = 0;
|
|
153
|
+
break;
|
|
154
|
+
case 'End':
|
|
155
|
+
next = this.sampleCount - 1;
|
|
156
|
+
break;
|
|
157
|
+
default:
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
event.preventDefault();
|
|
161
|
+
event.stopPropagation();
|
|
162
|
+
this.selectHandler?.();
|
|
163
|
+
next = Math.max(0, Math.min(this.sampleCount - 1, next));
|
|
164
|
+
if (next !== this.sampleIndex) {
|
|
165
|
+
this.sampleHandler?.(next);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
destroy() {
|
|
169
|
+
if (this.destroyed) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
this.destroyed = true;
|
|
173
|
+
this.dragHandler = null;
|
|
174
|
+
this.sampleHandler = null;
|
|
175
|
+
this.selectHandler = null;
|
|
176
|
+
this.cancelDrag();
|
|
177
|
+
this.dragStateHandler = null;
|
|
178
|
+
this.Element.on('.drag', null).on('.smithkit', null).interrupt();
|
|
179
|
+
this.Element.selectAll('*').on('.drag', null).interrupt();
|
|
180
|
+
this.Element.remove();
|
|
181
|
+
}
|
|
182
|
+
cancelDrag() {
|
|
183
|
+
if (this.activeDrags.size > 0) {
|
|
184
|
+
this.activeDrags.clear();
|
|
185
|
+
this.dragStateHandler?.(false);
|
|
186
|
+
}
|
|
187
|
+
this.mouseGesture.destroy();
|
|
188
|
+
}
|
|
189
|
+
setColor(color) {
|
|
190
|
+
this.inner.attr('fill', color);
|
|
191
|
+
}
|
|
192
|
+
get Position() {
|
|
193
|
+
return this.rc;
|
|
194
|
+
}
|
|
195
|
+
move(p) {
|
|
196
|
+
this.rc = p;
|
|
197
|
+
this.triangle.attr('transform', `translate(${p[0]},${p[1]})`);
|
|
198
|
+
this.inner.attr('transform', `translate(${p[0]},${p[1]})`);
|
|
199
|
+
this.text.attr('x', p[0]).attr('y', p[1]);
|
|
200
|
+
const centerY = p[1] - this.size / this.scale / 2;
|
|
201
|
+
this.hitArea.attr('cx', p[0]).attr('cy', centerY);
|
|
202
|
+
this.focusRing.selectAll('circle').attr('cx', p[0]).attr('cy', centerY);
|
|
203
|
+
}
|
|
204
|
+
zoom(k) {
|
|
205
|
+
if (!Number.isFinite(k) || k <= 0) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
this.scale = k;
|
|
209
|
+
const size = this.size / k;
|
|
210
|
+
const h = size / 12;
|
|
211
|
+
const alpha = Math.atan(1 / 2);
|
|
212
|
+
const beta = Math.PI / 2 - alpha;
|
|
213
|
+
const x = h / Math.sin(alpha);
|
|
214
|
+
const y = h / Math.sin(beta) + h / Math.tan(beta);
|
|
215
|
+
this.triangle.attr('points', `0,0 ${size / 2},${-size} ${-size / 2},${-size}`);
|
|
216
|
+
this.inner.attr('points', `0,${-x} ${size / 2 - y},${-size + h} ${-size / 2 + y},${-size + h}`);
|
|
217
|
+
this.text.attr('font-size', size / 1.8).attr('dy', -size / 1.6);
|
|
218
|
+
this.hitArea.attr('r', 22 / k);
|
|
219
|
+
this.focusRing.selectAll('circle').attr('r', 15 / k);
|
|
220
|
+
this.move(this.rc);
|
|
221
|
+
}
|
|
222
|
+
show() {
|
|
223
|
+
this.Element.attr('opacity', null);
|
|
224
|
+
}
|
|
225
|
+
hide() {
|
|
226
|
+
this.Element.attr('opacity', '0');
|
|
227
|
+
}
|
|
228
|
+
setDragHandler(handler) {
|
|
229
|
+
this.dragHandler = handler;
|
|
230
|
+
}
|
|
231
|
+
setSampleHandler(handler) {
|
|
232
|
+
this.sampleHandler = handler;
|
|
233
|
+
}
|
|
234
|
+
setSelectHandler(handler) {
|
|
235
|
+
this.selectHandler = handler;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { MarkerSelectionStrategy } from '../measurements.js';
|
|
2
|
+
import type { Point } from '../math/geometry.js';
|
|
3
|
+
import type { TraceSample, TraceSamples } from '../samples.js';
|
|
4
|
+
export interface TraceMarker {
|
|
5
|
+
readonly number: number;
|
|
6
|
+
selectedPoint: TraceSample;
|
|
7
|
+
}
|
|
8
|
+
/** Owns validated samples and marker selections without depending on SVG or D3. */
|
|
9
|
+
export declare class TraceModel {
|
|
10
|
+
private samples;
|
|
11
|
+
private markers;
|
|
12
|
+
private markerCount;
|
|
13
|
+
constructor(samples: TraceSamples);
|
|
14
|
+
get Samples(): TraceSamples;
|
|
15
|
+
get Markers(): readonly TraceMarker[];
|
|
16
|
+
addMarker(sampleIndex: number): TraceMarker;
|
|
17
|
+
removeMarker(index: number): TraceMarker | undefined;
|
|
18
|
+
getMarker(index: number): TraceMarker | undefined;
|
|
19
|
+
markerIndex(marker: TraceMarker): number;
|
|
20
|
+
markerSampleIndex(index: number): number;
|
|
21
|
+
setMarkerSample(index: number, sampleIndex: number): TraceMarker | undefined;
|
|
22
|
+
setMarkerFrequency(index: number, frequencyHz: number): TraceMarker | undefined;
|
|
23
|
+
/** Returns false when dragging still selects the same sample. */
|
|
24
|
+
selectNearestPoint(marker: TraceMarker, point: Readonly<Point>): boolean;
|
|
25
|
+
update(values: TraceSamples, strategy: MarkerSelectionStrategy): void;
|
|
26
|
+
clear(): void;
|
|
27
|
+
private nearestFrequencyIndex;
|
|
28
|
+
private nearestPoint;
|
|
29
|
+
private validateSampleIndex;
|
|
30
|
+
private copySamples;
|
|
31
|
+
}
|