lots
This commit is contained in:
parent
e1de404665
commit
bd86998755
@ -22,12 +22,12 @@ opt-level = 2
|
||||
lto = false
|
||||
|
||||
[dependencies]
|
||||
meval = { git = "https://github.com/Titaniumtown/meval-rs.git" }
|
||||
eframe = { git = "https://github.com/Titaniumtown/egui", default-features = false, features = ["egui_glow"] }
|
||||
include-flate = { git = "https://github.com/Titaniumtown/include-flate.git" }
|
||||
shadow-rs = { version = "0.9", default-features = false }
|
||||
const_format = { version = "0.2.22", default-features = false, features = ["fmt"] }
|
||||
cfg-if = "1.0.0"
|
||||
evalexpr = { git = "https://github.com/Titaniumtown/evalexpr.git" }
|
||||
|
||||
[build-dependencies]
|
||||
shadow-rs = "0.9"
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
use crate::function::{FunctionEntry, RiemannSum};
|
||||
use crate::misc::{add_asterisks, digits_precision, test_func};
|
||||
use crate::misc::{add_asterisks, digits_precision};
|
||||
use crate::parsing::test_func;
|
||||
|
||||
use const_format::formatc;
|
||||
use eframe::{egui, epi};
|
||||
use egui::plot::Plot;
|
||||
@ -141,7 +143,7 @@ pub struct MathApp {
|
||||
func_strs: Vec<String>,
|
||||
|
||||
// Stores last error from parsing functions (used to display the same error when side panel is minimized)
|
||||
last_error: String,
|
||||
last_error: Vec<(usize, String)>,
|
||||
|
||||
// Stores font data that's used when displaying text
|
||||
font: FontData,
|
||||
@ -158,7 +160,7 @@ impl Default for MathApp {
|
||||
Self {
|
||||
functions: vec![FunctionEntry::empty().integral(true)],
|
||||
func_strs: vec![String::from(DEFAULT_FUNCION)],
|
||||
last_error: String::new(),
|
||||
last_error: Vec::new(),
|
||||
font: FontData::from_static(&FONT_FILE),
|
||||
last_info: (vec![0.0], Duration::ZERO),
|
||||
settings: AppSettings::default(),
|
||||
@ -230,7 +232,6 @@ impl MathApp {
|
||||
);
|
||||
|
||||
let mut remove_i: Option<usize> = None;
|
||||
self.last_error = String::new();
|
||||
for (i, function) in self.functions.iter_mut().enumerate() {
|
||||
let mut integral_toggle: bool = false;
|
||||
let mut derivative_toggle: bool = false;
|
||||
@ -286,8 +287,8 @@ impl MathApp {
|
||||
if ui
|
||||
.add(Button::new("d/dx"))
|
||||
.on_hover_text(match derivative_enabled {
|
||||
true => "Don't Calculate Derivative",
|
||||
false => "Calculate Derivative",
|
||||
true => "Don't Differentiate",
|
||||
false => "Differentiate",
|
||||
})
|
||||
.clicked()
|
||||
{
|
||||
@ -297,12 +298,14 @@ impl MathApp {
|
||||
ui.text_edit_singleline(&mut self.func_strs[i]);
|
||||
});
|
||||
|
||||
if !self.func_strs[i].is_empty() {
|
||||
if (self.func_strs[i].clone() != function.get_func_str())
|
||||
| self.last_error.iter().any(|ele| ele.0 == i)
|
||||
{
|
||||
let proc_func_str = add_asterisks(self.func_strs[i].clone());
|
||||
let func_test_output = test_func(proc_func_str.clone());
|
||||
// let proc_func_str = self.func_strs[i].clone();
|
||||
let func_test_output = test_func(&proc_func_str);
|
||||
if let Some(test_output_value) = func_test_output {
|
||||
self.last_error +=
|
||||
&format!("(Function #{}) {}\n", i, test_output_value);
|
||||
self.last_error.push((i, test_output_value));
|
||||
} else {
|
||||
function.update(
|
||||
proc_func_str,
|
||||
@ -321,6 +324,12 @@ impl MathApp {
|
||||
Some(self.settings.integral_num),
|
||||
Some(self.settings.sum),
|
||||
);
|
||||
self.last_error = self
|
||||
.last_error
|
||||
.iter()
|
||||
.filter(|(i_ele, _)| i_ele != &i)
|
||||
.map(|(a, b)| (*a, b.clone()))
|
||||
.collect();
|
||||
}
|
||||
} else {
|
||||
function.empty_func_str();
|
||||
@ -470,7 +479,9 @@ impl epi::App for MathApp {
|
||||
CentralPanel::default().show(ctx, |ui| {
|
||||
if !self.last_error.is_empty() {
|
||||
ui.centered_and_justified(|ui| {
|
||||
ui.heading(self.last_error.clone());
|
||||
self.last_error.iter().for_each(|ele| {
|
||||
ui.heading(&(&format!("(Function #{}) {}\n", ele.0, ele.1)).to_string());
|
||||
})
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
#![allow(clippy::too_many_arguments)] // Clippy, shut
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use crate::misc::{debug_log, BackingFunction, BoxFunction, SteppedVector, EPSILON};
|
||||
use crate::misc::{debug_log, SteppedVector};
|
||||
|
||||
use crate::parsing::BackingFunction;
|
||||
|
||||
use eframe::egui::{
|
||||
plot::{BarChart, Line, Value, Values},
|
||||
widgets::plot::Bar,
|
||||
};
|
||||
use meval::Expr;
|
||||
use std::fmt::{self, Debug};
|
||||
|
||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
||||
@ -41,14 +42,11 @@ pub struct FunctionEntry {
|
||||
sum: RiemannSum,
|
||||
}
|
||||
|
||||
// x^2 function, set here so we don't have to regenerate it every time a new function is made
|
||||
fn default_function(x: f64) -> f64 { x.powi(2) }
|
||||
|
||||
impl FunctionEntry {
|
||||
// Creates Empty Function instance
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
function: BackingFunction::new(Box::new(default_function)),
|
||||
function: BackingFunction::new("x^2").unwrap(),
|
||||
func_str: String::new(),
|
||||
min_x: -1.0,
|
||||
max_x: 1.0,
|
||||
@ -76,10 +74,7 @@ impl FunctionEntry {
|
||||
// If the function string changes, just wipe and restart from scratch
|
||||
if func_str != self.func_str {
|
||||
self.func_str = func_str.clone();
|
||||
self.function = BackingFunction::new(Box::new({
|
||||
let expr: Expr = func_str.parse().unwrap();
|
||||
expr.bind("x").unwrap()
|
||||
}));
|
||||
self.function = BackingFunction::new(&func_str).unwrap();
|
||||
self.back_cache = None;
|
||||
self.front_cache = None;
|
||||
self.derivative_cache = None;
|
||||
@ -245,7 +240,7 @@ impl FunctionEntry {
|
||||
let step = (self.integral_min_x - self.integral_max_x).abs() / (self.integral_num as f64);
|
||||
|
||||
let mut area: f64 = 0.0;
|
||||
let data2: Vec<(f64, f64, f64)> = (1..=self.integral_num)
|
||||
let data2: Vec<(f64, f64, f64)> = (0..self.integral_num)
|
||||
.map(|e| {
|
||||
let x: f64 = ((e as f64) * step) + self.integral_min_x;
|
||||
let step_offset = step * x.signum(); // store the offset here so it doesn't have to be calculated multiple times
|
||||
@ -293,16 +288,19 @@ impl FunctionEntry {
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn integral_num(mut self, integral_num: usize) -> Self {
|
||||
self.integral_num = integral_num;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn pixel_width(mut self, pixel_width: usize) -> Self {
|
||||
self.pixel_width = pixel_width;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn integral_bounds(mut self, min_x: f64, max_x: f64) -> Self {
|
||||
if min_x >= max_x {
|
||||
panic!("integral_bounds: min_x is larger than max_x");
|
||||
@ -353,9 +351,10 @@ fn left_function_test() {
|
||||
(0.8, 1.5999999999349868),
|
||||
];
|
||||
|
||||
let area_target = 0.8720000000000001;
|
||||
let area_target = 0.9600000000000001;
|
||||
|
||||
let vec_bars_target = vec![
|
||||
1.44,
|
||||
1.0,
|
||||
0.6400000000000001,
|
||||
0.3599999999999998,
|
||||
@ -365,20 +364,19 @@ fn left_function_test() {
|
||||
0.16000000000000011,
|
||||
0.3600000000000001,
|
||||
0.6400000000000001,
|
||||
1.0,
|
||||
];
|
||||
|
||||
let vec_integral_target = vec![
|
||||
(-0.9, 0.2),
|
||||
(-0.7, 0.32800000000000007),
|
||||
(-0.4999999999999999, 0.4),
|
||||
(-0.29999999999999993, 0.432),
|
||||
(0.1, 0.432),
|
||||
(0.30000000000000016, 0.44),
|
||||
(0.5000000000000001, 0.47200000000000003),
|
||||
(0.7000000000000001, 0.544),
|
||||
(0.9, 0.672),
|
||||
(1.1, 0.8720000000000001),
|
||||
(-1.1, 0.288),
|
||||
(-0.9, 0.488),
|
||||
(-0.7, 0.616),
|
||||
(-0.4999999999999999, 0.688),
|
||||
(-0.29999999999999993, 0.72),
|
||||
(0.1, 0.72),
|
||||
(0.30000000000000016, 0.728),
|
||||
(0.5000000000000001, 0.76),
|
||||
(0.7000000000000001, 0.8320000000000001),
|
||||
(0.9, 0.9600000000000001),
|
||||
];
|
||||
|
||||
{
|
||||
@ -476,9 +474,10 @@ fn middle_function_test() {
|
||||
(0.8, 1.5999999999349868),
|
||||
];
|
||||
|
||||
let area_target = 0.9200000000000002;
|
||||
let area_target = 0.92;
|
||||
|
||||
let vec_bars_target = vec![
|
||||
1.22,
|
||||
0.8200000000000001,
|
||||
0.5,
|
||||
0.2599999999999999,
|
||||
@ -488,20 +487,19 @@ fn middle_function_test() {
|
||||
0.2600000000000001,
|
||||
0.5000000000000001,
|
||||
0.8200000000000001,
|
||||
1.22,
|
||||
];
|
||||
|
||||
let vec_integral_target = vec![
|
||||
(-0.9, 0.16400000000000003),
|
||||
(-0.7, 0.264),
|
||||
(-0.4999999999999999, 0.316),
|
||||
(-0.29999999999999993, 0.336),
|
||||
(0.1, 0.34),
|
||||
(0.30000000000000016, 0.36000000000000004),
|
||||
(0.5000000000000001, 0.4120000000000001),
|
||||
(0.7000000000000001, 0.5120000000000001),
|
||||
(0.9, 0.6760000000000002),
|
||||
(1.1, 0.9200000000000002),
|
||||
(-1.1, 0.244),
|
||||
(-0.9, 0.40800000000000003),
|
||||
(-0.7, 0.508),
|
||||
(-0.4999999999999999, 0.5599999999999999),
|
||||
(-0.29999999999999993, 0.58),
|
||||
(0.1, 0.584),
|
||||
(0.30000000000000016, 0.604),
|
||||
(0.5000000000000001, 0.656),
|
||||
(0.7000000000000001, 0.756),
|
||||
(0.9, 0.92),
|
||||
];
|
||||
|
||||
{
|
||||
@ -599,9 +597,10 @@ fn right_function_test() {
|
||||
(0.8, 1.5999999999349868),
|
||||
];
|
||||
|
||||
let area_target = 0.9680000000000002;
|
||||
let area_target = 0.8800000000000001;
|
||||
|
||||
let vec_bars_target = vec![
|
||||
1.0,
|
||||
0.6400000000000001,
|
||||
0.36,
|
||||
0.15999999999999992,
|
||||
@ -611,20 +610,19 @@ fn right_function_test() {
|
||||
0.3600000000000001,
|
||||
0.6400000000000001,
|
||||
1.0,
|
||||
1.44,
|
||||
];
|
||||
|
||||
let vec_integral_target = vec![
|
||||
(-0.9, 0.12800000000000003),
|
||||
(-0.7, 0.2),
|
||||
(-0.4999999999999999, 0.23199999999999998),
|
||||
(-0.29999999999999993, 0.24),
|
||||
(0.1, 0.248),
|
||||
(0.30000000000000016, 0.28),
|
||||
(0.5000000000000001, 0.35200000000000004),
|
||||
(0.7000000000000001, 0.4800000000000001),
|
||||
(0.9, 0.6800000000000002),
|
||||
(1.1, 0.9680000000000002),
|
||||
(-1.1, 0.2),
|
||||
(-0.9, 0.32800000000000007),
|
||||
(-0.7, 0.4000000000000001),
|
||||
(-0.4999999999999999, 0.43200000000000005),
|
||||
(-0.29999999999999993, 0.44000000000000006),
|
||||
(0.1, 0.44800000000000006),
|
||||
(0.30000000000000016, 0.4800000000000001),
|
||||
(0.5000000000000001, 0.5520000000000002),
|
||||
(0.7000000000000001, 0.6800000000000002),
|
||||
(0.9, 0.8800000000000001),
|
||||
];
|
||||
|
||||
{
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
mod egui_app;
|
||||
mod function;
|
||||
mod misc;
|
||||
mod parsing;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_arch = "wasm32")] {
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
mod egui_app;
|
||||
mod function;
|
||||
mod misc;
|
||||
mod parsing;
|
||||
|
||||
// For running the program natively! (Because why not?)
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
|
||||
79
src/misc.rs
79
src/misc.rs
@ -1,67 +1,32 @@
|
||||
use meval::Expr;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_bindgen::prelude::*;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_arch = "wasm32")] {
|
||||
use wasm_bindgen::prelude::*;
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
// Use `js_namespace` here to bind `console.log(..)` instead of just
|
||||
// `log(..)`
|
||||
#[wasm_bindgen(js_namespace = console)]
|
||||
fn log(s: &str);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn log_helper(s: &str) {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
log(s);
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
println!("{}", s);
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[allow(dead_code)]
|
||||
pub fn debug_log(s: &str) {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
log(s);
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
println!("{}", s);
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
#[allow(dead_code)]
|
||||
pub fn debug_log(_s: &str) {}
|
||||
|
||||
pub const EPSILON: f64 = 5.0e-7;
|
||||
|
||||
pub type BoxFunction = Box<dyn Fn(f64) -> f64>;
|
||||
|
||||
pub struct BackingFunction {
|
||||
function: BoxFunction,
|
||||
}
|
||||
|
||||
impl BackingFunction {
|
||||
pub fn new(function: BoxFunction) -> BackingFunction { BackingFunction { function } }
|
||||
|
||||
pub fn get(&self, x: f64) -> f64 { (self.function)(x) }
|
||||
|
||||
pub fn derivative(&self, x: f64, n: u64) -> f64 {
|
||||
if n == 0 {
|
||||
return self.get(x);
|
||||
}
|
||||
|
||||
let (x1, x2) = (x - EPSILON, x + EPSILON);
|
||||
let (y1, y2) = (self.derivative(x1, n - 1), (self.derivative(x2, n - 1)));
|
||||
(y2 - y1) / (EPSILON * 2.0)
|
||||
#[allow(dead_code)]
|
||||
pub fn log_helper(s: &str) {
|
||||
log(s);
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BoxFunction> for BackingFunction {
|
||||
fn from(boxfunction: BoxFunction) -> BackingFunction {
|
||||
BackingFunction {
|
||||
function: boxfunction,
|
||||
#[allow(dead_code)]
|
||||
pub fn debug_log(s: &str) {
|
||||
log(s);
|
||||
}
|
||||
} else {
|
||||
#[allow(dead_code)]
|
||||
pub fn log_helper(s: &str) {
|
||||
println!("{}", s);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn debug_log(s: &str) {
|
||||
println!("{}", s);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -137,6 +102,7 @@ pub fn add_asterisks(function_in: String) -> String {
|
||||
output_string.replace('π', "pi") // π -> pi
|
||||
}
|
||||
|
||||
/*
|
||||
// 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: String) -> Option<String> {
|
||||
// Factorials do not work, and it would be really difficult to make them work
|
||||
@ -166,6 +132,7 @@ pub fn test_func(function_string: String) -> Option<String> {
|
||||
|
||||
None
|
||||
}
|
||||
*/
|
||||
|
||||
pub struct SteppedVector {
|
||||
data: Vec<f64>,
|
||||
|
||||
61
src/parsing.rs
Normal file
61
src/parsing.rs
Normal file
@ -0,0 +1,61 @@
|
||||
use evalexpr::*;
|
||||
|
||||
pub const EPSILON: f64 = 5.0e-7;
|
||||
pub const DOUBLE_EPSILON: f64 = 10.0e-7;
|
||||
|
||||
pub fn test_func(func_str: &str) -> Option<String> {
|
||||
let precompiled = build_operator_tree(&("x = 10;".to_owned() + func_str));
|
||||
|
||||
match precompiled {
|
||||
Ok(_) => {
|
||||
let precompiled2 = precompiled.unwrap().eval();
|
||||
match precompiled2 {
|
||||
Ok(_) => None,
|
||||
Err(e) => Some(e.to_string()),
|
||||
}
|
||||
}
|
||||
Err(e) => Some(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BackingFunction {
|
||||
node: Node,
|
||||
}
|
||||
|
||||
impl BackingFunction {
|
||||
pub fn new(func_str: &str) -> Result<Self, String> {
|
||||
let precompiled = build_operator_tree(func_str);
|
||||
|
||||
match precompiled {
|
||||
Ok(_) => Ok(Self {
|
||||
node: precompiled.unwrap(),
|
||||
}),
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, _x: f64) -> f64 {
|
||||
let context: HashMapContext = context_map! {
|
||||
"x" => 0,
|
||||
"e" => std::f64::consts::E,
|
||||
"pi" => std::f64::consts::PI,
|
||||
}
|
||||
.unwrap();
|
||||
|
||||
self.node
|
||||
.eval_with_context(&context)
|
||||
.unwrap()
|
||||
.as_number()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn derivative(&self, x: f64, n: u64) -> f64 {
|
||||
if n == 0 {
|
||||
return self.get(x);
|
||||
}
|
||||
|
||||
let (x1, x2) = (x - EPSILON, x + EPSILON);
|
||||
let (y1, y2) = (self.derivative(x1, n - 1), (self.derivative(x2, n - 1)));
|
||||
(y2 - y1) / DOUBLE_EPSILON
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user