cleanup
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -4145,7 +4145,6 @@ dependencies = [
|
|||||||
"base64",
|
"base64",
|
||||||
"benchmarks",
|
"benchmarks",
|
||||||
"bincode",
|
"bincode",
|
||||||
"cfg-if",
|
|
||||||
"eframe",
|
"eframe",
|
||||||
"egui",
|
"egui",
|
||||||
"egui_plot",
|
"egui_plot",
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ epaint = { git = "https://github.com/titaniumtown/egui.git", default-features =
|
|||||||
emath = { git = "https://github.com/titaniumtown/egui.git", default-features = false }
|
emath = { git = "https://github.com/titaniumtown/egui.git", default-features = false }
|
||||||
egui_plot = { version = "0.34.0", default-features = false }
|
egui_plot = { version = "0.34.0", default-features = false }
|
||||||
|
|
||||||
cfg-if = "1"
|
|
||||||
ruzstd = "0.8"
|
ruzstd = "0.8"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
itertools = "0.14"
|
itertools = "0.14"
|
||||||
|
|||||||
@@ -8,10 +8,7 @@ use epaint::Color32;
|
|||||||
use parsing::{AutoComplete, generate_hint};
|
use parsing::{AutoComplete, generate_hint};
|
||||||
use parsing::{BackingFunction, process_func_str};
|
use parsing::{BackingFunction, process_func_str};
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer, ser::SerializeStruct};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer, ser::SerializeStruct};
|
||||||
use std::{
|
use std::fmt::{self, Debug};
|
||||||
fmt::{self, Debug},
|
|
||||||
hash::{Hash, Hasher},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Represents the possible variations of Riemann Sums
|
/// Represents the possible variations of Riemann Sums
|
||||||
#[derive(PartialEq, Eq, Debug, Copy, Clone, Default)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone, Default)]
|
||||||
@@ -35,9 +32,6 @@ pub struct FunctionEntry {
|
|||||||
/// The `BackingFunction` instance that is used to generate `f(x)`, `f'(x)`, and `f''(x)`
|
/// The `BackingFunction` instance that is used to generate `f(x)`, `f'(x)`, and `f''(x)`
|
||||||
function: BackingFunction,
|
function: BackingFunction,
|
||||||
|
|
||||||
/// Stores a function string (that hasn't been processed via `process_func_str`) to display to the user
|
|
||||||
pub raw_func_str: String,
|
|
||||||
|
|
||||||
/// If calculating/displayingintegrals are enabled
|
/// If calculating/displayingintegrals are enabled
|
||||||
pub integral: bool,
|
pub integral: bool,
|
||||||
|
|
||||||
@@ -61,23 +55,13 @@ pub struct FunctionEntry {
|
|||||||
pub settings_opened: bool,
|
pub settings_opened: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for FunctionEntry {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
self.raw_func_str.hash(state);
|
|
||||||
self.integral.hash(state);
|
|
||||||
self.nth_derivative.hash(state);
|
|
||||||
self.curr_nth.hash(state);
|
|
||||||
self.settings_opened.hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Serialize for FunctionEntry {
|
impl Serialize for FunctionEntry {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
S: Serializer,
|
S: Serializer,
|
||||||
{
|
{
|
||||||
let mut s = serializer.serialize_struct("FunctionEntry", 4)?;
|
let mut s = serializer.serialize_struct("FunctionEntry", 4)?;
|
||||||
s.serialize_field("raw_func_str", &self.raw_func_str)?;
|
s.serialize_field("raw_func_str", &self.autocomplete.string)?;
|
||||||
s.serialize_field("integral", &self.integral)?;
|
s.serialize_field("integral", &self.integral)?;
|
||||||
s.serialize_field("derivative", &self.derivative)?;
|
s.serialize_field("derivative", &self.derivative)?;
|
||||||
s.serialize_field("curr_nth", &self.curr_nth)?;
|
s.serialize_field("curr_nth", &self.curr_nth)?;
|
||||||
@@ -126,7 +110,6 @@ impl Default for FunctionEntry {
|
|||||||
fn default() -> FunctionEntry {
|
fn default() -> FunctionEntry {
|
||||||
FunctionEntry {
|
FunctionEntry {
|
||||||
function: BackingFunction::default(),
|
function: BackingFunction::default(),
|
||||||
raw_func_str: String::new(),
|
|
||||||
integral: false,
|
integral: false,
|
||||||
derivative: false,
|
derivative: false,
|
||||||
nth_derivative: false,
|
nth_derivative: false,
|
||||||
@@ -151,7 +134,7 @@ impl FunctionEntry {
|
|||||||
|
|
||||||
pub fn settings_window(&mut self, ctx: &Context) {
|
pub fn settings_window(&mut self, ctx: &Context) {
|
||||||
let mut invalidate_nth = false;
|
let mut invalidate_nth = false;
|
||||||
egui::Window::new(format!("Settings: {}", self.raw_func_str))
|
egui::Window::new(format!("Settings: {}", self.func_str()))
|
||||||
.open(&mut self.settings_opened)
|
.open(&mut self.settings_opened)
|
||||||
.default_pos([200.0, 200.0])
|
.default_pos([200.0, 200.0])
|
||||||
.resizable(false)
|
.resizable(false)
|
||||||
@@ -181,13 +164,32 @@ impl FunctionEntry {
|
|||||||
&self.test_result
|
&self.test_result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the raw function string
|
||||||
|
#[inline]
|
||||||
|
pub fn func_str(&self) -> &str {
|
||||||
|
&self.autocomplete.string
|
||||||
|
}
|
||||||
|
|
||||||
/// Update function string and test it
|
/// Update function string and test it
|
||||||
pub fn update_string(&mut self, raw_func_str: &str) {
|
pub fn update_string(&mut self, raw_func_str: &str) {
|
||||||
if raw_func_str == self.raw_func_str {
|
if raw_func_str == self.func_str() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.raw_func_str = raw_func_str.to_owned();
|
// Update the autocomplete string (which is now the source of truth for the raw string)
|
||||||
|
self.autocomplete.update_string(raw_func_str);
|
||||||
|
|
||||||
|
self.reparse_function(raw_func_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Re-parse the function from the current autocomplete string.
|
||||||
|
/// Call this when the autocomplete string was updated externally (e.g., via hint application).
|
||||||
|
pub fn sync_from_autocomplete(&mut self) {
|
||||||
|
self.reparse_function(&self.autocomplete.string.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Internal helper to parse a function string and update internal state
|
||||||
|
fn reparse_function(&mut self, raw_func_str: &str) {
|
||||||
let processed_func = process_func_str(raw_func_str);
|
let processed_func = process_func_str(raw_func_str);
|
||||||
let new_func_result = BackingFunction::new(&processed_func);
|
let new_func_result = BackingFunction::new(&processed_func);
|
||||||
|
|
||||||
@@ -213,17 +215,16 @@ impl FunctionEntry {
|
|||||||
) -> (Vec<(f64, f64)>, f64) {
|
) -> (Vec<(f64, f64)>, f64) {
|
||||||
let step = (integral_max_x - integral_min_x) / (integral_num as f64);
|
let step = (integral_max_x - integral_min_x) / (integral_num as f64);
|
||||||
|
|
||||||
// let sum_func = self.get_sum_func(sum);
|
let data: Vec<(f64, f64)> = step_helper(integral_num, integral_min_x, step)
|
||||||
|
|
||||||
let data2: Vec<(f64, f64)> = step_helper(integral_num, integral_min_x, step)
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|x| {
|
.map(|x| {
|
||||||
let step_offset = step.copysign(x); // store the offset here so it doesn't have to be calculated multiple times
|
let step_offset = step.copysign(x);
|
||||||
let x2: f64 = x + step_offset;
|
let x2 = x + step_offset;
|
||||||
|
|
||||||
let (left_x, right_x) = match x.is_sign_positive() {
|
let (left_x, right_x) = if x.is_sign_positive() {
|
||||||
true => (x, x2),
|
(x, x2)
|
||||||
false => (x2, x),
|
} else {
|
||||||
|
(x2, x)
|
||||||
};
|
};
|
||||||
|
|
||||||
let y = match sum {
|
let y = match sum {
|
||||||
@@ -239,9 +240,9 @@ impl FunctionEntry {
|
|||||||
.filter(|(_, y)| y.is_finite())
|
.filter(|(_, y)| y.is_finite())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let area = data2.iter().map(move |(_, y)| y * step).sum();
|
let area = data.iter().map(|(_, y)| y * step).sum();
|
||||||
|
|
||||||
(data2, area)
|
(data, area)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helps with processing newton's method depending on level of derivative
|
/// Helps with processing newton's method depending on level of derivative
|
||||||
@@ -377,26 +378,9 @@ impl FunctionEntry {
|
|||||||
let step = (settings.max_x - settings.min_x) / (settings.plot_width as f64);
|
let step = (settings.max_x - settings.min_x) / (settings.plot_width as f64);
|
||||||
debug_assert!(step > 0.0);
|
debug_assert!(step > 0.0);
|
||||||
|
|
||||||
// Collect special points (extrema and roots) for exclusion from line hover detection
|
// Check if we have any special points that need exclusion zones
|
||||||
let special_points: Vec<&PlotPoint> = {
|
let has_special_points = (settings.do_extrema && !self.extrema_data.is_empty())
|
||||||
let mut points = Vec::new();
|
|| (settings.do_roots && !self.root_data.is_empty());
|
||||||
if settings.do_extrema {
|
|
||||||
points.extend(self.extrema_data.iter());
|
|
||||||
}
|
|
||||||
if settings.do_roots {
|
|
||||||
points.extend(self.root_data.iter());
|
|
||||||
}
|
|
||||||
points
|
|
||||||
};
|
|
||||||
|
|
||||||
// Helper to check if a point is near any special point
|
|
||||||
// Uses a radius in x-axis units based on the step size
|
|
||||||
let exclusion_radius = step * 3.0; // Exclude points within 3 steps of special points
|
|
||||||
let is_near_special_point = |p: &PlotPoint| -> bool {
|
|
||||||
special_points
|
|
||||||
.iter()
|
|
||||||
.any(|sp| (p.x - sp.x).abs() < exclusion_radius)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Plot back data, filtering out points near special points for better hover detection
|
// Plot back data, filtering out points near special points for better hover detection
|
||||||
if !self.back_data.is_empty() {
|
if !self.back_data.is_empty() {
|
||||||
@@ -417,31 +401,62 @@ impl FunctionEntry {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter out points near special points so cursor snaps to them more easily
|
// Only filter when there are special points to avoid
|
||||||
let filtered_back_data: Vec<PlotPoint> = self
|
let main_line = if has_special_points {
|
||||||
.back_data
|
let exclusion_radius = step * 3.0;
|
||||||
|
let is_near_special = |p: &PlotPoint| {
|
||||||
|
(settings.do_extrema
|
||||||
|
&& self
|
||||||
|
.extrema_data
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|p| !is_near_special_point(p))
|
.any(|sp| (p.x - sp.x).abs() < exclusion_radius))
|
||||||
|
|| (settings.do_roots
|
||||||
|
&& self
|
||||||
|
.root_data
|
||||||
|
.iter()
|
||||||
|
.any(|sp| (p.x - sp.x).abs() < exclusion_radius))
|
||||||
|
};
|
||||||
|
self.back_data
|
||||||
|
.iter()
|
||||||
|
.filter(|p| !is_near_special(p))
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect();
|
.collect::<Vec<PlotPoint>>()
|
||||||
|
|
||||||
plot_ui.line(
|
|
||||||
filtered_back_data
|
|
||||||
.to_line()
|
.to_line()
|
||||||
.stroke(egui::Stroke::new(4.0, main_plot_color)),
|
} else {
|
||||||
);
|
// No filtering needed - use data directly
|
||||||
|
self.back_data.clone().to_line()
|
||||||
|
};
|
||||||
|
|
||||||
|
plot_ui.line(main_line.stroke(egui::Stroke::new(4.0, main_plot_color)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plot derivative data, also filtering near special points
|
// Plot derivative data
|
||||||
if self.derivative && !self.derivative_data.is_empty() {
|
if self.derivative && !self.derivative_data.is_empty() {
|
||||||
let filtered_derivative_data: Vec<PlotPoint> = self
|
let derivative_line = if has_special_points {
|
||||||
.derivative_data
|
let exclusion_radius = step * 3.0;
|
||||||
|
let is_near_special = |p: &PlotPoint| {
|
||||||
|
(settings.do_extrema
|
||||||
|
&& self
|
||||||
|
.extrema_data
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|p| !is_near_special_point(p))
|
.any(|sp| (p.x - sp.x).abs() < exclusion_radius))
|
||||||
|
|| (settings.do_roots
|
||||||
|
&& self
|
||||||
|
.root_data
|
||||||
|
.iter()
|
||||||
|
.any(|sp| (p.x - sp.x).abs() < exclusion_radius))
|
||||||
|
};
|
||||||
|
self.derivative_data
|
||||||
|
.iter()
|
||||||
|
.filter(|p| !is_near_special(p))
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect();
|
.collect::<Vec<PlotPoint>>()
|
||||||
|
.to_line()
|
||||||
|
} else {
|
||||||
|
self.derivative_data.clone().to_line()
|
||||||
|
};
|
||||||
|
|
||||||
plot_ui.line(filtered_derivative_data.to_line().color(Color32::GREEN));
|
plot_ui.line(derivative_line.color(Color32::GREEN));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plot extrema points
|
// Plot extrema points
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
use crate::{consts::COLORS, function_entry::FunctionEntry, widgets::widgets_ontop};
|
use crate::{function_entry::FunctionEntry, widgets::widgets_ontop};
|
||||||
use egui::{Button, Id, Key, Modifiers, PopupCloseBehavior, TextEdit, WidgetText};
|
use egui::{Button, Id, Key, Modifiers, PopupCloseBehavior, TextEdit, WidgetText};
|
||||||
use emath::vec2;
|
use emath::vec2;
|
||||||
use parsing::Movement;
|
use parsing::Movement;
|
||||||
use serde::ser::SerializeStruct;
|
use serde::{Deserialize, Serialize};
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
|
||||||
use std::collections::hash_map::DefaultHasher;
|
|
||||||
use std::hash::{Hash, Hasher};
|
|
||||||
use std::ops::BitXorAssign;
|
use std::ops::BitXorAssign;
|
||||||
|
|
||||||
type Functions = Vec<(Id, FunctionEntry)>;
|
type Functions = Vec<(Id, FunctionEntry)>;
|
||||||
@@ -33,8 +30,8 @@ fn func_manager_roundtrip_serdes() {
|
|||||||
let ser = bincode::serialize(&func_manager).expect("unable to serialize");
|
let ser = bincode::serialize(&func_manager).expect("unable to serialize");
|
||||||
let des: FunctionManager = bincode::deserialize(&ser).expect("unable to deserialize");
|
let des: FunctionManager = bincode::deserialize(&ser).expect("unable to deserialize");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
func_manager.functions[0].1.raw_func_str,
|
func_manager.functions[0].1.func_str(),
|
||||||
des.functions[0].1.raw_func_str
|
des.functions[0].1.func_str()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,17 +47,9 @@ impl FunctionManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_hash(&self) -> u64 {
|
|
||||||
let mut hasher = DefaultHasher::new();
|
|
||||||
self.functions.hash(&mut hasher);
|
|
||||||
hasher.finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Displays function entries alongside returning whether or not functions have been modified
|
/// Displays function entries alongside returning whether or not functions have been modified
|
||||||
pub fn display_entries(&mut self, ui: &mut egui::Ui) -> bool {
|
pub fn display_entries(&mut self, ui: &mut egui::Ui) -> bool {
|
||||||
let initial_hash = self.get_hash();
|
let mut changed = false;
|
||||||
|
|
||||||
let can_remove = self.functions.len() > 1;
|
let can_remove = self.functions.len() > 1;
|
||||||
|
|
||||||
let available_width = ui.available_width();
|
let available_width = ui.available_width();
|
||||||
@@ -68,7 +57,6 @@ impl FunctionManager {
|
|||||||
let target_size = vec2(available_width, crate::consts::FONT_SIZE);
|
let target_size = vec2(available_width, crate::consts::FONT_SIZE);
|
||||||
for (i, (te_id, function)) in self.functions.iter_mut().map(|(a, b)| (*a, b)).enumerate() {
|
for (i, (te_id, function)) in self.functions.iter_mut().map(|(a, b)| (*a, b)).enumerate() {
|
||||||
let mut new_string = function.autocomplete.string.clone();
|
let mut new_string = function.autocomplete.string.clone();
|
||||||
function.update_string(&new_string);
|
|
||||||
|
|
||||||
let mut movement: Movement = Movement::default();
|
let mut movement: Movement = Movement::default();
|
||||||
|
|
||||||
@@ -92,11 +80,15 @@ impl FunctionManager {
|
|||||||
// Only keep valid chars
|
// Only keep valid chars
|
||||||
new_string.retain(crate::misc::is_valid_char);
|
new_string.retain(crate::misc::is_valid_char);
|
||||||
|
|
||||||
|
// Track if function string changed and update the function
|
||||||
|
if new_string != function.autocomplete.string {
|
||||||
|
changed = true;
|
||||||
|
function.update_string(&new_string);
|
||||||
|
}
|
||||||
|
|
||||||
// If not fully open, return here as buttons cannot yet be displayed, therefore the user is inable to mark it for deletion
|
// If not fully open, return here as buttons cannot yet be displayed, therefore the user is inable to mark it for deletion
|
||||||
let animate_bool = ui.ctx().animate_bool(te_id, re.has_focus());
|
let animate_bool = ui.ctx().animate_bool(te_id, re.has_focus());
|
||||||
if animate_bool == 1.0 {
|
if animate_bool == 1.0 {
|
||||||
function.autocomplete.update_string(&new_string);
|
|
||||||
|
|
||||||
if function.autocomplete.hint.is_some() {
|
if function.autocomplete.hint.is_some() {
|
||||||
// only register up and down arrow movements if hint is type `Hint::Many`
|
// only register up and down arrow movements if hint is type `Hint::Many`
|
||||||
if !function.autocomplete.hint.is_single() {
|
if !function.autocomplete.hint.is_single() {
|
||||||
@@ -121,9 +113,18 @@ impl FunctionManager {
|
|||||||
movement = Movement::Complete;
|
movement = Movement::Complete;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remember string before movement to detect changes
|
||||||
|
let string_before = function.autocomplete.string.clone();
|
||||||
|
|
||||||
// Register movement and apply proper changes
|
// Register movement and apply proper changes
|
||||||
function.autocomplete.register_movement(&movement);
|
function.autocomplete.register_movement(&movement);
|
||||||
|
|
||||||
|
// If the string changed (hint was applied), update the backing function
|
||||||
|
if function.autocomplete.string != string_before {
|
||||||
|
function.sync_from_autocomplete();
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
if movement != Movement::Complete
|
if movement != Movement::Complete
|
||||||
&& let Some(hints) = function.autocomplete.hint.many()
|
&& let Some(hints) = function.autocomplete.hint.many()
|
||||||
{
|
{
|
||||||
@@ -153,6 +154,9 @@ impl FunctionManager {
|
|||||||
function
|
function
|
||||||
.autocomplete
|
.autocomplete
|
||||||
.apply_hint(hints[function.autocomplete.i]);
|
.apply_hint(hints[function.autocomplete.i]);
|
||||||
|
// Update the backing function with the new string after hint was applied
|
||||||
|
function.sync_from_autocomplete();
|
||||||
|
changed = true;
|
||||||
|
|
||||||
movement = Movement::Complete;
|
movement = Movement::Complete;
|
||||||
} else {
|
} else {
|
||||||
@@ -230,11 +234,10 @@ impl FunctionManager {
|
|||||||
// Remove function if the user requests it
|
// Remove function if the user requests it
|
||||||
if let Some(remove_i_unwrap) = remove_i {
|
if let Some(remove_i_unwrap) = remove_i {
|
||||||
self.functions.remove(remove_i_unwrap);
|
self.functions.remove(remove_i_unwrap);
|
||||||
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let final_hash = self.get_hash();
|
changed
|
||||||
|
|
||||||
initial_hash != final_hash
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create and push new empty function entry
|
/// Create and push new empty function entry
|
||||||
|
|||||||
38
src/lib.rs
38
src/lib.rs
@@ -17,17 +17,19 @@ pub use crate::{
|
|||||||
unicode_helper::{to_chars_array, to_unicode_hash},
|
unicode_helper::{to_chars_array, to_unicode_hash},
|
||||||
};
|
};
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
// WASM-specific setup
|
||||||
if #[cfg(target_arch = "wasm32")] {
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
mod wasm {
|
||||||
|
use super::math_app;
|
||||||
|
use eframe::WebRunner;
|
||||||
|
use lol_alloc::{FreeListAllocator, LockedAllocator};
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
use web_sys::HtmlCanvasElement;
|
use web_sys::HtmlCanvasElement;
|
||||||
|
|
||||||
use lol_alloc::{FreeListAllocator, LockedAllocator};
|
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static ALLOCATOR: LockedAllocator<FreeListAllocator> = LockedAllocator::new(FreeListAllocator::new());
|
static ALLOCATOR: LockedAllocator<FreeListAllocator> =
|
||||||
|
LockedAllocator::new(FreeListAllocator::new());
|
||||||
|
|
||||||
use eframe::WebRunner;
|
|
||||||
// use tracing::metadata::LevelFilter;
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub struct WebHandle {
|
pub struct WebHandle {
|
||||||
@@ -40,9 +42,7 @@ cfg_if::cfg_if! {
|
|||||||
#[allow(clippy::new_without_default)]
|
#[allow(clippy::new_without_default)]
|
||||||
#[wasm_bindgen(constructor)]
|
#[wasm_bindgen(constructor)]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
// eframe::WebLogger::init(LevelFilter::Debug).ok();
|
|
||||||
tracing_wasm::set_as_global_default();
|
tracing_wasm::set_as_global_default();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
runner: WebRunner::new(),
|
runner: WebRunner::new(),
|
||||||
}
|
}
|
||||||
@@ -50,7 +50,10 @@ cfg_if::cfg_if! {
|
|||||||
|
|
||||||
/// Call this once from JavaScript to start your app.
|
/// Call this once from JavaScript to start your app.
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub async fn start(&self, canvas_id: HtmlCanvasElement) -> Result<(), wasm_bindgen::JsValue> {
|
pub async fn start(
|
||||||
|
&self,
|
||||||
|
canvas_id: HtmlCanvasElement,
|
||||||
|
) -> Result<(), wasm_bindgen::JsValue> {
|
||||||
self.runner
|
self.runner
|
||||||
.start(
|
.start(
|
||||||
canvas_id,
|
canvas_id,
|
||||||
@@ -65,11 +68,20 @@ cfg_if::cfg_if! {
|
|||||||
pub async fn start() {
|
pub async fn start() {
|
||||||
tracing::info!("Starting...");
|
tracing::info!("Starting...");
|
||||||
|
|
||||||
let document = web_sys::window().unwrap().document().unwrap();
|
let document = web_sys::window()
|
||||||
let canvas = document.get_element_by_id("canvas").unwrap().dyn_into::<HtmlCanvasElement>().unwrap();
|
.expect("no window")
|
||||||
|
.document()
|
||||||
|
.expect("no document");
|
||||||
|
let canvas = document
|
||||||
|
.get_element_by_id("canvas")
|
||||||
|
.expect("no canvas element")
|
||||||
|
.dyn_into::<HtmlCanvasElement>()
|
||||||
|
.expect("canvas is not an HtmlCanvasElement");
|
||||||
|
|
||||||
let web_handle = WebHandle::new();
|
let web_handle = WebHandle::new();
|
||||||
web_handle.start(canvas).await.unwrap()
|
web_handle
|
||||||
}
|
.start(canvas)
|
||||||
|
.await
|
||||||
|
.expect("failed to start web app");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,41 +124,26 @@ const DATA_NAME: &str = "YTBN-DECOMPRESSED";
|
|||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
const FUNC_NAME: &str = "YTBN-FUNCTIONS";
|
const FUNC_NAME: &str = "YTBN-FUNCTIONS";
|
||||||
|
|
||||||
impl MathApp {
|
/// Load functions from localStorage (WASM only)
|
||||||
#[allow(dead_code)] // This is used lol
|
#[cfg(target_arch = "wasm32")]
|
||||||
/// Create new instance of [`MathApp`] and return it
|
|
||||||
pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
|
|
||||||
tracing::info!("Initializing...");
|
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
|
||||||
if #[cfg(target_arch = "wasm32")] {
|
|
||||||
|
|
||||||
tracing::info!("Web Info: {:?}", &cc.integration_info.web_info);
|
|
||||||
|
|
||||||
fn load_functions() -> Option<FunctionManager> {
|
fn load_functions() -> Option<FunctionManager> {
|
||||||
let data = get_localstorage().get_item(FUNC_NAME).ok()??;
|
let data = get_localstorage().get_item(FUNC_NAME).ok()??;
|
||||||
let func_data = crate::misc::hashed_storage_read(&data)?;
|
let func_data = crate::misc::hashed_storage_read(&data)?;
|
||||||
|
|
||||||
|
|
||||||
tracing::info!("Reading previous function data");
|
tracing::info!("Reading previous function data");
|
||||||
if let Ok(Some(function_manager)) = bincode::deserialize(&func_data) {
|
match bincode::deserialize(&func_data) {
|
||||||
return Some(function_manager);
|
Ok(Some(function_manager)) => Some(function_manager),
|
||||||
} else {
|
_ => {
|
||||||
tracing::info!("Unable to load functionManager instance");
|
tracing::info!("Unable to load functionManager instance");
|
||||||
return None;
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decompress_fonts() -> epaint::text::FontDefinitions {
|
fn decompress_fonts() -> epaint::text::FontDefinitions {
|
||||||
let mut data = Vec::new();
|
let mut data = Vec::new();
|
||||||
let _ =
|
|
||||||
ruzstd::decoding::StreamingDecoder::new(
|
ruzstd::decoding::StreamingDecoder::new(
|
||||||
&mut const {
|
const { include_bytes!(concat!(env!("OUT_DIR"), "/compressed_data")).as_slice() },
|
||||||
include_bytes!(concat!(env!("OUT_DIR"), "/compressed_data")).as_slice()
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
.expect("unable to decode compressed data")
|
.expect("unable to decode compressed data")
|
||||||
.read_to_end(&mut data)
|
.read_to_end(&mut data)
|
||||||
@@ -167,17 +152,20 @@ impl MathApp {
|
|||||||
bincode::deserialize(data.as_slice()).expect("unable to deserialize bincode")
|
bincode::deserialize(data.as_slice()).expect("unable to deserialize bincode")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MathApp {
|
||||||
|
#[allow(dead_code)] // This is used lol
|
||||||
|
/// Create new instance of [`MathApp`] and return it
|
||||||
|
pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
|
||||||
|
tracing::info!("Initializing...");
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
tracing::info!("Web Info: {:?}", &cc.integration_info.web_info);
|
||||||
|
|
||||||
tracing::info!("Reading fonts...");
|
tracing::info!("Reading fonts...");
|
||||||
|
|
||||||
// Initialize fonts
|
// Initialize fonts
|
||||||
// This used to be in the `update` method, but (after a ton of digging) this actually caused OOMs. that was a pain to debug
|
// This used to be in the `update` method, but (after a ton of digging) this actually caused OOMs. that was a pain to debug
|
||||||
cc.egui_ctx.set_fonts({ decompress_fonts() });
|
cc.egui_ctx.set_fonts(decompress_fonts());
|
||||||
|
|
||||||
// Set dark mode by default
|
|
||||||
// cc.egui_ctx.set_visuals(crate::style::style());
|
|
||||||
|
|
||||||
// Set spacing
|
|
||||||
// cc.egui_ctx.set_spacing(crate::style::SPACING);
|
|
||||||
|
|
||||||
tracing::info!("Initialized!");
|
tracing::info!("Initialized!");
|
||||||
|
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ fn do_test(sum: Riemann, area_target: f64) {
|
|||||||
{
|
{
|
||||||
function.update_string("sin(x)");
|
function.update_string("sin(x)");
|
||||||
assert!(function.get_test_result().is_none());
|
assert!(function.get_test_result().is_none());
|
||||||
assert_eq!(&function.raw_func_str, "sin(x)");
|
assert_eq!(function.func_str(), "sin(x)");
|
||||||
|
|
||||||
function.integral = false;
|
function.integral = false;
|
||||||
function.derivative = false;
|
function.derivative = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user