From c09c2d9bebf02d402a43ef28e0d802d3448808e6 Mon Sep 17 00:00:00 2001 From: Simon Gardling Date: Thu, 24 Feb 2022 11:13:33 -0500 Subject: [PATCH] improve caching --- src/chart_manager.rs | 80 +++++++++++++-------------------- src/egui_app.rs | 104 ++++++++++++++++++++++++++----------------- 2 files changed, 94 insertions(+), 90 deletions(-) diff --git a/src/chart_manager.rs b/src/chart_manager.rs index 7102832..627cf78 100644 --- a/src/chart_manager.rs +++ b/src/chart_manager.rs @@ -1,4 +1,12 @@ -use crate::misc::{add_asterisks, Cache, Function}; +use crate::misc::{add_asterisks, Function}; + + +pub enum UpdateType { + FULL, + FRONT, + BACK, + NONE, +} // Manages Chart generation and caching of values pub struct ChartManager { @@ -7,8 +15,6 @@ pub struct ChartManager { max_x: f64, num_interval: usize, resolution: usize, - back_cache: Cache>, - front_cache: Cache<(Vec<(f64, f64)>, f64)>, } impl ChartManager { @@ -21,71 +27,37 @@ impl ChartManager { max_x, num_interval, resolution, - back_cache: Cache::new_empty(), - front_cache: Cache::new_empty(), } } #[inline] - fn draw(&mut self) -> (Vec<(f64, f64)>, Vec<(f64, f64)>, f64) { + pub fn draw_back(&mut self) -> Vec<(f64, f64)> { let absrange = (self.max_x - self.min_x).abs(); - let data: Vec<(f64, f64)> = match self.back_cache.is_valid() { - true => self.back_cache.get().clone(), - false => { - let output: Vec<(f64, f64)> = (1..=self.resolution) + let output: Vec<(f64, f64)> = (1..=self.resolution) .map(|x| ((x as f64 / self.resolution as f64) * absrange) + self.min_x) .map(|x| (x, self.function.run(x))) .collect(); - self.back_cache.set(output.clone()); - output - } - }; - - let filtered_data: Vec<(f64, f64)> = data.iter().map(|(x, y)| (*x, *y)).collect(); - - let (rect_data, area): (Vec<(f64, f64)>, f64) = match self.front_cache.is_valid() { - true => self.front_cache.get().clone(), - false => { - let step = absrange / (self.num_interval as f64); - let output: (Vec<(f64, f64)>, f64) = self.integral_rectangles(step); - self.front_cache.set(output.clone()); - output - } - }; - - (filtered_data, rect_data, area) + output } + #[inline] + pub fn draw_front(&mut self) -> (Vec<(f64, f64)>, f64) { + self.integral_rectangles(self.get_step()) + } + + #[inline] pub fn get_step(&self) -> f64 { (self.max_x - self.min_x).abs() / (self.num_interval as f64) } - pub fn do_update_front(&self, resolution: usize, num_interval: usize) -> bool { - (self.resolution != resolution) | (num_interval != self.num_interval) - } - - pub fn do_update_back(&self, func_str_new: String, min_x: f64, max_x: f64) -> bool { - let func_str: String = add_asterisks(func_str_new); - let update_func: bool = !self.function.str_compare(func_str); - - update_func | (min_x != self.min_x) | (max_x != self.max_x) - } - #[allow(clippy::too_many_arguments)] pub fn update( &mut self, func_str_new: String, min_x: f64, max_x: f64, num_interval: usize, resolution: usize, - ) -> (Vec<(f64, f64)>, Vec<(f64, f64)>, f64) { + ) -> UpdateType { let func_str: String = add_asterisks(func_str_new); let update_func: bool = !self.function.str_compare(func_str.clone()); - let underlying_update = update_func | (min_x != self.min_x) | (max_x != self.max_x); - - if underlying_update | (self.resolution != resolution) { - self.back_cache.invalidate(); - } - - if underlying_update | (num_interval != self.num_interval) { - self.front_cache.invalidate(); - } + let update_back = update_func | (min_x != self.min_x) | (max_x != self.max_x); + let update_front = update_back | (self.resolution != resolution) | (num_interval != self.num_interval); if update_func { self.function = Function::from_string(func_str); @@ -96,7 +68,15 @@ impl ChartManager { self.num_interval = num_interval; self.resolution = resolution; - self.draw() + if update_back && update_front { + UpdateType::FULL + } else if update_back { + UpdateType::BACK + } else if update_front { + UpdateType::FRONT + } else { + UpdateType::NONE + } } // Creates and does the math for creating all the rectangles under the graph diff --git a/src/egui_app.rs b/src/egui_app.rs index c295817..4d69365 100644 --- a/src/egui_app.rs +++ b/src/egui_app.rs @@ -1,4 +1,4 @@ -use crate::chart_manager::ChartManager; +use crate::chart_manager::{ChartManager, UpdateType}; use crate::misc::{digits_precision, test_func, Cache}; use eframe::{egui, epi}; use egui::plot::{Line, Plot, Value, Values}; @@ -12,7 +12,8 @@ pub struct MathApp { num_interval: usize, resolution: usize, chart_manager: ChartManager, - bar_cache: Cache>, + back_cache: Cache>, + front_cache: Cache<(Vec, f64)> } impl Default for MathApp { @@ -24,11 +25,50 @@ impl Default for MathApp { num_interval: 100, resolution: 10000, chart_manager: ChartManager::new("x^2".to_string(), -10.0, 10.0, 100, 10000), - bar_cache: Cache::new_empty(), + back_cache: Cache::new_empty(), + front_cache: Cache::new_empty(), } } } +impl MathApp { + #[inline] + fn get_back(&mut self) -> Line { + let data = if self.back_cache.is_valid() { + self.back_cache.get().clone() + } else { + let data = self.chart_manager.draw_back(); + let data_values: Vec = data.iter().map(|(x, y)| Value::new(*x, *y)).collect(); + self.back_cache.set(data_values.clone()); + data_values + }; + Line::new(Values::from_values(data)).color(Color32::RED) + } + + #[inline] + fn get_front(&mut self) -> (Vec, f64) { + if self.front_cache.is_valid() { + let cache = self.front_cache.get(); + let vec_bars: Vec = cache.0.to_vec(); + (vec_bars, cache.1) + } else { + let (data, area) = self.chart_manager.draw_front(); + let bars: Vec = data.iter().map(|(x, y)| Bar::new(*x, *y)).collect(); + + let output = (bars, area); + self.front_cache.set(output.clone()); + output + } + } + + #[inline] + fn get_data(&mut self) -> (Line, Vec, f64) { + let (bars, area) = self.get_front(); + (self.get_back(), bars, area) + } +} + + impl epi::App for MathApp { fn name(&self) -> &str { "Integral Demonstration" } @@ -48,7 +88,8 @@ impl epi::App for MathApp { num_interval, resolution, chart_manager, - bar_cache, + back_cache, + front_cache, } = self; // Note: This Instant implementation does not show microseconds when using wasm. @@ -97,8 +138,23 @@ impl epi::App for MathApp { ); }); - // let update_back = chart_manager.do_update_back(func_str.clone(), *min_x, *max_x); - let update_front = chart_manager.do_update_front(*num_interval, *resolution); + let do_update = chart_manager.update( + func_str.clone(), + *min_x, + *max_x, + *num_interval, + *resolution, + ); + + match do_update { + UpdateType::FULL => { + back_cache.invalidate(); + front_cache.invalidate(); + }, + UpdateType::BACK => back_cache.invalidate(), + UpdateType::FRONT => front_cache.invalidate(), + _ => {} + } egui::CentralPanel::default().show(ctx, |ui| { if !parse_error.is_empty() { @@ -106,43 +162,11 @@ impl epi::App for MathApp { return; } - let (filtered_data, rect_data, area) = chart_manager.update( - self.func_str.clone(), - *min_x, - *max_x, - *num_interval, - *resolution, - ); + let (curve, bars, area) = self.get_data(); - let filtered_data_values = filtered_data - .iter() - .map(|(x, y)| Value::new(*x, *y)) - .collect(); - - let curve = Line::new(Values::from_values(filtered_data_values)).color(Color32::RED); - - let bars: Vec = match update_front { - true => { - let bars: Vec = rect_data.iter().map(|(x, y)| Bar::new(*x, *y)).collect(); - - bar_cache.set(bars.clone()); - bars - } - false => { - if bar_cache.is_valid() { - bar_cache.get().clone() - } else { - let bars: Vec = - rect_data.iter().map(|(x, y)| Bar::new(*x, *y)).collect(); - - bar_cache.set(bars.clone()); - bars - } - } - }; let bar_chart = BarChart::new(bars) .color(Color32::BLUE) - .width(chart_manager.get_step()); + .width(self.chart_manager.get_step()); Plot::new("plot") .view_aspect(1.0)