nth derivative

This commit is contained in:
Simon Gardling 2022-04-12 12:47:36 -04:00
parent 927ca265e6
commit e46040d616
5 changed files with 123 additions and 8 deletions

11
TODO.md
View File

@ -9,9 +9,8 @@
- Display of intersections between functions
4. Allow constants in min/max integral input (like pi or euler's number)
5. Sliding values for functions (like a user-interactable slider that adjusts a variable in the function, like desmos)
6. nth derivative support (again)
7. Threading (Partially mplemented when running natively)
8. Fix integral display
9. Look into other, better methods of compression that would be faster
10. Better handling of panics and errors to display to the user
11. Turn Dynamic Iterator functions into traits
6. Threading (Partially implemented when running natively)
7. Fix integral display
8. Look into other, better methods of compression that would be faster
9. Better handling of panics and errors to display to the user
10. Turn Dynamic Iterator functions into traits

View File

@ -50,7 +50,7 @@ pub const DEFAULT_INTEGRAL_NUM: usize = 100;
/// Colors used for plotting
// Colors commented out are used elsewhere and are not included here for better user experience
pub const COLORS: &[Color32; 14] = &[
pub const COLORS: &[Color32; 13] = &[
Color32::RED,
// Color32::GREEN,
// Color32::YELLOW,
@ -65,7 +65,7 @@ pub const COLORS: &[Color32; 14] = &[
Color32::LIGHT_GRAY,
Color32::LIGHT_RED,
Color32::DARK_GRAY,
Color32::DARK_RED,
// Color32::DARK_RED,
Color32::KHAKI,
Color32::DARK_GREEN,
Color32::DARK_BLUE,

View File

@ -8,6 +8,7 @@ use eframe::{egui, epaint};
use egui::{
plot::{BarChart, PlotUi, Value},
widgets::plot::Bar,
Checkbox, Context,
};
use epaint::Color32;
use std::fmt::{self, Debug};
@ -52,15 +53,21 @@ pub struct FunctionEntry {
/// If displaying derivatives are enabled (note, they are still calculated for other purposes)
pub derivative: bool,
pub nth_derviative: bool,
back_data: Vec<Value>,
integral_data: Option<(Vec<Bar>, f64)>,
derivative_data: Vec<Value>,
extrema_data: Vec<Value>,
root_data: Vec<Value>,
nth_derivative_data: Option<Vec<Value>>,
autocomplete: AutoComplete<'static>,
test_result: Option<String>,
curr_nth: usize,
pub settings_opened: bool,
}
impl Default for FunctionEntry {
@ -73,13 +80,17 @@ impl Default for FunctionEntry {
max_x: 1.0,
integral: false,
derivative: false,
nth_derviative: false,
back_data: Vec::new(),
integral_data: None,
derivative_data: Vec::new(),
extrema_data: Vec::new(),
root_data: Vec::new(),
nth_derivative_data: None,
autocomplete: AutoComplete::default(),
test_result: None,
curr_nth: 3,
settings_opened: false,
}
}
}
@ -94,6 +105,32 @@ impl FunctionEntry {
self.update_string(&output_string);
}
pub fn settings(&mut self, ctx: &Context) {
let mut invalidate_nth = false;
egui::Window::new(format!("Settings: {}", self.raw_func_str))
.open(&mut self.settings_opened)
.default_pos([200.0, 200.0])
.resizable(false)
.collapsible(false)
.show(ctx, |ui| {
ui.add(Checkbox::new(
&mut self.nth_derviative,
"Display Nth Derivative",
));
if ui
.add(egui::Slider::new(&mut self.curr_nth, 3..=5).text("Nth Derivative"))
.changed()
{
invalidate_nth = true;
}
});
if invalidate_nth {
self.invalidate_nth();
}
}
/// Get function's cached test result
pub fn get_test_result(&self) -> &Option<String> { &self.test_result }
@ -263,6 +300,24 @@ impl FunctionEntry {
} else {
self.invalidate_derivative();
}
if self.nth_derviative && let Some(nth_derivative_data) = &self.nth_derivative_data {
let new_nth_derivative_data: Vec<Value> = dyn_iter(&resolution_iter)
.map(|x| {
if let Some(i) = x_data.get_index(x) {
(*nth_derivative_data)[i]
} else {
Value::new(*x, self.function.get_nth_derivative(self.curr_nth, *x))
}
})
.collect();
debug_assert_eq!(new_nth_derivative_data.len(), settings.plot_width + 1);
self.nth_derivative_data = Some(new_nth_derivative_data);
} else {
self.invalidate_nth();
}
} else {
self.invalidate_back();
self.invalidate_derivative();
@ -287,6 +342,14 @@ impl FunctionEntry {
debug_assert_eq!(data.len(), settings.plot_width + 1);
self.derivative_data = data;
}
if self.nth_derviative && self.nth_derivative_data.is_none() {
let data: Vec<Value> = dyn_iter(&resolution_iter)
.map(|x| Value::new(*x, self.function.get_nth_derivative(self.curr_nth, *x)))
.collect();
debug_assert_eq!(data.len(), settings.plot_width + 1);
self.nth_derivative_data = Some(data);
}
}
if self.integral {
@ -370,6 +433,15 @@ impl FunctionEntry {
);
}
if self.nth_derviative && let Some(nth_derviative) = &self.nth_derivative_data {
plot_ui.line(
(*nth_derviative)
.to_line()
.color(Color32::DARK_RED)
.name(self.function.get_nth_derivative_str()),
);
}
// Plot integral data
match &self.integral_data {
Some(integral_data) => {
@ -391,6 +463,7 @@ impl FunctionEntry {
self.invalidate_back();
self.invalidate_integral();
self.invalidate_derivative();
self.invalidate_nth();
self.extrema_data.clear();
self.root_data.clear();
}
@ -404,6 +477,8 @@ impl FunctionEntry {
/// Invalidate Derivative data
pub fn invalidate_derivative(&mut self) { self.derivative_data.clear(); }
pub fn invalidate_nth(&mut self) { self.nth_derivative_data = None }
/// Runs asserts to make sure everything is the expected value
#[cfg(test)]
pub fn tests(

View File

@ -464,9 +464,19 @@ impl MathApp {
.clicked(),
);
function.settings_opened.bitxor_assign(
ui.add(Button::new(""))
.on_hover_text(match function.settings_opened {
true => "Close Settings",
false => "Open Settings",
})
.clicked(),
);
// Contains the function string in a text box that the user can edit
function.auto_complete(ui, i as i32)
});
function.settings(ctx);
}
// Remove function if the user requests it

View File

@ -16,6 +16,8 @@ pub struct BackingFunction {
derivative_1_str: String,
/// f''(x)
derivative_2: FlatEx<f64>,
nth_derivative: Option<(usize, FlatEx<f64>, String)>,
}
impl BackingFunction {
@ -66,6 +68,7 @@ impl BackingFunction {
derivative_1,
derivative_1_str,
derivative_2,
nth_derivative: None,
})
}
@ -84,6 +87,34 @@ impl BackingFunction {
pub fn get_derivative_2(&self, x: f64) -> f64 {
self.derivative_2.eval(&[x]).unwrap_or(f64::NAN)
}
pub fn get_nth_derivative_str(&self) -> &str { &self.nth_derivative.as_ref().unwrap().2 }
pub fn get_nth_derivative(&mut self, n: usize, x: f64) -> f64 {
match n {
0 => self.get(x),
1 => self.get_derivative_1(x),
2 => self.get_derivative_2(x),
_ => {
if let Some((curr_n, curr_n_func, _)) = &self.nth_derivative {
if curr_n == &n {
return curr_n_func.eval(&[x]).unwrap_or(f64::NAN);
}
}
let new_func = self
.function
.partial_iter((1..=n).map(|_| 0).collect::<Vec<usize>>().iter())
.unwrap_or_else(|_| EMPTY_FUNCTION.clone());
self.nth_derivative = Some((
n,
new_func.clone(),
new_func.unparse().to_owned().replace("{x}", "x"),
));
return new_func.eval(&[x]).unwrap_or(f64::NAN);
}
}
}
}
const VALID_VARIABLES: [char; 5] = ['x', 'X', 'e', 'E', 'π'];