This commit is contained in:
Simon Gardling 2022-03-15 00:14:07 -04:00
parent 32e4699c5f
commit 5e9dc18469
6 changed files with 69 additions and 34 deletions

View File

@ -1,4 +1,5 @@
edition = "2021"
fn_args_layout = "Compressed"
fn_single_line = true
hard_tabs = true
hard_tabs = true
wrap_comments = true

View File

@ -289,13 +289,16 @@ pub struct MathApp {
// Stores vector of functions
functions: Vec<FunctionEntry>,
// Stores vector containing the string representation of the functions. This is used because of hacky reasons
// Stores vector containing the string representation of the functions. This is used because of
// hacky reasons
func_strs: Vec<String>,
// Stores last error from parsing functions (used to display the same error when side panel is minimized)
// Stores last error from parsing functions (used to display the same error when side panel is
// minimized)
last_error: Vec<(usize, String)>,
// Contains the list of Areas calculated (the vector of f64) and time it took for the last frame (the Duration). Stored in a Tuple.
// Contains the list of Areas calculated (the vector of f64) and time it took for the last
// frame (the Duration). Stored in a Tuple.
last_info: (Vec<f64>, Duration),
// Stores Settings (pretty self-explanatory)
@ -316,7 +319,8 @@ impl Default for MathApp {
impl MathApp {
fn side_panel(&mut self, ctx: &Context) {
// Side Panel which contains vital options to the operation of the application (such as adding functions and other options)
// Side Panel which contains vital options to the operation of the application
// (such as adding functions and other options)
SidePanel::left("side_panel")
.resizable(false)
.show(ctx, |ui| {
@ -432,7 +436,8 @@ impl MathApp {
})
.clicked();
// Toggle showing the derivative (even though it's already calculated, this option just toggles if it's displayed or not)
// Toggle showing the derivative (even though it's already calculated, this
// option just toggles if it's displayed or not)
derivative_toggle = ui
.add(Button::new("d/dx"))
.on_hover_text(match derivative_enabled {
@ -496,7 +501,10 @@ impl epi::App for MathApp {
fn name(&self) -> &str { "(Yet-to-be-named) Graphing Software" }
// Called once before the first frame.
fn setup(&mut self, _ctx: &Context, _frame: &Frame, _storage: Option<&dyn Storage>, _gl: &std::rc::Rc<epi::glow::Context>,) {
fn setup(
&mut self, _ctx: &Context, _frame: &Frame, _storage: Option<&dyn Storage>,
_gl: &std::rc::Rc<epi::glow::Context>,
) {
#[cfg(target_arch = "wasm32")]
stop_loading();
log_helper("egui app initialized.");
@ -632,9 +640,11 @@ impl epi::App for MathApp {
self.side_panel(ctx);
}
let mut area_list: Vec<f64> = Vec::new(); // Referenced in plotting code, but needs to be here so it can be later referenced when storing `last_info`
let mut area_list: Vec<f64> = Vec::new(); // Referenced in plotting code, but needs to be here so it can be later
// referenced when storing `last_info`
// Central panel which contains the central plot (or an error created when parsing)
// Central panel which contains the central plot (or an error created when
// parsing)
CentralPanel::default().show(ctx, |ui| {
// Display an error if it exists
if !self.last_error.is_empty() {
@ -679,9 +689,12 @@ impl epi::App for MathApp {
.collect();
});
});
self.last_info = (area_list, start.elapsed()); // Store list of functions' areas along with the time it took to process.
self.last_info = (area_list, start.elapsed()); // Store list of functions' areas
// along with the time it took to
// process.
}
// Uncaps max canvas size. This was capped in egui due to a bug in Firefox. But it's fixed now.
// Uncaps max canvas size. This was capped in egui due to a bug in Firefox. But
// it's fixed now.
fn max_size_points(&self) -> Vec2 { Vec2::new(f32::MAX, f32::MAX) }
}

View File

@ -27,20 +27,24 @@ lazy_static::lazy_static! {
pub static ref EMPTY_FUNCTION_ENTRY: FunctionEntry = FunctionEntry::empty();
}
/// `FunctionEntry` is a function that can calculate values, integrals, derivatives, etc etc
/// `FunctionEntry` is a function that can calculate values, integrals,
/// derivatives, etc etc
#[derive(Clone)]
pub struct FunctionEntry {
/// The `BackingFunction` instance that is used to generate `f(x)`, `f'(x)`, and `f''(x)`
/// The `BackingFunction` instance that is used to generate `f(x)`, `f'(x)`,
/// and `f''(x)`
function: BackingFunction,
/// Stores a function string (that hasn't been processed via `process_func_str`) to display to the user
/// Stores a function string (that hasn't been processed via
/// `process_func_str`) to display to the user
func_str: String,
/// Minimum and Maximum values of what do display
min_x: f64,
max_x: f64,
/// How many horizontal pixels? (used for calculating the step at which to generate values at)
/// How many horizontal pixels? (used for calculating the step at which to
/// generate values at)
pixel_width: usize,
/// output/cached data
@ -49,7 +53,8 @@ pub struct FunctionEntry {
/// If calculating/displayingintegrals are enabled
pub(crate) integral: bool,
/// If displaying derivatives are enabled (note, they are still calculated for other purposes)
/// If displaying derivatives are enabled (note, they are still calculated
/// for other purposes)
pub(crate) derivative: bool,
/// Minumum and maximum range of integral
@ -113,7 +118,8 @@ impl FunctionEntry {
}
// TODO: refactor this
/// Returns back values, integral data (Bars and total area), and Derivative values
/// Returns back values, integral data (Bars and total area), and Derivative
/// values
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 back_values: Vec<Value> = {
@ -157,7 +163,8 @@ impl FunctionEntry {
(back_values, integral_data, derivative_values)
}
/// 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
fn integral_rectangles(&self) -> (Vec<(f64, f64)>, f64) {
if self.integral_min_x.is_nan() {
panic!("integral_min_x is NaN")

View File

@ -47,7 +47,8 @@ impl FunctionOutput {
pub fn invalidate_derivative(&mut self) { self.derivative = None; }
/// Display output on PlotUi `plot_ui`
/// Returns `f64` containing rounded integral area (if integrals are disabled, it returns `f64::NAN`)
/// Returns `f64` containing rounded integral area (if integrals are
/// disabled, it returns `f64::NAN`)
pub fn display(
&self, plot_ui: &mut PlotUi, func_str: &str, derivative_str: &str, step: f64,
derivative_enabled: bool,

View File

@ -2,7 +2,8 @@ use std::ops::Range;
use eframe::egui::plot::Value;
// Handles logging based on if the target is wasm (or not) and if `debug_assertions` is enabled or not
// Handles logging based on if the target is wasm (or not) and if
// `debug_assertions` is enabled or not
cfg_if::cfg_if! {
if #[cfg(target_arch = "wasm32")] {
use wasm_bindgen::prelude::*;
@ -43,8 +44,11 @@ cfg_if::cfg_if! {
}
}
/// `SteppedVector` is used in order to efficiently sort through an ordered `Vec<f64>`
/// Used in order to speedup the processing of cached data when moving horizontally without zoom in `FunctionEntry`. Before this struct, the index was calculated with `.iter().position(....` which was horribly inefficient
/// `SteppedVector` is used in order to efficiently sort through an ordered
/// `Vec<f64>` Used in order to speedup the processing of cached data when
/// moving horizontally without zoom in `FunctionEntry`. Before this struct, the
/// index was calculated with `.iter().position(....` which was horribly
/// inefficient
pub struct SteppedVector {
// Actual data being referenced. HAS to be sorted from maximum value to minumum
data: Vec<f64>,
@ -55,14 +59,17 @@ pub struct SteppedVector {
// Maximum value
max: f64,
// Since all entries in `data` are evenly spaced, this field stores the step between 2 adjacent elements
// Since all entries in `data` are evenly spaced, this field stores the step between 2 adjacent
// elements
step: f64,
}
impl SteppedVector {
/// Returns `Option<usize>` with index of element with value `x`. and `None` if `x` does not exist in `data`
/// Returns `Option<usize>` with index of element with value `x`. and `None`
/// if `x` does not exist in `data`
pub fn get_index(&self, x: f64) -> Option<usize> {
// if `x` is outside range, just go ahead and return `None` as it *shouldn't* be in `data`
// if `x` is outside range, just go ahead and return `None` as it *shouldn't* be
// in `data`
if (x > self.max) | (self.min > x) {
return None;
}
@ -70,7 +77,8 @@ impl SteppedVector {
// Do some math in order to calculate the expected index value
let possible_i = ((x + self.min) / self.step) as usize;
// Make sure that the index is valid by checking the data returned vs the actual data (just in case)
// Make sure that the index is valid by checking the data returned vs the actual
// data (just in case)
if self.data[possible_i] == x {
// It is valid!
Some(possible_i)
@ -96,7 +104,8 @@ impl SteppedVector {
// Convert `Vec<f64>` into `SteppedVector`
impl From<Vec<f64>> for SteppedVector {
/// Note: input `data` is assumed to be sorted properly
/// `data` is a Vector of 64 bit floating point numbers ordered from max -> min
/// `data` is a Vector of 64 bit floating point numbers ordered from max ->
/// min
fn from(data: Vec<f64>) -> SteppedVector {
let max = data[0]; // The max value should be the first element
let min = data[data.len() - 1]; // The minimum value should be the last element
@ -115,14 +124,15 @@ impl From<Vec<f64>> for SteppedVector {
// Rounds f64 to specific number of decimal places
pub fn decimal_round(x: f64, n: usize) -> f64 {
let large_number: f64 = 10.0_f64.powf(n as f64); // 10^n
(x * large_number).round() / large_number // round and devide in order to cut off after the `n`th decimal place
(x * large_number).round() / large_number // round and devide in order to cut
// off after the `n`th decimal place
}
/// Implements newton's method of finding roots.
/// `threshold` is the target accuracy threshold
/// `range` is the range of valid x values (used to stop calculation when the point won't display anyways)
/// `data` is the data to iterate over (a Vector of egui's `Value` struct)
/// `f` is f(x)
/// `range` is the range of valid x values (used to stop calculation when the
/// point won't display anyways) `data` is the data to iterate over (a Vector of
/// egui's `Value` struct) `f` is f(x)
/// `f_1` is f'(x)
/// The function returns a Vector of `x` values where roots occur
pub fn newtons_method(

View File

@ -133,7 +133,8 @@ pub fn process_func_str(function_in: String) -> String {
output_string.replace("log(", "log10(")
}
// 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.
// 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.
pub fn test_func(function_string: &str) -> Option<String> {
let parse_result = exmex::parse::<f64>(function_string);
@ -169,7 +170,8 @@ pub fn test_func(function_string: &str) -> Option<String> {
}
}
// Used for testing: passes function to `add_asterisks` before running `test_func`
// Used for testing: passes function to `add_asterisks` before running
// `test_func`
#[cfg(test)]
fn test_func_helper(function_string: &str) -> Option<String> {
test_func(&process_func_str(function_string.to_string()))
@ -225,7 +227,8 @@ fn func_process_test() {
test_process_helper("10pi", "10*π");
test_process_helper("pi10", "π*10");
// Need to fix these checks, maybe I need to rewrite the whole asterisk adding system... (or just implement these changes into meval-rs, idk)
// Need to fix these checks, maybe I need to rewrite the whole asterisk
// adding system... (or just implement these changes into meval-rs, idk)
// test_process_helper("emax(x)", "e*max(x)");
// test_process_helper("pisin(x)", "pi*sin(x)");
}