HIGHLY optimize partial regen of values

This commit is contained in:
Simon Gardling
2022-05-24 14:04:02 -04:00
parent 4b29ce9333
commit ab8652ee3e
7 changed files with 99 additions and 211 deletions

View File

@@ -1,117 +1,9 @@
use std::{intrinsics::assume, ops::RangeInclusive};
use std::intrinsics::assume;
use egui::plot::{Line, Points, Value, Values};
use getrandom::getrandom;
use itertools::Itertools;
/// [`SteppedVector`] is used in order to efficiently sort through an ordered
/// `Vec<f64>` Used in order to speedup the processing of cached data when
/// moving horizontally without zoom in `FunctionEntry`. Before this struct, the
/// index was calculated with `.iter().position(....` which was horribly
/// inefficient
pub struct SteppedVector<'a> {
/// Actual data being referenced. HAS to be sorted from minimum to maximum
data: &'a [f64],
/// Since all entries in `data` are evenly spaced, this field stores the step between 2 adjacent elements
step: f64,
range: RangeInclusive<f64>,
}
impl<'a> SteppedVector<'a> {
/// Returns `Option<usize>` with index of element with value `x`. and `None` if `x` does not exist in `data`
#[inline]
pub fn get_index(&self, x: f64) -> Option<usize> {
debug_assert!(!x.is_nan());
debug_assert!(self.step > 0.0);
debug_assert!(self.step.is_sign_positive());
debug_assert!(self.step.is_finite());
debug_assert!(self.data.len() >= 2);
unsafe {
assume(!self.step.is_nan());
assume(self.step > 0.0);
assume(self.step.is_sign_positive());
assume(self.step.is_finite());
assume(self.data.len() >= 2);
}
if !self.range.contains(&x) {
return None;
}
if &x == self.get_min() {
return Some(0);
} else if &x == self.get_max() {
return Some(self.data.len() - 1);
}
// Do some math in order to calculate the expected index value
let possible_i = (x - self.get_min() / self.step) as usize;
// Make sure that the index is valid by checking the data returned vs the actual data (just in case)
if self.data.get(possible_i) == Some(&x) {
// It is valid!
Some(possible_i)
} else {
// (For some reason) it wasn't!
None
}
}
#[inline]
#[allow(dead_code)]
pub const fn get_min(&self) -> &f64 { self.range.start() }
#[inline]
#[allow(dead_code)]
pub const fn get_max(&self) -> &f64 { self.range.end() }
#[allow(dead_code)]
pub fn get_data(&self) -> &'a [f64] { self.data }
}
// Convert `&[f64]` into [`SteppedVector`]
impl<'a> From<&'a [f64]> for SteppedVector<'a> {
fn from(data: &'a [f64]) -> SteppedVector {
// Ensure data is of correct length
debug_assert!(data.len() > 2);
// check on debug if data is sorted
debug_assert!(data.windows(2).all(|w| w[0] <= w[1]));
unsafe {
assume(data.len() > 2);
assume(!data.is_empty());
}
// length of data subtracted by 1 (represents the maximum index value)
let max: f64 = data[data.len() - 1]; // The max value should be the last element
let min: f64 = data[0]; // The minimum value should be the first element
debug_assert!(max > min);
unsafe {
assume(max > min);
}
// Calculate the step between elements
let step = (max - min) / (data.len() as f64);
debug_assert!(step.is_sign_positive());
debug_assert!(step.is_finite());
debug_assert!(step > 0.0);
// Create and return the struct
SteppedVector {
data,
step,
range: min..=max,
}
}
}
/// Implements traits that are useful when dealing with Vectors of egui's `Value`
pub trait EguiHelper {
/// Converts to `egui::plot::Values`