initial function management refactoring
This commit is contained in:
parent
22d1be59f5
commit
2172f3da61
11
Cargo.lock
generated
11
Cargo.lock
generated
@ -1913,6 +1913,16 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8cfcd319456c4d6ea10087ed423473267e1a071f3bc0aa89f80d60997843c6f0"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
"rand",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "valuable"
|
name = "valuable"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -2337,6 +2347,7 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"tracing-wasm",
|
"tracing-wasm",
|
||||||
|
"uuid",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"wee_alloc",
|
"wee_alloc",
|
||||||
|
|||||||
@ -47,6 +47,8 @@ tracing = "0.1.34"
|
|||||||
itertools = "0.10.3"
|
itertools = "0.10.3"
|
||||||
static_assertions = "1.1.0"
|
static_assertions = "1.1.0"
|
||||||
phf = "0.10.1"
|
phf = "0.10.1"
|
||||||
|
uuid = { version = "1.0.0", features = ["v4", "fast-rng"] }
|
||||||
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
shadow-rs = "0.11.0"
|
shadow-rs = "0.11.0"
|
||||||
|
|||||||
32
TODO.md
32
TODO.md
@ -1,18 +1,18 @@
|
|||||||
## TODO:
|
## TODO:
|
||||||
1. Multiple functions in one graph.
|
1. Function management
|
||||||
- Backend support
|
- Integrals between functions (too hard to implement, maybe will shelve)
|
||||||
- Integrals between functions (too hard to implement, maybe will shelve)
|
- Display intersection between functions (would have to rewrite a lot of the function plotting handling)
|
||||||
- Display intersection between functions (would have to rewrite a lot of the function plotting handling)
|
- Sort by UUIDs
|
||||||
2. Rerwite of function parsing code
|
- [Drag and drop support](https://github.com/emilk/egui/discussions/1530) in the UI
|
||||||
- Non `y=` functions.
|
- Hide/disable functions
|
||||||
3. Smart display of graph
|
2. Smart display of graph
|
||||||
- Display of intersections between functions
|
- Display of intersections between functions
|
||||||
4. Allow constants in min/max integral input (like pi or euler's number)
|
3. 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)
|
4. Sliding values for functions (like a user-interactable slider that adjusts a variable in the function, like desmos)
|
||||||
6. Fix integral display
|
5. Fix integral display
|
||||||
7. Better handling of panics and errors to display to the user
|
6. Better handling of panics and errors to display to the user
|
||||||
8. Turn Dynamic Iterator functions into traits
|
7. Turn Dynamic Iterator functions into traits
|
||||||
9. Better handling of roots and extrema finding
|
8. Better handling of roots and extrema finding
|
||||||
10. Add closing animation for function entry
|
9. Add closing animation for function entry
|
||||||
11. Create actual icon(s) for PWA/favicon (using placeholder from eframe_template)
|
10. Create actual icon(s) for PWA/favicon (using placeholder from eframe_template)
|
||||||
12. Fix mobile text input
|
11. Fix mobile text input
|
||||||
|
|||||||
@ -7,17 +7,18 @@
|
|||||||
"- PI is available through 'pi' or 'π'"
|
"- PI is available through 'pi' or 'π'"
|
||||||
],
|
],
|
||||||
"help_panel": [
|
"help_panel": [
|
||||||
"- The 'Panel' button on the top bar toggles if the side bar should be shown or not. This can also be accomplished by pressing the 'h' key.",
|
"- The 'Panel' button toggles if the side bar should be shown or not. This can also be accomplished by pressing the 'h' key.",
|
||||||
"- The 'Add Function' button on the top panel adds a new function to be graphed. You can then configure that function in the side panel.",
|
"- The 'Add Function' button adds a new function to be graphed. You can then configure that function in the side panel.",
|
||||||
"- The 'Help' button on the top bar opens and closes this window!",
|
"- The 'Help' button opens and closes this window!",
|
||||||
"- The 'Info' button provides information on the build currently running.",
|
"- The 'Info' button provides information on the build currently running.",
|
||||||
"- The Sun/Moon button toggles Dark and Light mode."
|
"- The Sun/Moon button toggles Dark and Light mode."
|
||||||
],
|
],
|
||||||
"help_function": [
|
"help_function": [
|
||||||
"- The '✖' button before the '∫' button allows you to delete the function in question. Deleting a function is prevented if only 1 function exists.",
|
"(From Left to Right)",
|
||||||
"- The ∫ button (between the '✖' and 'd/dx' buttons) indicates whether to integrate the function in question.",
|
"- The `✖` allows you to delete the function in question. Deleting a function is prevented if only 1 function exists.",
|
||||||
"- The 'd/dx' button next to the gear icon indicates whether or not calculating the derivative is enabled or not.",
|
"- The `∫` indicates whether to integrate the function in question.",
|
||||||
"- The gear icon next to the function input allows you to tweak settings in relation to the selected function."
|
"- The `d/dx` toggles the calculation of derivatives.",
|
||||||
|
"- The `⚙` opens a window to tweak function options."
|
||||||
],
|
],
|
||||||
"help_other": [
|
"help_other": [
|
||||||
"- Extrema (local minimums and maximums) and Roots (intersections with the x-axis) are displayed though yellow and light blue points on the graph. You can toggle these in the Side Panel.",
|
"- Extrema (local minimums and maximums) and Roots (intersections with the x-axis) are displayed though yellow and light blue points on the graph. You can toggle these in the Side Panel.",
|
||||||
|
|||||||
@ -18,3 +18,6 @@ lazy_static = "1.4.0"
|
|||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
phf_codegen = "0.10.0"
|
phf_codegen = "0.10.0"
|
||||||
|
|
||||||
|
[package.metadata.cargo-all-features]
|
||||||
|
skip_optional_dependencies = true #don't test optional dependencies, only features
|
||||||
|
|||||||
@ -71,3 +71,20 @@ pub const COLORS: &[Color32; 13] = &[
|
|||||||
Color32::DARK_GREEN,
|
Color32::DARK_GREEN,
|
||||||
Color32::DARK_BLUE,
|
Color32::DARK_BLUE,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
pub static IS_MOBILE: bool = {
|
||||||
|
fn is_mobile() -> Option<bool> {
|
||||||
|
const MOBILE_DEVICE: [&str; 6] = ["Android", "iPhone", "iPad", "iPod", "webOS", "BlackBerry"];
|
||||||
|
|
||||||
|
let user_agent = web_sys::window()?.navigator().user_agent().ok()?;
|
||||||
|
Some(MOBILE_DEVICE.iter().any(|&name| user_agent.contains(name)))
|
||||||
|
}
|
||||||
|
|
||||||
|
is_mobile().unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
pub const IS_MOBILE: bool = false;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#![allow(clippy::too_many_arguments)] // Clippy, shut
|
#![allow(clippy::too_many_arguments)] // Clippy, shut
|
||||||
|
|
||||||
|
use crate::consts::IS_MOBILE;
|
||||||
use crate::math_app::AppSettings;
|
use crate::math_app::AppSettings;
|
||||||
use crate::misc::*;
|
use crate::misc::*;
|
||||||
use crate::widgets::{widgets_ontop, AutoComplete, Movement};
|
use crate::widgets::{widgets_ontop, AutoComplete, Movement};
|
||||||
@ -101,9 +102,7 @@ impl Default for FunctionEntry {
|
|||||||
impl FunctionEntry {
|
impl FunctionEntry {
|
||||||
/// Creates edit box for [`FunctionEntry`] to edit function settings and string.
|
/// Creates edit box for [`FunctionEntry`] to edit function settings and string.
|
||||||
/// Returns whether or not this function was marked for removal.
|
/// Returns whether or not this function was marked for removal.
|
||||||
pub fn function_entry(
|
pub fn function_entry(&mut self, ui: &mut egui::Ui, can_remove: bool, i: usize) -> bool {
|
||||||
&mut self, ui: &mut egui::Ui, can_remove: bool, i: usize, mobile: bool,
|
|
||||||
) -> bool {
|
|
||||||
let output_string = self.autocomplete.string.clone();
|
let output_string = self.autocomplete.string.clone();
|
||||||
self.update_string(&output_string);
|
self.update_string(&output_string);
|
||||||
|
|
||||||
@ -154,7 +153,7 @@ impl FunctionEntry {
|
|||||||
self.autocomplete.update_string(&new_string);
|
self.autocomplete.update_string(&new_string);
|
||||||
|
|
||||||
if !self.autocomplete.hint.is_none() {
|
if !self.autocomplete.hint.is_none() {
|
||||||
if !self.autocomplete.hint.is_single() {
|
if !IS_MOBILE && !self.autocomplete.hint.is_single() {
|
||||||
if ui.input().key_pressed(Key::ArrowDown) {
|
if ui.input().key_pressed(Key::ArrowDown) {
|
||||||
movement = Movement::Down;
|
movement = Movement::Down;
|
||||||
} else if ui.input().key_pressed(Key::ArrowUp) {
|
} else if ui.input().key_pressed(Key::ArrowUp) {
|
||||||
@ -729,7 +728,6 @@ mod tests {
|
|||||||
do_extrema: false,
|
do_extrema: false,
|
||||||
do_roots: false,
|
do_roots: false,
|
||||||
plot_width: pixel_width,
|
plot_width: pixel_width,
|
||||||
is_mobile: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
55
src/function_manager.rs
Normal file
55
src/function_manager.rs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
use crate::function_entry::{FunctionEntry, DEFAULT_FUNCTION_ENTRY};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
pub struct Manager {
|
||||||
|
functions: Vec<(Uuid, FunctionEntry)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Manager {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
functions: vec![(Uuid::new_v4(), DEFAULT_FUNCTION_ENTRY.clone())],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Manager {
|
||||||
|
pub fn display_entries(&mut self, ui: &mut egui::Ui) {
|
||||||
|
// ui.label("Functions:");
|
||||||
|
let can_remove = self.functions.len() > 1;
|
||||||
|
|
||||||
|
let mut remove_i: Option<usize> = None;
|
||||||
|
for (i, (uuid, function)) in self.functions.iter_mut().enumerate() {
|
||||||
|
// Entry for a function
|
||||||
|
if function.function_entry(ui, can_remove, i) {
|
||||||
|
remove_i = Some(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
function.settings_window(ui.ctx());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove function if the user requests it
|
||||||
|
if let Some(remove_i_unwrap) = remove_i {
|
||||||
|
self.functions.remove(remove_i_unwrap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_function(&mut self) {
|
||||||
|
self.functions
|
||||||
|
.push((Uuid::new_v4(), DEFAULT_FUNCTION_ENTRY.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn any_using_integral(&self) -> bool {
|
||||||
|
self.functions
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, func)| func.integral)
|
||||||
|
.count() > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn len(&self) -> usize { self.functions.len() }
|
||||||
|
|
||||||
|
pub fn get_entries_mut(&mut self) -> &mut Vec<(Uuid, FunctionEntry)> { &mut self.functions }
|
||||||
|
|
||||||
|
pub fn get_entries(&self) -> &Vec<(Uuid, FunctionEntry)> { &self.functions }
|
||||||
|
}
|
||||||
@ -7,6 +7,7 @@ extern crate static_assertions;
|
|||||||
|
|
||||||
mod consts;
|
mod consts;
|
||||||
mod function_entry;
|
mod function_entry;
|
||||||
|
mod function_manager;
|
||||||
mod math_app;
|
mod math_app;
|
||||||
mod misc;
|
mod misc;
|
||||||
mod widgets;
|
mod widgets;
|
||||||
|
|||||||
@ -7,6 +7,7 @@ extern crate static_assertions;
|
|||||||
|
|
||||||
mod consts;
|
mod consts;
|
||||||
mod function_entry;
|
mod function_entry;
|
||||||
|
mod function_manager;
|
||||||
mod math_app;
|
mod math_app;
|
||||||
mod misc;
|
mod misc;
|
||||||
mod widgets;
|
mod widgets;
|
||||||
|
|||||||
150
src/math_app.rs
150
src/math_app.rs
@ -1,5 +1,6 @@
|
|||||||
use crate::consts::*;
|
use crate::consts::*;
|
||||||
use crate::function_entry::{FunctionEntry, Riemann, DEFAULT_FUNCTION_ENTRY};
|
use crate::function_entry::Riemann;
|
||||||
|
use crate::function_manager::Manager;
|
||||||
use crate::misc::{dyn_mut_iter, option_vec_printer, JsonFileOutput, SerdeValueHelper};
|
use crate::misc::{dyn_mut_iter, option_vec_printer, JsonFileOutput, SerdeValueHelper};
|
||||||
use egui::style::Margin;
|
use egui::style::Margin;
|
||||||
use egui::Frame;
|
use egui::Frame;
|
||||||
@ -30,14 +31,6 @@ cfg_if::cfg_if! {
|
|||||||
// Remove the element
|
// Remove the element
|
||||||
loading_element.remove();
|
loading_element.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_mobile() -> Option<bool> {
|
|
||||||
const MOBILE_DEVICE: [&str; 6] = ["Android", "iPhone", "iPad", "iPod", "webOS", "BlackBerry"];
|
|
||||||
|
|
||||||
let user_agent = web_sys::window()?.navigator().user_agent().ok()?;
|
|
||||||
Some(MOBILE_DEVICE.iter().any(|&name| user_agent.contains(name)))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,8 +61,6 @@ pub struct AppSettings {
|
|||||||
|
|
||||||
/// Stores current plot pixel width
|
/// Stores current plot pixel width
|
||||||
pub plot_width: usize,
|
pub plot_width: usize,
|
||||||
|
|
||||||
pub is_mobile: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AppSettings {
|
impl Default for AppSettings {
|
||||||
@ -85,7 +76,6 @@ impl Default for AppSettings {
|
|||||||
do_extrema: true,
|
do_extrema: true,
|
||||||
do_roots: true,
|
do_roots: true,
|
||||||
plot_width: 0,
|
plot_width: 0,
|
||||||
is_mobile: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,7 +102,7 @@ impl Default for Opened {
|
|||||||
/// The actual application
|
/// The actual application
|
||||||
pub struct MathApp {
|
pub struct MathApp {
|
||||||
/// Stores vector of functions
|
/// Stores vector of functions
|
||||||
functions: Vec<FunctionEntry>,
|
functions: Manager,
|
||||||
|
|
||||||
/// 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<Option<f64>>, Duration),
|
last_info: (Vec<Option<f64>>, Duration),
|
||||||
@ -135,19 +125,10 @@ impl MathApp {
|
|||||||
pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
|
pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
|
||||||
let start = instant::Instant::now();
|
let start = instant::Instant::now();
|
||||||
|
|
||||||
#[allow(unused_mut)]
|
|
||||||
#[allow(unused_assignments)]
|
|
||||||
let mut mobile = false;
|
|
||||||
|
|
||||||
// Remove loading indicator on wasm
|
// Remove loading indicator on wasm
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
stop_loading();
|
stop_loading();
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
{
|
|
||||||
mobile = is_mobile().unwrap_or_default();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(threading)]
|
#[cfg(threading)]
|
||||||
tracing::info!("Threading: Enabled");
|
tracing::info!("Threading: Enabled");
|
||||||
|
|
||||||
@ -271,15 +252,12 @@ impl MathApp {
|
|||||||
tracing::info!("Initialized! Took: {:?}", start.elapsed());
|
tracing::info!("Initialized! Took: {:?}", start.elapsed());
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
functions: vec![DEFAULT_FUNCTION_ENTRY.clone()],
|
functions: Default::default(),
|
||||||
last_info: (vec![None], Duration::ZERO),
|
last_info: (vec![None], Duration::ZERO),
|
||||||
dark_mode: true,
|
dark_mode: true,
|
||||||
text: text_data.expect("text.json failed to load"),
|
text: text_data.expect("text.json failed to load"),
|
||||||
opened: Opened::default(),
|
opened: Opened::default(),
|
||||||
settings: AppSettings {
|
settings: Default::default(),
|
||||||
is_mobile: mobile,
|
|
||||||
..AppSettings::default()
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,30 +270,27 @@ impl MathApp {
|
|||||||
.show(ctx, |ui| {
|
.show(ctx, |ui| {
|
||||||
let prev_sum = self.settings.riemann_sum;
|
let prev_sum = self.settings.riemann_sum;
|
||||||
// ComboBox for selecting what Riemann sum type to use
|
// ComboBox for selecting what Riemann sum type to use
|
||||||
ui.add_enabled_ui(
|
ui.add_enabled_ui(self.functions.any_using_integral(), |ui| {
|
||||||
self.functions.iter().filter(|func| func.integral).count() > 0,
|
ComboBox::from_label("Riemann Sum")
|
||||||
|ui| {
|
.selected_text(self.settings.riemann_sum.to_string())
|
||||||
ComboBox::from_label("Riemann Sum")
|
.show_ui(ui, |ui| {
|
||||||
.selected_text(self.settings.riemann_sum.to_string())
|
ui.selectable_value(
|
||||||
.show_ui(ui, |ui| {
|
&mut self.settings.riemann_sum,
|
||||||
ui.selectable_value(
|
Riemann::Left,
|
||||||
&mut self.settings.riemann_sum,
|
"Left",
|
||||||
Riemann::Left,
|
);
|
||||||
"Left",
|
ui.selectable_value(
|
||||||
);
|
&mut self.settings.riemann_sum,
|
||||||
ui.selectable_value(
|
Riemann::Middle,
|
||||||
&mut self.settings.riemann_sum,
|
"Middle",
|
||||||
Riemann::Middle,
|
);
|
||||||
"Middle",
|
ui.selectable_value(
|
||||||
);
|
&mut self.settings.riemann_sum,
|
||||||
ui.selectable_value(
|
Riemann::Right,
|
||||||
&mut self.settings.riemann_sum,
|
"Right",
|
||||||
Riemann::Right,
|
);
|
||||||
"Right",
|
});
|
||||||
);
|
});
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let riemann_changed = prev_sum != self.settings.riemann_sum;
|
let riemann_changed = prev_sum != self.settings.riemann_sum;
|
||||||
|
|
||||||
@ -379,39 +354,26 @@ impl MathApp {
|
|||||||
self.settings.integral_changed =
|
self.settings.integral_changed =
|
||||||
max_x_changed | min_x_changed | integral_num_changed | riemann_changed;
|
max_x_changed | min_x_changed | integral_num_changed | riemann_changed;
|
||||||
|
|
||||||
let can_remove = self.functions.len() > 1;
|
self.functions.display_entries(ui);
|
||||||
ui.label("Functions:");
|
|
||||||
|
|
||||||
let mut remove_i: Option<usize> = None;
|
// Only render if there's enough space
|
||||||
for (i, function) in self.functions.iter_mut().enumerate() {
|
if ui.available_height() > 0.0 {
|
||||||
// Entry for a function
|
ui.with_layout(egui::Layout::bottom_up(emath::Align::Min), |ui| {
|
||||||
if function.function_entry(ui, can_remove, i, self.settings.is_mobile) {
|
// Contents put in reverse order from bottom to top due to the 'buttom_up' layout
|
||||||
remove_i = Some(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
function.settings_window(ctx);
|
// Licensing information
|
||||||
|
ui.label(
|
||||||
|
RichText::new("(and licensed under AGPLv3)").color(Color32::LIGHT_GRAY),
|
||||||
|
)
|
||||||
|
.on_hover_text(&self.text.license_info);
|
||||||
|
|
||||||
|
// Hyperlink to project's github
|
||||||
|
ui.hyperlink_to(
|
||||||
|
"I'm Open Source!",
|
||||||
|
"https://github.com/Titaniumtown/YTBN-Graphing-Software",
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove function if the user requests it
|
|
||||||
if let Some(remove_i_unwrap) = remove_i {
|
|
||||||
self.functions.remove(remove_i_unwrap);
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.with_layout(egui::Layout::bottom_up(emath::Align::Min), |ui| {
|
|
||||||
// Contents put in reverse order from bottom to top due to the 'buttom_up' layout
|
|
||||||
|
|
||||||
// Licensing information
|
|
||||||
ui.label(
|
|
||||||
RichText::new("(and licensed under AGPLv3)").color(Color32::LIGHT_GRAY),
|
|
||||||
)
|
|
||||||
.on_hover_text(&self.text.license_info);
|
|
||||||
|
|
||||||
// Hyperlink to project's github
|
|
||||||
ui.hyperlink_to(
|
|
||||||
"I'm Open Source!",
|
|
||||||
"https://github.com/Titaniumtown/YTBN-Graphing-Software",
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -458,7 +420,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(DEFAULT_FUNCTION_ENTRY.clone());
|
self.functions.new_function();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggles opening the Help window
|
// Toggles opening the Help window
|
||||||
@ -575,8 +537,9 @@ impl epi::App for MathApp {
|
|||||||
// Display an error if it exists
|
// Display an error if it exists
|
||||||
let errors_formatted: String = self
|
let errors_formatted: String = self
|
||||||
.functions
|
.functions
|
||||||
|
.get_entries()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|func| func.get_test_result())
|
.map(|(_, func)| func.get_test_result())
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter(|(_, error)| error.is_some())
|
.filter(|(_, error)| error.is_some())
|
||||||
.map(|(i, error)| {
|
.map(|(i, error)| {
|
||||||
@ -612,22 +575,21 @@ impl epi::App for MathApp {
|
|||||||
let minx_bounds: f64 = bounds.min()[0];
|
let minx_bounds: f64 = bounds.min()[0];
|
||||||
let maxx_bounds: f64 = bounds.max()[0];
|
let maxx_bounds: f64 = bounds.max()[0];
|
||||||
|
|
||||||
dyn_mut_iter(&mut self.functions)
|
dyn_mut_iter(self.functions.get_entries_mut()).for_each(|(_, function)| {
|
||||||
.enumerate()
|
function.calculate(
|
||||||
.for_each(|(_, function)| {
|
&minx_bounds,
|
||||||
function.calculate(
|
&maxx_bounds,
|
||||||
&minx_bounds,
|
width_changed,
|
||||||
&maxx_bounds,
|
&self.settings,
|
||||||
width_changed,
|
)
|
||||||
&self.settings,
|
});
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
area_list = self
|
area_list = self
|
||||||
.functions
|
.functions
|
||||||
|
.get_entries()
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, function)| {
|
.map(|(i, (_, function))| {
|
||||||
function.display(plot_ui, &self.settings, COLORS[i])
|
function.display(plot_ui, &self.settings, COLORS[i])
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user