use std::collections::Bound;
use std::ops::{RangeBounds, RangeInclusive};
use crate::items::{ExplicitGenerator, Value};
use crate::transform::Bounds;
pub struct Values {
pub values: Vec<Value>,
generator: Option<ExplicitGenerator>,
}
impl Values {
pub fn from_values(values: Vec<Value>) -> Self {
Self {
values,
generator: None,
}
}
pub fn from_values_iter(iter: impl Iterator<Item = Value>) -> Self {
Self::from_values(iter.collect())
}
pub fn from_explicit_callback(
function: impl Fn(f64) -> f64 + 'static,
x_range: impl RangeBounds<f64>,
points: usize,
) -> Self {
let start = match x_range.start_bound() {
Bound::Included(x) | Bound::Excluded(x) => *x,
Bound::Unbounded => f64::NEG_INFINITY,
};
let end = match x_range.end_bound() {
Bound::Included(x) | Bound::Excluded(x) => *x,
Bound::Unbounded => f64::INFINITY,
};
let x_range = start..=end;
let generator = ExplicitGenerator {
function: Box::new(function),
x_range,
points,
};
Self {
values: Vec::new(),
generator: Some(generator),
}
}
pub fn from_parametric_callback(
function: impl Fn(f64) -> (f64, f64),
t_range: impl RangeBounds<f64>,
points: usize,
) -> Self {
let start = match t_range.start_bound() {
Bound::Included(x) => x,
Bound::Excluded(_) => unreachable!(),
Bound::Unbounded => panic!("The range for parametric functions must be bounded!"),
};
let end = match t_range.end_bound() {
Bound::Included(x) | Bound::Excluded(x) => x,
Bound::Unbounded => panic!("The range for parametric functions must be bounded!"),
};
let last_point_included = matches!(t_range.end_bound(), Bound::Included(_));
let increment = if last_point_included {
(end - start) / (points - 1) as f64
} else {
(end - start) / points as f64
};
let values = (0..points).map(|i| {
let t = start + i as f64 * increment;
let (x, y) = function(t);
Value { x, y }
});
Self::from_values_iter(values)
}
pub fn from_ys_f32(ys: &[f32]) -> Self {
let values: Vec<Value> = ys
.iter()
.enumerate()
.map(|(i, &y)| Value {
x: i as f64,
y: y as f64,
})
.collect();
Self::from_values(values)
}
pub fn is_empty(&self) -> bool {
self.generator.is_none() && self.values.is_empty()
}
pub fn generate_points(&mut self, x_range: RangeInclusive<f64>) {
if let Some(generator) = self.generator.take() {
if let Some(intersection) = Self::range_intersection(&x_range, &generator.x_range) {
let increment =
(intersection.end() - intersection.start()) / (generator.points - 1) as f64;
self.values = (0..generator.points)
.map(|i| {
let x = intersection.start() + i as f64 * increment;
let y = (generator.function)(x);
Value { x, y }
})
.collect();
}
}
}
fn range_intersection(
range1: &RangeInclusive<f64>,
range2: &RangeInclusive<f64>,
) -> Option<RangeInclusive<f64>> {
let start = range1.start().max(*range2.start());
let end = range1.end().min(*range2.end());
(start < end).then_some(start..=end)
}
pub(crate) fn get_bounds(&self) -> Bounds {
let mut bounds = Bounds::NOTHING;
self.values
.iter()
.for_each(|value| bounds.extend_with(value));
bounds
}
}