From 45ea316bef5dc65a519f271657a92dd76707e690 Mon Sep 17 00:00:00 2001 From: DustVoice Date: Wed, 2 Nov 2022 12:40:10 +0100 Subject: [PATCH] Make some changes to gtk beanconqueror GUI part, but prepare for switching to dioxus or flutter with the html5 canvas backend for plotters --- config.toml | 19 +++-- src/main.rs | 204 +++++++++++++++++----------------------------------- 2 files changed, 82 insertions(+), 141 deletions(-) diff --git a/config.toml b/config.toml index 8bdfd86..a375ee8 100644 --- a/config.toml +++ b/config.toml @@ -4,10 +4,6 @@ output_dir = "images" width = 1600 height = 900 -max_time = 45 -max_weight = 35 -max_flow = 5 - [shots] @@ -123,22 +119,37 @@ title = "Laura 19.07.2022 #1" [charts.0] title = "Elisabeth Shots" shots = [0, 1, 10, 14, 15] +max_time = 45 +max_weight = 35 +max_flow = 5 [charts.1] title = "Laura Shots" shots = [2, 3, 4, 5, 6, 7, 8, 9, 12, 16, 17, 18, 19, 20, 21, 22, 23] +max_time = 45 +max_weight = 35 +max_flow = 5 [charts.2] title = "Josephine Shots" shots = [11, 13] +max_time = 45 +max_weight = 35 +max_flow = 5 [charts.3] title = "First Shots" shots = [0, 1, 2, 4, 5, 7, 9, 10, 12, 13, 14, 16, 18, 20, 23] +max_time = 45 +max_weight = 35 +max_flow = 5 [charts.4] title = "Second Shots" shots = [3, 6, 8, 15, 17, 19, 21] +max_time = 45 +max_weight = 35 +max_flow = 5 diff --git a/src/main.rs b/src/main.rs index b21038e..7c27fd8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,10 +24,14 @@ struct Shot { disable: Option, } -#[derive(Deserialize)] +#[derive(Clone, Deserialize)] struct Chart { title: String, shots: Vec, + + max_time: u64, + max_weight: u64, + max_flow: u64, } #[derive(Deserialize)] @@ -40,10 +44,6 @@ struct Config { width: u32, height: u32, - - max_time: u64, - max_weight: u64, - max_flow: u64, } const WGHT_SHEET: usize = 0; @@ -144,158 +144,43 @@ fn load_data(path: &str, cutoff: Option) -> Option { #[derive(Clone, Copy)] struct PlottingState { - mean_x: f64, - mean_y: f64, - std_x: f64, - std_y: f64, - pitch: f64, - roll: f64, } impl PlottingState { - fn guassian_pdf(&self, x: f64, y: f64) -> f64 { - let x_diff = (x - self.mean_x) / self.std_x; - let y_diff = (y - self.mean_y) / self.std_y; - let exponent = -(x_diff * x_diff + y_diff * y_diff) / 2.0; - let denom = (2.0 * std::f64::consts::PI / self.std_x / self.std_y).sqrt(); - let gaussian_pdf = 1.0 / denom; - gaussian_pdf * exponent.exp() - } - fn plot_pdf<'a, DB: DrawingBackend + 'a>( + fn plot<'a, DB: DrawingBackend + 'a>( &self, backend: DB, + chart: Rc>, + config: Rc>, ) -> Result<(), Box> { - let root = backend.into_drawing_area(); + let root_area = backend.into_drawing_area(); - root.fill(&WHITE)?; - - let mut chart = ChartBuilder::on(&root).build_cartesian_3d( - -10.0f64..10.0, - 0.0f64..1.2, - -10.0f64..10.0, - )?; - - chart.with_projection(|mut p| { - p.pitch = self.pitch; - p.yaw = self.roll; - p.scale = 0.7; - p.into_matrix() // build the projection matrix - }); - - chart - .configure_axes() - .light_grid_style(BLACK.mix(0.15)) - .max_light_lines(3) - .draw()?; - let self_cloned = self.clone(); - chart.draw_series( - SurfaceSeries::xoz( - (-50..=50).map(|x| x as f64 / 5.0), - (-50..=50).map(|x| x as f64 / 5.0), - move |x, y| self_cloned.guassian_pdf(x, y), - ) - .style_func(&|&v| (&HSLColor(240.0 / 360.0 - 240.0 / 360.0 * v, 1.0, 0.7)).into()), - )?; - - root.present()?; - Ok(()) - } -} - -fn build_ui(app: >k::Application) { - let builder = gtk::Builder::from_string(UI_SOURCE); - let window: gtk::Window = builder - .object::("MainWindow") - .unwrap(); - - window.set_title(Some("Beanconqueror GUI (gtk4)")); - - let drawing_area: gtk::DrawingArea = builder.object("MainDrawingArea").unwrap(); - let pitch_scale = builder.object::("PitchScale").unwrap(); - let yaw_scale = builder.object::("YawScale").unwrap(); - let mean_x_scale = builder.object::("MeanXScale").unwrap(); - let mean_y_scale = builder.object::("MeanYScale").unwrap(); - let std_x_scale = builder.object::("SDXScale").unwrap(); - let std_y_scale = builder.object::("SDYScale").unwrap(); - - let app_state = Rc::new(RefCell::new(PlottingState { - mean_x: mean_x_scale.value(), - mean_y: mean_y_scale.value(), - std_x: std_x_scale.value(), - std_y: std_y_scale.value(), - pitch: pitch_scale.value(), - roll: yaw_scale.value(), - })); - - window.set_application(Some(app)); - - let state_cloned = app_state.clone(); - drawing_area.set_draw_func(move |widget, cr, _i, _j| { - let state = state_cloned.borrow().clone(); - let w = widget.allocated_width(); - let h = widget.allocated_height(); - let backend = CairoBackend::new(cr, (w as u32, h as u32)).unwrap(); - state.plot_pdf(backend).unwrap(); - }); - - let handle_change = - |what: >k::Scale, how: Box &mut f64 + 'static>| { - let app_state = app_state.clone(); - let drawing_area = drawing_area.clone(); - what.connect_value_changed(move |target| { - let mut state = app_state.borrow_mut(); - *how(&mut *state) = target.value(); - drawing_area.queue_draw(); - }); - }; - - handle_change(&pitch_scale, Box::new(|s| &mut s.pitch)); - handle_change(&yaw_scale, Box::new(|s| &mut s.roll)); - handle_change(&mean_x_scale, Box::new(|s| &mut s.mean_x)); - handle_change(&mean_y_scale, Box::new(|s| &mut s.mean_y)); - handle_change(&std_x_scale, Box::new(|s| &mut s.std_x)); - handle_change(&std_y_scale, Box::new(|s| &mut s.std_y)); - - window.show(); -} - -fn main() { - let config_file = fs::read_to_string("config.toml").expect("Can't read config.toml"); - let config: Config = toml::from_str(&config_file).expect("Can't deserialize config.toml"); - - for chart in config.charts { - // println!("Chart: {}\n", chart.1.title); - - let title = format!("{}/{}.svg", config.output_dir, chart.1.title); - let root_area = SVGBackend::new(&title, (config.width, config.height)).into_drawing_area(); - root_area.fill(&WHITE).unwrap(); + root_area.fill(&WHITE)?; let mut ctx = ChartBuilder::on(&root_area) .set_label_area_size(LabelAreaPosition::Left, 40) .set_label_area_size(LabelAreaPosition::Bottom, 40) - .caption(&chart.1.title, ("Fira Code", 24)) + .caption(&chart.borrow().title, ("Fira Code", 24)) .build_cartesian_2d( - 0f64..(config.max_time as f64), - 0f64..(config.max_weight as f64), - ) - .unwrap(); + 0f64..(chart.borrow().max_time as f64), + 0f64..(chart.borrow().max_weight as f64), + )?; ctx.configure_mesh() .label_style(("Fira Code", 12)) - .draw() - .unwrap(); + .draw()?; - let shot_count = chart.1.shots.len(); + let shot_count = chart.borrow().shots.len(); let palette = ColorPalette::new(shot_count as u32, PaletteType::Random, false); let mut palette_iter = palette.colors.iter(); - for shot_nr in chart.1.shots { - if let Some(shot) = config.shots.get(&shot_nr.to_string()) { + for shot_nr in &chart.borrow().shots { + if let Some(shot) = config.borrow().shots.get(&shot_nr.to_string()) { // println!("\tShot: {}n", shot.title); if let Some(data) = load_data( - &format!("{}/{}", config.shot_dir, shot.filename), + &format!("{}/{}", config.borrow().shot_dir, shot.filename), shot.cutoff, ) { if let Some(disable) = shot.disable { @@ -346,13 +231,58 @@ fn main() { .label_font(("Fira Code", 12)) .draw() .unwrap(); - } + root_area.present()?; + Ok(()) + } +} + +fn build_ui(app: >k::Application) { + let builder = gtk::Builder::from_string(UI_SOURCE); + + let config_file = fs::read_to_string("config.toml").expect("Can't read config.toml"); + let config: Rc> = Rc::new(RefCell::new(toml::from_str(&config_file).expect("Can't deserialize config.toml"))); + + let chart_values = config.clone().borrow().charts; + for value in chart_values { + let config_cloned = config.clone(); + let chart = Rc::new(RefCell::new(value.1)); + + let window: gtk::Window = builder + .object::("MainWindow") + .unwrap(); + + window.set_title(Some(&chart.clone().borrow().title)); + + let drawing_area: gtk::DrawingArea = builder.object("MainDrawingArea").unwrap(); + + let app_state = Rc::new(RefCell::new(PlottingState {})); + + window.set_application(Some(app)); + + { + let state_cloned = app_state.clone(); + let config_cloned = Rc::clone(&config); + let chart_cloned = Rc::clone(&chart); + + drawing_area.set_draw_func(move |widget, cr, _i, _j| { + let w = widget.allocated_width(); + let h = widget.allocated_height(); + let backend = CairoBackend::new(cr, (w as u32, h as u32)).unwrap(); + (*state_cloned).borrow_mut().plot(backend, chart_cloned, config_cloned).unwrap(); + }); + } + + window.show(); + } +} + +fn main() { let application = gtk::Application::new(Some("de.dustvoice.beanconqueror-gtk"), Default::default()); - application.connect_activate(|app| { - build_ui(app); + application.connect_activate(move |app| { + build_ui(&app) }); application.run();