120 lines
3.6 KiB
Rust
120 lines
3.6 KiB
Rust
use crate::misc::{add_asterisks, Function};
|
|
|
|
pub enum UpdateType {
|
|
FULL,
|
|
FRONT,
|
|
BACK,
|
|
NONE,
|
|
}
|
|
|
|
// Manages Chart generation and caching of values
|
|
pub struct ChartManager {
|
|
function: Function,
|
|
min_x: f64,
|
|
max_x: f64,
|
|
num_interval: usize,
|
|
resolution: usize,
|
|
}
|
|
|
|
impl ChartManager {
|
|
pub fn new(
|
|
func_str: String, min_x: f64, max_x: f64, num_interval: usize, resolution: usize,
|
|
) -> Self {
|
|
Self {
|
|
function: Function::from_string(func_str),
|
|
min_x,
|
|
max_x,
|
|
num_interval,
|
|
resolution,
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
pub fn draw_back(&mut self) -> Vec<(f64, f64)> {
|
|
let absrange = (self.max_x - self.min_x).abs();
|
|
let output: Vec<(f64, f64)> = (1..=self.resolution)
|
|
.map(|x| ((x as f64 / self.resolution as f64) * absrange) + self.min_x)
|
|
.map(|x| (x, self.function.run(x)))
|
|
.collect();
|
|
output
|
|
}
|
|
|
|
#[inline]
|
|
pub fn draw_front(&mut self) -> (Vec<(f64, f64)>, f64) {
|
|
self.integral_rectangles(self.get_step())
|
|
}
|
|
|
|
#[inline]
|
|
pub fn get_step(&self) -> f64 { (self.max_x - self.min_x).abs() / (self.num_interval as f64) }
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub fn update(
|
|
&mut self, func_str_new: String, min_x: f64, max_x: f64, num_interval: usize,
|
|
resolution: usize,
|
|
) -> UpdateType {
|
|
let func_str: String = add_asterisks(func_str_new);
|
|
let update_func: bool = !self.function.str_compare(func_str.clone());
|
|
|
|
let update_back = update_func | (min_x != self.min_x) | (max_x != self.max_x);
|
|
let update_front =
|
|
update_back | (self.resolution != resolution) | (num_interval != self.num_interval);
|
|
|
|
if update_func {
|
|
self.function = Function::from_string(func_str);
|
|
}
|
|
|
|
self.min_x = min_x;
|
|
self.max_x = max_x;
|
|
self.num_interval = num_interval;
|
|
self.resolution = resolution;
|
|
|
|
if update_back && update_front {
|
|
UpdateType::FULL
|
|
} else if update_back {
|
|
UpdateType::BACK
|
|
} else if update_front {
|
|
UpdateType::FRONT
|
|
} else {
|
|
UpdateType::NONE
|
|
}
|
|
}
|
|
|
|
// Creates and does the math for creating all the rectangles under the graph
|
|
#[inline]
|
|
fn integral_rectangles(&self, step: f64) -> (Vec<(f64, f64)>, f64) {
|
|
let half_step = step / 2.0;
|
|
let data2: Vec<(f64, f64)> = (0..self.num_interval)
|
|
.map(|e| {
|
|
let x: f64 = ((e as f64) * step) + self.min_x;
|
|
|
|
// Makes sure rectangles are properly handled on x values below 0
|
|
let x2: f64 = match x > 0.0 {
|
|
true => x + step,
|
|
false => x - step,
|
|
};
|
|
|
|
let tmp1: f64 = self.function.run(x);
|
|
let tmp2: f64 = self.function.run(x2);
|
|
|
|
// Chooses the y value who's absolute value is the smallest
|
|
let mut output = match tmp2.abs() > tmp1.abs() {
|
|
true => (x, tmp1),
|
|
false => (x2, tmp2),
|
|
};
|
|
|
|
// Applies `half_step` in order to make the bar graph display properly
|
|
if output.0 > 0.0 {
|
|
output.0 += half_step;
|
|
} else {
|
|
output.0 -= half_step;
|
|
}
|
|
|
|
output
|
|
})
|
|
.filter(|(_, y)| !y.is_nan())
|
|
.collect();
|
|
let area: f64 = data2.iter().map(|(_, y)| y * step).sum(); // sum of all rectangles' areas
|
|
(data2, area)
|
|
}
|
|
}
|