Files
YTBN-Graphing-Software/src/misc.rs

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)
}