TONS of refactoring
This commit is contained in:
parent
5f2fdce026
commit
cd8cb6b587
@ -38,7 +38,6 @@ const INTEGRAL_X_MAX: f64 = 1000.0;
|
|||||||
const INTEGRAL_X_RANGE: RangeInclusive<f64> = INTEGRAL_X_MIN..=INTEGRAL_X_MAX;
|
const INTEGRAL_X_RANGE: RangeInclusive<f64> = INTEGRAL_X_MIN..=INTEGRAL_X_MAX;
|
||||||
|
|
||||||
// Default values
|
// Default values
|
||||||
pub const DEFAULT_FUNCION: &str = "x^2";
|
|
||||||
pub const DEFAULT_RIEMANN: RiemannSum = RiemannSum::Left;
|
pub const DEFAULT_RIEMANN: RiemannSum = RiemannSum::Left;
|
||||||
const DEFAULT_MIN_X: f64 = -10.0;
|
const DEFAULT_MIN_X: f64 = -10.0;
|
||||||
const DEFAULT_MAX_X: f64 = 10.0;
|
const DEFAULT_MAX_X: f64 = 10.0;
|
||||||
@ -259,7 +258,8 @@ cfg_if::cfg_if! {
|
|||||||
|
|
||||||
/// Stores current settings/state of `MathApp`
|
/// Stores current settings/state of `MathApp`
|
||||||
// TODO: find a better name for this
|
// TODO: find a better name for this
|
||||||
struct AppSettings {
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct AppSettings {
|
||||||
/// Stores whether or not the Help window is open
|
/// Stores whether or not the Help window is open
|
||||||
pub help_open: bool,
|
pub help_open: bool,
|
||||||
|
|
||||||
@ -276,6 +276,8 @@ struct AppSettings {
|
|||||||
pub integral_min_x: f64,
|
pub integral_min_x: f64,
|
||||||
pub integral_max_x: f64,
|
pub integral_max_x: f64,
|
||||||
|
|
||||||
|
pub integral_changed: bool,
|
||||||
|
|
||||||
/// Number of rectangles used to calculate integral
|
/// Number of rectangles used to calculate integral
|
||||||
pub integral_num: usize,
|
pub integral_num: usize,
|
||||||
|
|
||||||
@ -287,6 +289,8 @@ struct AppSettings {
|
|||||||
|
|
||||||
/// Stores whether or not displaying roots is enabled
|
/// Stores whether or not displaying roots is enabled
|
||||||
pub roots: bool,
|
pub roots: bool,
|
||||||
|
|
||||||
|
pub pixel_width: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AppSettings {
|
impl Default for AppSettings {
|
||||||
@ -300,10 +304,12 @@ impl Default for AppSettings {
|
|||||||
sum: DEFAULT_RIEMANN,
|
sum: DEFAULT_RIEMANN,
|
||||||
integral_min_x: DEFAULT_MIN_X,
|
integral_min_x: DEFAULT_MIN_X,
|
||||||
integral_max_x: DEFAULT_MAX_X,
|
integral_max_x: DEFAULT_MAX_X,
|
||||||
|
integral_changed: true,
|
||||||
integral_num: DEFAULT_INTEGRAL_NUM,
|
integral_num: DEFAULT_INTEGRAL_NUM,
|
||||||
dark_mode: true,
|
dark_mode: true,
|
||||||
extrema: true,
|
extrema: true,
|
||||||
roots: true,
|
roots: true,
|
||||||
|
pixel_width: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -332,8 +338,8 @@ pub struct MathApp {
|
|||||||
impl Default for MathApp {
|
impl Default for MathApp {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
functions: vec![DEFAULT_FUNCTION_ENTRY.clone().integral(true)],
|
functions: vec![DEFAULT_FUNCTION_ENTRY.clone()],
|
||||||
func_strs: vec![String::from(DEFAULT_FUNCION)],
|
func_strs: vec![String::new()],
|
||||||
last_error: Vec::new(),
|
last_error: Vec::new(),
|
||||||
last_info: (vec![0.0], Duration::ZERO),
|
last_info: (vec![0.0], Duration::ZERO),
|
||||||
settings: AppSettings::default(),
|
settings: AppSettings::default(),
|
||||||
@ -433,6 +439,9 @@ impl MathApp {
|
|||||||
)
|
)
|
||||||
.changed();
|
.changed();
|
||||||
|
|
||||||
|
self.settings.integral_changed =
|
||||||
|
max_x_changed | min_x_changed | integral_num_changed | riemann_changed;
|
||||||
|
|
||||||
// Stores whether global config options changed
|
// Stores whether global config options changed
|
||||||
// TODO: only take into account integral settings if integral is enabled (maybe)
|
// TODO: only take into account integral settings if integral is enabled (maybe)
|
||||||
let configs_changed = max_x_changed
|
let configs_changed = max_x_changed
|
||||||
@ -569,11 +578,7 @@ impl epi::App for MathApp {
|
|||||||
.on_hover_text("Create and graph new function")
|
.on_hover_text("Create and graph new function")
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
self.functions.push(
|
self.functions.push(DEFAULT_FUNCTION_ENTRY.clone());
|
||||||
DEFAULT_FUNCTION_ENTRY
|
|
||||||
.clone()
|
|
||||||
.update_riemann(self.settings.sum),
|
|
||||||
);
|
|
||||||
self.func_strs.push(String::new());
|
self.func_strs.push(String::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -679,7 +684,13 @@ impl epi::App for MathApp {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let available_width: usize = ui.available_width() as usize; // Used in later logic
|
let available_width: usize = (ui.available_width() as usize) + 1; // Used in later logic
|
||||||
|
let width_changed = available_width != self.settings.pixel_width;
|
||||||
|
|
||||||
|
if width_changed {
|
||||||
|
self.settings.pixel_width = available_width;
|
||||||
|
}
|
||||||
|
let settings_copy = self.settings;
|
||||||
|
|
||||||
// Create and setup plot
|
// Create and setup plot
|
||||||
Plot::new("plot")
|
Plot::new("plot")
|
||||||
@ -705,12 +716,8 @@ impl epi::App for MathApp {
|
|||||||
minx_bounds,
|
minx_bounds,
|
||||||
maxx_bounds,
|
maxx_bounds,
|
||||||
available_width,
|
available_width,
|
||||||
self.settings.extrema,
|
width_changed,
|
||||||
self.settings.roots,
|
settings_copy,
|
||||||
self.settings.integral_min_x,
|
|
||||||
self.settings.integral_max_x,
|
|
||||||
self.settings.integral_num,
|
|
||||||
self.settings.sum,
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|||||||
258
src/function.rs
258
src/function.rs
@ -1,6 +1,6 @@
|
|||||||
#![allow(clippy::too_many_arguments)] // Clippy, shut
|
#![allow(clippy::too_many_arguments)] // Clippy, shut
|
||||||
|
|
||||||
use crate::egui_app::{DEFAULT_FUNCION, DEFAULT_RIEMANN};
|
use crate::egui_app::AppSettings;
|
||||||
use crate::function_output::FunctionOutput;
|
use crate::function_output::FunctionOutput;
|
||||||
use crate::misc::{newtons_method, resolution_helper, step_helper, SteppedVector};
|
use crate::misc::{newtons_method, resolution_helper, step_helper, SteppedVector};
|
||||||
use crate::parsing::BackingFunction;
|
use crate::parsing::BackingFunction;
|
||||||
@ -45,10 +45,6 @@ pub struct FunctionEntry {
|
|||||||
min_x: f64,
|
min_x: f64,
|
||||||
max_x: f64,
|
max_x: f64,
|
||||||
|
|
||||||
/// How many horizontal pixels? (used for calculating the step at which to
|
|
||||||
/// generate values at)
|
|
||||||
pixel_width: usize,
|
|
||||||
|
|
||||||
/// output/cached data
|
/// output/cached data
|
||||||
output: FunctionOutput,
|
output: FunctionOutput,
|
||||||
|
|
||||||
@ -58,34 +54,19 @@ pub struct FunctionEntry {
|
|||||||
/// If displaying derivatives are enabled (note, they are still calculated
|
/// If displaying derivatives are enabled (note, they are still calculated
|
||||||
/// for other purposes)
|
/// for other purposes)
|
||||||
pub derivative: bool,
|
pub derivative: bool,
|
||||||
|
|
||||||
/// Minumum and maximum range of integral
|
|
||||||
integral_min_x: f64,
|
|
||||||
integral_max_x: f64,
|
|
||||||
|
|
||||||
/// Number of rectangles used to approximate the integral via a Riemann Sum
|
|
||||||
integral_num: usize,
|
|
||||||
|
|
||||||
/// The type of RiemannSum to use
|
|
||||||
sum: RiemannSum,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FunctionEntry {
|
impl Default for FunctionEntry {
|
||||||
/// Creates default FunctionEntry instance (which is empty)
|
/// Creates default FunctionEntry instance (which is empty)
|
||||||
fn default() -> FunctionEntry {
|
fn default() -> FunctionEntry {
|
||||||
FunctionEntry {
|
FunctionEntry {
|
||||||
function: BackingFunction::new(DEFAULT_FUNCION),
|
function: BackingFunction::new(""),
|
||||||
func_str: String::new(),
|
func_str: String::new(),
|
||||||
min_x: -1.0,
|
min_x: -1.0,
|
||||||
max_x: 1.0,
|
max_x: 1.0,
|
||||||
pixel_width: 100,
|
|
||||||
output: FunctionOutput::new_empty(),
|
output: FunctionOutput::new_empty(),
|
||||||
integral: false,
|
integral: false,
|
||||||
derivative: false,
|
derivative: false,
|
||||||
integral_min_x: f64::NAN,
|
|
||||||
integral_max_x: f64::NAN,
|
|
||||||
integral_num: 0,
|
|
||||||
sum: DEFAULT_RIEMANN,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,82 +85,35 @@ impl FunctionEntry {
|
|||||||
self.integral = integral;
|
self.integral = integral;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: refactor this
|
|
||||||
/// Returns back values, integral data (Bars and total area), and Derivative
|
|
||||||
/// values
|
|
||||||
#[allow(clippy::type_complexity)]
|
|
||||||
pub fn run_back(&mut self) -> (Vec<Value>, Option<(Vec<Bar>, f64)>, Option<Vec<Value>>) {
|
|
||||||
let resolution: f64 = (self.pixel_width as f64 / (self.max_x - self.min_x).abs()) as f64;
|
|
||||||
let resolution_iter = resolution_helper(self.pixel_width, self.min_x, resolution);
|
|
||||||
let back_values: Vec<Value> = {
|
|
||||||
if self.output.back.is_none() {
|
|
||||||
self.output.back = Some(
|
|
||||||
resolution_iter
|
|
||||||
.clone()
|
|
||||||
.iter()
|
|
||||||
.map(|x| Value::new(*x, self.function.get(*x)))
|
|
||||||
.collect(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.output.back.as_ref().unwrap().clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
let derivative_values: Option<Vec<Value>> = {
|
|
||||||
if self.output.derivative.is_none() {
|
|
||||||
self.output.derivative = Some(
|
|
||||||
resolution_iter
|
|
||||||
.iter()
|
|
||||||
.map(|x| Value::new(*x, self.function.get_derivative_1(*x)))
|
|
||||||
.collect(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(self.output.derivative.as_ref().unwrap().clone())
|
|
||||||
};
|
|
||||||
|
|
||||||
let integral_data = match self.integral {
|
|
||||||
true => {
|
|
||||||
if self.output.integral.is_none() {
|
|
||||||
let (data, area) = self.integral_rectangles();
|
|
||||||
self.output.integral =
|
|
||||||
Some((data.iter().map(|(x, y)| Bar::new(*x, *y)).collect(), area));
|
|
||||||
}
|
|
||||||
|
|
||||||
let cache = self.output.integral.as_ref().unwrap();
|
|
||||||
Some((cache.0.clone(), cache.1))
|
|
||||||
}
|
|
||||||
false => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
(back_values, integral_data, derivative_values)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates and does the math for creating all the rectangles under the
|
/// Creates and does the math for creating all the rectangles under the
|
||||||
/// graph
|
/// graph
|
||||||
fn integral_rectangles(&self) -> (Vec<(f64, f64)>, f64) {
|
fn integral_rectangles(
|
||||||
if self.integral_min_x.is_nan() {
|
&self, integral_min_x: f64, integral_max_x: f64, sum: RiemannSum, integral_num: usize,
|
||||||
|
) -> (Vec<(f64, f64)>, f64) {
|
||||||
|
if 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 integral_max_x.is_nan() {
|
||||||
panic!("integral_max_x is NaN")
|
panic!("integral_max_x is NaN")
|
||||||
}
|
}
|
||||||
|
|
||||||
let step = (self.integral_min_x - self.integral_max_x).abs() / (self.integral_num as f64);
|
let step = (integral_min_x - integral_max_x).abs() / (integral_num as f64);
|
||||||
|
|
||||||
let mut area: f64 = 0.0;
|
let mut area: f64 = 0.0;
|
||||||
let data2: Vec<(f64, f64)> = step_helper(self.integral_num, self.integral_min_x, step)
|
let mut i: usize = 0;
|
||||||
|
let data2: Vec<(f64, f64)> = (step_helper(integral_num, integral_min_x, step))
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| {
|
.map(|x| {
|
||||||
let x: f64 = (*x * step) + self.integral_min_x;
|
i += 1;
|
||||||
|
|
||||||
let step_offset = step * x.signum(); // store the offset here so it doesn't have to be calculated multiple times
|
let step_offset = step * x.signum(); // store the offset here so it doesn't have to be calculated multiple times
|
||||||
let x2: f64 = x + step_offset;
|
let x2: f64 = x + step_offset;
|
||||||
|
|
||||||
let (left_x, right_x) = match x.is_sign_positive() {
|
let (left_x, right_x) = match x.is_sign_positive() {
|
||||||
true => (x, x2),
|
true => (*x, x2),
|
||||||
false => (x2, x),
|
false => (x2, *x),
|
||||||
};
|
};
|
||||||
|
|
||||||
let y = match self.sum {
|
let y = match sum {
|
||||||
RiemannSum::Left => self.function.get(left_x),
|
RiemannSum::Left => self.function.get(left_x),
|
||||||
RiemannSum::Right => self.function.get(right_x),
|
RiemannSum::Right => self.function.get(right_x),
|
||||||
RiemannSum::Middle => {
|
RiemannSum::Middle => {
|
||||||
@ -195,7 +129,7 @@ impl FunctionEntry {
|
|||||||
})
|
})
|
||||||
.filter(|(_, y)| !y.is_nan())
|
.filter(|(_, y)| !y.is_nan())
|
||||||
.collect();
|
.collect();
|
||||||
// assert_eq!(data2.len(), self.integral_num);
|
assert_eq!(i, integral_num);
|
||||||
|
|
||||||
(data2, area)
|
(data2, area)
|
||||||
}
|
}
|
||||||
@ -203,47 +137,6 @@ impl FunctionEntry {
|
|||||||
/// Returns `func_str`
|
/// Returns `func_str`
|
||||||
pub fn get_func_str(&self) -> &str { &self.func_str }
|
pub fn get_func_str(&self) -> &str { &self.func_str }
|
||||||
|
|
||||||
/// Updates riemann value and invalidates integral_cache if needed
|
|
||||||
pub fn update_riemann(mut self, riemann: RiemannSum) -> Self {
|
|
||||||
if self.sum != riemann {
|
|
||||||
self.sum = riemann;
|
|
||||||
self.output.invalidate_integral();
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets whether integral is enabled or not
|
|
||||||
pub fn integral(mut self, enabled: bool) -> Self {
|
|
||||||
self.integral = enabled;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets number of rectangles to use to calculate the integral
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn integral_num(mut self, integral_num: usize) -> Self {
|
|
||||||
self.integral_num = integral_num;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the number of horizontal pixels
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn pixel_width(mut self, pixel_width: usize) -> Self {
|
|
||||||
self.pixel_width = pixel_width;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the bounds of the integral
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn integral_bounds(mut self, min_x: f64, max_x: f64) -> Self {
|
|
||||||
if min_x >= max_x {
|
|
||||||
panic!("integral_bounds: min_x is larger than max_x");
|
|
||||||
}
|
|
||||||
|
|
||||||
self.integral_min_x = min_x;
|
|
||||||
self.integral_max_x = max_x;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn newtons_method_helper(&self, threshold: f64, derivative_level: usize) -> Option<Vec<Value>> {
|
fn newtons_method_helper(&self, threshold: f64, derivative_level: usize) -> Option<Vec<Value>> {
|
||||||
let newtons_method_output: Vec<f64> = match derivative_level {
|
let newtons_method_output: Vec<f64> = match derivative_level {
|
||||||
0 => newtons_method(
|
0 => newtons_method(
|
||||||
@ -278,34 +171,27 @@ impl FunctionEntry {
|
|||||||
|
|
||||||
/// Calculates and displays the function on PlotUI `plot_ui`
|
/// Calculates and displays the function on PlotUI `plot_ui`
|
||||||
pub fn display(
|
pub fn display(
|
||||||
&mut self, plot_ui: &mut PlotUi, min_x: f64, max_x: f64, pixel_width: usize, extrema: bool,
|
&mut self, plot_ui: &mut PlotUi, min_x: f64, max_x: f64, pixel_width: usize,
|
||||||
roots: bool, integral_min_x: f64, integral_max_x: f64, integral_num: usize,
|
width_changed: bool, settings: AppSettings,
|
||||||
sum: RiemannSum,
|
|
||||||
) -> f64 {
|
) -> f64 {
|
||||||
let resolution: f64 = self.pixel_width as f64 / (max_x.abs() + min_x.abs());
|
let resolution: f64 = pixel_width as f64 / (max_x.abs() + min_x.abs());
|
||||||
let resolution_iter = resolution_helper(self.pixel_width, self.min_x, resolution);
|
let resolution_iter = resolution_helper(pixel_width, min_x, resolution);
|
||||||
|
|
||||||
// Makes sure proper arguments are passed when integral is enabled
|
// Makes sure proper arguments are passed when integral is enabled
|
||||||
if self.integral
|
if self.integral && settings.integral_changed {
|
||||||
&& (integral_min_x != self.integral_min_x)
|
|
||||||
| (integral_max_x != self.integral_max_x)
|
|
||||||
| (integral_num != self.integral_num)
|
|
||||||
| (sum != self.sum)
|
|
||||||
{
|
|
||||||
self.output.invalidate_integral();
|
self.output.invalidate_integral();
|
||||||
self.integral_min_x = integral_min_x;
|
|
||||||
self.integral_max_x = integral_max_x;
|
|
||||||
self.integral_num = integral_num;
|
|
||||||
self.sum = sum;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if pixel_width != self.pixel_width {
|
let mut partial_regen = false;
|
||||||
|
|
||||||
|
if width_changed {
|
||||||
self.output.invalidate_back();
|
self.output.invalidate_back();
|
||||||
self.output.invalidate_derivative();
|
self.output.invalidate_derivative();
|
||||||
self.min_x = min_x;
|
self.min_x = min_x;
|
||||||
self.max_x = max_x;
|
self.max_x = max_x;
|
||||||
self.pixel_width = pixel_width;
|
|
||||||
} else if ((min_x != self.min_x) | (max_x != self.max_x)) && self.output.back.is_some() {
|
} else if ((min_x != self.min_x) | (max_x != self.max_x)) && self.output.back.is_some() {
|
||||||
|
partial_regen = true;
|
||||||
|
|
||||||
let back_cache = self.output.back.as_ref().unwrap();
|
let back_cache = self.output.back.as_ref().unwrap();
|
||||||
|
|
||||||
let x_data: SteppedVector = back_cache
|
let x_data: SteppedVector = back_cache
|
||||||
@ -325,11 +211,11 @@ impl FunctionEntry {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
assert_eq!(back_data.len(), self.pixel_width);
|
assert_eq!(back_data.len(), pixel_width + 1);
|
||||||
self.output.back = Some(back_data);
|
self.output.back = Some(back_data);
|
||||||
|
|
||||||
let derivative_cache = self.output.derivative.as_ref().unwrap();
|
let derivative_cache = self.output.derivative.as_ref().unwrap();
|
||||||
let new_data: Vec<Value> = resolution_iter
|
let new_derivative_data: Vec<Value> = resolution_iter
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| {
|
.map(|x| {
|
||||||
if let Some(i) = x_data.get_index(*x) {
|
if let Some(i) = x_data.get_index(*x) {
|
||||||
@ -339,29 +225,73 @@ impl FunctionEntry {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
assert_eq!(new_data.len(), self.pixel_width);
|
|
||||||
|
|
||||||
self.output.derivative = Some(new_data);
|
assert_eq!(new_derivative_data.len(), pixel_width + 1);
|
||||||
|
|
||||||
|
self.output.derivative = Some(new_derivative_data);
|
||||||
} else {
|
} else {
|
||||||
self.output.invalidate_back();
|
self.output.invalidate_back();
|
||||||
self.output.invalidate_derivative();
|
self.output.invalidate_derivative();
|
||||||
self.pixel_width = pixel_width;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let do_extrema = extrema
|
let do_extrema = settings.extrema
|
||||||
&& ((min_x != self.min_x) | (max_x != self.max_x) | self.output.extrema.is_none());
|
&& ((min_x != self.min_x) | (max_x != self.max_x) | self.output.extrema.is_none());
|
||||||
let do_roots =
|
let do_roots = settings.roots
|
||||||
roots && ((min_x != self.min_x) | (max_x != self.max_x) | self.output.roots.is_none());
|
&& ((min_x != self.min_x) | (max_x != self.max_x) | self.output.roots.is_none());
|
||||||
|
|
||||||
self.min_x = min_x;
|
self.min_x = min_x;
|
||||||
self.max_x = max_x;
|
self.max_x = max_x;
|
||||||
|
|
||||||
let threshold: f64 = resolution / 2.0;
|
let threshold: f64 = resolution / 2.0;
|
||||||
|
|
||||||
let (back_values, integral, derivative) = self.run_back();
|
if !partial_regen {
|
||||||
self.output.back = Some(back_values);
|
self.output.back = Some({
|
||||||
self.output.integral = integral;
|
if self.output.back.is_none() {
|
||||||
self.output.derivative = derivative;
|
let data: Vec<Value> = resolution_iter
|
||||||
|
.clone()
|
||||||
|
.iter()
|
||||||
|
.map(|x| Value::new(*x, self.function.get(*x)))
|
||||||
|
.collect();
|
||||||
|
assert_eq!(data.len(), pixel_width + 1);
|
||||||
|
|
||||||
|
self.output.back = Some(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.output.back.as_ref().unwrap().clone()
|
||||||
|
});
|
||||||
|
|
||||||
|
self.output.derivative = {
|
||||||
|
if self.output.derivative.is_none() {
|
||||||
|
let data: Vec<Value> = resolution_iter
|
||||||
|
.iter()
|
||||||
|
.map(|x| Value::new(*x, self.function.get_derivative_1(*x)))
|
||||||
|
.collect();
|
||||||
|
assert_eq!(data.len(), pixel_width + 1);
|
||||||
|
self.output.derivative = Some(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(self.output.derivative.as_ref().unwrap().clone())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
self.output.integral = match self.integral {
|
||||||
|
true => {
|
||||||
|
if self.output.integral.is_none() {
|
||||||
|
let (data, area) = self.integral_rectangles(
|
||||||
|
settings.integral_min_x,
|
||||||
|
settings.integral_max_x,
|
||||||
|
settings.sum,
|
||||||
|
settings.integral_num,
|
||||||
|
);
|
||||||
|
self.output.integral =
|
||||||
|
Some((data.iter().map(|(x, y)| Bar::new(*x, *y)).collect(), area));
|
||||||
|
}
|
||||||
|
|
||||||
|
let cache = self.output.integral.as_ref().unwrap();
|
||||||
|
Some((cache.0.clone(), cache.1))
|
||||||
|
}
|
||||||
|
false => None,
|
||||||
|
};
|
||||||
|
|
||||||
// Calculates extrema
|
// Calculates extrema
|
||||||
if do_extrema {
|
if do_extrema {
|
||||||
@ -373,12 +303,10 @@ impl FunctionEntry {
|
|||||||
self.output.roots = self.newtons_method_helper(threshold, 0);
|
self.output.roots = self.newtons_method_helper(threshold, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
let func_str = self.get_func_str();
|
let func_str = self.get_func_str();
|
||||||
let derivative_str = self.function.get_derivative_str();
|
let derivative_str = self.function.get_derivative_str();
|
||||||
let step =
|
let step = (settings.integral_min_x - settings.integral_max_x).abs()
|
||||||
(self.integral_min_x - self.integral_max_x).abs() / (self.integral_num as f64);
|
/ (settings.integral_num as f64);
|
||||||
let derivative_enabled = self.derivative;
|
|
||||||
// Plot back data
|
// Plot back data
|
||||||
plot_ui.line(
|
plot_ui.line(
|
||||||
Line::new(Values::from_values(self.output.back.clone().unwrap()))
|
Line::new(Values::from_values(self.output.back.clone().unwrap()))
|
||||||
@ -387,7 +315,7 @@ impl FunctionEntry {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Plot derivative data
|
// Plot derivative data
|
||||||
if derivative_enabled {
|
if self.derivative {
|
||||||
if let Some(derivative_data) = self.output.derivative.clone() {
|
if let Some(derivative_data) = self.output.derivative.clone() {
|
||||||
plot_ui.line(
|
plot_ui.line(
|
||||||
Line::new(Values::from_values(derivative_data))
|
Line::new(Values::from_values(derivative_data))
|
||||||
@ -398,7 +326,7 @@ impl FunctionEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Plot extrema points
|
// Plot extrema points
|
||||||
if extrema {
|
if settings.extrema {
|
||||||
if let Some(extrema_data) = self.output.extrema.clone() {
|
if let Some(extrema_data) = self.output.extrema.clone() {
|
||||||
plot_ui.points(
|
plot_ui.points(
|
||||||
Points::new(Values::from_values(extrema_data))
|
Points::new(Values::from_values(extrema_data))
|
||||||
@ -410,7 +338,7 @@ impl FunctionEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Plot roots points
|
// Plot roots points
|
||||||
if roots {
|
if settings.roots {
|
||||||
if let Some(roots_data) = self.output.roots.clone() {
|
if let Some(roots_data) = self.output.roots.clone() {
|
||||||
plot_ui.points(
|
plot_ui.points(
|
||||||
Points::new(Values::from_values(roots_data))
|
Points::new(Values::from_values(roots_data))
|
||||||
@ -435,9 +363,9 @@ impl FunctionEntry {
|
|||||||
f64::NAN // return NaN if integrals are disabled
|
f64::NAN // return NaN if integrals are disabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -447,7 +375,7 @@ mod tests {
|
|||||||
back_values_target: Vec<(f64, f64)>, area_target: f64,
|
back_values_target: Vec<(f64, f64)>, area_target: f64,
|
||||||
) {
|
) {
|
||||||
{
|
{
|
||||||
let (back_values, bars, derivative) = function.run_back();
|
let (back_values, bars, derivative) = function.run_back(-1.0, 1.0);
|
||||||
assert!(derivative.is_some());
|
assert!(derivative.is_some());
|
||||||
assert!(bars.is_none());
|
assert!(bars.is_none());
|
||||||
assert_eq!(back_values.len(), pixel_width);
|
assert_eq!(back_values.len(), pixel_width);
|
||||||
@ -458,7 +386,7 @@ mod tests {
|
|||||||
|
|
||||||
{
|
{
|
||||||
*function = function.clone().integral(true);
|
*function = function.clone().integral(true);
|
||||||
let (back_values, bars, derivative) = function.run_back();
|
let (back_values, bars, derivative) = function.run_back(-1.0, 1.0);
|
||||||
assert!(derivative.is_some());
|
assert!(derivative.is_some());
|
||||||
assert!(bars.is_some());
|
assert!(bars.is_some());
|
||||||
assert_eq!(back_values.len(), pixel_width);
|
assert_eq!(back_values.len(), pixel_width);
|
||||||
@ -474,7 +402,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let (back_values, bars, derivative) = function.run_back();
|
let (back_values, bars, derivative) = function.run_back(-1.0, 1.0);
|
||||||
assert!(derivative.is_some());
|
assert!(derivative.is_some());
|
||||||
|
|
||||||
assert!(bars.is_some());
|
assert!(bars.is_some());
|
||||||
@ -494,8 +422,7 @@ mod tests {
|
|||||||
let mut function = FunctionEntry::default()
|
let mut function = FunctionEntry::default()
|
||||||
.update_riemann(RiemannSum::Left)
|
.update_riemann(RiemannSum::Left)
|
||||||
.pixel_width(pixel_width)
|
.pixel_width(pixel_width)
|
||||||
.integral_num(integral_num)
|
.integral_num(integral_num);
|
||||||
.integral_bounds(-1.0, 1.0);
|
|
||||||
|
|
||||||
let back_values_target = vec![
|
let back_values_target = vec![
|
||||||
(-1.0, 1.0),
|
(-1.0, 1.0),
|
||||||
@ -591,3 +518,4 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|||||||
@ -229,7 +229,7 @@ pub fn newtons_method(
|
|||||||
// Returns a vector of length `max_i` starting at value `min_x` with resolution
|
// Returns a vector of length `max_i` starting at value `min_x` with resolution
|
||||||
// of `resolution`
|
// of `resolution`
|
||||||
pub fn resolution_helper(max_i: usize, min_x: f64, resolution: f64) -> Vec<f64> {
|
pub fn resolution_helper(max_i: usize, min_x: f64, resolution: f64) -> Vec<f64> {
|
||||||
(0..max_i)
|
(0..=max_i)
|
||||||
.map(|x| (x as f64 / resolution as f64) + min_x)
|
.map(|x| (x as f64 / resolution as f64) + min_x)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,11 @@ pub struct BackingFunction {
|
|||||||
impl BackingFunction {
|
impl BackingFunction {
|
||||||
/// Create new BackingFunction instance
|
/// Create new BackingFunction instance
|
||||||
pub fn new(func_str: &str) -> Self {
|
pub fn new(func_str: &str) -> Self {
|
||||||
let function = exmex::parse::<f64>(func_str).unwrap();
|
let function = match func_str {
|
||||||
|
"" => EMPTY_FUNCTION.clone(),
|
||||||
|
_ => exmex::parse::<f64>(func_str).unwrap(),
|
||||||
|
};
|
||||||
|
|
||||||
let derivative_1 = function
|
let derivative_1 = function
|
||||||
.partial(0)
|
.partial(0)
|
||||||
.unwrap_or_else(|_| EMPTY_FUNCTION.clone());
|
.unwrap_or_else(|_| EMPTY_FUNCTION.clone());
|
||||||
@ -169,6 +173,10 @@ pub fn process_func_str(function_in: &str) -> String {
|
|||||||
/// Tests function to make sure it's able to be parsed. Returns the string of
|
/// Tests function to make sure it's able to be parsed. Returns the string of
|
||||||
/// the Error produced, or an empty string if it runs successfully.
|
/// the Error produced, or an empty string if it runs successfully.
|
||||||
pub fn test_func(function_string: &str) -> Option<String> {
|
pub fn test_func(function_string: &str) -> Option<String> {
|
||||||
|
if function_string == "" {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
let parse_result = exmex::parse::<f64>(function_string);
|
let parse_result = exmex::parse::<f64>(function_string);
|
||||||
|
|
||||||
match parse_result {
|
match parse_result {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user