extrema
This commit is contained in:
3
TODO.md
3
TODO.md
@@ -9,10 +9,9 @@
|
|||||||
3. Smart display of graph
|
3. Smart display of graph
|
||||||
- Display of intersections between functions
|
- Display of intersections between functions
|
||||||
- Display of roots
|
- Display of roots
|
||||||
- Local maximums and minimums (should be easy)
|
|
||||||
4. Fix integral line
|
4. Fix integral line
|
||||||
5. re-add euler's number (well it works if you use capital e like `E^x`)
|
5. re-add euler's number (well it works if you use capital e like `E^x`)
|
||||||
6. allow constants in min/max integral input
|
6. allow constants in min/max integral input (like pi or euler's number)
|
||||||
7. sliding values for functions (like a user-interactable slider that adjusts a variable in the function, like desmos)
|
7. sliding values for functions (like a user-interactable slider that adjusts a variable in the function, like desmos)
|
||||||
8. Keybinds
|
8. Keybinds
|
||||||
9. nth derivative support (again)
|
9. nth derivative support (again)
|
||||||
@@ -123,27 +123,19 @@ impl FunctionEntry {
|
|||||||
);
|
);
|
||||||
// assert_eq!(self.output.back.as_ref().unwrap().len(), self.pixel_width);
|
// assert_eq!(self.output.back.as_ref().unwrap().len(), self.pixel_width);
|
||||||
|
|
||||||
if self.output.derivative.is_some() {
|
let derivative_cache = self.output.derivative.as_ref().unwrap();
|
||||||
if self.derivative {
|
let new_data = (0..self.pixel_width)
|
||||||
let derivative_cache = self.output.derivative.as_ref().unwrap();
|
.map(|x| (x as f64 / resolution as f64) + min_x)
|
||||||
|
.map(|x| {
|
||||||
|
if let Some(i) = x_data.get_index(x) {
|
||||||
|
derivative_cache[i]
|
||||||
|
} else {
|
||||||
|
Value::new(x, self.function.derivative(x))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
self.output.derivative = Some(
|
self.output.derivative = Some(new_data);
|
||||||
(0..self.pixel_width)
|
|
||||||
.map(|x| (x as f64 / resolution as f64) + min_x)
|
|
||||||
.map(|x| {
|
|
||||||
if let Some(i) = x_data.get_index(x) {
|
|
||||||
derivative_cache[i]
|
|
||||||
} else {
|
|
||||||
Value::new(x, self.function.derivative(x))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
);
|
|
||||||
// assert_eq!(self.output.derivative.as_ref().unwrap().len(), self.pixel_width);
|
|
||||||
} else {
|
|
||||||
self.output.invalidate_derivative();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
self.output.invalidate_back();
|
self.output.invalidate_back();
|
||||||
self.output.invalidate_derivative();
|
self.output.invalidate_derivative();
|
||||||
@@ -168,19 +160,16 @@ impl FunctionEntry {
|
|||||||
self.output.back.as_ref().unwrap().clone()
|
self.output.back.as_ref().unwrap().clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
let derivative_values: Option<Vec<Value>> = match self.derivative {
|
let derivative_values: Option<Vec<Value>> = {
|
||||||
true => {
|
if self.output.derivative.is_none() {
|
||||||
if self.output.derivative.is_none() {
|
self.output.derivative = Some(
|
||||||
self.output.derivative = Some(
|
(0..self.pixel_width)
|
||||||
(0..self.pixel_width)
|
.map(|x| (x as f64 / resolution as f64) + self.min_x)
|
||||||
.map(|x| (x as f64 / resolution as f64) + self.min_x)
|
.map(|x| Value::new(x, self.function.derivative(x)))
|
||||||
.map(|x| Value::new(x, self.function.derivative(x)))
|
.collect(),
|
||||||
.collect(),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
Some(self.output.derivative.as_ref().unwrap().clone())
|
|
||||||
}
|
}
|
||||||
false => None,
|
Some(self.output.derivative.as_ref().unwrap().clone())
|
||||||
};
|
};
|
||||||
|
|
||||||
let integral_data = match self.integral {
|
let integral_data = match self.integral {
|
||||||
@@ -287,17 +276,46 @@ impl FunctionEntry {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Finds extrema
|
||||||
|
fn extrema(&mut self) {
|
||||||
|
let mut extrama_list: Vec<Value> = Vec::new();
|
||||||
|
let mut last_ele: Option<Value> = None;
|
||||||
|
for ele in self.output.derivative.as_ref().unwrap().iter() {
|
||||||
|
if last_ele.is_none() {
|
||||||
|
last_ele = Some(*ele);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if last_ele.unwrap().y.signum() != ele.y.signum() {
|
||||||
|
// Do 10 iterations of newton's method, should be more than accurate
|
||||||
|
let x = {
|
||||||
|
let mut x1: f64 = last_ele.unwrap().x;
|
||||||
|
for _ in 0..10 {
|
||||||
|
x1 = last_ele.unwrap().x
|
||||||
|
- (self.function.derivative(x1) / self.function.get_derivative_2(x1))
|
||||||
|
}
|
||||||
|
x1
|
||||||
|
};
|
||||||
|
extrama_list.push(Value::new(x, self.function.get(x)));
|
||||||
|
}
|
||||||
|
last_ele = Some(*ele);
|
||||||
|
}
|
||||||
|
self.output.extrema = Some(extrama_list);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn display(&mut self, plot_ui: &mut PlotUi) -> f64 {
|
pub fn display(&mut self, plot_ui: &mut PlotUi) -> f64 {
|
||||||
let (back_values, integral, derivative) = self.run_back();
|
let (back_values, integral, derivative) = self.run_back();
|
||||||
self.output.back = Some(back_values);
|
self.output.back = Some(back_values);
|
||||||
self.output.integral = integral;
|
self.output.integral = integral;
|
||||||
self.output.derivative = derivative;
|
self.output.derivative = derivative;
|
||||||
|
self.extrema();
|
||||||
|
|
||||||
self.output.display(
|
self.output.display(
|
||||||
plot_ui,
|
plot_ui,
|
||||||
self.get_func_str(),
|
self.get_func_str(),
|
||||||
&self.function.get_derivative_str(),
|
&self.function.get_derivative_str(),
|
||||||
(self.integral_min_x - self.integral_max_x).abs() / (self.integral_num as f64),
|
(self.integral_min_x - self.integral_max_x).abs() / (self.integral_num as f64),
|
||||||
|
self.derivative,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use eframe::{
|
use eframe::{
|
||||||
egui::{
|
egui::{
|
||||||
plot::{BarChart, Line, PlotUi, Value, Values},
|
plot::{BarChart, Line, PlotUi, Points, Value, Values},
|
||||||
widgets::plot::Bar,
|
widgets::plot::Bar,
|
||||||
},
|
},
|
||||||
epaint::Color32,
|
epaint::Color32,
|
||||||
@@ -13,24 +13,16 @@ pub struct FunctionOutput {
|
|||||||
pub(crate) back: Option<Vec<Value>>,
|
pub(crate) back: Option<Vec<Value>>,
|
||||||
pub(crate) integral: Option<(Vec<Bar>, f64)>,
|
pub(crate) integral: Option<(Vec<Bar>, f64)>,
|
||||||
pub(crate) derivative: Option<Vec<Value>>,
|
pub(crate) derivative: Option<Vec<Value>>,
|
||||||
|
pub(crate) extrema: Option<Vec<Value>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FunctionOutput {
|
impl FunctionOutput {
|
||||||
pub fn new(
|
|
||||||
back: Option<Vec<Value>>, integral: Option<(Vec<Bar>, f64)>, derivative: Option<Vec<Value>>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
back,
|
|
||||||
integral,
|
|
||||||
derivative,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_empty() -> Self {
|
pub fn new_empty() -> Self {
|
||||||
Self {
|
Self {
|
||||||
back: None,
|
back: None,
|
||||||
integral: None,
|
integral: None,
|
||||||
derivative: None,
|
derivative: None,
|
||||||
|
extrema: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,6 +30,7 @@ impl FunctionOutput {
|
|||||||
self.back = None;
|
self.back = None;
|
||||||
self.integral = None;
|
self.integral = None;
|
||||||
self.derivative = None;
|
self.derivative = None;
|
||||||
|
self.extrema = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn invalidate_back(&mut self) { self.back = None; }
|
pub fn invalidate_back(&mut self) { self.back = None; }
|
||||||
@@ -48,17 +41,30 @@ impl FunctionOutput {
|
|||||||
|
|
||||||
pub fn display(
|
pub fn display(
|
||||||
&self, plot_ui: &mut PlotUi, func_str: &str, derivative_str: &str, step: f64,
|
&self, plot_ui: &mut PlotUi, func_str: &str, derivative_str: &str, step: f64,
|
||||||
|
derivative_enabled: bool,
|
||||||
) -> f64 {
|
) -> f64 {
|
||||||
plot_ui.line(
|
plot_ui.line(
|
||||||
Line::new(Values::from_values(self.back.clone().unwrap()))
|
Line::new(Values::from_values(self.back.clone().unwrap()))
|
||||||
.color(Color32::RED)
|
.color(Color32::RED)
|
||||||
.name(func_str),
|
.name(func_str),
|
||||||
);
|
);
|
||||||
if let Some(derivative_data) = self.derivative.clone() {
|
|
||||||
plot_ui.line(
|
if derivative_enabled {
|
||||||
Line::new(Values::from_values(derivative_data))
|
if let Some(derivative_data) = self.derivative.clone() {
|
||||||
.color(Color32::GREEN)
|
plot_ui.line(
|
||||||
.name(derivative_str),
|
Line::new(Values::from_values(derivative_data))
|
||||||
|
.color(Color32::GREEN)
|
||||||
|
.name(derivative_str),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(extrema_data) = self.extrema.clone() {
|
||||||
|
plot_ui.points(
|
||||||
|
Points::new(Values::from_values(extrema_data))
|
||||||
|
.color(Color32::YELLOW)
|
||||||
|
.name("Extrema")
|
||||||
|
.radius(5.0),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +1,30 @@
|
|||||||
use exmex::prelude::*;
|
use exmex::prelude::*;
|
||||||
|
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
static ref EMPTY_FUNCTION: FlatEx<f64> = exmex::parse::<f64>("0").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct BackingFunction {
|
pub struct BackingFunction {
|
||||||
function: FlatEx<f64>,
|
function: FlatEx<f64>,
|
||||||
derivative_1: FlatEx<f64>,
|
derivative_1: FlatEx<f64>,
|
||||||
// derivative_2: FlatEx<f64>,
|
derivative_2: FlatEx<f64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BackingFunction {
|
impl BackingFunction {
|
||||||
pub fn new(func_str: &str) -> Self {
|
pub fn new(func_str: &str) -> Self {
|
||||||
let function = exmex::parse::<f64>(func_str).unwrap();
|
let function = exmex::parse::<f64>(func_str).unwrap();
|
||||||
let derivative_1 = function.partial(0).unwrap_or_else(|_| function.clone());
|
let derivative_1 = function
|
||||||
// let derivative_2 = function.partial(0).unwrap_or(derivative_1.clone());
|
.partial(0)
|
||||||
|
.unwrap_or_else(|_| EMPTY_FUNCTION.clone());
|
||||||
|
let derivative_2 = function
|
||||||
|
.partial_iter([0, 0].iter())
|
||||||
|
.unwrap_or_else(|_| EMPTY_FUNCTION.clone());
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
function,
|
function,
|
||||||
derivative_1,
|
derivative_1,
|
||||||
// derivative_2,
|
derivative_2,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,6 +35,10 @@ impl BackingFunction {
|
|||||||
pub fn get(&self, x: f64) -> f64 { self.function.eval(&[x]).unwrap_or(f64::NAN) }
|
pub fn get(&self, x: f64) -> f64 { self.function.eval(&[x]).unwrap_or(f64::NAN) }
|
||||||
|
|
||||||
pub fn derivative(&self, x: f64) -> f64 { self.derivative_1.eval(&[x]).unwrap_or(f64::NAN) }
|
pub fn derivative(&self, x: f64) -> f64 { self.derivative_1.eval(&[x]).unwrap_or(f64::NAN) }
|
||||||
|
|
||||||
|
pub fn get_derivative_2(&self, x: f64) -> f64 {
|
||||||
|
self.derivative_2.eval(&[x]).unwrap_or(f64::NAN)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
|
|||||||
Reference in New Issue
Block a user