pain
This commit is contained in:
parent
26a95d3dd5
commit
a2d2a7a551
127
src/egui_app.rs
127
src/egui_app.rs
@ -18,13 +18,15 @@ const MIN_X_TOTAL: f64 = -1000.0;
|
|||||||
const MAX_X_TOTAL: f64 = 1000.0;
|
const MAX_X_TOTAL: f64 = 1000.0;
|
||||||
const X_RANGE: RangeInclusive<f64> = MIN_X_TOTAL..=MAX_X_TOTAL;
|
const X_RANGE: RangeInclusive<f64> = MIN_X_TOTAL..=MAX_X_TOTAL;
|
||||||
const DEFAULT_FUNCION: &str = "x^2";
|
const DEFAULT_FUNCION: &str = "x^2";
|
||||||
|
const MARGINS: f64 = 0.9;
|
||||||
|
|
||||||
pub struct MathApp {
|
pub struct MathApp {
|
||||||
functions: Vec<Function>,
|
functions: Vec<Function>,
|
||||||
min_x: f64,
|
|
||||||
max_x: f64,
|
|
||||||
|
|
||||||
// Currently really unused. But once fully implemented it will represent the full graph's min_x and max_x, being seperate from min_x and max_x for the intergral.
|
// No clue why I need this, but I do. Rust being weird I guess.
|
||||||
|
// Ideally this should be information directly accessed from `functions` but it always returns an empty string. I don't know, I've been debuging this for a while now.
|
||||||
|
func_strs: Vec<String>,
|
||||||
|
|
||||||
integral_min_x: f64,
|
integral_min_x: f64,
|
||||||
integral_max_x: f64,
|
integral_max_x: f64,
|
||||||
|
|
||||||
@ -38,7 +40,8 @@ impl Default for MathApp {
|
|||||||
let def_max_x = 10.0;
|
let def_max_x = 10.0;
|
||||||
let def_interval: usize = 1000;
|
let def_interval: usize = 1000;
|
||||||
|
|
||||||
let def_funcs: Vec<Function> = vec![Function::new(
|
Self {
|
||||||
|
functions: vec![Function::new(
|
||||||
String::from(DEFAULT_FUNCION),
|
String::from(DEFAULT_FUNCION),
|
||||||
def_min_x,
|
def_min_x,
|
||||||
def_max_x,
|
def_max_x,
|
||||||
@ -46,12 +49,8 @@ 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),
|
||||||
)];
|
)],
|
||||||
|
func_strs: vec![String::from(DEFAULT_FUNCION)],
|
||||||
Self {
|
|
||||||
functions: def_funcs,
|
|
||||||
min_x: def_min_x,
|
|
||||||
max_x: def_max_x,
|
|
||||||
integral_min_x: def_min_x,
|
integral_min_x: def_min_x,
|
||||||
integral_max_x: def_max_x,
|
integral_max_x: def_max_x,
|
||||||
integral_num: def_interval,
|
integral_num: def_interval,
|
||||||
@ -78,12 +77,11 @@ impl epi::App for MathApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Called each time the UI needs repainting, which may be many times per second.
|
// Called each time the UI needs repainting, which may be many times per second.
|
||||||
#[inline]
|
#[inline(always)]
|
||||||
fn update(&mut self, ctx: &egui::Context, _frame: &epi::Frame) {
|
fn update(&mut self, ctx: &egui::Context, _frame: &epi::Frame) {
|
||||||
let Self {
|
let Self {
|
||||||
functions,
|
functions,
|
||||||
min_x,
|
func_strs,
|
||||||
max_x,
|
|
||||||
integral_min_x,
|
integral_min_x,
|
||||||
integral_max_x,
|
integral_max_x,
|
||||||
integral_num,
|
integral_num,
|
||||||
@ -105,64 +103,68 @@ impl epi::App for MathApp {
|
|||||||
ui.label("- signum, min, max");
|
ui.label("- signum, min, max");
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut new_func_data: Vec<(String, bool, bool)> = Vec::new();
|
|
||||||
let mut parse_error: String = "".to_string();
|
let mut parse_error: String = "".to_string();
|
||||||
egui::SidePanel::left("side_panel")
|
egui::SidePanel::left("side_panel")
|
||||||
.resizable(false)
|
.resizable(false)
|
||||||
.show(ctx, |ui| {
|
.show(ctx, |ui| {
|
||||||
ui.heading("Side Panel");
|
ui.heading("Side Panel");
|
||||||
if ui.add(egui::Button::new("Add function")).clicked() {
|
if ui.add(egui::Button::new("Add function")).clicked() {
|
||||||
functions.push(Function::new(String::from(DEFAULT_FUNCION), *min_x,
|
// min_x and max_x will be updated later, doesn't matter here
|
||||||
*max_x,
|
functions.push(Function::new(String::from(DEFAULT_FUNCION), -1.0,
|
||||||
true,
|
1.0,
|
||||||
Some(*min_x),
|
false,
|
||||||
Some(*max_x),
|
None,
|
||||||
Some(*integral_num)));
|
None,
|
||||||
|
None));
|
||||||
|
func_strs.push(String::from(DEFAULT_FUNCION));
|
||||||
}
|
}
|
||||||
|
|
||||||
for function in functions.iter() {
|
let min_x_old = *integral_min_x;
|
||||||
let mut func_str = function.get_string();
|
let min_x_response =
|
||||||
|
ui.add(egui::Slider::new(integral_min_x, X_RANGE.clone()).text("Min X"));
|
||||||
|
|
||||||
|
let max_x_old = *integral_max_x;
|
||||||
|
let max_x_response = ui.add(egui::Slider::new(integral_max_x, X_RANGE).text("Max X"));
|
||||||
|
|
||||||
|
// Checks bounds, and if they are invalid, fix them
|
||||||
|
if integral_min_x >= integral_max_x {
|
||||||
|
if max_x_response.changed() {
|
||||||
|
*integral_max_x = max_x_old;
|
||||||
|
} else if min_x_response.changed() {
|
||||||
|
*integral_min_x = min_x_old;
|
||||||
|
} else {
|
||||||
|
*integral_min_x = -10.0;
|
||||||
|
*integral_max_x = 10.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.add(egui::Slider::new(integral_num, INTEGRAL_NUM_RANGE).text("Interval"));
|
||||||
|
|
||||||
|
for (i, function) in functions.iter_mut().enumerate() {
|
||||||
let mut integral_toggle: bool = false;
|
let mut integral_toggle: bool = false;
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label("Function: ");
|
ui.label("Function: ");
|
||||||
if ui.add(Button::new("Toggle Integrals")).clicked() {
|
if ui.add(Button::new("Toggle Integrals")).clicked() {
|
||||||
integral_toggle = true;
|
integral_toggle = true;
|
||||||
}
|
}
|
||||||
ui.text_edit_singleline(&mut func_str);
|
ui.text_edit_singleline(&mut func_strs[i]);
|
||||||
});
|
});
|
||||||
|
|
||||||
let func_test_output = test_func(func_str.clone());
|
let integral: bool = if integral_toggle {
|
||||||
let mut got_error: bool = false;
|
!function.is_integral()
|
||||||
|
} else {
|
||||||
|
function.is_integral()
|
||||||
|
};
|
||||||
|
|
||||||
|
if !func_strs[i].is_empty() {
|
||||||
|
let func_test_output = test_func(func_strs[i].clone());
|
||||||
if !func_test_output.is_empty() {
|
if !func_test_output.is_empty() {
|
||||||
parse_error += &func_test_output;
|
parse_error += &func_test_output;
|
||||||
got_error = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
new_func_data.push((func_str, integral_toggle, got_error));
|
|
||||||
}
|
|
||||||
|
|
||||||
let min_x_old = *min_x;
|
|
||||||
let min_x_response =
|
|
||||||
ui.add(egui::Slider::new(min_x, X_RANGE.clone()).text("Min X"));
|
|
||||||
|
|
||||||
let max_x_old = *max_x;
|
|
||||||
let max_x_response = ui.add(egui::Slider::new(max_x, X_RANGE).text("Max X"));
|
|
||||||
|
|
||||||
// Checks bounds, and if they are invalid, fix them
|
|
||||||
if min_x >= max_x {
|
|
||||||
if max_x_response.changed() {
|
|
||||||
*max_x = max_x_old;
|
|
||||||
} else if min_x_response.changed() {
|
|
||||||
*min_x = min_x_old;
|
|
||||||
} else {
|
} else {
|
||||||
*min_x = -10.0;
|
function.update(func_strs[i].clone(), integral, Some(*integral_min_x), Some(*integral_max_x), Some(*integral_num));
|
||||||
*max_x = 10.0;
|
}
|
||||||
}
|
}
|
||||||
*integral_min_x = *min_x;
|
|
||||||
*integral_max_x = *max_x;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.add(egui::Slider::new(integral_num, INTEGRAL_NUM_RANGE).text("Interval"));
|
|
||||||
|
|
||||||
// Opensource and Licensing information
|
// Opensource and Licensing information
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
@ -190,21 +192,6 @@ impl epi::App for MathApp {
|
|||||||
ui.label(GIT_VERSION);
|
ui.label(GIT_VERSION);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut i: usize = 0;
|
|
||||||
for function in functions.iter_mut() {
|
|
||||||
let (func_str, integral_toggle, got_error) = (new_func_data[i].0.clone(), new_func_data[i].1, new_func_data[i].2);
|
|
||||||
|
|
||||||
let integral: bool = if integral_toggle {
|
|
||||||
!function.is_integral()
|
|
||||||
} else {
|
|
||||||
function.is_integral()
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
function.update(func_str, *min_x, *max_x, integral, Some(*integral_min_x), Some(*integral_max_x), Some(*integral_num), got_error);
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
@ -220,8 +207,14 @@ impl epi::App for MathApp {
|
|||||||
.data_aspect(1.0)
|
.data_aspect(1.0)
|
||||||
.include_y(0)
|
.include_y(0)
|
||||||
.show(ui, |plot_ui| {
|
.show(ui, |plot_ui| {
|
||||||
for function in self.functions.iter_mut() {
|
let bounds = plot_ui.plot_bounds();
|
||||||
if function.is_broken() {
|
let minx_bounds: f64 = bounds.min()[0];
|
||||||
|
let maxx_bounds: f64 = bounds.max()[0];
|
||||||
|
// println!("({}, {})", minx_bounds, maxx_bounds);
|
||||||
|
|
||||||
|
for (i, function) in self.functions.iter_mut().enumerate() {
|
||||||
|
function.update_bounds(minx_bounds * MARGINS, maxx_bounds * MARGINS);
|
||||||
|
if self.func_strs[i].is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -24,7 +24,7 @@ impl FunctionOutput {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_front(&self) -> (Vec<Bar>, f64) {
|
pub fn get_front(&self) -> (Vec<Bar>, f64) {
|
||||||
match &self.front {
|
match &self.front {
|
||||||
Some(x) => (x.0.clone(), x.1.clone()),
|
Some(x) => (x.0.clone(), x.1),
|
||||||
None => panic!(""),
|
None => panic!(""),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -40,7 +40,7 @@ impl FunctionOutput {
|
|||||||
|
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
function: Box<dyn Fn(f64) -> f64>,
|
function: Box<dyn Fn(f64) -> f64>,
|
||||||
func_str: String,
|
pub(crate) func_str: String,
|
||||||
min_x: f64,
|
min_x: f64,
|
||||||
max_x: f64,
|
max_x: f64,
|
||||||
|
|
||||||
@ -51,7 +51,6 @@ 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,
|
||||||
broken_state: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Function {
|
impl Function {
|
||||||
@ -72,7 +71,7 @@ impl Function {
|
|||||||
|
|
||||||
let expr: Expr = func_str.parse().unwrap();
|
let expr: Expr = func_str.parse().unwrap();
|
||||||
let func = expr.bind("x").unwrap();
|
let func = expr.bind("x").unwrap();
|
||||||
Self {
|
let mut output = Self {
|
||||||
function: Box::new(func),
|
function: Box::new(func),
|
||||||
func_str,
|
func_str,
|
||||||
min_x,
|
min_x,
|
||||||
@ -88,12 +87,11 @@ impl Function {
|
|||||||
Some(x) => x,
|
Some(x) => x,
|
||||||
None => f64::NAN,
|
None => f64::NAN,
|
||||||
},
|
},
|
||||||
integral_num: match integral_num {
|
integral_num: integral_num.unwrap_or(0),
|
||||||
Some(x) => x,
|
};
|
||||||
None => 0,
|
|
||||||
},
|
output.func_str = "".to_string();
|
||||||
broken_state: false,
|
output
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runs the internal function to get values
|
// Runs the internal function to get values
|
||||||
@ -102,13 +100,11 @@ impl Function {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn update(
|
pub fn update(
|
||||||
&mut self, func_str: String, min_x: f64, max_x: f64, integral: bool,
|
&mut self, func_str: String, integral: bool, integral_min_x: Option<f64>,
|
||||||
integral_min_x: Option<f64>, integral_max_x: Option<f64>, integral_num: Option<usize>,
|
integral_max_x: Option<f64>, integral_num: Option<usize>,
|
||||||
broken_state: bool,
|
|
||||||
) {
|
) {
|
||||||
if broken_state {
|
if func_str.is_empty() {
|
||||||
self.func_str = func_str.clone();
|
self.func_str = func_str;
|
||||||
self.broken_state = true;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,8 +112,8 @@ impl Function {
|
|||||||
if func_str != self.func_str {
|
if func_str != self.func_str {
|
||||||
*self = Self::new(
|
*self = Self::new(
|
||||||
func_str,
|
func_str,
|
||||||
min_x,
|
self.min_x,
|
||||||
max_x,
|
self.max_x,
|
||||||
integral,
|
integral,
|
||||||
integral_min_x,
|
integral_min_x,
|
||||||
integral_max_x,
|
integral_max_x,
|
||||||
@ -126,12 +122,6 @@ impl Function {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (min_x != self.min_x) | (max_x != self.max_x) {
|
|
||||||
self.back_cache.invalidate();
|
|
||||||
self.min_x = min_x;
|
|
||||||
self.max_x = max_x;
|
|
||||||
}
|
|
||||||
|
|
||||||
if integral != self.integral {
|
if integral != self.integral {
|
||||||
self.integral = integral;
|
self.integral = integral;
|
||||||
}
|
}
|
||||||
@ -158,6 +148,15 @@ impl Function {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn update_bounds(&mut self, min_x: f64, max_x: f64) {
|
||||||
|
if (min_x != self.min_x) | (max_x != self.max_x) {
|
||||||
|
self.back_cache.invalidate();
|
||||||
|
self.min_x = min_x;
|
||||||
|
self.max_x = max_x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_step(&self) -> f64 {
|
pub fn get_step(&self) -> f64 {
|
||||||
(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)
|
||||||
@ -166,10 +165,7 @@ impl Function {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_integral(&self) -> bool { self.integral }
|
pub fn is_integral(&self) -> bool { self.integral }
|
||||||
|
|
||||||
#[inline]
|
#[inline(always)]
|
||||||
pub fn is_broken(&self) -> bool { self.broken_state }
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn run(&mut self) -> FunctionOutput {
|
pub fn run(&mut self) -> FunctionOutput {
|
||||||
let front_values: Vec<Value> = match self.back_cache.is_valid() {
|
let front_values: Vec<Value> = match self.back_cache.is_valid() {
|
||||||
false => {
|
false => {
|
||||||
@ -208,14 +204,11 @@ impl Function {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn get_string(&self) -> String { self.func_str.clone() }
|
pub fn get_string(&self) -> String { self.func_str.clone() }
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn str_compare(&self, other_string: String) -> bool { self.func_str == other_string }
|
pub fn str_compare(&self, other_string: String) -> bool { self.func_str == other_string }
|
||||||
|
|
||||||
// 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
|
||||||
#[inline]
|
|
||||||
fn integral_rectangles(&self) -> (Vec<(f64, f64)>, f64) {
|
fn integral_rectangles(&self) -> (Vec<(f64, f64)>, f64) {
|
||||||
if !self.integral {
|
if !self.integral {
|
||||||
panic!("integral_rectangles called, but self.integral is false!");
|
panic!("integral_rectangles called, but self.integral is false!");
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user