diff --git a/src/decoding.rs b/src/decoding.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/encoding.rs b/src/encoding.rs index e4b0776..9c7a9b9 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -1,5 +1,5 @@ -use std::{iter, ops::Add}; use crate::message::{self, Message}; +use std::ops::Add; pub fn encrypt_message(message: &mut Message, circle_length: &usize) -> Vec { encode_circles( @@ -8,13 +8,13 @@ pub fn encrypt_message(message: &mut Message, circle_length: &usize) -> Vec char { - shift.wrapping_add(char as u8) as char +fn caesar_shift(char: &char, shift: &u8) -> char { + shift.wrapping_add(*char as u8) as char } pub type Circle = Vec; -pub fn circle_to_points(circle: Circle) -> Vec { +pub fn circle_to_points(circle: &Circle) -> Vec { circle.iter().flat_map(|triple| { vec![ triple.first as u8 as f64, @@ -44,11 +44,11 @@ fn encode_circles(circles: &mut Vec, shift: &mut u8) -> Vec { 0 => circle_iter.skip(1), _ => circle_iter.skip(0), }.map(|triple| { - triple.first = caesar_shift(triple.first, *shift); + triple.first = caesar_shift(&triple.first, shift); *shift = shift.wrapping_add_signed(shift_delta); - triple.second = caesar_shift(triple.second, *shift); + triple.second = caesar_shift(&triple.second, shift); *shift = shift.wrapping_add_signed(shift_delta); - triple.second = caesar_shift(triple.third, *shift); + triple.second = caesar_shift(&triple.third, shift); *shift = shift.wrapping_add_signed(shift_delta); triple.clone() diff --git a/src/gui.rs b/src/gui.rs index e69de29..f2ef988 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -0,0 +1,29 @@ +struct PlotDisplay { + circles: Vec, + circle_length: usize, +} + +impl PlotDisplay { + fn new(circles: Vec, circle_length: usize) -> Self { + Self { + circles, + circle_length, + } + } +} + +impl eframe::App for PlotDisplay { //TODO: Define axis ranges + fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { + egui::CentralPanel::default().show(ctx, |ui| { + crate::plot(ui, &mut self.circles, &self.circle_length); + }); + } +} + + +pub fn display_plot(circles: Vec) -> Result<(), eframe::Error> { + let options = eframe::NativeOptions::default(); + eframe::run_native("Encoded Message", options, Box::new(|_cc| Ok(Box::::new( + PlotDisplay::new(circles, 1000) + )))) +} diff --git a/src/lib.rs b/src/lib.rs index ad11c41..c2b0a3d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,11 @@ use clap::{Parser, Subcommand}; use std::path::PathBuf; +mod cli; mod encoding; +mod gui; mod message; -pub mod plotting; - -pub use message::Message; -pub use encoding::encrypt_message; -pub use encoding::circle_to_points; -pub use encoding::Circle; +mod plotting; #[derive(Parser)] #[command(version, about, long_about = None)] @@ -26,9 +23,26 @@ pub struct Args { #[derive(Subcommand)] pub enum Mode { Gui, + Encode { + #[command(subcommand)] + output: OutputMode, + }, +} + +#[derive(Subcommand)] +pub enum OutputMode { Display, Save { #[arg(short, long)] - path: PathBuf + path: PathBuf, } } + + +pub use encoding::encrypt_message; +pub use encoding::circle_to_points; +pub use encoding::Circle; +pub use gui::display_plot; +pub use message::Message; +pub use plotting::plot; + diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index d44407b..0000000 --- a/src/main.rs +++ /dev/null @@ -1,52 +0,0 @@ -use circle_cipher::{encrypt_message, plotting}; -use eframe::egui; -use egui_plot::{Plot, PlotPoints}; - -struct MyApp { - circles: Vec, - cicrle_length: usize, -} - -impl MyApp { - fn new(circles: Vec, cicrle_length: usize) -> Self { - Self { - circles, - cicrle_length, - } - } -} - -impl eframe::App for MyApp { //TODO: Define axis ranges - fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { - egui::CentralPanel::default().show(ctx, |ui| { - Plot::new("Polar Plot") - .height(500.0) - .width(500.0) - .data_aspect(1.0) - .view_aspect(1.0) - .show(ui, |plot_ui| { - self.circles.iter().enumerate().for_each(|(count, circle)| { - plot_ui.line(egui_plot::Line::new( - plotting::plot(ctx, _frame, - self.cicrle_length, - circle_cipher::circle_to_points(circle.clone()), - count - ) - )); - }); - }); - }); - } -} - - -fn main() -> Result<(), eframe::Error> { - let mut message = circle_cipher::Message::new("Hello".to_string(), 12); - let circles = encrypt_message(&mut message, &127); - println!("{:?}", circles.len()); - - let options = eframe::NativeOptions::default(); - eframe::run_native("Polar Plot Example", options, Box::new(|_cc| Ok(Box::::new( - MyApp::new(circles, 1000) - )))) -} diff --git a/src/message.rs b/src/message.rs index 84133f4..80541f6 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1,6 +1,5 @@ use std::vec::IntoIter; - pub struct Message { pub parts: Parts, pub combined: String, diff --git a/src/plotting.rs b/src/plotting.rs index 65dc0a5..0c948d9 100644 --- a/src/plotting.rs +++ b/src/plotting.rs @@ -1,44 +1,78 @@ -use std::f64::{self, consts::PI, INFINITY, NEG_INFINITY}; - +use egui::Ui; use egui_plot::{Plot, PlotPoints}; +use std::f64::{self, consts::PI}; -pub fn plot(ctx: &egui::Context, _frame: &mut eframe::Frame, num_points: usize, peak_values: Vec, offset: usize) -> PlotPoints { - let baseline_r = 1.0; +pub fn plot(ui: &mut Ui, circles: &mut Vec, num_points: &usize) { + Plot::new("Encoded Message") + .height(500.0) + .width(500.0) + .data_aspect(1.0) + .view_aspect(1.0) + .show(ui, |plot_ui| { + circles.iter_mut().enumerate().for_each(|(count, circle)| { + let points = match count { + count if count % 2 == 0 => crate::circle_to_points(circle), + _ => { + circle.reverse(); + crate::circle_to_points(circle) + }, + }; + plot_ui.line(egui_plot::Line::new( + gaussian_distribution(num_points, &points, count) + )); + }); + }); +} + +fn gaussian_distribution(num_points: &usize, peak_values: &Vec, offset: &f64) -> PlotPoints { + let mut radii: Vec = vec![1.0; *num_points]; let num_peaks = peak_values.len(); + let peak_angles = generate_peak_angles(&num_peaks); + let theta = generate_theta(&num_points); - let peak_angles: Vec = (0..num_peaks) - .map(|i| { - let angle = 2.0 * PI * (i as f64) / (num_peaks as f64); - angle + (PI / 2.0) - }) - .collect(); - - - let theta: Vec = (0..num_points) - .map(|i| 2.0 * PI * (i as f64) / (num_points as f64)) - .collect(); - - let mut r: Vec = vec![baseline_r; num_points]; for (peak_value, peak_angle) in peak_values.iter().zip(peak_angles.iter()) { - let peak_width: f64 = 1.0 / num_points as f64; + let peak_width: f64 = 1.0 / *num_points as f64; for (j, theta_value) in theta.iter().enumerate() { let delta = (theta_value - peak_angle + PI).rem_euclid(2.0 * PI) - PI; - r[j] += peak_value * (-((delta.powf(2.0)) / (2.0 * peak_width.powf(2.0)))).exp(); + radii[j] += peak_value * (-((delta.powf(2.0)) / (2.0 * peak_width.powf(2.0)))).exp(); } - } - let min_r = r.iter().cloned().fold(INFINITY, f64::min); - let max_r = r.iter().cloned().fold(NEG_INFINITY, f64::max); - - - let r: Vec = r.iter_mut().map(|value| { - 1.0 + (offset as f64) + (*value - min_r) / (max_r - min_r) - }).collect(); - + } + normalize(&mut radii, &offset); + + cartesian_to_polar(&radii, &theta) +} + +fn normalize(radii: &mut Vec, offset: &f64) -> Vec { + let min_r = radii.iter().cloned().fold(f64::INFINITY, f64::min); + let max_r = radii.iter().cloned().fold(f64::NEG_INFINITY, f64::max); + + radii.iter_mut().map(|value| { + 1.0 + offset + (*value - min_r) / (max_r - min_r) + }).collect() +} + +fn cartesian_to_polar(radii: &Vec, theta: &Vec) -> PlotPoints { theta.iter() - .zip(r.iter()) + .zip(radii.iter()) .map(|(&theta, &radius)| { let x = radius * theta.cos(); let y = radius * theta.sin(); [x, y] }).collect() } + +fn generate_peak_angles(num_peaks: &usize) -> Vec { + (0..*num_peaks) + .map(|i| { + let angle = 2.0 * PI * (i as f64) / (*num_peaks as f64); + angle + (PI / 2.0) + }).collect() +} + +fn generate_theta(num_points: &usize) -> Vec { + (0..*num_points) + .map(|i| 2.0 * PI * (i as f64) / (*num_points as f64)) + .collect() +} + +