Skip to main content

scope/display/
mod.rs

1pub mod oscilloscope;
2pub mod spectroscope;
3pub mod vectorscope;
4
5use crossterm::event::Event;
6use ratatui::{
7	style::{Color, Style},
8	symbols::Marker,
9	widgets::{Axis, Dataset, GraphType},
10};
11
12use crate::input::Matrix;
13
14pub enum Dimension {
15	X,
16	Y,
17}
18
19#[derive(Debug, Clone, Default)]
20pub struct GraphConfig {
21	pub pause: bool,
22	pub samples: u32,
23	pub sampling_rate: u32,
24	pub scale: f64,
25	pub width: u32,
26	pub scatter: bool,
27	pub references: bool,
28	pub show_ui: bool,
29	pub marker_type: Marker,
30	pub palette: Vec<Color>,
31	pub labels_color: Color,
32	pub axis_color: Color,
33}
34
35impl GraphConfig {
36	pub fn palette(&self, index: usize) -> Color {
37		*self
38			.palette
39			.get(index % self.palette.len())
40			.unwrap_or(&Color::White)
41	}
42}
43
44#[allow(clippy::ptr_arg)] // TODO temporarily! it's a shitty solution
45pub trait DisplayMode {
46	// MUST define
47	fn axis(&'_ self, cfg: &GraphConfig, dimension: Dimension) -> Axis<'_>; // TODO simplify this
48	fn process(&mut self, cfg: &GraphConfig, data: &Matrix<f64>) -> Vec<DataSet>;
49	fn mode_str(&self) -> &'static str;
50
51	// SHOULD override
52	fn channel_name(&self, index: usize) -> String {
53		format!("{}", index)
54	}
55	fn header(&self, _cfg: &GraphConfig) -> String {
56		"".into()
57	}
58	fn references(&self, _cfg: &GraphConfig) -> Vec<DataSet> {
59		vec![]
60	}
61	fn handle(&mut self, _event: Event) {}
62}
63
64pub struct DataSet {
65	name: Option<String>,
66	data: Vec<(f64, f64)>,
67	marker_type: Marker,
68	graph_type: GraphType,
69	color: Color,
70}
71
72impl<'a> From<&'a DataSet> for Dataset<'a> {
73	fn from(ds: &'a DataSet) -> Dataset<'a> {
74		let mut out = Dataset::default(); // TODO creating a binding is kinda ugly, is it avoidable?
75		if let Some(name) = &ds.name {
76			out = out.name(name.clone());
77		}
78		out.marker(ds.marker_type)
79			.graph_type(ds.graph_type)
80			.style(Style::default().fg(ds.color))
81			.data(&ds.data)
82	}
83}
84
85// TODO this is pretty ugly but I need datasets which own the data
86impl DataSet {
87	pub fn new(
88		name: Option<String>,
89		data: Vec<(f64, f64)>,
90		marker_type: Marker,
91		graph_type: GraphType,
92		color: Color,
93	) -> Self {
94		DataSet {
95			name,
96			data,
97			marker_type,
98			graph_type,
99			color,
100		}
101	}
102}
103
104pub(crate) fn update_value_f(
105	val: &mut f64,
106	base: f64,
107	magnitude: f64,
108	range: std::ops::Range<f64>,
109) {
110	let delta = base * magnitude;
111	if *val + delta > range.end {
112		*val = range.end
113	} else if *val + delta < range.start {
114		*val = range.start
115	} else {
116		*val += delta;
117	}
118}
119
120pub(crate) fn update_value_i(
121	val: &mut u32,
122	inc: bool,
123	base: u32,
124	magnitude: f64,
125	range: std::ops::Range<u32>,
126) {
127	let delta = (base as f64 * magnitude) as u32;
128	if inc {
129		if range.end - delta < *val {
130			*val = range.end
131		} else {
132			*val += delta
133		}
134	} else if range.start + delta > *val {
135		*val = range.start
136	} else {
137		*val -= delta
138	}
139}