it works!!
This commit is contained in:
parent
a9a870ec2f
commit
24d2a5bbd8
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,6 +1,5 @@
|
||||
/target
|
||||
/pkg
|
||||
/tmp
|
||||
/assets.tar.zst
|
||||
/Cargo.lock
|
||||
/assets/font_data
|
||||
/assets/data
|
||||
|
||||
64
Cargo.lock
generated
64
Cargo.lock
generated
@ -794,18 +794,6 @@ dependencies = [
|
||||
"instant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0408e2626025178a6a7f7ffc05a25bc47103229f19c113755de7bf63816290c"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "findshlibs"
|
||||
version = "0.10.2"
|
||||
@ -2164,17 +2152,6 @@ dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tar"
|
||||
version = "0.4.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6"
|
||||
dependencies = [
|
||||
"filetime",
|
||||
"libc",
|
||||
"xattr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.3.0"
|
||||
@ -2783,15 +2760,6 @@ dependencies = [
|
||||
"winapi-wsapoll",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xattr"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xcursor"
|
||||
version = "0.3.4"
|
||||
@ -2828,10 +2796,10 @@ dependencies = [
|
||||
"parsing",
|
||||
"rayon",
|
||||
"ruzstd",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"shadow-rs",
|
||||
"static_assertions",
|
||||
"tar",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"tracing-wasm",
|
||||
@ -2839,4 +2807,34 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
"wee_alloc",
|
||||
"zstd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd"
|
||||
version = "0.11.1+zstd.1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77a16b8414fde0414e90c612eba70985577451c4c504b99885ebed24762cb81a"
|
||||
dependencies = [
|
||||
"zstd-safe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-safe"
|
||||
version = "5.0.1+zstd.1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c12659121420dd6365c5c3de4901f97145b79651fb1d25814020ed2ed0585ae"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"zstd-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-sys"
|
||||
version = "2.0.1+zstd.1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fd07cbbc53846d9145dbffdf6dd09a7a0aa52be46741825f5c97bdd4f73f12b"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@ -47,7 +47,6 @@ shadow-rs = { version = "0.11", default-features = false }
|
||||
const_format = { version = "0.2", default-features = false, features = ["fmt"] }
|
||||
cfg-if = "1"
|
||||
lazy_static = "1"
|
||||
tar = "0.4"
|
||||
ruzstd = { git = "https://github.com/Titaniumtown/zstd-rs.git", branch = "ringbuffer" }
|
||||
serde_json = "1.0"
|
||||
tracing = "0.1"
|
||||
@ -55,6 +54,7 @@ itertools = "0.10"
|
||||
static_assertions = "1.1"
|
||||
uuid = { version = "1", features = ["v4", "fast-rng", "js"] }
|
||||
bincode = "1.3.3"
|
||||
serde = "1"
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
@ -67,6 +67,9 @@ epaint = { git = "https://github.com/Titaniumtown/egui.git", default-features =
|
||||
"serde",
|
||||
] }
|
||||
bincode = "1.3.3"
|
||||
serde = "1"
|
||||
serde_json = "1.0"
|
||||
zstd = "0.11.1+zstd.1.5.2"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
instant = "0.1"
|
||||
|
||||
1
TODO.md
1
TODO.md
@ -21,3 +21,4 @@
|
||||
11. Fix mobile text input
|
||||
12. Don't use json for text storage
|
||||
13. Write custom plotter
|
||||
14. Cache decompressed data in browser's [Local Storage](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Storage.html)
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
"- The 'Info' button provides information on the build currently running.",
|
||||
"- The Sun/Moon button toggles Dark and Light mode."
|
||||
],
|
||||
"help_func": [
|
||||
"help_function": [
|
||||
"(From Left to Right)",
|
||||
"- The `✖` allows you to delete the function in question. Deleting a function is prevented if only 1 function exists.",
|
||||
"- The `∫` indicates whether to integrate the function in question.",
|
||||
@ -24,7 +24,7 @@
|
||||
"- Extrema (local minimums and maximums) and Roots (intersections with the x-axis) are displayed though yellow and light blue points on the graph. You can toggle these in the Side Panel.",
|
||||
"- In some edge cases, math functions may not parse correctly. More specifically with implicit multiplication. If you incounter this issue, please do report it on the project's Github page (linked on the side panel). But a current workaround would be explicitly stating a multiplication operation through the use of an asterisk."
|
||||
],
|
||||
"agpl_info": "The AGPL license ensures that the end user, even if not hosting the program itself, is still guaranteed access to the source code of the project in question.",
|
||||
"license_info": "The AGPL license ensures that the end user, even if not hosting the program itself, is still guaranteed access to the source code of the project in question.",
|
||||
"welcome": [
|
||||
"Welcome to the (Yet-to-be-named) Graphing Software!",
|
||||
"This project aims to provide an intuitive experience graphing mathematical functions with features such as Integration, differentiation, extrema, roots, and much more! (see Help menu for more details)"
|
||||
|
||||
36
build.rs
36
build.rs
@ -9,6 +9,8 @@ use epaint::{
|
||||
FontFamily,
|
||||
};
|
||||
|
||||
include!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/data.rs"));
|
||||
|
||||
fn main() {
|
||||
// rebuild if new commit or contents of `assets` folder changed
|
||||
println!("cargo:rerun-if-changed=.git/logs/HEAD");
|
||||
@ -49,16 +51,34 @@ fn main() {
|
||||
]),
|
||||
};
|
||||
|
||||
let path = "./assets/font_data";
|
||||
let mut file = BufWriter::new(File::create("./assets/data").expect("Could not create file"));
|
||||
|
||||
let fonts_data = bincode::serialize(&fonts).unwrap();
|
||||
// ::to_string(&fonts).expect("Failed to serialize fonts");
|
||||
let aa: serde_json::Value = serde_json::from_str(include_str!("assets/text.json")).unwrap();
|
||||
let mut json_file_array = aa.as_object().unwrap().clone();
|
||||
for value in json_file_array.iter_mut() {
|
||||
if let serde_json::Value::Array(values) = value.1 {
|
||||
let values_copy = values.clone();
|
||||
*value.1 = serde_json::Value::String(
|
||||
values_copy
|
||||
.iter()
|
||||
.map(|s| s.as_str().unwrap())
|
||||
.collect::<Vec<&str>>()
|
||||
.join("\n"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let mut file = BufWriter::new(File::create(&path).expect("Could not create file"));
|
||||
let text_data: TextData =
|
||||
serde_json::from_value(serde_json::Value::Object(json_file_array)).unwrap();
|
||||
|
||||
file.write_all(fonts_data.as_slice()).unwrap();
|
||||
let data = bincode::serialize(&TotalData {
|
||||
text: text_data,
|
||||
fonts,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let _ = command_run::Command::with_args("./pack_assets.sh", &[&path])
|
||||
.enable_capture()
|
||||
.run();
|
||||
let data_compressed = zstd::encode_all(data.as_slice(), 22).unwrap();
|
||||
println!("{:?}", data_compressed);
|
||||
|
||||
file.write_all(data_compressed.as_slice()).unwrap();
|
||||
}
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
rm -fr assets.tar.zst | true
|
||||
tar -I 'zstd --ultra -22' --strip-components=9999 -cf ./assets.tar.zst assets/text.json $1
|
||||
18
src/data.rs
Normal file
18
src/data.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(PartialEq, Debug, Serialize, Deserialize)]
|
||||
pub struct TextData {
|
||||
pub help_expr: String,
|
||||
pub help_vars: String,
|
||||
pub help_panel: String,
|
||||
pub help_function: String,
|
||||
pub help_other: String,
|
||||
pub license_info: String,
|
||||
pub welcome: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct TotalData {
|
||||
pub text: TextData,
|
||||
pub fonts: epaint::text::FontDefinitions,
|
||||
}
|
||||
@ -32,7 +32,7 @@ impl FunctionManager {
|
||||
// ui.label("Functions:");
|
||||
let can_remove = self.functions.len() > 1;
|
||||
|
||||
// update if font settings are ever changed
|
||||
// Update if font settings are ever changed
|
||||
const ROW_HEIGHT: f32 = 14.0;
|
||||
// ui.fonts().row_height(&egui::FontSelection::default().resolve(ui.style()));
|
||||
|
||||
@ -55,9 +55,9 @@ impl FunctionManager {
|
||||
egui::TextEdit::singleline(&mut new_string)
|
||||
.hint_forward(true) // Make the hint appear after the last text in the textbox
|
||||
.lock_focus(true)
|
||||
.id(*te_id) // set widget's id to `te_id`
|
||||
.id(*te_id) // Set widget's id to `te_id`
|
||||
.hint_text({
|
||||
// if there's a single hint, go ahead and apply the hint here, if not, set the hint to an empty string
|
||||
// If there's a single hint, go ahead and apply the hint here, if not, set the hint to an empty string
|
||||
if let Hint::Single(single_hint) = function.autocomplete.hint {
|
||||
*single_hint
|
||||
} else {
|
||||
@ -66,7 +66,7 @@ impl FunctionManager {
|
||||
}),
|
||||
);
|
||||
|
||||
// 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
|
||||
if ui.ctx().animate_bool(*te_id, re.has_focus()) >= 1.0 {
|
||||
function.autocomplete.update_string(&new_string);
|
||||
|
||||
@ -110,7 +110,7 @@ impl FunctionManager {
|
||||
if clicked {
|
||||
function.autocomplete.apply_hint(hints[function.autocomplete.i]);
|
||||
|
||||
// don't need this here as it simply won't be display next frame
|
||||
// Don't need this here as it simply won't be display next frame
|
||||
// ui.memory().close_popup();
|
||||
|
||||
movement = Movement::Complete;
|
||||
@ -190,11 +190,13 @@ impl FunctionManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create and push new empty function entry
|
||||
pub fn push_empty(&mut self) {
|
||||
self.functions
|
||||
.push((Id::new(Uuid::new_v4()), FunctionEntry::EMPTY));
|
||||
}
|
||||
|
||||
/// Detect if any functions are using integrals
|
||||
pub fn any_using_integral(&self) -> bool {
|
||||
self.functions.iter().any(|(_, func)| func.integral)
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ extern crate static_assertions;
|
||||
extern crate uuid;
|
||||
|
||||
mod consts;
|
||||
mod data;
|
||||
mod function_entry;
|
||||
mod function_manager;
|
||||
mod math_app;
|
||||
|
||||
@ -11,6 +11,7 @@ extern crate static_assertions;
|
||||
extern crate uuid;
|
||||
|
||||
mod consts;
|
||||
mod data;
|
||||
mod function_entry;
|
||||
mod function_manager;
|
||||
mod math_app;
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
use crate::consts::*;
|
||||
use crate::data::TextData;
|
||||
use crate::function_entry::Riemann;
|
||||
use crate::function_manager::FunctionManager;
|
||||
use crate::misc::{dyn_mut_iter, option_vec_printer, TextData};
|
||||
use crate::misc::{dyn_mut_iter, option_vec_printer};
|
||||
use eframe::App;
|
||||
use egui::{
|
||||
plot::Plot, style::Margin, vec2, Button, CentralPanel, Color32, ComboBox, Context,
|
||||
FontDefinitions, Frame, Key, Label, Layout, RichText, SidePanel, Slider, TopBottomPanel, Vec2,
|
||||
Visuals, Window,
|
||||
plot::Plot, style::Margin, vec2, Button, CentralPanel, Color32, ComboBox, Context, Frame, Key,
|
||||
Label, Layout, RichText, SidePanel, Slider, TopBottomPanel, Vec2, Visuals, Window,
|
||||
};
|
||||
use emath::{Align, Align2};
|
||||
use epaint::Rounding;
|
||||
use instant::Duration;
|
||||
use std::{io::Read, ops::BitXorAssign, str};
|
||||
use std::{io::Read, ops::BitXorAssign};
|
||||
|
||||
#[cfg(threading)]
|
||||
use rayon::iter::{IndexedParallelIterator, ParallelIterator};
|
||||
@ -142,55 +142,24 @@ impl MathApp {
|
||||
tracing::info!("Initializing...");
|
||||
let start = instant::Instant::now();
|
||||
|
||||
let mut tar_file_data = Vec::new();
|
||||
let mut data = Vec::new();
|
||||
let _ = unsafe {
|
||||
ruzstd::StreamingDecoder::new(&mut include_bytes!("../assets.tar.zst").as_slice())
|
||||
ruzstd::StreamingDecoder::new(&mut include_bytes!("../assets/data").as_slice())
|
||||
.unwrap_unchecked()
|
||||
.read_to_end(&mut tar_file_data)
|
||||
.read_to_end(&mut data)
|
||||
.unwrap_unchecked()
|
||||
};
|
||||
|
||||
let data: crate::data::TotalData = bincode::deserialize(data.as_slice()).unwrap();
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
update_loading(&loading_element, 30);
|
||||
|
||||
// Stores fonts
|
||||
let mut font_data: Option<FontDefinitions> = None;
|
||||
|
||||
// Stores text
|
||||
let mut text_data: Option<TextData> = None;
|
||||
|
||||
tracing::info!("Reading assets...");
|
||||
|
||||
// Iterate through all entries in the tarball
|
||||
for file in unsafe {
|
||||
tar::Archive::new(&*tar_file_data)
|
||||
.entries()
|
||||
.unwrap_unchecked()
|
||||
} {
|
||||
let mut file = unsafe { file.unwrap_unchecked() };
|
||||
let mut data: Vec<u8> = Vec::new();
|
||||
unsafe { file.read_to_end(&mut data).unwrap_unchecked() };
|
||||
let path = unsafe { file.header().path().unwrap_unchecked() };
|
||||
let path_string = path.to_string_lossy();
|
||||
|
||||
// Match the file extention
|
||||
if path_string.ends_with("font_data") {
|
||||
font_data = Some(bincode::deserialize(&data).unwrap());
|
||||
} else if path_string.ends_with("text.json") {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
update_loading(&loading_element, 10);
|
||||
text_data = Some(TextData::from_json_str(unsafe {
|
||||
str::from_utf8(&data).unwrap_unchecked()
|
||||
}));
|
||||
} else {
|
||||
panic!("File {} not expected!", path_string);
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
cc.egui_ctx
|
||||
.set_fonts(font_data.expect("Failed to load font_data"));
|
||||
cc.egui_ctx.set_fonts(data.fonts);
|
||||
|
||||
// Set dark mode by default
|
||||
cc.egui_ctx.set_visuals(Visuals::dark());
|
||||
@ -208,7 +177,7 @@ impl MathApp {
|
||||
functions: Default::default(),
|
||||
last_info: (vec![None], None),
|
||||
dark_mode: true, // dark mode is default and is previously set
|
||||
text: text_data.expect("Didn't find text.json"),
|
||||
text: data.text,
|
||||
opened: Opened::default(),
|
||||
settings: Default::default(),
|
||||
}
|
||||
|
||||
45
src/misc.rs
45
src/misc.rs
@ -1,6 +1,5 @@
|
||||
use eframe::egui::plot::{Line, Points, Value as EguiValue, Values};
|
||||
use itertools::Itertools;
|
||||
use serde_json::Value as JsonValue;
|
||||
|
||||
#[cfg(threading)]
|
||||
use rayon::prelude::*;
|
||||
@ -185,50 +184,6 @@ impl EguiHelper for Vec<EguiValue> {
|
||||
fn to_tuple(&self) -> Vec<(f64, f64)> { self.iter().map(|ele| (ele.x, ele.y)).collect() }
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct TextData {
|
||||
pub help_expr: String,
|
||||
pub help_vars: String,
|
||||
pub help_panel: String,
|
||||
pub help_function: String,
|
||||
pub help_other: String,
|
||||
pub license_info: String,
|
||||
pub welcome: String,
|
||||
}
|
||||
|
||||
/// Parses an array of strings at `self.value[key]` as a multiline string
|
||||
fn parse_multiline(value: &JsonValue, key: &str) -> String {
|
||||
(value[key])
|
||||
.as_array()
|
||||
.expect("Cannot cast to array")
|
||||
.iter()
|
||||
.map(|ele| ele.as_str().unwrap())
|
||||
.fold(String::new(), |s, l| s + l + "\n")
|
||||
.trim_end()
|
||||
.to_owned()
|
||||
}
|
||||
|
||||
/// Parses `self.value[key]` as a single line string
|
||||
fn parse_singleline(value: &JsonValue, key: &str) -> String {
|
||||
value[key].as_str().expect("cannot cast to str").to_owned()
|
||||
}
|
||||
|
||||
impl TextData {
|
||||
pub fn from_json_str(string: &str) -> Self {
|
||||
let value = serde_json::from_str(string).expect("Cannot parse json file");
|
||||
|
||||
Self {
|
||||
help_expr: parse_multiline(&value, "help_expr"),
|
||||
help_vars: parse_multiline(&value, "help_vars"),
|
||||
help_panel: parse_multiline(&value, "help_panel"),
|
||||
help_function: parse_multiline(&value, "help_func"),
|
||||
help_other: parse_multiline(&value, "help_other"),
|
||||
license_info: parse_singleline(&value, "agpl_info"),
|
||||
welcome: parse_multiline(&value, "welcome"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Rounds f64 to `n` decimal places
|
||||
pub fn decimal_round(x: f64, n: usize) -> f64 {
|
||||
let large_number: f64 = 10.0_f64.powf(n as f64); // 10^n
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user