167 lines
4.6 KiB
Rust
167 lines
4.6 KiB
Rust
use base64::Engine;
|
|
use base64::engine::general_purpose;
|
|
use egui_plot::{Line, PlotPoint, PlotPoints, Points};
|
|
use emath::Pos2;
|
|
use itertools::Itertools;
|
|
use parsing::FlatExWrapper;
|
|
|
|
/// Implements traits that are useful when dealing with Vectors of egui's `Value`
|
|
pub trait EguiHelper {
|
|
/// Converts to `egui::plot::Values`
|
|
fn to_values(self) -> PlotPoints<'static>;
|
|
|
|
/// Converts to `egui::plot::Line`
|
|
fn to_line(self) -> Line<'static>;
|
|
|
|
/// Converts to `egui::plot::Points`
|
|
fn to_points(self) -> Points<'static>;
|
|
|
|
/// Converts Vector of Values into vector of tuples
|
|
fn to_tuple(self) -> Vec<(f64, f64)>;
|
|
}
|
|
|
|
impl EguiHelper for Vec<PlotPoint> {
|
|
#[inline(always)]
|
|
fn to_values(self) -> PlotPoints<'static> {
|
|
PlotPoints::from(unsafe { std::mem::transmute::<Vec<PlotPoint>, Vec<[f64; 2]>>(self) })
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn to_line(self) -> Line<'static> {
|
|
Line::new("", self.to_values())
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn to_points(self) -> Points<'static> {
|
|
Points::new("", self.to_values())
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn to_tuple(self) -> Vec<(f64, f64)> {
|
|
unsafe { std::mem::transmute::<Vec<PlotPoint>, Vec<(f64, f64)>>(self) }
|
|
}
|
|
}
|
|
|
|
pub trait Offset {
|
|
fn offset_y(self, y_offset: f32) -> Pos2;
|
|
fn offset_x(self, x_offset: f32) -> Pos2;
|
|
}
|
|
|
|
impl Offset for Pos2 {
|
|
fn offset_y(self, y_offset: f32) -> Pos2 {
|
|
Pos2 {
|
|
x: self.x,
|
|
y: self.y + y_offset,
|
|
}
|
|
}
|
|
|
|
fn offset_x(self, x_offset: f32) -> Pos2 {
|
|
Pos2 {
|
|
x: self.x + x_offset,
|
|
y: self.y,
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
/// 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
|
|
|
|
// round and devide in order to cutoff after the `n`th decimal place
|
|
(x * large_number).round() / large_number
|
|
}
|
|
*/
|
|
|
|
/// Helper that assists with using newton's method of finding roots, iterating over data `data`
|
|
/// `threshold` is the target accuracy threshold
|
|
/// `range` is the range of valid x values (used to stop calculation when the point won't display anyways) `data` is the data to iterate over (a Vector of egui's `Value` struct)
|
|
/// `f` is f(x)
|
|
/// `f_1` is f'(x) aka the derivative of f(x)
|
|
/// The function returns a Vector of `x` values where roots occur
|
|
pub fn newtons_method_helper(
|
|
threshold: f64,
|
|
range: &std::ops::Range<f64>,
|
|
data: &[PlotPoint],
|
|
f: &FlatExWrapper,
|
|
f_1: &FlatExWrapper,
|
|
) -> Vec<f64> {
|
|
data.iter()
|
|
.tuple_windows()
|
|
.filter(|(prev, curr)| prev.y.is_finite() && curr.y.is_finite())
|
|
.filter(|(prev, curr)| prev.y.signum() != curr.y.signum())
|
|
.map(|(start, _)| start.x)
|
|
.filter_map(|x| newtons_method(f, f_1, x, range, threshold))
|
|
.collect()
|
|
}
|
|
|
|
/// `range` is the range of valid x values (used to stop calculation when
|
|
/// `f` is f(x)
|
|
/// `f_1` is f'(x) aka the derivative of f(x)
|
|
/// The function returns an `Option<f64>` of the x value at which a root occurs
|
|
pub fn newtons_method(
|
|
f: &FlatExWrapper,
|
|
f_1: &FlatExWrapper,
|
|
start_x: f64,
|
|
range: &std::ops::Range<f64>,
|
|
threshold: f64,
|
|
) -> Option<f64> {
|
|
let mut x1: f64 = start_x;
|
|
let mut x2: f64;
|
|
let mut derivative: f64;
|
|
loop {
|
|
derivative = f_1.eval(&[x1]);
|
|
if !derivative.is_finite() {
|
|
return None;
|
|
}
|
|
|
|
x2 = x1 - (f.eval(&[x1]) / derivative);
|
|
if !x2.is_finite() | !range.contains(&x2) {
|
|
return None;
|
|
}
|
|
|
|
// If below threshold, break
|
|
if (x2 - x1).abs() < threshold {
|
|
return Some(x2);
|
|
}
|
|
|
|
x1 = x2;
|
|
}
|
|
}
|
|
|
|
/// Inputs `Vec<Option<T>>` and outputs a `String` containing a pretty representation of the Vector
|
|
pub fn option_vec_printer<T: ToString>(data: &[Option<T>]) -> String {
|
|
let formatted: String = data
|
|
.iter()
|
|
.map(|item| match item {
|
|
Some(x) => x.to_string(),
|
|
None => "None".to_owned(),
|
|
})
|
|
.join(", ");
|
|
|
|
format!("[{}]", formatted)
|
|
}
|
|
|
|
/// Returns a vector of length `max_i` starting at value `min_x` with step of `step`
|
|
pub fn step_helper(max_i: usize, min_x: f64, step: f64) -> Vec<f64> {
|
|
(0..max_i)
|
|
.map(move |x: usize| (x as f64 * step) + min_x)
|
|
.collect()
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn hashed_storage_create(data: &[u8]) -> String {
|
|
general_purpose::STANDARD.encode(data)
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn hashed_storage_read(data: &str) -> Option<Vec<u8>> {
|
|
general_purpose::STANDARD.decode(data).ok()
|
|
}
|
|
|
|
include!(concat!(env!("OUT_DIR"), "/valid_chars.rs"));
|
|
|
|
pub fn is_valid_char(c: char) -> bool {
|
|
c.is_alphanumeric() | VALID_EXTRA_CHARS.contains(&c)
|
|
}
|