left, middle, and right Riemann sums

This commit is contained in:
Simon Gardling 2022-03-01 11:41:50 -05:00
parent 450874d2fd
commit 4028ba0a74
3 changed files with 52 additions and 18 deletions

View File

@ -1,6 +1,6 @@
use std::ops::RangeInclusive; use std::ops::RangeInclusive;
use crate::function::Function; use crate::function::{Function, RiemannSum};
use crate::misc::{add_asterisks, digits_precision, test_func}; use crate::misc::{add_asterisks, digits_precision, test_func};
use eframe::{egui, epi}; use eframe::{egui, epi};
use egui::plot::Plot; use egui::plot::Plot;
@ -40,6 +40,8 @@ pub struct MathApp {
// Stores font data that's used when displaying text // Stores font data that's used when displaying text
font: FontData, font: FontData,
sum: RiemannSum,
} }
impl Default for MathApp { impl Default for MathApp {
@ -58,6 +60,7 @@ impl Default for MathApp {
Some(def_min_x), Some(def_min_x),
Some(def_max_x), Some(def_max_x),
Some(def_interval), Some(def_interval),
Some(RiemannSum::Left),
)], )],
func_strs: vec![String::from(DEFAULT_FUNCION)], func_strs: vec![String::from(DEFAULT_FUNCION)],
integral_min_x: def_min_x, integral_min_x: def_min_x,
@ -65,6 +68,7 @@ impl Default for MathApp {
integral_num: def_interval, integral_num: def_interval,
help_open: false, help_open: false,
font: FontData::from_static(&FONT_DATA), font: FontData::from_static(&FONT_DATA),
sum: RiemannSum::Left,
} }
} }
} }
@ -134,6 +138,7 @@ impl epi::App for MathApp {
None, None,
None, None,
None, None,
Some(self.sum),
)); ));
self.func_strs.push(String::from(DEFAULT_FUNCION)); self.func_strs.push(String::from(DEFAULT_FUNCION));
} }
@ -150,7 +155,13 @@ impl epi::App for MathApp {
egui::SidePanel::left("side_panel") egui::SidePanel::left("side_panel")
.resizable(false) .resizable(false)
.show(ctx, |ui| { .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_old = self.integral_min_x;
let min_x_response = let min_x_response =
@ -197,7 +208,7 @@ impl epi::App for MathApp {
if let Some(test_output_value) = func_test_output { if let Some(test_output_value) = func_test_output {
parse_error += &format!("(Function #{}) {}", i, test_output_value); parse_error += &format!("(Function #{}) {}", i, test_output_value);
} else { } 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 { } else {
function.func_str = "".to_string(); function.func_str = "".to_string();

View File

@ -1,3 +1,5 @@
#![allow(clippy::too_many_arguments)] // Clippy, shut
#[allow(unused_imports)] #[allow(unused_imports)]
use crate::misc::debug_log; use crate::misc::debug_log;
@ -6,6 +8,18 @@ use eframe::egui::{
widgets::plot::Bar, widgets::plot::Bar,
}; };
use meval::Expr; 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 { pub struct Function {
function: Box<dyn Fn(f64) -> f64>, function: Box<dyn Fn(f64) -> f64>,
@ -21,12 +35,14 @@ pub struct Function {
integral_min_x: f64, integral_min_x: f64,
integral_max_x: f64, integral_max_x: f64,
integral_num: usize, integral_num: usize,
sum: RiemannSum,
} }
impl Function { impl Function {
pub fn new( pub fn new(
func_str: String, min_x: f64, max_x: f64, pixel_width: usize, integral: bool, func_str: String, min_x: f64, max_x: f64, pixel_width: usize, integral: bool,
integral_min_x: Option<f64>, integral_max_x: Option<f64>, integral_num: Option<usize>, integral_min_x: Option<f64>, integral_max_x: Option<f64>, integral_num: Option<usize>,
sum: Option<RiemannSum>,
) -> Self { ) -> Self {
// Makes sure proper arguments are passed when integral is enabled // Makes sure proper arguments are passed when integral is enabled
if integral { if integral {
@ -36,6 +52,7 @@ impl Function {
.expect("Invalid arguments: integral_max_x is None, but integral is enabled."); .expect("Invalid arguments: integral_max_x is None, but integral is enabled.");
integral_num integral_num
.expect("Invalid arguments: integral_num is None, but integral is enabled."); .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(); let expr: Expr = func_str.parse().unwrap();
@ -58,6 +75,7 @@ impl Function {
None => f64::NAN, None => f64::NAN,
}, },
integral_num: integral_num.unwrap_or(0), integral_num: integral_num.unwrap_or(0),
sum: sum.unwrap_or(RiemannSum::Left),
} }
} }
@ -66,7 +84,7 @@ impl Function {
pub fn update( pub fn update(
&mut self, func_str: String, integral: bool, integral_min_x: Option<f64>, &mut self, func_str: String, integral: bool, integral_min_x: Option<f64>,
integral_max_x: Option<f64>, integral_num: Option<usize>, integral_max_x: Option<f64>, integral_num: Option<usize>, sum: Option<RiemannSum>,
) { ) {
if func_str.is_empty() { if func_str.is_empty() {
self.func_str = func_str; self.func_str = func_str;
@ -84,6 +102,7 @@ impl Function {
integral_min_x, integral_min_x,
integral_max_x, integral_max_x,
integral_num, integral_num,
sum,
); );
return; return;
} }
@ -97,11 +116,13 @@ impl Function {
&& (integral_min_x != Some(self.integral_min_x)) && (integral_min_x != Some(self.integral_min_x))
| (integral_max_x != Some(self.integral_max_x)) | (integral_max_x != Some(self.integral_max_x))
| (integral_num != Some(self.integral_num)) | (integral_num != Some(self.integral_num))
| (sum != Some(self.sum))
{ {
self.front_cache = None; self.front_cache = None;
self.integral_min_x = integral_min_x.expect("integral_min_x is 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_max_x = integral_max_x.expect("integral_max_x is None");
self.integral_num = integral_num.expect("integral_num 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 => { false => {
debug_log("front_cache: regen"); debug_log("front_cache: regen");
let (data, area) = self.integral_rectangles(); let (data, area) = self.integral_rectangles(self.sum);
let bars: Vec<Bar> = data.iter().map(|(x, y)| Bar::new(*x, *y)).collect(); let bars: Vec<Bar> = data.iter().map(|(x, y)| Bar::new(*x, *y)).collect();
let output = (bars, area); let output = (bars, area);
@ -187,8 +208,7 @@ impl Function {
} }
// Creates and does the math for creating all the rectangles under the graph // 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, riemann: RiemannSum) -> (Vec<(f64, f64)>, f64) {
fn integral_rectangles(&self) -> (Vec<(f64, f64)>, f64) {
if self.integral_min_x.is_nan() { if self.integral_min_x.is_nan() {
panic!("integral_min_x is NaN") panic!("integral_min_x is NaN")
} else if self.integral_max_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) let data2: Vec<(f64, f64)> = (0..self.integral_num)
.map(|e| { .map(|e| {
let x: f64 = ((e as f64) * step) + self.integral_min_x; 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 { let x2: f64 = match x > 0.0 {
true => x + step, true => x + step,
false => x - step, false => x - step,
}; };
let tmp1: f64 = self.run_func(x); let left_x: f64 = match x > 0.0 {
let tmp2: f64 = self.run_func(x2); true => x,
false => 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 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 // Applies `half_step` in order to make the bar graph display properly
if output.0 > 0.0 { if output.0 > 0.0 {
@ -223,7 +248,6 @@ impl Function {
} else { } else {
output.0 -= half_step; output.0 -= half_step;
} }
output output
}) })
.filter(|(_, y)| !y.is_nan()) .filter(|(_, y)| !y.is_nan())

View File

@ -1,5 +1,4 @@
#![allow(clippy::unused_unit)] // Fixes clippy keep complaining about wasm_bindgen #![allow(clippy::unused_unit)] // Fixes clippy keep complaining about wasm_bindgen
#![allow(clippy::type_complexity)] // Clippy, my types are fine.
mod egui_app; mod egui_app;
mod function; mod function;