left, middle, and right Riemann sums
This commit is contained in:
parent
450874d2fd
commit
4028ba0a74
@ -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();
|
||||
|
||||
@ -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<dyn Fn(f64) -> 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<f64>, integral_max_x: Option<f64>, integral_num: Option<usize>,
|
||||
sum: Option<RiemannSum>,
|
||||
) -> 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<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() {
|
||||
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<Bar> = 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())
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user