This commit is contained in:
Simon Gardling 2022-03-04 16:29:45 -05:00
parent e1de404665
commit bd86998755
7 changed files with 159 additions and 120 deletions

View File

@ -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"

View File

@ -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;
}

View File

@ -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),
];
{

View File

@ -4,6 +4,7 @@
mod egui_app;
mod function;
mod misc;
mod parsing;
cfg_if::cfg_if! {
if #[cfg(target_arch = "wasm32")] {

View File

@ -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"))]

View File

@ -1,67 +1,32 @@
use meval::Expr;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
#[cfg(target_arch = "wasm32")]
#[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);
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);
}
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
View 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
}
}