it works!!

This commit is contained in:
Simon Gardling 2022-05-11 12:38:24 -04:00
parent a9a870ec2f
commit 24d2a5bbd8
13 changed files with 106 additions and 142 deletions

3
.gitignore vendored
View File

@ -1,6 +1,5 @@
/target /target
/pkg /pkg
/tmp /tmp
/assets.tar.zst
/Cargo.lock /Cargo.lock
/assets/font_data /assets/data

64
Cargo.lock generated
View File

@ -794,18 +794,6 @@ dependencies = [
"instant", "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]] [[package]]
name = "findshlibs" name = "findshlibs"
version = "0.10.2" version = "0.10.2"
@ -2164,17 +2152,6 @@ dependencies = [
"unicode-xid", "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]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.3.0" version = "3.3.0"
@ -2783,15 +2760,6 @@ dependencies = [
"winapi-wsapoll", "winapi-wsapoll",
] ]
[[package]]
name = "xattr"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "xcursor" name = "xcursor"
version = "0.3.4" version = "0.3.4"
@ -2828,10 +2796,10 @@ dependencies = [
"parsing", "parsing",
"rayon", "rayon",
"ruzstd", "ruzstd",
"serde",
"serde_json", "serde_json",
"shadow-rs", "shadow-rs",
"static_assertions", "static_assertions",
"tar",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
"tracing-wasm", "tracing-wasm",
@ -2839,4 +2807,34 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
"web-sys", "web-sys",
"wee_alloc", "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",
] ]

View File

@ -47,7 +47,6 @@ shadow-rs = { version = "0.11", default-features = false }
const_format = { version = "0.2", default-features = false, features = ["fmt"] } const_format = { version = "0.2", default-features = false, features = ["fmt"] }
cfg-if = "1" cfg-if = "1"
lazy_static = "1" lazy_static = "1"
tar = "0.4"
ruzstd = { git = "https://github.com/Titaniumtown/zstd-rs.git", branch = "ringbuffer" } ruzstd = { git = "https://github.com/Titaniumtown/zstd-rs.git", branch = "ringbuffer" }
serde_json = "1.0" serde_json = "1.0"
tracing = "0.1" tracing = "0.1"
@ -55,6 +54,7 @@ itertools = "0.10"
static_assertions = "1.1" static_assertions = "1.1"
uuid = { version = "1", features = ["v4", "fast-rng", "js"] } uuid = { version = "1", features = ["v4", "fast-rng", "js"] }
bincode = "1.3.3" bincode = "1.3.3"
serde = "1"
[dev-dependencies] [dev-dependencies]
@ -67,6 +67,9 @@ epaint = { git = "https://github.com/Titaniumtown/egui.git", default-features =
"serde", "serde",
] } ] }
bincode = "1.3.3" 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] [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
instant = "0.1" instant = "0.1"

View File

@ -21,3 +21,4 @@
11. Fix mobile text input 11. Fix mobile text input
12. Don't use json for text storage 12. Don't use json for text storage
13. Write custom plotter 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)

View File

@ -13,7 +13,7 @@
"- The 'Info' button provides information on the build currently running.", "- The 'Info' button provides information on the build currently running.",
"- The Sun/Moon button toggles Dark and Light mode." "- The Sun/Moon button toggles Dark and Light mode."
], ],
"help_func": [ "help_function": [
"(From Left to Right)", "(From Left to Right)",
"- The `✖` allows you to delete the function in question. Deleting a function is prevented if only 1 function exists.", "- 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.", "- 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.", "- 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." "- 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": [
"Welcome to the (Yet-to-be-named) Graphing Software!", "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)" "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)"

View File

@ -9,6 +9,8 @@ use epaint::{
FontFamily, FontFamily,
}; };
include!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/data.rs"));
fn main() { fn main() {
// rebuild if new commit or contents of `assets` folder changed // rebuild if new commit or contents of `assets` folder changed
println!("cargo:rerun-if-changed=.git/logs/HEAD"); 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(); let aa: serde_json::Value = serde_json::from_str(include_str!("assets/text.json")).unwrap();
// ::to_string(&fonts).expect("Failed to serialize fonts"); let mut json_file_array = aa.as_object().unwrap().clone();
for value in json_file_array.iter_mut() {
let mut file = BufWriter::new(File::create(&path).expect("Could not create file")); if let serde_json::Value::Array(values) = value.1 {
let values_copy = values.clone();
file.write_all(fonts_data.as_slice()).unwrap(); *value.1 = serde_json::Value::String(
values_copy
let _ = command_run::Command::with_args("./pack_assets.sh", &[&path]) .iter()
.enable_capture() .map(|s| s.as_str().unwrap())
.run(); .collect::<Vec<&str>>()
.join("\n"),
);
}
}
let text_data: TextData =
serde_json::from_value(serde_json::Value::Object(json_file_array)).unwrap();
let data = bincode::serialize(&TotalData {
text: text_data,
fonts,
})
.unwrap();
let data_compressed = zstd::encode_all(data.as_slice(), 22).unwrap();
println!("{:?}", data_compressed);
file.write_all(data_compressed.as_slice()).unwrap();
} }

View File

@ -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
View 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,
}

View File

@ -32,7 +32,7 @@ impl FunctionManager {
// ui.label("Functions:"); // ui.label("Functions:");
let can_remove = self.functions.len() > 1; 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; const ROW_HEIGHT: f32 = 14.0;
// ui.fonts().row_height(&egui::FontSelection::default().resolve(ui.style())); // ui.fonts().row_height(&egui::FontSelection::default().resolve(ui.style()));
@ -55,9 +55,9 @@ impl FunctionManager {
egui::TextEdit::singleline(&mut new_string) egui::TextEdit::singleline(&mut new_string)
.hint_forward(true) // Make the hint appear after the last text in the textbox .hint_forward(true) // Make the hint appear after the last text in the textbox
.lock_focus(true) .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({ .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 { if let Hint::Single(single_hint) = function.autocomplete.hint {
*single_hint *single_hint
} else { } 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 { if ui.ctx().animate_bool(*te_id, re.has_focus()) >= 1.0 {
function.autocomplete.update_string(&new_string); function.autocomplete.update_string(&new_string);
@ -110,7 +110,7 @@ impl FunctionManager {
if clicked { if clicked {
function.autocomplete.apply_hint(hints[function.autocomplete.i]); 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(); // ui.memory().close_popup();
movement = Movement::Complete; movement = Movement::Complete;
@ -190,11 +190,13 @@ impl FunctionManager {
} }
} }
/// Create and push new empty function entry
pub fn push_empty(&mut self) { pub fn push_empty(&mut self) {
self.functions self.functions
.push((Id::new(Uuid::new_v4()), FunctionEntry::EMPTY)); .push((Id::new(Uuid::new_v4()), FunctionEntry::EMPTY));
} }
/// Detect if any functions are using integrals
pub fn any_using_integral(&self) -> bool { pub fn any_using_integral(&self) -> bool {
self.functions.iter().any(|(_, func)| func.integral) self.functions.iter().any(|(_, func)| func.integral)
} }

View File

@ -13,6 +13,7 @@ extern crate static_assertions;
extern crate uuid; extern crate uuid;
mod consts; mod consts;
mod data;
mod function_entry; mod function_entry;
mod function_manager; mod function_manager;
mod math_app; mod math_app;

View File

@ -11,6 +11,7 @@ extern crate static_assertions;
extern crate uuid; extern crate uuid;
mod consts; mod consts;
mod data;
mod function_entry; mod function_entry;
mod function_manager; mod function_manager;
mod math_app; mod math_app;

View File

@ -1,17 +1,17 @@
use crate::consts::*; use crate::consts::*;
use crate::data::TextData;
use crate::function_entry::Riemann; use crate::function_entry::Riemann;
use crate::function_manager::FunctionManager; 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 eframe::App;
use egui::{ use egui::{
plot::Plot, style::Margin, vec2, Button, CentralPanel, Color32, ComboBox, Context, plot::Plot, style::Margin, vec2, Button, CentralPanel, Color32, ComboBox, Context, Frame, Key,
FontDefinitions, Frame, Key, Label, Layout, RichText, SidePanel, Slider, TopBottomPanel, Vec2, Label, Layout, RichText, SidePanel, Slider, TopBottomPanel, Vec2, Visuals, Window,
Visuals, Window,
}; };
use emath::{Align, Align2}; use emath::{Align, Align2};
use epaint::Rounding; use epaint::Rounding;
use instant::Duration; use instant::Duration;
use std::{io::Read, ops::BitXorAssign, str}; use std::{io::Read, ops::BitXorAssign};
#[cfg(threading)] #[cfg(threading)]
use rayon::iter::{IndexedParallelIterator, ParallelIterator}; use rayon::iter::{IndexedParallelIterator, ParallelIterator};
@ -142,55 +142,24 @@ impl MathApp {
tracing::info!("Initializing..."); tracing::info!("Initializing...");
let start = instant::Instant::now(); let start = instant::Instant::now();
let mut tar_file_data = Vec::new(); let mut data = Vec::new();
let _ = unsafe { 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() .unwrap_unchecked()
.read_to_end(&mut tar_file_data) .read_to_end(&mut data)
.unwrap_unchecked() .unwrap_unchecked()
}; };
let data: crate::data::TotalData = bincode::deserialize(data.as_slice()).unwrap();
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
update_loading(&loading_element, 30); 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..."); 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 // 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 cc.egui_ctx.set_fonts(data.fonts);
.set_fonts(font_data.expect("Failed to load font_data"));
// Set dark mode by default // Set dark mode by default
cc.egui_ctx.set_visuals(Visuals::dark()); cc.egui_ctx.set_visuals(Visuals::dark());
@ -208,7 +177,7 @@ impl MathApp {
functions: Default::default(), functions: Default::default(),
last_info: (vec![None], None), last_info: (vec![None], None),
dark_mode: true, // dark mode is default and is previously set 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(), opened: Opened::default(),
settings: Default::default(), settings: Default::default(),
} }

View File

@ -1,6 +1,5 @@
use eframe::egui::plot::{Line, Points, Value as EguiValue, Values}; use eframe::egui::plot::{Line, Points, Value as EguiValue, Values};
use itertools::Itertools; use itertools::Itertools;
use serde_json::Value as JsonValue;
#[cfg(threading)] #[cfg(threading)]
use rayon::prelude::*; 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() } 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 /// Rounds f64 to `n` decimal places
pub fn decimal_round(x: f64, n: usize) -> f64 { pub fn decimal_round(x: f64, n: usize) -> f64 {
let large_number: f64 = 10.0_f64.powf(n as f64); // 10^n let large_number: f64 = 10.0_f64.powf(n as f64); // 10^n