pub mod multiplied_curve;
pub mod point;
pub mod polynomial;
pub mod pos;
use self::point::CurvePoint;
use super::AssetTrait;
use polynomial::BezierCurve;
use pos::CurvePos;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct Curve {
pub linked: bool,
pub points: Vec<CurvePoint>,
}
impl Eq for Curve {}
impl Default for Curve {
fn default() -> Self {
Self::alternating()
}
}
impl Curve {
pub(crate) fn alternating() -> Self {
let points = Self::linear_points(vec![
(0.0, 1.0),
(1.0, 0.0),
(2.0, 1.0),
(3.0, 0.0),
(4.0, 1.0),
]);
Self {
linked: true,
points,
}
}
pub fn invert_y_axis(&mut self) {
self.points
.iter_mut()
.for_each(|point| point.invert_y_axis());
}
pub fn invert_x_axis(&mut self) {
self.points
.iter_mut()
.for_each(|point| point.invert_x_axis());
}
fn linear_points(points: Vec<(f32, f32)>) -> Vec<CurvePoint> {
let mut linear_points = Vec::with_capacity(2 * points.len() - 1);
let mut prev = None;
for (i, (x, y)) in points.iter().enumerate() {
if let Some((prev_x, prev_y)) = prev {
linear_points.push(CurvePoint::Bezier(CurvePos::new(
(prev_x + x) / 2.0,
(prev_y + y) / 2.0,
)));
}
let pos = CurvePos::new(*x, *y);
linear_points.push(if i == 0 {
CurvePoint::First(pos)
} else if i == points.len() - 1 {
CurvePoint::Last(pos)
} else {
CurvePoint::Inner(pos)
});
prev = Some((x, y));
}
linear_points
}
pub fn value(&self, beat_position: f32) -> f32 {
assert!(
(0.0..=4.0).contains(&beat_position),
"Beat position out of range 0.0..=4.0: {beat_position}"
);
let value = self
.points
.iter()
.position(|point| point.pos().x > beat_position)
.and_then(|i| {
self.points.get(i).and_then(|point| {
if point.is_bezier() {
self.points.get(i - 1).and_then(|start| {
self.points
.get(i + 1)
.map(|end| BezierCurve::new(start.pos(), point.pos(), end.pos()))
})
} else {
self.points.get(i - 1).and_then(|bezier| {
self.points.get(i - 2).map(|start| {
BezierCurve::new(start.pos(), bezier.pos(), point.pos())
})
})
}
})
})
.map(|curve| curve.value(beat_position))
.unwrap_or_default();
1.0 - value
}
}
impl AssetTrait for Curve {
const DIR_NAME: &'static str = "curves";
const NAME: &'static str = "Curve";
const SHOW_NAME_IF_SELECTED: bool = false;
}