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