This commit is contained in:
Simon Gardling 2022-11-13 14:14:44 -05:00
parent b0a98b197b
commit 9a8f8a6539
11 changed files with 91 additions and 221 deletions

12
Cargo.lock generated
View File

@ -736,7 +736,7 @@ checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c"
[[package]]
name = "eframe"
version = "0.18.0"
source = "git+https://github.com/Titaniumtown/egui.git#913aac5a8c4faaedda8455ad91e13d702dd39df3"
source = "git+https://github.com/Titaniumtown/egui.git#d6194a56eb94e35a46d4391161d905058fc0dad9"
dependencies = [
"bytemuck",
"egui",
@ -756,7 +756,7 @@ dependencies = [
[[package]]
name = "egui"
version = "0.18.1"
source = "git+https://github.com/Titaniumtown/egui.git#913aac5a8c4faaedda8455ad91e13d702dd39df3"
source = "git+https://github.com/Titaniumtown/egui.git#d6194a56eb94e35a46d4391161d905058fc0dad9"
dependencies = [
"ahash",
"epaint",
@ -768,7 +768,7 @@ dependencies = [
[[package]]
name = "egui-winit"
version = "0.18.0"
source = "git+https://github.com/Titaniumtown/egui.git#913aac5a8c4faaedda8455ad91e13d702dd39df3"
source = "git+https://github.com/Titaniumtown/egui.git#d6194a56eb94e35a46d4391161d905058fc0dad9"
dependencies = [
"arboard",
"egui",
@ -782,7 +782,7 @@ dependencies = [
[[package]]
name = "egui_glow"
version = "0.18.1"
source = "git+https://github.com/Titaniumtown/egui.git#913aac5a8c4faaedda8455ad91e13d702dd39df3"
source = "git+https://github.com/Titaniumtown/egui.git#d6194a56eb94e35a46d4391161d905058fc0dad9"
dependencies = [
"bytemuck",
"egui",
@ -802,7 +802,7 @@ checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
[[package]]
name = "emath"
version = "0.18.0"
source = "git+https://github.com/Titaniumtown/egui.git#913aac5a8c4faaedda8455ad91e13d702dd39df3"
source = "git+https://github.com/Titaniumtown/egui.git#d6194a56eb94e35a46d4391161d905058fc0dad9"
dependencies = [
"bytemuck",
"serde",
@ -811,7 +811,7 @@ dependencies = [
[[package]]
name = "epaint"
version = "0.18.1"
source = "git+https://github.com/Titaniumtown/egui.git#913aac5a8c4faaedda8455ad91e13d702dd39df3"
source = "git+https://github.com/Titaniumtown/egui.git#d6194a56eb94e35a46d4391161d905058fc0dad9"
dependencies = [
"ab_glyph",
"ahash",

View File

@ -1,36 +0,0 @@
{
// help for supported expressions
help_expr: [
"abs, signum, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, floor, round, ceil, trunc, fract, exp, sqrt, cbrt, ln, log2, log10, log"
],
// help for supported variables
help_vars: [
"- Euler's number is supported via 'e' or 'E'",
"- PI is available through 'pi' or 'π'"
],
// help for buttons located on the top panel
help_panel: [
"- The 'Panel' button toggles if the side bar should be shown or not. This can also be accomplished by pressing the 'h' key.",
"- The 'Add Function' button adds a new function to be graphed. You can then configure that function in the side panel.",
"- The 'Help' button opens and closes this window!",
"- The 'Info' button provides information on the build currently running."
],
// help for buttons located in the drop down of functions
help_function: [
"(From Left to Right)",
"`✖` allows you to delete the selected function. Deleting a function is prevented if only 1 function exists.",
"`∫` toggles integration.",
"`d/dx` toggles the calculation of derivatives.",
"`⚙` opens a window to tweak function options."
],
// help for other "misc" things
help_other: [
"- Extrema (local minimums and maximums) and Roots (intersections with the x-axis) are displayed though yellow and light blue points respectively located on the graph. These can be toggled in the side panel."
],
// welcome text
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 the Help Window for more details)"
]
}

View File

@ -146,26 +146,7 @@ fn main() {
]),
};
let text_json: serde_json::Value = json5::from_str(include_str!("assets/text.json5")).unwrap();
let mut json_file_array = text_json.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().expect("failed to make a string"))
.collect::<Vec<&str>>()
.join("\n"),
);
}
}
let text_data: TextDataRaw = serde_json::from_value(serde_json::Value::Object(json_file_array))
.expect("Failed to convert data to TextDataRaw");
let data = bincode::serialize(&TotalData {
text: text_data.into_rich(),
fonts,
})
.unwrap();

View File

@ -26,7 +26,12 @@ impl FlatExWrapper {
fn partial(&self, x: usize) -> Self {
self.func
.as_ref()
.map(|f| f.partial(x).map(|a| Self::new(a)).unwrap_or(Self::EMPTY))
.map(|f| {
f.clone()
.partial(x)
.map(|a| Self::new(a))
.unwrap_or(Self::EMPTY)
})
.unwrap_or(Self::EMPTY)
}
@ -34,11 +39,12 @@ impl FlatExWrapper {
fn get_string(&self) -> &str { self.func.as_ref().map(|f| f.unparse()).unwrap_or("") }
#[inline]
fn partial_iter(&self, x: &[usize]) -> Self {
fn partial_iter(&self, n: usize) -> Self {
self.func
.as_ref()
.map(|f| {
f.partial_iter(x.iter())
f.clone()
.partial_iter((0..=n).map(|_| 0).into_iter())
.map(|a| Self::new(a))
.unwrap_or(Self::EMPTY)
})
@ -163,9 +169,7 @@ impl BackingFunction {
return curr_n_func.eval(&[x]);
}
}
let new_func = self
.function
.partial_iter((1..=n).map(|_| 0).collect::<Vec<usize>>().as_slice());
let new_func = self.function.partial_iter(n);
self.nth_derivative = Some((
n,

View File

@ -1,43 +1,6 @@
#[derive(PartialEq, serde::Serialize, serde::Deserialize)]
pub struct TextData {
pub help_expr: egui::RichText,
pub help_vars: egui::RichText,
pub help_panel: egui::RichText,
pub help_function: egui::RichText,
pub help_other: egui::RichText,
pub welcome: egui::RichText,
}
#[derive(PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct TextDataRaw {
pub help_expr: String,
pub help_vars: String,
pub help_panel: String,
pub help_function: String,
pub help_other: String,
pub welcome: String,
}
pub const FONT_SIZE: f32 = 14.0;
// ui.fonts().crate::data::FONT_SIZE(&egui::FontSelection::default().resolve(ui.style()));
impl TextDataRaw {
#[allow(dead_code)]
fn into_rich(self) -> TextData {
use egui::RichText;
TextData {
help_expr: RichText::from(self.help_expr),
help_vars: RichText::from(self.help_vars),
help_panel: RichText::from(self.help_panel),
help_function: RichText::from(self.help_function),
help_other: RichText::from(self.help_other),
welcome: RichText::from(self.welcome).size(FONT_SIZE + 1.0),
}
}
}
#[derive(serde::Serialize, serde::Deserialize, PartialEq)]
pub struct TotalData {
pub text: TextData,
pub fonts: epaint::text::FontDefinitions,
}

View File

@ -149,7 +149,7 @@ impl FunctionEntry {
pub const fn is_some(&self) -> bool { !self.function.is_none() }
pub fn settings_window(&mut self, ctx: &mut Context) {
pub fn settings_window(&mut self, ctx: &Context) {
let mut invalidate_nth = false;
egui::Window::new(format!("Settings: {}", self.raw_func_str))
.open(&mut self.settings_opened)
@ -382,8 +382,8 @@ impl FunctionEntry {
.cloned()
.collect::<Vec<Value>>()
.to_line()
.stroke(const { epaint::Stroke::none() })
.color(const { Color32::from_rgb(4, 4, 255) })
.stroke(epaint::Stroke::none())
.color(Color32::from_rgb(4, 4, 255))
.fill(0.0),
);
}

View File

@ -19,7 +19,7 @@ impl Default for FunctionManager {
fn default() -> Self {
let mut vec: Functions = Vec::with_capacity(COLORS.len());
vec.push((
Id::new_from_u64(11414819524356497634), // Random number here to avoid call to crate::misc::random_u64()
Id(11414819524356497634), // Random number here to avoid call to crate::misc::random_u64()
FunctionEntry::EMPTY,
));
Self { functions: vec }
@ -37,7 +37,7 @@ impl Serialize for FunctionManager {
&self
.functions
.iter()
.map(|(id, func)| (id.value(), func.clone()))
.map(|(id, func)| (id.0, func.clone()))
.collect::<Vec<(u64, FunctionEntry)>>(),
)?;
s.end()
@ -59,16 +59,14 @@ impl<'de> Deserialize<'de> for FunctionManager {
.0
.iter()
.cloned()
.map(|(id, func)| (egui::Id::new_from_u64(id), func))
.map(|(id, func)| (Id(id), func))
.collect::<Vec<(Id, FunctionEntry)>>(),
})
}
}
/// Function that creates button that's used with the `button_area`
const fn button_area_button(text: impl Into<WidgetText>) -> Button {
Button::new(text).frame(false)
}
fn button_area_button(text: impl Into<WidgetText>) -> Button { Button::new(text).frame(false) }
impl FunctionManager {
#[inline]
@ -94,8 +92,8 @@ impl FunctionManager {
let mut movement: Movement = Movement::default();
let size_multiplier = vec2(1.0, {
let had_focus = ui.ctx.memory().has_focus(*te_id);
(ui.ctx.animate_bool(*te_id, had_focus) * 1.5) + 1.0
let had_focus = ui.ctx().memory().has_focus(*te_id);
(ui.ctx().animate_bool(*te_id, had_focus) * 1.5) + 1.0
});
let re = ui.add_sized(
@ -117,7 +115,7 @@ impl FunctionManager {
new_string.retain(|c| crate::misc::is_valid_char(&c));
// 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 {
function.autocomplete.update_string(&new_string);
@ -145,7 +143,7 @@ impl FunctionManager {
// Doesn't need to have a number in id as there should only be 1 autocomplete popup in the entire gui
// hashed "autocomplete_popup"
const POPUP_ID: Id = Id::new_from_u64(7574801616484505465);
const POPUP_ID: Id = Id(7574801616484505465);
let mut clicked = false;
@ -166,17 +164,17 @@ impl FunctionManager {
movement = Movement::Complete;
} else {
ui.memory_mut().open_popup(POPUP_ID);
ui.memory().open_popup(POPUP_ID);
}
}
// Push cursor to end if needed
if movement == Movement::Complete {
let mut state =
unsafe { TextEdit::load_state(ui.ctx, *te_id).unwrap_unchecked() };
unsafe { TextEdit::load_state(ui.ctx(), *te_id).unwrap_unchecked() };
let ccursor = egui::text::CCursor::new(function.autocomplete.string.len());
state.set_ccursor_range(Some(egui::text::CCursorRange::one(ccursor)));
TextEdit::store_state(ui.ctx, *te_id, state);
TextEdit::store_state(ui.ctx(), *te_id, state);
}
}
@ -189,7 +187,7 @@ impl FunctionManager {
// There's more than 1 function! Functions can now be deleted
if ui
.add_enabled(can_remove, button_area_button(""))
.on_hover_text(ui.ctx, "Delete Function")
.on_hover_text("Delete Function")
.clicked()
{
remove_i = Some(i);
@ -199,39 +197,30 @@ impl FunctionManager {
// Toggle integral being enabled or not
function.integral.bitxor_assign(
ui.add(button_area_button(""))
.on_hover_text(
ui.ctx,
match function.integral {
.on_hover_text(match function.integral {
true => "Don't integrate",
false => "Integrate",
},
)
})
.clicked(),
);
// Toggle showing the derivative (even though it's already calculated this option just toggles if it's displayed or not)
function.derivative.bitxor_assign(
ui.add(button_area_button("d/dx"))
.on_hover_text(
ui.ctx,
match function.derivative {
.on_hover_text(match function.derivative {
true => "Don't Differentiate",
false => "Differentiate",
},
)
})
.clicked(),
);
// Toggle showing the settings window
function.settings_opened.bitxor_assign(
ui.add(button_area_button(""))
.on_hover_text(
ui.ctx,
match function.settings_opened {
.on_hover_text(match function.settings_opened {
true => "Close Settings",
false => "Open Settings",
},
)
})
.clicked(),
);
});
@ -239,7 +228,7 @@ impl FunctionManager {
});
}
function.settings_window(ui.ctx);
function.settings_window(ui.ctx());
}
// Remove function if the user requests it
@ -255,7 +244,7 @@ impl FunctionManager {
/// Create and push new empty function entry
pub fn push_empty(&mut self) {
self.functions.push((
Id::new_from_u64(random_u64().expect("unable to generate random id")),
Id(random_u64().expect("unable to generate random id")),
FunctionEntry::EMPTY,
));
}

View File

@ -1,6 +1,5 @@
use crate::{
consts::*, data::TextData, function_entry::Riemann, function_manager::FunctionManager,
misc::option_vec_printer,
consts::*, function_entry::Riemann, function_manager::FunctionManager, misc::option_vec_printer,
};
use eframe::App;
use egui::{
@ -102,9 +101,6 @@ pub struct MathApp {
/// Stores opened windows/elements for later reference
opened: Opened,
/// Stores loaded text data from `test.json`
text: TextData,
/// Stores settings (pretty self-explanatory)
settings: AppSettings,
}
@ -128,7 +124,7 @@ const FUNC_NAME: &str = "YTBN-FUNCTIONS";
impl MathApp {
#[allow(dead_code)] // This is used lol
/// Create new instance of [`MathApp`] and return it
pub fn new(cc: &mut eframe::CreationContext<'_, '_>) -> Self {
pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
#[cfg(threading)]
tracing::info!("Threading: Enabled");
@ -252,10 +248,10 @@ impl MathApp {
cc.egui_ctx.set_fonts(data.fonts);
// Set dark mode by default
cc.egui_ctx.set_visuals(crate::style::STYLE);
cc.egui_ctx.set_visuals(crate::style::style());
// Set spacing
cc.egui_ctx.set_spacing(crate::style::SPACING);
// cc.egui_ctx.set_spacing(crate::style::SPACING);
tracing::info!("Initialized! Took: {:?}", start.elapsed());
@ -267,14 +263,13 @@ impl MathApp {
functions: FunctionManager::default(),
last_info: (None, None),
text: data.text,
opened: const { Opened::default() },
settings: const { AppSettings::default() },
}
}
/// Creates SidePanel which contains configuration options
fn side_panel(&mut self, ctx: &mut Context) {
fn side_panel(&mut self, ctx: &Context) {
// Side Panel which contains vital options to the operation of the application
// (such as adding functions and other options)
SidePanel::left("side_panel")
@ -369,25 +364,19 @@ impl MathApp {
ui.horizontal(|ui| {
self.settings.do_extrema.bitxor_assign(
ui.add(Button::new("Extrema"))
.on_hover_text(
ui.ctx,
match self.settings.do_extrema {
.on_hover_text(match self.settings.do_extrema {
true => "Disable Displaying Extrema",
false => "Display Extrema",
},
)
})
.clicked(),
);
self.settings.do_roots.bitxor_assign(
ui.add(Button::new("Roots"))
.on_hover_text(
ui.ctx,
match self.settings.do_roots {
.on_hover_text(match self.settings.do_roots {
true => "Disable Displaying Roots",
false => "Display Roots",
},
)
})
.clicked(),
);
});
@ -428,7 +417,7 @@ impl MathApp {
impl App for MathApp {
/// Called each time the UI needs repainting.
fn update(&mut self, ctx: &mut Context, _frame: &mut eframe::Frame) {
fn update(&mut self, ctx: &Context, _frame: &mut eframe::Frame) {
// start timer
let start = if self.opened.info {
Some(instant::Instant::now())
@ -453,13 +442,10 @@ impl App for MathApp {
// Button in top bar to toggle showing the side panel
self.opened.side_panel.bitxor_assign(
ui.add(Button::new("Panel"))
.on_hover_text(
ui.ctx,
match self.opened.side_panel {
.on_hover_text(match self.opened.side_panel {
true => "Hide Side Panel",
false => "Show Side Panel",
},
)
})
.clicked(),
);
@ -469,7 +455,7 @@ impl App for MathApp {
COLORS.len() > self.functions.len(),
Button::new("Add Function"),
)
.on_hover_text(ui.ctx, "Create and graph new function")
.on_hover_text("Create and graph new function")
.clicked()
{
self.functions.push_empty();
@ -478,26 +464,20 @@ impl App for MathApp {
// Toggles opening the Help window
self.opened.help.bitxor_assign(
ui.add(Button::new("Help"))
.on_hover_text(
ui.ctx,
match self.opened.help {
.on_hover_text(match self.opened.help {
true => "Close Help Window",
false => "Open Help Window",
},
)
})
.clicked(),
);
// Toggles opening the Info window
self.opened.info.bitxor_assign(
ui.add(Button::new("Info"))
.on_hover_text(
ui.ctx,
match self.opened.info {
.on_hover_text(match self.opened.info {
true => "Close Info Window",
false => "Open Info Window",
},
)
})
.clicked(),
);
@ -516,23 +496,23 @@ impl App for MathApp {
.collapsible(false)
.show(ctx, |ui| {
ui.collapsing("Supported Expressions", |ui| {
ui.label(self.text.help_expr.clone());
ui.label("abs, signum, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, floor, round, ceil, trunc, fract, exp, sqrt, cbrt, ln, log2, log10, log");
});
ui.collapsing("Supported Constants", |ui| {
ui.label(self.text.help_vars.clone());
ui.label("- Euler's number is supported via 'e' or 'E'\n- PI is available through 'pi' or 'π'");
});
ui.collapsing("Panel", |ui| {
ui.label(self.text.help_panel.clone());
ui.label("- The 'Panel' button toggles if the side bar should be shown or not. This can also be accomplished by pressing the 'h' key.\n- The 'Add Function' button adds a new function to be graphed. You can then configure that function in the side panel.\n- The 'Help' button opens and closes this window!\n- The 'Info' button provides information on the build currently running.");
});
ui.collapsing("Functions", |ui| {
ui.label(self.text.help_function.clone());
ui.label("(From Left to Right)\n`✖` allows you to delete the selected function. Deleting a function is prevented if only 1 function exists.\n`∫` toggles integration.\n`d/dx` toggles the calculation of derivatives.\n`⚙` opens a window to tweak function options.");
});
ui.collapsing("Other", |ui| {
ui.label(self.text.help_other.clone());
ui.label("- Extrema (local minimums and maximums) and Roots (intersections with the x-axis) are displayed though yellow and light blue points respectively located on the graph. These can be toggled in the side panel.");
});
});
@ -544,7 +524,7 @@ impl App for MathApp {
.collapsible(false)
.title_bar(false)
.show(ctx, |ui| {
ui.label(self.text.welcome.clone());
ui.label("Welcome to the (Yet-to-be-named) Graphing Software!\n\nThis project aims to provide an intuitive experience graphing mathematical functions with features such as Integration, Differentiation, Extrema, Roots, and much more! (see the Help Window for more details)");
});
if let Some(response) = welcome_response {
@ -576,16 +556,12 @@ impl App for MathApp {
// Central panel which contains the central plot (or an error created when parsing)
CentralPanel::default()
.frame(
const {
Frame {
.frame(Frame {
inner_margin: Margin::symmetric(0.0, 0.0),
rounding: Rounding::none(),
fill: crate::style::STYLE.window_fill(),
// fill: crate::style::STYLE.window_fill(),
..Frame::none()
}
},
)
})
.show(ctx, |ui| {
// Display an error if it exists
let errors_formatted: String = self
@ -663,8 +639,4 @@ impl App for MathApp {
// Calculate and store the last time it took to draw the frame
self.last_info.1 = start.map(|a| format!("Took: {}ms", a.elapsed().as_micros()));
}
fn clear_color(&self, _visuals: &egui::Visuals) -> egui::Rgba {
crate::style::STYLE.window_fill().into()
}
}

View File

@ -17,7 +17,7 @@ pub trait EguiHelper {
fn to_tuple(self) -> Vec<(f64, f64)>;
}
impl const EguiHelper for Vec<Value> {
impl EguiHelper for Vec<Value> {
#[inline(always)]
fn to_values(self) -> Values { Values::from_values(self) }

View File

@ -5,7 +5,7 @@ use egui::{
use emath::vec2;
use epaint::{Color32, Rounding, Shadow, Stroke};
const fn widgets_dark() -> Widgets {
fn widgets_dark() -> Widgets {
Widgets {
noninteractive: WidgetVisuals {
bg_fill: Color32::from_gray(27), // window background
@ -45,10 +45,7 @@ const fn widgets_dark() -> Widgets {
}
}
pub const STYLE: Visuals = dark();
pub const SPACING: Spacing = spacing();
const fn dark() -> Visuals {
pub fn style() -> Visuals {
Visuals {
dark_mode: true,
override_text_color: None,
@ -70,7 +67,7 @@ const fn dark() -> Visuals {
}
}
const fn spacing() -> Spacing {
pub fn spacing() -> Spacing {
Spacing {
item_spacing: vec2(8.0, 3.0),
window_margin: Margin::same(6.0),

View File

@ -7,8 +7,8 @@ pub fn widgets_ontop<R>(
add_contents: impl FnOnce(&mut egui::Ui) -> R,
) -> InnerResponse<R> {
let area = egui::Area::new(id)
.fixed_pos(re.rect().min.offset_y(y_offset))
.fixed_pos(re.rect.min.offset_y(y_offset))
.order(egui::Order::Foreground);
area.show(ui.ctx, |ui| add_contents(ui))
area.show(ui.ctx(), |ui| add_contents(ui))
}