1use crate::utils;
2use crate::window::{Vertex, Window};
3use cgmath::Point2;
4use glium::glutin::platform::desktop::EventLoopExtDesktop;
5use itertools_num::linspace;
6use num::Complex;
7use slice_deque::SliceDeque;
8
9#[derive(Copy, Clone, Debug)]
10pub enum PlotType {
11 Line,
13
14 Dot,
16}
17
18impl Default for PlotType {
19 fn default() -> Self {
20 PlotType::Dot
21 }
22}
23
24#[derive(Clone, Default)]
25pub struct FigureConfig<'a> {
26 pub xlim: Option<[f32; 2]>,
29
30 pub ylim: Option<[f32; 2]>,
33
34 pub xlabel: Option<&'a str>,
36
37 pub ylabel: Option<&'a str>,
39
40 pub color: [u8; 3],
43
44 pub plot_type: PlotType,
46}
47
48#[derive(Default)]
49pub struct Figure<'a> {
52 window: Window<'a>,
53 config: FigureConfig<'a>,
54
55 samples: SliceDeque<f32>,
58
59 complex_samples: SliceDeque<Complex<f32>>,
61
62 queue_size: usize,
64
65 x_dynamic: bool,
67
68 y_dynamic: bool,
70}
71
72impl<'a> Figure<'a> {
73 pub fn new(queue_size: usize) -> Self {
75 Self {
76 window: Window::new(),
77 config: FigureConfig::default(),
78 samples: SliceDeque::new(),
79 complex_samples: SliceDeque::new(),
80 queue_size,
81 x_dynamic: true,
82 y_dynamic: true,
83 }
84 }
85
86 pub fn new_with_config(config: FigureConfig<'a>, queue_size: usize) -> Self {
89 let x_dynamic = config.xlim.is_none();
90 let y_dynamic = config.ylim.is_none();
91 Self {
92 window: Window::new(),
93 config,
94 samples: SliceDeque::new(),
95 complex_samples: SliceDeque::new(),
96 queue_size,
97 x_dynamic,
98 y_dynamic,
99 }
100 }
101
102 pub fn xlim(mut self, xlim: [f32; 2]) -> Self {
104 self.config.xlim = Some(xlim);
105 self.x_dynamic = false;
106 self
107 }
108
109 pub fn ylim(mut self, ylim: [f32; 2]) -> Self {
111 self.config.ylim = Some(ylim);
112 self.y_dynamic = false;
113 self
114 }
115
116 pub fn xlabel(mut self, xlabel: &'a str) -> Self {
118 self.config.xlabel = Some(xlabel);
119 self
120 }
121
122 pub fn ylabel(mut self, ylabel: &'a str) -> Self {
124 self.config.ylabel = Some(ylabel);
125 self
126 }
127
128 pub fn color(mut self, r: u8, g: u8, b: u8) -> Self {
130 self.config.color = [r, g, b];
131 self
132 }
133
134 pub fn plot_type(mut self, plot_type: PlotType) -> Self {
136 self.config.plot_type = plot_type;
137 self
138 }
139
140 pub fn should_close_window(&mut self) -> bool {
145 let mut should_close_window = false;
146
147 let events_loop = &mut self.window.events_loop;
148
149 events_loop.run_return(|event, _, control_flow| {
150 use glium::glutin::event::{Event, WindowEvent};
151 use glium::glutin::event_loop::ControlFlow;
152 #[allow(clippy::single_match)]
153 match event {
154 Event::WindowEvent { event, .. } => match event {
155 WindowEvent::Destroyed | WindowEvent::CloseRequested => {
156 should_close_window = true
157 }
158 _ => (),
159 },
160 _ => (),
161 }
162 *control_flow = ControlFlow::Exit;
163 });
164 should_close_window
165 }
166
167 fn normalize(&mut self, points: &[Point2<f32>]) -> Vec<Vertex> {
169 let [min_x, max_x] = if self.x_dynamic {
170 let xlims = utils::calc_xlims(points);
171 self.config.xlim = Some(xlims);
172 xlims
173 } else {
174 self.config.xlim.unwrap()
175 };
176 let [min_y, max_y] = if self.y_dynamic {
177 let ylims = utils::calc_ylims(points);
178 self.config.ylim = Some(ylims);
179 ylims
180 } else {
181 self.config.ylim.unwrap()
182 };
183 let mut vertices = vec![];
184 for point in points {
185 if point.x > max_x || point.x < min_x || point.y > max_y || point.y < min_y {
188 continue;
189 }
190 let error: f32 = 0.0;
191 let x = if (max_x - min_x).abs() > error {
192 1.5 * (point.x - min_x) / (max_x - min_x) - 0.75
193 } else {
194 1.5 * point.x - 0.75
195 };
196 let y = if (max_y - min_y).abs() > error {
197 1.5 * (point.y - min_y) / (max_y - min_y) - 0.75
198 } else {
199 1.5 * point.y - 0.75
200 };
201 vertices.push(Vertex::new(x, y, self.config.color));
202 }
203 vertices
204 }
205
206 fn plot(&mut self, points: &[Point2<f32>]) {
208 let vertices = self.normalize(&points);
209 self.window.draw(&vertices, &self.config);
210 }
211
212 pub fn plot_xy<T>(&mut self, points: &[(T, T)])
215 where
216 T: Into<f32> + Copy,
217 {
218 let points: Vec<Point2<f32>> = points
219 .iter()
220 .map(|pt| Point2::new(pt.0.into(), pt.1.into()))
221 .collect();
222 self.plot(&points);
223 }
224
225 pub fn plot_y<T>(&mut self, y_coords: &[T])
228 where
229 T: Into<f32> + Copy,
230 {
231 let x_coords = linspace(-0.5f32, 0.5f32, y_coords.len());
232 let points: Vec<Point2<f32>> = x_coords
233 .zip(y_coords.iter())
234 .map(|(x, y)| Point2::new(x, (*y).into()))
235 .collect();
236 self.plot(&points);
237 }
238
239 pub fn plot_stream<T>(&mut self, y_coords: &[T])
243 where
244 T: Into<f32> + Copy,
245 {
246 if self.samples.len() >= self.queue_size + y_coords.len() {
247 for _ in 0..self.samples.len() - self.queue_size + y_coords.len() {
248 self.samples.pop_front();
249 }
250 }
251 let y: Vec<f32> = y_coords.iter().map(|y| (*y).into()).collect();
252 for point in &y {
253 self.samples.push_back(*point);
254 }
255 let x_coords = linspace(-0.5f32, 0.5f32, self.queue_size);
256 let points: Vec<Point2<f32>> = x_coords
257 .zip(self.samples.iter())
258 .map(|(x, y)| Point2::new(x, *y))
259 .collect();
260 let vertices = self.normalize(&points);
261 self.window.draw(&vertices, &self.config);
262 }
263
264 pub fn plot_complex_stream<T>(&mut self, points: &[Complex<T>])
268 where
269 T: Into<f32> + Copy,
270 {
271 if self.complex_samples.len() >= self.queue_size + points.len() {
272 for _ in 0..self.complex_samples.len() - self.queue_size + points.len() {
273 self.complex_samples.pop_front();
274 }
275 }
276
277 let points: Vec<Complex<f32>> = points
278 .iter()
279 .map(|x| Complex::new(x.re.into(), x.im.into()))
280 .collect();
281 for point in points {
282 self.complex_samples.push_back(point);
283 }
284
285 let points: Vec<Point2<f32>> = self
286 .complex_samples
287 .iter()
288 .map(|x| Point2::new(x.re, x.im))
289 .collect();
290 let vertices = self.normalize(&points);
291 self.window.draw(&vertices, &self.config);
292 }
293
294 pub fn plot_complex<T>(&mut self, coords: &[Complex<T>])
297 where
298 T: Into<f32> + Copy,
299 {
300 let points: Vec<Point2<f32>> = coords
301 .iter()
302 .map(|pt| Point2::new(pt.re.into(), pt.im.into()))
303 .collect();
304 self.plot(&points);
305 }
306
307 pub fn display(figure: &mut Figure, mut plot_fn: impl FnMut(&mut Figure)) {
309 while !figure.should_close_window() {
310 plot_fn(figure);
311 }
312 }
313}