use tabs, not spaces
This commit is contained in:
parent
f851b365ab
commit
dd6d8c80b8
14
TODO.md
14
TODO.md
@ -1,14 +1,14 @@
|
||||
## TODO:
|
||||
1. Multiple functions in one graph.
|
||||
- Backend support
|
||||
- Integrals between functions (too hard to implement, maybe will shelve)
|
||||
- UI
|
||||
- Different colors (kinda)
|
||||
- Backend support
|
||||
- Integrals between functions (too hard to implement, maybe will shelve)
|
||||
- UI
|
||||
- Different colors (kinda)
|
||||
2. Rerwite of function parsing code
|
||||
- Non `y=` functions.
|
||||
- Non `y=` functions.
|
||||
3. Smart display of graph
|
||||
- Display of intersections between functions
|
||||
- Caching of roots and extrema
|
||||
- Display of intersections between functions
|
||||
- Caching of roots and extrema
|
||||
4. Fix integral line
|
||||
5. re-add euler's number (well it works if you use capital e like `E^x`)
|
||||
6. allow constants in min/max integral input (like pi or euler's number)
|
||||
|
||||
20
build.sh
20
build.sh
@ -8,21 +8,21 @@ cargo test
|
||||
|
||||
#apply optimizations via wasm-opt
|
||||
wasm_opt() {
|
||||
wasm-opt -Oz -o pkg/ytbn_graphing_software_bg_2.wasm pkg/ytbn_graphing_software_bg.wasm
|
||||
mv pkg/ytbn_graphing_software_bg_2.wasm pkg/ytbn_graphing_software_bg.wasm
|
||||
wasm-opt -Oz -o pkg/ytbn_graphing_software_bg_2.wasm pkg/ytbn_graphing_software_bg.wasm
|
||||
mv pkg/ytbn_graphing_software_bg_2.wasm pkg/ytbn_graphing_software_bg.wasm
|
||||
}
|
||||
|
||||
if test "$1" == "" || test "$1" == "release"; then
|
||||
wasm-pack build --target web --release --no-typescript
|
||||
echo "Binary size (pre-wasm_opt): $(du -sb pkg/ytbn_graphing_software_bg.wasm)"
|
||||
wasm_opt #apply wasm optimizations
|
||||
echo "Binary size (pre-strip): $(du -sb pkg/ytbn_graphing_software_bg.wasm)"
|
||||
llvm-strip --strip-all pkg/ytbn_graphing_software_bg.wasm
|
||||
wasm-pack build --target web --release --no-typescript
|
||||
echo "Binary size (pre-wasm_opt): $(du -sb pkg/ytbn_graphing_software_bg.wasm)"
|
||||
wasm_opt #apply wasm optimizations
|
||||
echo "Binary size (pre-strip): $(du -sb pkg/ytbn_graphing_software_bg.wasm)"
|
||||
llvm-strip --strip-all pkg/ytbn_graphing_software_bg.wasm
|
||||
elif test "$1" == "debug"; then
|
||||
wasm-pack build --target web --debug --no-typescript
|
||||
wasm-pack build --target web --debug --no-typescript
|
||||
else
|
||||
echo "ERROR: build.sh, argument invalid"
|
||||
exit 1
|
||||
echo "ERROR: build.sh, argument invalid"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir tmp
|
||||
|
||||
1054
src/egui_app.rs
1054
src/egui_app.rs
File diff suppressed because it is too large
Load Diff
912
src/function.rs
912
src/function.rs
File diff suppressed because it is too large
Load Diff
@ -1,100 +1,100 @@
|
||||
use eframe::{
|
||||
egui::{
|
||||
plot::{BarChart, Line, PlotUi, Points, Value, Values},
|
||||
widgets::plot::Bar,
|
||||
},
|
||||
epaint::Color32,
|
||||
egui::{
|
||||
plot::{BarChart, Line, PlotUi, Points, Value, Values},
|
||||
widgets::plot::Bar,
|
||||
},
|
||||
epaint::Color32,
|
||||
};
|
||||
|
||||
use crate::misc::digits_precision;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FunctionOutput {
|
||||
pub(crate) back: Option<Vec<Value>>,
|
||||
pub(crate) integral: Option<(Vec<Bar>, f64)>,
|
||||
pub(crate) derivative: Option<Vec<Value>>,
|
||||
pub(crate) extrema: Option<Vec<Value>>,
|
||||
pub(crate) roots: Option<Vec<Value>>,
|
||||
pub(crate) back: Option<Vec<Value>>,
|
||||
pub(crate) integral: Option<(Vec<Bar>, f64)>,
|
||||
pub(crate) derivative: Option<Vec<Value>>,
|
||||
pub(crate) extrema: Option<Vec<Value>>,
|
||||
pub(crate) roots: Option<Vec<Value>>,
|
||||
}
|
||||
|
||||
impl FunctionOutput {
|
||||
pub fn new_empty() -> Self {
|
||||
Self {
|
||||
back: None,
|
||||
integral: None,
|
||||
derivative: None,
|
||||
extrema: None,
|
||||
roots: None,
|
||||
}
|
||||
}
|
||||
pub fn new_empty() -> Self {
|
||||
Self {
|
||||
back: None,
|
||||
integral: None,
|
||||
derivative: None,
|
||||
extrema: None,
|
||||
roots: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invalidate_whole(&mut self) {
|
||||
self.back = None;
|
||||
self.integral = None;
|
||||
self.derivative = None;
|
||||
self.extrema = None;
|
||||
self.roots = None;
|
||||
}
|
||||
pub fn invalidate_whole(&mut self) {
|
||||
self.back = None;
|
||||
self.integral = None;
|
||||
self.derivative = None;
|
||||
self.extrema = None;
|
||||
self.roots = None;
|
||||
}
|
||||
|
||||
pub fn invalidate_back(&mut self) { self.back = None; }
|
||||
pub fn invalidate_back(&mut self) { self.back = None; }
|
||||
|
||||
pub fn invalidate_integral(&mut self) { self.integral = None; }
|
||||
pub fn invalidate_integral(&mut self) { self.integral = None; }
|
||||
|
||||
pub fn invalidate_derivative(&mut self) { self.derivative = None; }
|
||||
pub fn invalidate_derivative(&mut self) { self.derivative = None; }
|
||||
|
||||
pub fn invalidate_points(&mut self) {
|
||||
self.extrema = None;
|
||||
self.roots = None;
|
||||
}
|
||||
pub fn invalidate_points(&mut self) {
|
||||
self.extrema = None;
|
||||
self.roots = None;
|
||||
}
|
||||
|
||||
pub fn display(
|
||||
&self, plot_ui: &mut PlotUi, func_str: &str, derivative_str: &str, step: f64,
|
||||
derivative_enabled: bool,
|
||||
) -> f64 {
|
||||
plot_ui.line(
|
||||
Line::new(Values::from_values(self.back.clone().unwrap()))
|
||||
.color(Color32::RED)
|
||||
.name(func_str),
|
||||
);
|
||||
pub fn display(
|
||||
&self, plot_ui: &mut PlotUi, func_str: &str, derivative_str: &str, step: f64,
|
||||
derivative_enabled: bool,
|
||||
) -> f64 {
|
||||
plot_ui.line(
|
||||
Line::new(Values::from_values(self.back.clone().unwrap()))
|
||||
.color(Color32::RED)
|
||||
.name(func_str),
|
||||
);
|
||||
|
||||
if derivative_enabled {
|
||||
if let Some(derivative_data) = self.derivative.clone() {
|
||||
plot_ui.line(
|
||||
Line::new(Values::from_values(derivative_data))
|
||||
.color(Color32::GREEN)
|
||||
.name(derivative_str),
|
||||
);
|
||||
}
|
||||
}
|
||||
if derivative_enabled {
|
||||
if let Some(derivative_data) = self.derivative.clone() {
|
||||
plot_ui.line(
|
||||
Line::new(Values::from_values(derivative_data))
|
||||
.color(Color32::GREEN)
|
||||
.name(derivative_str),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(extrema_data) = self.extrema.clone() {
|
||||
plot_ui.points(
|
||||
Points::new(Values::from_values(extrema_data))
|
||||
.color(Color32::YELLOW)
|
||||
.name("Extrema")
|
||||
.radius(5.0),
|
||||
);
|
||||
}
|
||||
if let Some(extrema_data) = self.extrema.clone() {
|
||||
plot_ui.points(
|
||||
Points::new(Values::from_values(extrema_data))
|
||||
.color(Color32::YELLOW)
|
||||
.name("Extrema")
|
||||
.radius(5.0),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(roots_data) = self.roots.clone() {
|
||||
plot_ui.points(
|
||||
Points::new(Values::from_values(roots_data))
|
||||
.color(Color32::LIGHT_BLUE)
|
||||
.name("Root")
|
||||
.radius(5.0),
|
||||
);
|
||||
}
|
||||
if let Some(roots_data) = self.roots.clone() {
|
||||
plot_ui.points(
|
||||
Points::new(Values::from_values(roots_data))
|
||||
.color(Color32::LIGHT_BLUE)
|
||||
.name("Root")
|
||||
.radius(5.0),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(integral_data) = self.integral.clone() {
|
||||
plot_ui.bar_chart(
|
||||
BarChart::new(integral_data.0)
|
||||
.color(Color32::BLUE)
|
||||
.width(step),
|
||||
);
|
||||
if let Some(integral_data) = self.integral.clone() {
|
||||
plot_ui.bar_chart(
|
||||
BarChart::new(integral_data.0)
|
||||
.color(Color32::BLUE)
|
||||
.width(step),
|
||||
);
|
||||
|
||||
digits_precision(integral_data.1, 8)
|
||||
} else {
|
||||
f64::NAN
|
||||
}
|
||||
}
|
||||
digits_precision(integral_data.1, 8)
|
||||
} else {
|
||||
f64::NAN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
36
src/lib.rs
36
src/lib.rs
@ -8,27 +8,27 @@ mod misc;
|
||||
mod parsing;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_arch = "wasm32")] {
|
||||
use misc::log_helper;
|
||||
use wasm_bindgen::prelude::*;
|
||||
if #[cfg(target_arch = "wasm32")] {
|
||||
use misc::log_helper;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
||||
#[global_allocator]
|
||||
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn start() -> Result<(), wasm_bindgen::JsValue> {
|
||||
log_helper("Initializing...");
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn start() -> Result<(), wasm_bindgen::JsValue> {
|
||||
log_helper("Initializing...");
|
||||
|
||||
// Used in order to hook into `panic!()` to log in the browser's console
|
||||
log_helper("Initializing panic hooks...");
|
||||
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
|
||||
log_helper("Initialized panic hooks!");
|
||||
// Used in order to hook into `panic!()` to log in the browser's console
|
||||
log_helper("Initializing panic hooks...");
|
||||
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
|
||||
log_helper("Initialized panic hooks!");
|
||||
|
||||
log_helper("Finished initializing!");
|
||||
log_helper("Finished initializing!");
|
||||
|
||||
log_helper("Starting App...");
|
||||
let app = egui_app::MathApp::default();
|
||||
eframe::start_web("canvas", Box::new(app))
|
||||
}
|
||||
}
|
||||
log_helper("Starting App...");
|
||||
let app = egui_app::MathApp::default();
|
||||
eframe::start_web("canvas", Box::new(app))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
14
src/main.rs
14
src/main.rs
@ -9,11 +9,11 @@ mod parsing;
|
||||
// For running the program natively! (Because why not?)
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn main() {
|
||||
let app = egui_app::MathApp::default();
|
||||
let options = eframe::NativeOptions {
|
||||
transparent: true,
|
||||
drag_and_drop_support: true,
|
||||
..Default::default()
|
||||
};
|
||||
eframe::run_native(Box::new(app), options);
|
||||
let app = egui_app::MathApp::default();
|
||||
let options = eframe::NativeOptions {
|
||||
transparent: true,
|
||||
drag_and_drop_support: true,
|
||||
..Default::default()
|
||||
};
|
||||
eframe::run_native(Box::new(app), options);
|
||||
}
|
||||
|
||||
144
src/misc.rs
144
src/misc.rs
@ -1,92 +1,92 @@
|
||||
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);
|
||||
}
|
||||
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) {
|
||||
log(s);
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn log_helper(s: &str) {
|
||||
log(s);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[allow(unused_variables)]
|
||||
pub fn debug_log(s: &str) {
|
||||
#[cfg(debug_assertions)]
|
||||
log(s);
|
||||
}
|
||||
} else {
|
||||
#[allow(dead_code)]
|
||||
pub fn log_helper(s: &str) {
|
||||
println!("{}", s);
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
#[allow(unused_variables)]
|
||||
pub fn debug_log(s: &str) {
|
||||
#[cfg(debug_assertions)]
|
||||
log(s);
|
||||
}
|
||||
} else {
|
||||
#[allow(dead_code)]
|
||||
pub fn log_helper(s: &str) {
|
||||
println!("{}", s);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[allow(unused_variables)]
|
||||
pub fn debug_log(s: &str) {
|
||||
#[cfg(debug_assertions)]
|
||||
println!("{}", s);
|
||||
}
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
#[allow(unused_variables)]
|
||||
pub fn debug_log(s: &str) {
|
||||
#[cfg(debug_assertions)]
|
||||
println!("{}", s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SteppedVector {
|
||||
data: Vec<f64>,
|
||||
min: f64,
|
||||
max: f64,
|
||||
step: f64,
|
||||
data: Vec<f64>,
|
||||
min: f64,
|
||||
max: f64,
|
||||
step: f64,
|
||||
}
|
||||
|
||||
impl SteppedVector {
|
||||
pub fn get_index(&self, x: f64) -> Option<usize> {
|
||||
if (x > self.max) | (self.min > x) {
|
||||
return None;
|
||||
}
|
||||
pub fn get_index(&self, x: f64) -> Option<usize> {
|
||||
if (x > self.max) | (self.min > x) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Should work....
|
||||
let possible_i = ((x + self.min) / self.step) as usize;
|
||||
if self.data[possible_i] == x {
|
||||
Some(possible_i)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
// Should work....
|
||||
let possible_i = ((x + self.min) / self.step) as usize;
|
||||
if self.data[possible_i] == x {
|
||||
Some(possible_i)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
// Not really needed as the above code should handle everything
|
||||
/*
|
||||
for (i, ele) in self.data.iter().enumerate() {
|
||||
if ele > &x {
|
||||
return None;
|
||||
} else if &x == ele {
|
||||
return Some(i);
|
||||
}
|
||||
}
|
||||
None
|
||||
*/
|
||||
}
|
||||
// Not really needed as the above code should handle everything
|
||||
/*
|
||||
for (i, ele) in self.data.iter().enumerate() {
|
||||
if ele > &x {
|
||||
return None;
|
||||
} else if &x == ele {
|
||||
return Some(i);
|
||||
}
|
||||
}
|
||||
None
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<f64>> for SteppedVector {
|
||||
// Note: input `data` is assumed to be sorted from min to max
|
||||
fn from(data: Vec<f64>) -> SteppedVector {
|
||||
let max = data[0];
|
||||
let min = data[data.len() - 1];
|
||||
let step = (max - min).abs() / ((data.len() - 1) as f64);
|
||||
SteppedVector {
|
||||
data,
|
||||
min,
|
||||
max,
|
||||
step,
|
||||
}
|
||||
}
|
||||
// Note: input `data` is assumed to be sorted from min to max
|
||||
fn from(data: Vec<f64>) -> SteppedVector {
|
||||
let max = data[0];
|
||||
let min = data[data.len() - 1];
|
||||
let step = (max - min).abs() / ((data.len() - 1) as f64);
|
||||
SteppedVector {
|
||||
data,
|
||||
min,
|
||||
max,
|
||||
step,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rounds f64 to specific number of digits
|
||||
pub fn digits_precision(x: f64, digits: usize) -> f64 {
|
||||
let large_number: f64 = 10.0_f64.powf(digits as f64);
|
||||
(x * large_number).round() / large_number
|
||||
let large_number: f64 = 10.0_f64.powf(digits as f64);
|
||||
(x * large_number).round() / large_number
|
||||
}
|
||||
|
||||
338
src/parsing.rs
338
src/parsing.rs
@ -1,54 +1,54 @@
|
||||
use exmex::prelude::*;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref EMPTY_FUNCTION: FlatEx<f64> = exmex::parse::<f64>("0/0").unwrap();
|
||||
static ref EMPTY_FUNCTION: FlatEx<f64> = exmex::parse::<f64>("0/0").unwrap();
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BackingFunction {
|
||||
function: FlatEx<f64>,
|
||||
derivative_1: FlatEx<f64>,
|
||||
derivative_2: FlatEx<f64>,
|
||||
function: FlatEx<f64>,
|
||||
derivative_1: FlatEx<f64>,
|
||||
derivative_2: FlatEx<f64>,
|
||||
}
|
||||
|
||||
impl BackingFunction {
|
||||
pub fn new(func_str: &str) -> Self {
|
||||
let function = exmex::parse::<f64>(func_str).unwrap();
|
||||
let derivative_1 = function
|
||||
.partial(0)
|
||||
.unwrap_or_else(|_| EMPTY_FUNCTION.clone());
|
||||
let derivative_2 = function
|
||||
.partial_iter([0, 0].iter())
|
||||
.unwrap_or_else(|_| EMPTY_FUNCTION.clone());
|
||||
pub fn new(func_str: &str) -> Self {
|
||||
let function = exmex::parse::<f64>(func_str).unwrap();
|
||||
let derivative_1 = function
|
||||
.partial(0)
|
||||
.unwrap_or_else(|_| EMPTY_FUNCTION.clone());
|
||||
let derivative_2 = function
|
||||
.partial_iter([0, 0].iter())
|
||||
.unwrap_or_else(|_| EMPTY_FUNCTION.clone());
|
||||
|
||||
Self {
|
||||
function,
|
||||
derivative_1,
|
||||
derivative_2,
|
||||
}
|
||||
}
|
||||
Self {
|
||||
function,
|
||||
derivative_1,
|
||||
derivative_2,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_derivative_str(&self) -> String {
|
||||
String::from(self.derivative_1.unparse()).replace("{x}", "x")
|
||||
}
|
||||
pub fn get_derivative_str(&self) -> String {
|
||||
String::from(self.derivative_1.unparse()).replace("{x}", "x")
|
||||
}
|
||||
|
||||
pub fn get(&self, x: f64) -> f64 { self.function.eval(&[x]).unwrap_or(f64::NAN) }
|
||||
pub fn get(&self, x: f64) -> f64 { self.function.eval(&[x]).unwrap_or(f64::NAN) }
|
||||
|
||||
pub fn get_derivative_1(&self, x: f64) -> f64 {
|
||||
self.derivative_1.eval(&[x]).unwrap_or(f64::NAN)
|
||||
}
|
||||
pub fn get_derivative_1(&self, x: f64) -> f64 {
|
||||
self.derivative_1.eval(&[x]).unwrap_or(f64::NAN)
|
||||
}
|
||||
|
||||
pub fn get_derivative_2(&self, x: f64) -> f64 {
|
||||
self.derivative_2.eval(&[x]).unwrap_or(f64::NAN)
|
||||
}
|
||||
pub fn get_derivative_2(&self, x: f64) -> f64 {
|
||||
self.derivative_2.eval(&[x]).unwrap_or(f64::NAN)
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref VALID_VARIABLES: Vec<char> = "xXeEπ".chars().collect();
|
||||
static ref LETTERS: Vec<char> = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
.chars()
|
||||
.collect();
|
||||
static ref NUMBERS: Vec<char> = "0123456789".chars().collect();
|
||||
static ref VALID_VARIABLES: Vec<char> = "xXeEπ".chars().collect();
|
||||
static ref LETTERS: Vec<char> = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
.chars()
|
||||
.collect();
|
||||
static ref NUMBERS: Vec<char> = "0123456789".chars().collect();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -57,173 +57,173 @@ One limitation though, variables with multiple characters like `pi` cannot be mu
|
||||
In the future I may want to completely rewrite this or implement this natively in exmex.
|
||||
*/
|
||||
pub fn process_func_str(function_in: String) -> String {
|
||||
let function = function_in.replace("log10(", "log(").replace("pi", "π"); // pi -> π and log10 -> log
|
||||
let function_chars: Vec<char> = function.chars().collect();
|
||||
let mut output_string: String = String::new();
|
||||
let mut prev_chars: Vec<char> = Vec::new();
|
||||
for c in function_chars {
|
||||
let mut add_asterisk: bool = false;
|
||||
let prev_chars_len = prev_chars.len();
|
||||
let function = function_in.replace("log10(", "log(").replace("pi", "π"); // pi -> π and log10 -> log
|
||||
let function_chars: Vec<char> = function.chars().collect();
|
||||
let mut output_string: String = String::new();
|
||||
let mut prev_chars: Vec<char> = Vec::new();
|
||||
for c in function_chars {
|
||||
let mut add_asterisk: bool = false;
|
||||
let prev_chars_len = prev_chars.len();
|
||||
|
||||
let prev_prev_prev_char = if prev_chars_len >= 3 {
|
||||
*prev_chars.get(prev_chars_len - 3).unwrap()
|
||||
} else {
|
||||
' '
|
||||
};
|
||||
let prev_prev_prev_char = if prev_chars_len >= 3 {
|
||||
*prev_chars.get(prev_chars_len - 3).unwrap()
|
||||
} else {
|
||||
' '
|
||||
};
|
||||
|
||||
let prev_prev_char = if prev_chars_len >= 2 {
|
||||
*prev_chars.get(prev_chars_len - 2).unwrap()
|
||||
} else {
|
||||
' '
|
||||
};
|
||||
let prev_prev_char = if prev_chars_len >= 2 {
|
||||
*prev_chars.get(prev_chars_len - 2).unwrap()
|
||||
} else {
|
||||
' '
|
||||
};
|
||||
|
||||
let prev_char = if prev_chars_len >= 1 {
|
||||
*prev_chars.get(prev_chars_len - 1).unwrap()
|
||||
} else {
|
||||
' '
|
||||
};
|
||||
let prev_char = if prev_chars_len >= 1 {
|
||||
*prev_chars.get(prev_chars_len - 1).unwrap()
|
||||
} else {
|
||||
' '
|
||||
};
|
||||
|
||||
if (prev_prev_prev_char == 'l')
|
||||
&& (prev_prev_char == 'o')
|
||||
&& (prev_char == 'g')
|
||||
&& (NUMBERS.contains(&c))
|
||||
{
|
||||
prev_chars.push(c);
|
||||
output_string += &c.to_string();
|
||||
continue;
|
||||
}
|
||||
if (prev_prev_prev_char == 'l')
|
||||
&& (prev_prev_char == 'o')
|
||||
&& (prev_char == 'g')
|
||||
&& (NUMBERS.contains(&c))
|
||||
{
|
||||
prev_chars.push(c);
|
||||
output_string += &c.to_string();
|
||||
continue;
|
||||
}
|
||||
|
||||
let c_letters_var = LETTERS.contains(&c) | VALID_VARIABLES.contains(&c);
|
||||
let prev_letters_var = VALID_VARIABLES.contains(&prev_char) | LETTERS.contains(&prev_char);
|
||||
let c_letters_var = LETTERS.contains(&c) | VALID_VARIABLES.contains(&c);
|
||||
let prev_letters_var = VALID_VARIABLES.contains(&prev_char) | LETTERS.contains(&prev_char);
|
||||
|
||||
if prev_char == ')' {
|
||||
if (c == '(') | NUMBERS.contains(&c) | c_letters_var {
|
||||
add_asterisk = true;
|
||||
}
|
||||
} else if c == '(' {
|
||||
if (VALID_VARIABLES.contains(&prev_char)
|
||||
| (')' == prev_char)
|
||||
| NUMBERS.contains(&prev_char))
|
||||
&& !LETTERS.contains(&prev_prev_char)
|
||||
{
|
||||
add_asterisk = true;
|
||||
}
|
||||
} else if NUMBERS.contains(&prev_char) {
|
||||
if (c == '(') | c_letters_var {
|
||||
add_asterisk = true;
|
||||
}
|
||||
} else if LETTERS.contains(&c) {
|
||||
if NUMBERS.contains(&prev_char)
|
||||
| (VALID_VARIABLES.contains(&prev_char) && VALID_VARIABLES.contains(&c))
|
||||
{
|
||||
add_asterisk = true;
|
||||
}
|
||||
} else if (NUMBERS.contains(&c) | c_letters_var) && prev_letters_var {
|
||||
add_asterisk = true;
|
||||
}
|
||||
if prev_char == ')' {
|
||||
if (c == '(') | NUMBERS.contains(&c) | c_letters_var {
|
||||
add_asterisk = true;
|
||||
}
|
||||
} else if c == '(' {
|
||||
if (VALID_VARIABLES.contains(&prev_char)
|
||||
| (')' == prev_char)
|
||||
| NUMBERS.contains(&prev_char))
|
||||
&& !LETTERS.contains(&prev_prev_char)
|
||||
{
|
||||
add_asterisk = true;
|
||||
}
|
||||
} else if NUMBERS.contains(&prev_char) {
|
||||
if (c == '(') | c_letters_var {
|
||||
add_asterisk = true;
|
||||
}
|
||||
} else if LETTERS.contains(&c) {
|
||||
if NUMBERS.contains(&prev_char)
|
||||
| (VALID_VARIABLES.contains(&prev_char) && VALID_VARIABLES.contains(&c))
|
||||
{
|
||||
add_asterisk = true;
|
||||
}
|
||||
} else if (NUMBERS.contains(&c) | c_letters_var) && prev_letters_var {
|
||||
add_asterisk = true;
|
||||
}
|
||||
|
||||
if add_asterisk {
|
||||
output_string += "*";
|
||||
}
|
||||
if add_asterisk {
|
||||
output_string += "*";
|
||||
}
|
||||
|
||||
prev_chars.push(c);
|
||||
output_string += &c.to_string();
|
||||
}
|
||||
prev_chars.push(c);
|
||||
output_string += &c.to_string();
|
||||
}
|
||||
|
||||
output_string.replace("log(", "log10(")
|
||||
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.
|
||||
pub fn test_func(function_string: &str) -> Option<String> {
|
||||
let parse_result = exmex::parse::<f64>(function_string);
|
||||
let parse_result = exmex::parse::<f64>(function_string);
|
||||
|
||||
match parse_result {
|
||||
Err(e) => Some(e.to_string()),
|
||||
Ok(_) => {
|
||||
let var_names = parse_result.unwrap().var_names().to_vec();
|
||||
match parse_result {
|
||||
Err(e) => Some(e.to_string()),
|
||||
Ok(_) => {
|
||||
let var_names = parse_result.unwrap().var_names().to_vec();
|
||||
|
||||
if var_names != ["x"] {
|
||||
let var_names_not_x: Vec<String> = var_names
|
||||
.iter()
|
||||
.filter(|ele| *ele != &"x".to_owned())
|
||||
.cloned()
|
||||
.collect::<Vec<String>>();
|
||||
if var_names != ["x"] {
|
||||
let var_names_not_x: Vec<String> = var_names
|
||||
.iter()
|
||||
.filter(|ele| *ele != &"x".to_owned())
|
||||
.cloned()
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
return match var_names_not_x.len() {
|
||||
1 => {
|
||||
let var_name = &var_names_not_x[0];
|
||||
if var_name == "e" {
|
||||
Some(String::from(
|
||||
"If trying to use Euler's number, please use an uppercase E",
|
||||
))
|
||||
} else {
|
||||
Some(format!("Error: invalid variable: {}", var_name))
|
||||
}
|
||||
}
|
||||
_ => Some(format!("Error: invalid variables: {:?}", var_names_not_x)),
|
||||
};
|
||||
}
|
||||
return match var_names_not_x.len() {
|
||||
1 => {
|
||||
let var_name = &var_names_not_x[0];
|
||||
if var_name == "e" {
|
||||
Some(String::from(
|
||||
"If trying to use Euler's number, please use an uppercase E",
|
||||
))
|
||||
} else {
|
||||
Some(format!("Error: invalid variable: {}", var_name))
|
||||
}
|
||||
}
|
||||
_ => Some(format!("Error: invalid variables: {:?}", var_names_not_x)),
|
||||
};
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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()))
|
||||
test_func(&process_func_str(function_string.to_string()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_func_test() {
|
||||
// These shouldn't fail
|
||||
assert!(test_func_helper("x^2").is_none());
|
||||
assert!(test_func_helper("2x").is_none());
|
||||
// assert!(test_func_helper("e^x").is_none()); // need to fix!!! PR to exmex
|
||||
assert!(test_func_helper("E^x").is_none());
|
||||
assert!(test_func_helper("log10(x)").is_none());
|
||||
assert!(test_func_helper("xxxxx").is_none());
|
||||
// These shouldn't fail
|
||||
assert!(test_func_helper("x^2").is_none());
|
||||
assert!(test_func_helper("2x").is_none());
|
||||
// assert!(test_func_helper("e^x").is_none()); // need to fix!!! PR to exmex
|
||||
assert!(test_func_helper("E^x").is_none());
|
||||
assert!(test_func_helper("log10(x)").is_none());
|
||||
assert!(test_func_helper("xxxxx").is_none());
|
||||
|
||||
// Expect these to fail
|
||||
assert!(test_func_helper("a").is_some());
|
||||
assert!(test_func_helper("l^2").is_some());
|
||||
assert!(test_func_helper("log222(x)").is_some());
|
||||
assert!(test_func_helper("abcdef").is_some());
|
||||
// Expect these to fail
|
||||
assert!(test_func_helper("a").is_some());
|
||||
assert!(test_func_helper("l^2").is_some());
|
||||
assert!(test_func_helper("log222(x)").is_some());
|
||||
assert!(test_func_helper("abcdef").is_some());
|
||||
}
|
||||
|
||||
// Tests to make sure my cursed function works as intended
|
||||
#[test]
|
||||
fn func_process_test() {
|
||||
assert_eq!(&process_func_str("2x".to_string()), "2*x");
|
||||
assert_eq!(&process_func_str("x2".to_string()), "x*2");
|
||||
assert_eq!(&process_func_str("x(1+3)".to_string()), "x*(1+3)");
|
||||
assert_eq!(&process_func_str("(1+3)x".to_string()), "(1+3)*x");
|
||||
assert_eq!(&process_func_str("sin(x)".to_string()), "sin(x)");
|
||||
assert_eq!(&process_func_str("2sin(x)".to_string()), "2*sin(x)");
|
||||
assert_eq!(&process_func_str("max(x)".to_string()), "max(x)");
|
||||
assert_eq!(&process_func_str("2e^x".to_string()), "2*e^x");
|
||||
assert_eq!(&process_func_str("2max(x)".to_string()), "2*max(x)");
|
||||
assert_eq!(&process_func_str("cos(sin(x))".to_string()), "cos(sin(x))");
|
||||
assert_eq!(&process_func_str("x^(1+2x)".to_string()), "x^(1+2*x)");
|
||||
assert_eq!(
|
||||
&process_func_str("(x+2)x(1+3)".to_string()),
|
||||
"(x+2)*x*(1+3)"
|
||||
);
|
||||
assert_eq!(&process_func_str("(x+2)(1+3)".to_string()), "(x+2)*(1+3)");
|
||||
assert_eq!(&process_func_str("xxx".to_string()), "x*x*x");
|
||||
assert_eq!(&process_func_str("eee".to_string()), "e*e*e");
|
||||
assert_eq!(&process_func_str("pi(x+2)".to_string()), "π*(x+2)");
|
||||
assert_eq!(&process_func_str("(x)pi".to_string()), "(x)*π");
|
||||
assert_eq!(&process_func_str("2e".to_string()), "2*e");
|
||||
assert_eq!(&process_func_str("2log10(x)".to_string()), "2*log10(x)");
|
||||
assert_eq!(&process_func_str("2log(x)".to_string()), "2*log10(x)");
|
||||
assert_eq!(&process_func_str("x!".to_string()), "x!");
|
||||
assert_eq!(&process_func_str("pipipipipipi".to_string()), "π*π*π*π*π*π");
|
||||
assert_eq!(&process_func_str("10pi".to_string()), "10*π");
|
||||
assert_eq!(&process_func_str("pi10".to_string()), "π*10");
|
||||
assert_eq!(&process_func_str("2x".to_string()), "2*x");
|
||||
assert_eq!(&process_func_str("x2".to_string()), "x*2");
|
||||
assert_eq!(&process_func_str("x(1+3)".to_string()), "x*(1+3)");
|
||||
assert_eq!(&process_func_str("(1+3)x".to_string()), "(1+3)*x");
|
||||
assert_eq!(&process_func_str("sin(x)".to_string()), "sin(x)");
|
||||
assert_eq!(&process_func_str("2sin(x)".to_string()), "2*sin(x)");
|
||||
assert_eq!(&process_func_str("max(x)".to_string()), "max(x)");
|
||||
assert_eq!(&process_func_str("2e^x".to_string()), "2*e^x");
|
||||
assert_eq!(&process_func_str("2max(x)".to_string()), "2*max(x)");
|
||||
assert_eq!(&process_func_str("cos(sin(x))".to_string()), "cos(sin(x))");
|
||||
assert_eq!(&process_func_str("x^(1+2x)".to_string()), "x^(1+2*x)");
|
||||
assert_eq!(
|
||||
&process_func_str("(x+2)x(1+3)".to_string()),
|
||||
"(x+2)*x*(1+3)"
|
||||
);
|
||||
assert_eq!(&process_func_str("(x+2)(1+3)".to_string()), "(x+2)*(1+3)");
|
||||
assert_eq!(&process_func_str("xxx".to_string()), "x*x*x");
|
||||
assert_eq!(&process_func_str("eee".to_string()), "e*e*e");
|
||||
assert_eq!(&process_func_str("pi(x+2)".to_string()), "π*(x+2)");
|
||||
assert_eq!(&process_func_str("(x)pi".to_string()), "(x)*π");
|
||||
assert_eq!(&process_func_str("2e".to_string()), "2*e");
|
||||
assert_eq!(&process_func_str("2log10(x)".to_string()), "2*log10(x)");
|
||||
assert_eq!(&process_func_str("2log(x)".to_string()), "2*log10(x)");
|
||||
assert_eq!(&process_func_str("x!".to_string()), "x!");
|
||||
assert_eq!(&process_func_str("pipipipipipi".to_string()), "π*π*π*π*π*π");
|
||||
assert_eq!(&process_func_str("10pi".to_string()), "10*π");
|
||||
assert_eq!(&process_func_str("pi10".to_string()), "π*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)
|
||||
// assert_eq!(&add_asterisks("emax(x)".to_string()), "e*max(x)");
|
||||
// assert_eq!(&add_asterisks("pisin(x)".to_string()), "pi*sin(x)");
|
||||
// Need to fix these checks, maybe I need to rewrite the whole asterisk adding system... (or just implement these changes into meval-rs, idk)
|
||||
// assert_eq!(&add_asterisks("emax(x)".to_string()), "e*max(x)");
|
||||
// assert_eq!(&add_asterisks("pisin(x)".to_string()), "pi*sin(x)");
|
||||
}
|
||||
|
||||
@ -1,31 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!-- Disable zooming: -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
|
||||
<!-- Disable zooming: -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>(Yet-to-be-named) Graphing Software</title>
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<noscript>Please enable Javascript, this page uses both WebAssembly and Javascript to run.</noscript>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>(Yet-to-be-named) Graphing Software</title>
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<noscript>Please enable Javascript, this page uses both WebAssembly and Javascript to run.</noscript>
|
||||
|
||||
<canvas id="canvas"></canvas>
|
||||
<div class="centered" id="loading">
|
||||
<p style="font-size: 16px;">
|
||||
Loading…
|
||||
</p>
|
||||
<div class="lds-dual-ring"></div>
|
||||
</div>
|
||||
<canvas id="canvas"></canvas>
|
||||
<div class="centered" id="loading">
|
||||
<p style="font-size: 16px;">
|
||||
Loading…
|
||||
</p>
|
||||
<div class="lds-dual-ring"></div>
|
||||
</div>
|
||||
|
||||
<script type="module">
|
||||
import init, { start } from "./ytbn_graphing_software.js";
|
||||
<script type="module">
|
||||
import init, { start } from "./ytbn_graphing_software.js";
|
||||
|
||||
async function run() {
|
||||
await init();
|
||||
}
|
||||
run();
|
||||
</script>
|
||||
</body>
|
||||
async function run() {
|
||||
await init();
|
||||
}
|
||||
run();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -1,81 +1,81 @@
|
||||
html {
|
||||
/* Remove touch delay: */
|
||||
touch-action: manipulation;
|
||||
/* Remove touch delay: */
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
body {
|
||||
/* Light mode background color for what is not covered by the egui canvas,
|
||||
or where the egui canvas is translucent. */
|
||||
background: #909090;
|
||||
/* Light mode background color for what is not covered by the egui canvas,
|
||||
or where the egui canvas is translucent. */
|
||||
background: #909090;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
/* Dark mode background color for what is not covered by the egui canvas,
|
||||
or where the egui canvas is translucent. */
|
||||
background: #404040;
|
||||
}
|
||||
body {
|
||||
/* Dark mode background color for what is not covered by the egui canvas,
|
||||
or where the egui canvas is translucent. */
|
||||
background: #404040;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allow canvas to fill entire web page: */
|
||||
html,
|
||||
body {
|
||||
overflow: hidden;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
overflow: hidden;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
/* Position canvas in center-top: */
|
||||
canvas {
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0%);
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0%);
|
||||
}
|
||||
|
||||
.centered {
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
color: #f0f0f0;
|
||||
font-size: 24px;
|
||||
font-family: Ubuntu-Light, Helvetica, sans-serif;
|
||||
text-align: center;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
color: #f0f0f0;
|
||||
font-size: 24px;
|
||||
font-family: Ubuntu-Light, Helvetica, sans-serif;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------- */
|
||||
/* Loading animation from https://loading.io/css/ */
|
||||
.lds-dual-ring {
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.lds-dual-ring:after {
|
||||
content: " ";
|
||||
display: block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin: 0px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid #fff;
|
||||
border-color: #fff transparent #fff transparent;
|
||||
animation: lds-dual-ring 1.2s linear infinite;
|
||||
content: " ";
|
||||
display: block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin: 0px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid #fff;
|
||||
border-color: #fff transparent #fff transparent;
|
||||
animation: lds-dual-ring 1.2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes lds-dual-ring {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user