plotting.rs
1 use std::{f64::{self, consts::PI}, path::PathBuf}; 2 3 pub fn plot(ui: &mut egui::Ui, circles: &mut Vec<crate::Circle>, num_points: &usize, line_colors: &Vec<egui::Color32>) { 4 egui_plot::Plot::new("Encoded Message") 5 .height(ui.available_height()) 6 .width(ui.available_width()) 7 .show_axes(false) 8 .show_grid(false) 9 .show_x(false) 10 .show_y(false) 11 .data_aspect(1.0) 12 .view_aspect(1.0) 13 .show(ui, |plot_ui| { 14 circles.iter_mut().enumerate().for_each(|(count, circle)| { 15 let points = match count { 16 count if count % 2 == 0 => crate::circle_to_points(circle), 17 _ => { 18 circle.reverse(); 19 crate::circle_to_points(circle) 20 }, 21 }; 22 plot_ui.line(egui_plot::Line::new( 23 gaussian_distribution(num_points, &points, &(count as f64)) 24 ).color(line_colors[count])); 25 }); 26 }); 27 } 28 29 30 pub fn save(path: &PathBuf, message: &mut crate::Message, circle_length: &usize) -> anyhow::Result<()> { //TODO: Add line_colors 31 use plotters::prelude::*; 32 let root = BitMapBackend::new(path, (1000,1000)).into_drawing_area(); 33 root.fill(&WHITE)?; 34 35 let mut circles = crate::encrypt_message(message, circle_length); 36 let length = (circles.len() as f64) + 1.0; 37 38 let mut chart = ChartBuilder::on(&root) 39 .margin(5) 40 .build_cartesian_2d(-length..length, -length..length)?; 41 42 circles.iter_mut().enumerate().for_each(|(count, circle)| { 43 let peak_values = match count { 44 count if count % 2 == 0 => crate::circle_to_points(circle), 45 _ => { 46 circle.reverse(); 47 crate::circle_to_points(circle) 48 }, 49 }; 50 51 let points: Vec<(f64, f64)> = gaussian_distribution(circle_length, &peak_values, &(count as f64)).points().iter() 52 .map(|point| { 53 (point.x, point.y) 54 }).collect(); 55 56 chart.draw_series(LineSeries::new(points, &RED)).unwrap(); 57 }); 58 root.present()?; 59 Ok(()) 60 } 61 62 fn gaussian_distribution(num_points: &usize, peak_values: &Vec<f64>, offset: &f64) -> egui_plot::PlotPoints { 63 let mut radii: Vec<f64> = vec![1.0; *num_points]; 64 let num_peaks = peak_values.len(); 65 let peak_angles = generate_peak_angles(&num_peaks); 66 let theta = generate_theta(&num_points); 67 68 for (peak_value, peak_angle) in peak_values.iter().zip(peak_angles.iter()) { 69 let peak_width: f64 = 1.0 / *num_points as f64; 70 for (j, theta_value) in theta.iter().enumerate() { 71 let delta = (theta_value - peak_angle + PI).rem_euclid(2.0 * PI) - PI; 72 radii[j] += peak_value * (-((delta.powf(2.0)) / (2.0 * peak_width.powf(2.0)))).exp(); 73 } 74 } 75 radii = normalize(&mut radii, &offset); 76 77 cartesian_to_polar(&radii, &theta) 78 } 79 80 fn normalize(radii: &mut Vec<f64>, offset: &f64) -> Vec<f64> { 81 let min_r = radii.iter().cloned().fold(f64::INFINITY, f64::min); 82 let max_r = radii.iter().cloned().fold(f64::NEG_INFINITY, f64::max); 83 84 radii.iter_mut().map(|value| { 85 1.0 + offset + (*value - min_r) / (max_r - min_r) 86 }).collect() 87 } 88 89 fn cartesian_to_polar(radii: &Vec<f64>, theta: &Vec<f64>) -> egui_plot::PlotPoints { 90 theta.iter() 91 .zip(radii.iter()) 92 .map(|(&theta, &radius)| { 93 let x = radius * theta.cos(); 94 let y = radius * theta.sin(); 95 [x, y] 96 }).collect() 97 } 98 99 fn generate_peak_angles(num_peaks: &usize) -> Vec<f64> { 100 (0..*num_peaks) 101 .map(|i| { 102 let angle = 2.0 * PI * (i as f64) / (*num_peaks as f64); 103 angle + (PI / 2.0) 104 }).collect() 105 } 106 107 fn generate_theta(num_points: &usize) -> Vec<f64> { 108 (0..*num_points) 109 .map(|i| 2.0 * PI * (i as f64) / (*num_points as f64)) 110 .collect() 111 } 112 113