diff --git a/src/egui_app.rs b/src/egui_app.rs index 64cd081..3029bcb 100644 --- a/src/egui_app.rs +++ b/src/egui_app.rs @@ -1,6 +1,6 @@ use std::ops::RangeInclusive; -use crate::function::Function; +use crate::function::{Function, RiemannSum}; use crate::misc::{add_asterisks, digits_precision, test_func}; use eframe::{egui, epi}; use egui::plot::Plot; @@ -40,6 +40,8 @@ pub struct MathApp { // Stores font data that's used when displaying text font: FontData, + + sum: RiemannSum, } impl Default for MathApp { @@ -58,6 +60,7 @@ impl Default for MathApp { Some(def_min_x), Some(def_max_x), Some(def_interval), + Some(RiemannSum::Left), )], func_strs: vec![String::from(DEFAULT_FUNCION)], integral_min_x: def_min_x, @@ -65,6 +68,7 @@ impl Default for MathApp { integral_num: def_interval, help_open: false, font: FontData::from_static(&FONT_DATA), + sum: RiemannSum::Left, } } } @@ -134,6 +138,7 @@ impl epi::App for MathApp { None, None, None, + Some(self.sum), )); self.func_strs.push(String::from(DEFAULT_FUNCION)); } @@ -150,7 +155,13 @@ impl epi::App for MathApp { egui::SidePanel::left("side_panel") .resizable(false) .show(ctx, |ui| { - // ui.heading("Side Panel"); + egui::ComboBox::from_label("Riemann Sum Type") + .selected_text(self.sum.to_string()) + .show_ui(ui, |ui| { + ui.selectable_value(&mut self.sum, RiemannSum::Left, "Left"); + ui.selectable_value(&mut self.sum, RiemannSum::Middle, "Middle"); + ui.selectable_value(&mut self.sum, RiemannSum::Right, "Right"); + }); let min_x_old = self.integral_min_x; let min_x_response = @@ -197,7 +208,7 @@ impl epi::App for MathApp { if let Some(test_output_value) = func_test_output { parse_error += &format!("(Function #{}) {}", i, test_output_value); } else { - function.update(proc_func_str, integral, Some(self.integral_min_x), Some(self.integral_max_x), Some(self.integral_num)); + function.update(proc_func_str, integral, Some(self.integral_min_x), Some(self.integral_max_x), Some(self.integral_num), Some(self.sum)); } } else { function.func_str = "".to_string(); diff --git a/src/function.rs b/src/function.rs index 9c6de5f..3fed3c1 100644 --- a/src/function.rs +++ b/src/function.rs @@ -1,3 +1,5 @@ +#![allow(clippy::too_many_arguments)] // Clippy, shut + #[allow(unused_imports)] use crate::misc::debug_log; @@ -6,6 +8,18 @@ use eframe::egui::{ widgets::plot::Bar, }; use meval::Expr; +use std::fmt::{self, Debug}; + +#[derive(PartialEq, Debug, Copy, Clone)] +pub enum RiemannSum { + Left, + Middle, + Right, +} + +impl fmt::Display for RiemannSum { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self) } +} pub struct Function { function: Box f64>, @@ -21,12 +35,14 @@ pub struct Function { integral_min_x: f64, integral_max_x: f64, integral_num: usize, + sum: RiemannSum, } impl Function { pub fn new( func_str: String, min_x: f64, max_x: f64, pixel_width: usize, integral: bool, integral_min_x: Option, integral_max_x: Option, integral_num: Option, + sum: Option, ) -> Self { // Makes sure proper arguments are passed when integral is enabled if integral { @@ -36,6 +52,7 @@ impl Function { .expect("Invalid arguments: integral_max_x is None, but integral is enabled."); integral_num .expect("Invalid arguments: integral_num is None, but integral is enabled."); + sum.expect("Invalid arguments: sum is None, but integral is enabled"); } let expr: Expr = func_str.parse().unwrap(); @@ -58,6 +75,7 @@ impl Function { None => f64::NAN, }, integral_num: integral_num.unwrap_or(0), + sum: sum.unwrap_or(RiemannSum::Left), } } @@ -66,7 +84,7 @@ impl Function { pub fn update( &mut self, func_str: String, integral: bool, integral_min_x: Option, - integral_max_x: Option, integral_num: Option, + integral_max_x: Option, integral_num: Option, sum: Option, ) { if func_str.is_empty() { self.func_str = func_str; @@ -84,6 +102,7 @@ impl Function { integral_min_x, integral_max_x, integral_num, + sum, ); return; } @@ -97,11 +116,13 @@ impl Function { && (integral_min_x != Some(self.integral_min_x)) | (integral_max_x != Some(self.integral_max_x)) | (integral_num != Some(self.integral_num)) + | (sum != Some(self.sum)) { self.front_cache = None; self.integral_min_x = integral_min_x.expect("integral_min_x is None"); self.integral_max_x = integral_max_x.expect("integral_max_x is None"); self.integral_num = integral_num.expect("integral_num is None"); + self.sum = sum.expect("sum is None"); } } @@ -172,7 +193,7 @@ impl Function { } false => { debug_log("front_cache: regen"); - let (data, area) = self.integral_rectangles(); + let (data, area) = self.integral_rectangles(self.sum); let bars: Vec = data.iter().map(|(x, y)| Bar::new(*x, *y)).collect(); let output = (bars, area); @@ -187,8 +208,7 @@ impl Function { } // Creates and does the math for creating all the rectangles under the graph - // TODO: fix this stuff, it's half broken - fn integral_rectangles(&self) -> (Vec<(f64, f64)>, f64) { + fn integral_rectangles(&self, riemann: RiemannSum) -> (Vec<(f64, f64)>, f64) { if self.integral_min_x.is_nan() { panic!("integral_min_x is NaN") } else if self.integral_max_x.is_nan() { @@ -201,21 +221,26 @@ impl Function { let data2: Vec<(f64, f64)> = (0..self.integral_num) .map(|e| { let x: f64 = ((e as f64) * step) + self.integral_min_x; - - // Makes sure rectangles are properly handled on x values below 0 let x2: f64 = match x > 0.0 { true => x + step, false => x - step, }; - let tmp1: f64 = self.run_func(x); - let tmp2: f64 = self.run_func(x2); - - // Chooses the y value who's absolute value is the smallest - let mut output = match tmp2.abs() > tmp1.abs() { - true => (x, tmp1), - false => (x2, tmp2), + let left_x: f64 = match x > 0.0 { + true => x, + false => x2, }; + let right_x: f64 = match x > 0.0 { + true => x2, + false => x, + }; + + let y: f64 = match riemann { + RiemannSum::Left => self.run_func(left_x), + RiemannSum::Right => self.run_func(right_x), + RiemannSum::Middle => (self.run_func(left_x) + self.run_func(right_x)) / 2.0, + }; + let mut output = (x, y); // Applies `half_step` in order to make the bar graph display properly if output.0 > 0.0 { @@ -223,7 +248,6 @@ impl Function { } else { output.0 -= half_step; } - output }) .filter(|(_, y)| !y.is_nan()) diff --git a/src/lib.rs b/src/lib.rs index 185da05..1e1faf1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,4 @@ #![allow(clippy::unused_unit)] // Fixes clippy keep complaining about wasm_bindgen -#![allow(clippy::type_complexity)] // Clippy, my types are fine. mod egui_app; mod function;