├── README.md ├── .travis.yml ├── src ├── lib.rs ├── point.rs ├── bezier_point.rs ├── ease_point.rs ├── interpolate.rs ├── serde.rs └── envelope.rs ├── .gitignore ├── Cargo.toml └── tests └── impl_envelope_for_vec.rs /README.md: -------------------------------------------------------------------------------- 1 | # envelope [![Build Status](https://travis-ci.org/RustAudio/envelope.svg?branch=master)](https://travis-ci.org/RustAudio/envelope) 2 | 3 | An interpolatable Envelope trait along with a generic 2D Point trait. Useful for controlling parameters over time. 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | rust: 4 | - nightly 5 | - beta 6 | - stable 7 | 8 | os: 9 | - osx 10 | - linux 11 | 12 | script: 13 | - cargo build --verbose 14 | - cargo test --verbose 15 | - cargo test --features="serde_serialization" --verbose 16 | - cargo doc --verbose 17 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub extern crate interpolation; 2 | extern crate num; 3 | 4 | pub use bezier_point::BezierPoint; 5 | pub use ease_point::EasePoint; 6 | pub use envelope::{Envelope, Steps}; 7 | pub use point::Point; 8 | 9 | mod bezier_point; 10 | mod ease_point; 11 | mod envelope; 12 | pub mod interpolate; 13 | mod point; 14 | 15 | #[cfg(feature="serde_serialization")] 16 | mod serde; 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # RUST STUFF 2 | 3 | # Compiled files 4 | *.o 5 | *.so 6 | *.rlib 7 | *.dll 8 | 9 | # Executables 10 | *.exe 11 | 12 | # Generated by Cargo 13 | /target/ 14 | Cargo.lock 15 | 16 | 17 | 18 | # MAC STUFF 19 | 20 | .DS_Store 21 | .AppleDouble 22 | .LSOverride 23 | 24 | # Icon must end with two \r 25 | Icon 26 | 27 | # Thumbnails 28 | ._* 29 | 30 | # Files that might appear on external disk 31 | .Spotlight-V100 32 | .Trashes 33 | 34 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "envelope" 3 | description = "An interpolatable Envelope trait along with a generic 2D Point trait. Useful for controlling parameters over time." 4 | version = "0.8.1" 5 | authors = ["mitchmindtree "] 6 | readme = "README.md" 7 | keywords = ["interpolate", "dsp", "audio", "time", "2D"] 8 | license = "MIT" 9 | repository = "https://github.com/RustAudio/envelope.git" 10 | homepage = "https://github.com/RustAudio/envelope" 11 | 12 | [dependencies] 13 | interpolation = "0.1.0" 14 | num = "0.1.31" 15 | serde = { optional = true, version = "0.7.0" } 16 | serde_json = { optional = true, version = "0.7.0" } 17 | 18 | [features] 19 | serde_serialization = ["serde", "serde_json"] 20 | -------------------------------------------------------------------------------- /src/point.rs: -------------------------------------------------------------------------------- 1 | use interpolate::{self, Scalar}; 2 | use interpolation::Spatial; 3 | 4 | /// Implement this for types to be used as points on an Envelope. 5 | pub trait Point: Clone { 6 | type X: PartialEq + Clone; 7 | type Y: PartialEq + Spatial; 8 | 9 | /// Convert X to Y's Scalar. 10 | fn x_to_scalar(x: Self::X) -> ::Scalar; 11 | /// X (often associated with time). 12 | fn x(&self) -> Self::X; 13 | /// Y (often associated with some value). 14 | fn y(&self) -> Self::Y; 15 | /// Interpolate between two points and return y for the given x. 16 | #[inline] 17 | fn interpolate(x: Self::X, start: &Self, end: &Self) -> Self::Y 18 | where ::Scalar: Scalar, 19 | { 20 | interpolate::linear(x, start, end) 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /src/bezier_point.rs: -------------------------------------------------------------------------------- 1 | use interpolate; 2 | use interpolation::Spatial; 3 | use num::{Float, NumCast}; 4 | use point::Point; 5 | 6 | 7 | /// A type whose interpolation may involve some quadratic bezier curve. 8 | #[derive(Debug, Clone, Copy, PartialEq)] 9 | pub struct BezierPoint where 10 | X: Clone + Copy, 11 | Y: Spatial + Clone + Copy, 12 | Y::Scalar: Float, 13 | { 14 | pub x: X, 15 | pub y: Y, 16 | pub curve: Y::Scalar, 17 | } 18 | 19 | 20 | impl BezierPoint where 21 | X: Clone + Copy, 22 | Y: Spatial + Clone + Copy, 23 | Y::Scalar: Float, 24 | { 25 | /// Constructor for a BezierPoint. 26 | #[inline] 27 | pub fn new(x: X, y: Y, curve: Y::Scalar) -> BezierPoint { 28 | BezierPoint { 29 | x: x, 30 | y: y, 31 | curve: curve, 32 | } 33 | } 34 | } 35 | 36 | 37 | impl Point for BezierPoint 38 | where X: PartialEq + NumCast + Clone + Copy, 39 | Y: PartialEq + NumCast + Spatial + Clone + Copy, 40 | Y::Scalar: Float, 41 | { 42 | type X = X; 43 | type Y = Y; 44 | #[inline(always)] 45 | fn x_to_scalar(x: X) -> Y::Scalar { 46 | NumCast::from(x).unwrap() 47 | } 48 | #[inline(always)] 49 | fn x(&self) -> X { self.x } 50 | #[inline(always)] 51 | fn y(&self) -> Y { self.y } 52 | /// Interpolate between two points and return y for the given x. 53 | #[inline(always)] 54 | fn interpolate(x: X, start: &Self, end: &Self) -> Y where 55 | X: PartialEq, 56 | Y: PartialEq, 57 | { 58 | interpolate::bezier(x, start, end, start.curve) 59 | } 60 | } 61 | 62 | -------------------------------------------------------------------------------- /src/ease_point.rs: -------------------------------------------------------------------------------- 1 | use interpolate; 2 | use interpolation::{Ease, EaseFunction, Spatial}; 3 | use point::Point; 4 | use num::{Float, NumCast}; 5 | 6 | 7 | /// A type whose interpolation may involve some sort of easing. 8 | #[derive(Clone, Copy)] 9 | pub struct EasePoint 10 | where X: PartialEq + NumCast + Clone + Copy, 11 | Y: PartialEq + Spatial + Clone + Copy, 12 | Y::Scalar: Float + Ease, 13 | { 14 | pub x: X, 15 | pub y: Y, 16 | pub maybe_ease_fn: Option, 17 | } 18 | 19 | 20 | impl EasePoint 21 | where X: PartialEq + NumCast + Clone + Copy, 22 | Y: PartialEq + Spatial + Clone + Copy, 23 | Y::Scalar: Float + Ease, 24 | { 25 | /// Constructor for an EasePoint. 26 | #[inline] 27 | pub fn new(x: X, y: Y, maybe_ease_fn: Option) -> EasePoint { 28 | EasePoint { 29 | x: x, 30 | y: y, 31 | maybe_ease_fn: maybe_ease_fn, 32 | } 33 | } 34 | } 35 | 36 | 37 | impl Point for EasePoint 38 | where X: PartialEq + NumCast + Clone + Copy, 39 | Y: PartialEq + Spatial + Clone + Copy, 40 | Y::Scalar: Float + Ease, 41 | { 42 | type X = X; 43 | type Y = Y; 44 | 45 | #[inline(always)] 46 | fn x_to_scalar(x: X) -> Y::Scalar { 47 | NumCast::from(x).unwrap() 48 | } 49 | #[inline(always)] 50 | fn x(&self) -> X { self.x } 51 | #[inline(always)] 52 | fn y(&self) -> Y { self.y } 53 | #[inline(always)] 54 | fn interpolate(x: X, start: &Self, end: &Self) -> Y { 55 | match start.maybe_ease_fn { 56 | Some(ease_fn) => interpolate::ease(x, start, end, ease_fn), 57 | None => interpolate::linear(x, start, end), 58 | } 59 | } 60 | } 61 | 62 | -------------------------------------------------------------------------------- /src/interpolate.rs: -------------------------------------------------------------------------------- 1 | 2 | use interpolation::{Ease, EaseFunction, Spatial}; 3 | use num::NumCast; 4 | use point::Point; 5 | use std; 6 | 7 | 8 | /// Check if the given x lands exactly on either the start or end point and return the Y value if 9 | /// it does. 10 | #[inline] 11 | fn maybe_exact_point

(x: &P::X, start: &P, end: &P) -> Option 12 | where P: Point, 13 | { 14 | // No need to interpolate if: 15 | // - both y values are the same 16 | // - start_x and x are the same 17 | // - end_x and x are the same 18 | if start.y() == end.y() || start.x() == *x { 19 | Some(start.y()) 20 | } else if end.x() == *x { 21 | Some(end.y()) 22 | } else { 23 | None 24 | } 25 | } 26 | 27 | 28 | /// Set of traits required by ::Scalar. 29 | pub trait Scalar: Sized + Clone 30 | + std::ops::Add 31 | + std::ops::Sub 32 | + std::ops::Mul 33 | + std::ops::Div {} 34 | impl Scalar for T 35 | where T: Sized + Clone 36 | + std::ops::Add 37 | + std::ops::Sub 38 | + std::ops::Mul 39 | + std::ops::Div {} 40 | 41 | 42 | /// Interpolate linearly between the start and end points. 43 | #[inline] 44 | pub fn linear

(x: P::X, start: &P, end: &P) -> P::Y 45 | where P: Point, 46 | P::X: Clone, 47 | ::Scalar: Scalar, 48 | { 49 | maybe_exact_point(&x, start, end).unwrap_or_else(|| { 50 | let x = P::x_to_scalar(x.clone()); 51 | let start_x = P::x_to_scalar(start.x()); 52 | let end_x = P::x_to_scalar(end.x()); 53 | let scalar = (x - start_x.clone()) / (end_x - start_x); 54 | let difference = end.y().sub(&start.y()); 55 | let interpolated_difference = difference.scale(&scalar); 56 | start.y().add(&interpolated_difference) 57 | }) 58 | } 59 | 60 | 61 | /// Interpolate between the start and end points using the given easing function. 62 | #[inline] 63 | pub fn ease

(x: P::X, start: &P, end: &P, ease_fn: EaseFunction) -> P::Y 64 | where P: Point, 65 | ::Scalar: Ease + Scalar, 66 | { 67 | maybe_exact_point(&x, start, end).unwrap_or_else(|| { 68 | let x = P::x_to_scalar(x); 69 | let start_x = P::x_to_scalar(start.x()); 70 | let end_x = P::x_to_scalar(end.x()); 71 | let scalar = (x.clone() - start_x.clone()) / (end_x - start_x); 72 | let eased_scalar = Ease::calc(scalar, ease_fn); 73 | let difference = end.y().sub(&start.y()); 74 | let interpolated_difference = difference.scale(&eased_scalar); 75 | start.y().add(&interpolated_difference) 76 | }) 77 | } 78 | 79 | 80 | /// Get bezier point for bezier curve. 81 | #[inline] 82 | fn bezier_pt(n1: T, n2: T, perc: T) -> T 83 | where T: Scalar 84 | { 85 | (n2 - n1.clone()) * perc + n1 86 | } 87 | 88 | /// Interpolate between the given start and end points given some bezier curve. 89 | #[inline] 90 | pub fn bezier

(x: P::X, start: &P, end: &P, curve: ::Scalar) -> P::Y 91 | where P: Point, 92 | P::Y: NumCast, 93 | ::Scalar: Scalar + NumCast, 94 | { 95 | maybe_exact_point(&x, start, end).unwrap_or_else(|| { 96 | let x = P::x_to_scalar(x.clone()); 97 | let start_x = P::x_to_scalar(start.x()); 98 | let end_x = P::x_to_scalar(end.x()); 99 | // Find x passed from start of interpolation. 100 | let x_pos = x - start_x.clone(); 101 | // Find duration of interpolation. 102 | let duration = end_x - start_x; 103 | 104 | // Set gradient for interpolation. 105 | let end_y: ::Scalar = NumCast::from(end.y()).unwrap(); 106 | let start_y: ::Scalar = NumCast::from(start.y()).unwrap(); 107 | let diff_y: ::Scalar = end_y - start_y; 108 | let half_diff_y: ::Scalar = diff_y.clone() / NumCast::from(2.0).unwrap(); 109 | // Consider bezier curve. 110 | let y2 = half_diff_y.clone() + curve * half_diff_y; 111 | let perc_x = x_pos / duration; 112 | // Re-adjust linear trajectory. 113 | let zero: ::Scalar = NumCast::from(0.0).unwrap(); 114 | let ya = bezier_pt(zero, y2.clone(), perc_x.clone()); 115 | let yb = bezier_pt(y2, diff_y, perc_x.clone()); 116 | let y = NumCast::from(bezier_pt(ya, yb, perc_x)).unwrap(); 117 | start.y().add(&y) 118 | }) 119 | } 120 | -------------------------------------------------------------------------------- /src/serde.rs: -------------------------------------------------------------------------------- 1 | extern crate serde; 2 | 3 | // mod bezier_point { 4 | // use bezier_point::BezierPoint; 5 | // use interpolation::Spatial; 6 | // use num::Float; 7 | // 8 | // impl_serde_serialization!(struct BezierPoint { 9 | // generics: [X, Y], 10 | // where: { 11 | // X: [Clone, Copy], 12 | // Y: [Spatial, Clone, Copy], 13 | // ::Scalar: [Float], 14 | // }, 15 | // fields: { 16 | // 0 => x: X, 17 | // 1 => y: Y, 18 | // 2 => curve: Y::Scalar, 19 | // }, 20 | // }); 21 | // 22 | // #[test] 23 | // fn test() { 24 | // extern crate serde_json; 25 | // 26 | // let point = BezierPoint { x: 42, y: 5.0, curve: 0.0 }; 27 | // let serialized = serde_json::to_string(&point).unwrap(); 28 | // 29 | // println!("{}", serialized); 30 | // assert_eq!("{\"x\":42,\"y\":5,\"curve\":0}", &serialized); 31 | // 32 | // let deserialized: BezierPoint = serde_json::from_str(&serialized).unwrap(); 33 | // 34 | // println!("{:?}", deserialized); 35 | // assert_eq!(point, deserialized); 36 | // } 37 | // 38 | // } 39 | 40 | mod bezier_point { 41 | use bezier_point::BezierPoint; 42 | use interpolation::Spatial; 43 | use num::Float; 44 | use std; 45 | use super::serde; 46 | 47 | impl serde::Serialize for BezierPoint 48 | where X: Clone + Copy + serde::Serialize, 49 | Y: Spatial + Clone + Copy + serde::Serialize, 50 | ::Scalar: Float + serde::Serialize, 51 | { 52 | fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> 53 | where S: serde::Serializer, 54 | { 55 | 56 | struct MapVisitor<'a, X, Y> 57 | where X: Clone + Copy + 'a, 58 | Y: Spatial + Clone + Copy + 'a, 59 | Y::Scalar: Float, 60 | { 61 | t: &'a BezierPoint, 62 | field_idx: u8, 63 | } 64 | 65 | impl<'a, X, Y> serde::ser::MapVisitor for MapVisitor<'a, X, Y> 66 | where X: Clone + Copy + serde::Serialize, 67 | Y: Spatial + Clone + Copy + serde::Serialize, 68 | Y::Scalar: Float + serde::Serialize, 69 | { 70 | fn visit(&mut self, serializer: &mut S) -> Result, S::Error> 71 | where S: serde::Serializer, 72 | { 73 | match self.field_idx { 74 | 0 => { 75 | self.field_idx += 1; 76 | Ok(Some(try!(serializer.serialize_struct_elt("x", &self.t.x)))) 77 | }, 78 | 1 => { 79 | self.field_idx += 1; 80 | Ok(Some(try!(serializer.serialize_struct_elt("y", &self.t.y)))) 81 | }, 82 | 2 => { 83 | self.field_idx += 1; 84 | Ok(Some(try!(serializer.serialize_struct_elt("curve", &self.t.curve)))) 85 | }, 86 | _ => Ok(None), 87 | } 88 | } 89 | } 90 | 91 | serializer.serialize_struct("BezierPoint", MapVisitor { 92 | t: self, 93 | field_idx: 0, 94 | }) 95 | } 96 | } 97 | 98 | impl serde::Deserialize for BezierPoint 99 | where X: Clone + Copy + serde::Deserialize, 100 | Y: Spatial + Clone + Copy + serde::Deserialize, 101 | ::Scalar: Float + serde::Deserialize, 102 | { 103 | fn deserialize(deserializer: &mut D) -> Result, D::Error> 104 | where D: serde::de::Deserializer 105 | { 106 | 107 | struct Visitor { 108 | x: std::marker::PhantomData, 109 | y: std::marker::PhantomData, 110 | } 111 | 112 | impl serde::de::Visitor for Visitor 113 | where X: Clone + Copy + serde::Deserialize, 114 | Y: Spatial + Clone + Copy + serde::Deserialize, 115 | ::Scalar: Float + serde::Deserialize, 116 | { 117 | type Value = BezierPoint; 118 | 119 | fn visit_map(&mut self, mut visitor: V) -> Result, V::Error> 120 | where V: serde::de::MapVisitor, 121 | { 122 | 123 | enum Field { X, Y, Curve } 124 | 125 | impl serde::Deserialize for Field { 126 | fn deserialize(deserializer: &mut D) -> Result 127 | where D: serde::de::Deserializer, 128 | { 129 | struct FieldVisitor; 130 | 131 | impl serde::de::Visitor for FieldVisitor { 132 | type Value = Field; 133 | 134 | fn visit_str(&mut self, value: &str) -> Result 135 | where E: serde::de::Error, 136 | { 137 | match value { 138 | "x" => Ok(Field::X), 139 | "y" => Ok(Field::Y), 140 | "curve" => Ok(Field::Curve), 141 | _ => Err(serde::de::Error::custom("expected x, y or curve")), 142 | } 143 | } 144 | } 145 | 146 | deserializer.deserialize(FieldVisitor) 147 | } 148 | } 149 | 150 | let mut x = None; 151 | let mut y = None; 152 | let mut curve = None; 153 | 154 | loop { 155 | match try!(visitor.visit_key()) { 156 | Some(Field::X) => { x = Some(try!(visitor.visit_value())); }, 157 | Some(Field::Y) => { y = Some(try!(visitor.visit_value())); }, 158 | Some(Field::Curve) => { curve = Some(try!(visitor.visit_value())); }, 159 | None => { break; } 160 | } 161 | } 162 | 163 | let x = match x { 164 | Some(x) => x, 165 | None => try!(visitor.missing_field("x")), 166 | }; 167 | 168 | let y = match y { 169 | Some(y) => y, 170 | None => try!(visitor.missing_field("y")), 171 | }; 172 | 173 | let curve = match curve { 174 | Some(curve) => curve, 175 | None => try!(visitor.missing_field("curve")), 176 | }; 177 | 178 | try!(visitor.end()); 179 | 180 | Ok(BezierPoint { x: x, y: y, curve: curve }) 181 | } 182 | } 183 | 184 | static FIELDS: &'static [&'static str] = &["x", "y", "curve"]; 185 | let visitor = Visitor { 186 | x: std::marker::PhantomData, 187 | y: std::marker::PhantomData, 188 | }; 189 | deserializer.deserialize_struct("BezierPoint", FIELDS, visitor) 190 | } 191 | } 192 | 193 | 194 | #[test] 195 | fn test() { 196 | extern crate serde_json; 197 | 198 | let point = BezierPoint { x: 42, y: 5.0, curve: 0.0 }; 199 | let serialized = serde_json::to_string(&point).unwrap(); 200 | 201 | println!("{}", serialized); 202 | assert_eq!("{\"x\":42,\"y\":5,\"curve\":0}", &serialized); 203 | 204 | let deserialized: BezierPoint = serde_json::from_str(&serialized).unwrap(); 205 | 206 | println!("{:?}", deserialized); 207 | assert_eq!(point, deserialized); 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /tests/impl_envelope_for_vec.rs: -------------------------------------------------------------------------------- 1 | extern crate envelope; 2 | 3 | use envelope::Envelope; 4 | use envelope::interpolation::Spatial; 5 | use std::iter::{FromIterator, once}; 6 | 7 | struct Points

(Vec

); 8 | 9 | /// Implement a Point and Envelope for the given X and Y types. 10 | macro_rules! impl_point_and_envelope { 11 | ($X:ty, $Y:ty) => { 12 | #[derive(Copy, Clone, Debug, PartialEq)] 13 | struct Point { 14 | x: $X, 15 | y: $Y, 16 | } 17 | impl envelope::Point for Point { 18 | type X = $X; 19 | type Y = $Y; 20 | fn x_to_scalar(x: $X) -> ::Scalar { x as ::Scalar } 21 | fn x(&self) -> $X { self.x } 22 | fn y(&self) -> $Y { self.y } 23 | } 24 | impl<'a> Envelope<'a> for Points { 25 | type X = $X; 26 | type Y = $Y; 27 | type Point = Point; 28 | type Points = std::slice::Iter<'a, Point>; 29 | fn points(&'a self) -> Self::Points { self.0.iter() } 30 | } 31 | }; 32 | } 33 | 34 | 35 | /// Check that for the given types X and Y, `y` is accurate when X falls exactly upon a point. 36 | /// Y is expected to always be some float type. 37 | macro_rules! test_x_y_float { 38 | ($test_name:ident, $X:ty, $Y:ty) => { 39 | 40 | #[test] 41 | fn $test_name() { 42 | 43 | impl_point_and_envelope!($X, $Y); 44 | 45 | fn sine(x: $X) -> $Y { 46 | ((x as $Y).sin() / 2.0) + 0.5 47 | } 48 | 49 | // Values exactly on points. 50 | let points_a = Points(Vec::from_iter((0..1_000).map(|i| { 51 | let x = i as $X; 52 | let value = sine(x); 53 | Point { x: x, y: value } 54 | }))); 55 | for i in 0..1_000 { 56 | let x = i as $X; 57 | let value = sine(x); 58 | let y_at_x = match points_a.y(x) { 59 | Some(y) => y, 60 | None => panic!("Cannot interpolate x {:?}", x), 61 | }; 62 | assert_eq!(value, y_at_x); 63 | } 64 | 65 | // Interpolation betwen points. 66 | let points_b: Vec = once(Point { x: 0 as $X, y: 0.0 as $Y }) 67 | .chain(once(Point { x: 10 as $X, y: 1.0 as $Y })) 68 | .collect(); 69 | let points_b = Points(points_b); 70 | assert_eq!(points_b.y(0 as $X).expect("Cannot interpolate 0"), 0.0 as $Y); 71 | assert_eq!(points_b.y(1 as $X).expect("Cannot interpolate 1"), 0.1 as $Y); 72 | assert_eq!(points_b.y(2 as $X).expect("Cannot interpolate 2"), 0.2 as $Y); 73 | assert_eq!(points_b.y(3 as $X).expect("Cannot interpolate 3"), 0.3 as $Y); 74 | assert_eq!(points_b.y(4 as $X).expect("Cannot interpolate 4"), 0.4 as $Y); 75 | assert_eq!(points_b.y(5 as $X).expect("Cannot interpolate 5"), 0.5 as $Y); 76 | assert_eq!(points_b.y(6 as $X).expect("Cannot interpolate 6"), 0.6 as $Y); 77 | assert_eq!(points_b.y(7 as $X).expect("Cannot interpolate 7"), 0.7 as $Y); 78 | assert_eq!(points_b.y(8 as $X).expect("Cannot interpolate 8"), 0.8 as $Y); 79 | assert_eq!(points_b.y(9 as $X).expect("Cannot interpolate 9"), 0.9 as $Y); 80 | assert_eq!(points_b.y(10 as $X).expect("Cannot interpolate 10"), 1.0 as $Y); 81 | assert_eq!(points_b.y(11 as $X), None); 82 | 83 | // Finding points. 84 | assert_eq!(points_b.point_idx_before(5 as $X), Some(0)); 85 | assert_eq!(points_b.point_idx_before(0 as $X), None); 86 | assert_eq!(points_b.point_idx_on_or_before(5 as $X), Some(0)); 87 | assert_eq!(points_b.point_idx_on_or_before(0 as $X), Some(0)); 88 | assert_eq!(points_b.point_idx_on_or_before(10 as $X), Some(1)); 89 | 90 | assert_eq!(points_b.point_idx_after(5 as $X), Some(1)); 91 | assert_eq!(points_b.point_idx_after(10 as $X), None); 92 | assert_eq!(points_b.point_idx_on_or_after(5 as $X), Some(1)); 93 | assert_eq!(points_b.point_idx_on_or_after(10 as $X), Some(1)); 94 | assert_eq!(points_b.point_idx_on_or_after(0 as $X), Some(0)); 95 | 96 | assert_eq!(*points_b.point_before(5 as $X).unwrap(), points_b.0[0]); 97 | assert_eq!(*points_b.point_on_or_before(5 as $X).unwrap(), points_b.0[0]); 98 | assert_eq!(*points_b.point_on_or_before(0 as $X).unwrap(), points_b.0[0]); 99 | assert_eq!(*points_b.point_on_or_before(10 as $X).unwrap(), points_b.0[1]); 100 | 101 | assert_eq!(*points_b.point_after(5 as $X).unwrap(), points_b.0[1]); 102 | assert_eq!(*points_b.point_on_or_after(5 as $X).unwrap(), points_b.0[1]); 103 | assert_eq!(*points_b.point_on_or_after(0 as $X).unwrap(), points_b.0[0]); 104 | assert_eq!(*points_b.point_on_or_after(10 as $X).unwrap(), points_b.0[1]); 105 | } 106 | 107 | }; 108 | } 109 | 110 | 111 | test_x_y_float!(xf32_yf32, f32, f32); 112 | test_x_y_float!(xf32_yf64, f32, f64); 113 | 114 | test_x_y_float!(xf64_yf32, f64, f32); 115 | test_x_y_float!(xf64_yf64, f64, f64); 116 | 117 | test_x_y_float!(xi32_yf32, i32, f32); 118 | test_x_y_float!(xi32_yf64, i32, f64); 119 | 120 | test_x_y_float!(xi64_yf32, i64, f32); 121 | test_x_y_float!(xi64_yf64, i64, f64); 122 | 123 | test_x_y_float!(xu32_yf32, u32, f32); 124 | test_x_y_float!(xu32_yf64, u32, f64); 125 | 126 | test_x_y_float!(xu64_yf32, u64, f32); 127 | test_x_y_float!(xu64_yf64, u64, f64); 128 | 129 | 130 | /// Check that for the given types X and Y, `y` is accurate when X falls exactly upon a point. 131 | /// Y is expected to always be some float type. 132 | macro_rules! test_x_y_int { 133 | ($test_name:ident, $X:ty, $Y:ty) => { 134 | 135 | #[test] 136 | fn $test_name() { 137 | 138 | impl_point_and_envelope!($X, $Y); 139 | 140 | fn sine(x: $X) -> $Y { 141 | (((x as f64).sin() * 20.0) + 10.0) as $Y 142 | } 143 | 144 | // Values exactly on points. 145 | let points_a = Points(Vec::from_iter((0..1_000).map(|i| { 146 | let x = i as $X; 147 | let value = sine(x); 148 | Point { x: x, y: value } 149 | }))); 150 | for i in 0..1_000 { 151 | let x = i as $X; 152 | let value = sine(x); 153 | let y_at_x = match points_a.y(x) { 154 | Some(y) => y, 155 | None => panic!("Cannot interpolate x {:?}", x), 156 | }; 157 | assert_eq!(value, y_at_x); 158 | } 159 | 160 | // Interpolation betwen points. 161 | let points_b: Vec = once(Point { x: 0 as $X, y: 0 as $Y }) 162 | .chain(once(Point { x: 10 as $X, y: 100 as $Y })) 163 | .collect(); 164 | let points_b = Points(points_b); 165 | assert_eq!(points_b.y(0 as $X).expect("Cannot interpolate 0"), 0 as $Y); 166 | assert_eq!(points_b.y(1 as $X).expect("Cannot interpolate 1"), 10 as $Y); 167 | assert_eq!(points_b.y(2 as $X).expect("Cannot interpolate 2"), 20 as $Y); 168 | assert_eq!(points_b.y(3 as $X).expect("Cannot interpolate 3"), 30 as $Y); 169 | assert_eq!(points_b.y(4 as $X).expect("Cannot interpolate 4"), 40 as $Y); 170 | assert_eq!(points_b.y(5 as $X).expect("Cannot interpolate 5"), 50 as $Y); 171 | assert_eq!(points_b.y(6 as $X).expect("Cannot interpolate 6"), 60 as $Y); 172 | assert_eq!(points_b.y(7 as $X).expect("Cannot interpolate 7"), 70 as $Y); 173 | assert_eq!(points_b.y(8 as $X).expect("Cannot interpolate 8"), 80 as $Y); 174 | assert_eq!(points_b.y(9 as $X).expect("Cannot interpolate 9"), 90 as $Y); 175 | assert_eq!(points_b.y(10 as $X).expect("Cannot interpolate 10"), 100 as $Y); 176 | assert_eq!(points_b.y(11 as $X), None); 177 | 178 | // Finding points. 179 | assert_eq!(points_b.point_idx_before(5 as $X), Some(0)); 180 | assert_eq!(points_b.point_idx_before(0 as $X), None); 181 | assert_eq!(points_b.point_idx_on_or_before(5 as $X), Some(0)); 182 | assert_eq!(points_b.point_idx_on_or_before(0 as $X), Some(0)); 183 | assert_eq!(points_b.point_idx_on_or_before(10 as $X), Some(1)); 184 | 185 | assert_eq!(points_b.point_idx_after(5 as $X), Some(1)); 186 | assert_eq!(points_b.point_idx_after(10 as $X), None); 187 | assert_eq!(points_b.point_idx_on_or_after(5 as $X), Some(1)); 188 | assert_eq!(points_b.point_idx_on_or_after(10 as $X), Some(1)); 189 | assert_eq!(points_b.point_idx_on_or_after(0 as $X), Some(0)); 190 | 191 | assert_eq!(*points_b.point_before(5 as $X).unwrap(), points_b.0[0]); 192 | assert_eq!(*points_b.point_on_or_before(5 as $X).unwrap(), points_b.0[0]); 193 | assert_eq!(*points_b.point_on_or_before(0 as $X).unwrap(), points_b.0[0]); 194 | assert_eq!(*points_b.point_on_or_before(10 as $X).unwrap(), points_b.0[1]); 195 | 196 | assert_eq!(*points_b.point_after(5 as $X).unwrap(), points_b.0[1]); 197 | assert_eq!(*points_b.point_on_or_after(5 as $X).unwrap(), points_b.0[1]); 198 | assert_eq!(*points_b.point_on_or_after(0 as $X).unwrap(), points_b.0[0]); 199 | assert_eq!(*points_b.point_on_or_after(10 as $X).unwrap(), points_b.0[1]); 200 | } 201 | 202 | }; 203 | } 204 | 205 | 206 | test_x_y_int!(xf32_yi32, f32, i32); 207 | test_x_y_int!(xf32_yi64, f32, i64); 208 | 209 | test_x_y_int!(xf64_yi32, f64, i32); 210 | test_x_y_int!(xf64_yi64, f64, i64); 211 | 212 | test_x_y_int!(xi32_yi32, i32, i32); 213 | test_x_y_int!(xi32_yi64, i32, i64); 214 | 215 | test_x_y_int!(xi64_yi32, i64, i32); 216 | test_x_y_int!(xi64_yi64, i64, i64); 217 | 218 | test_x_y_int!(xu32_yi32, u32, i32); 219 | test_x_y_int!(xu32_yi64, u32, i64); 220 | 221 | test_x_y_int!(xu64_yi32, u64, i32); 222 | test_x_y_int!(xu64_yi64, u64, i64); 223 | 224 | -------------------------------------------------------------------------------- /src/envelope.rs: -------------------------------------------------------------------------------- 1 | 2 | use interpolate::Scalar; 3 | use interpolation::Spatial; 4 | use point::Point; 5 | use std; 6 | 7 | 8 | /// Types that are representable as an Envelope. 9 | pub trait Envelope<'a>: Sized { 10 | type X: PartialEq + PartialOrd + Clone; 11 | type Y: PartialEq + Spatial; 12 | /// The `Point` type which may be referenced and interpolated by the `Envelope`. 13 | type Point: Point + 'a; 14 | /// An iterator yielding references to `Self::Point`s. 15 | type Points: Iterator 16 | + ExactSizeIterator 17 | + DoubleEndedIterator 18 | + Clone 19 | + 'a; 20 | 21 | /// An iterator yielding the `Point`s of the Envelope. 22 | fn points(&'a self) -> Self::Points; 23 | 24 | /// The index of the `Point` that comes directly before the given `x`. 25 | #[inline] 26 | fn point_idx_before(&'a self, x: Self::X) -> Option { 27 | point_idx_before(self, x) 28 | } 29 | 30 | /// The index of the `Point` that either lands on or comes directly before the given `x`. 31 | #[inline] 32 | fn point_idx_on_or_before(&'a self, x: Self::X) -> Option { 33 | point_idx_on_or_before(self, x) 34 | } 35 | 36 | /// The index of the `Point` that comes directly after the given `x`. 37 | #[inline] 38 | fn point_idx_after(&'a self, x: Self::X) -> Option { 39 | point_idx_after(self, x) 40 | } 41 | 42 | /// The index of the `Point` that comes directly after the given `x`. 43 | #[inline] 44 | fn point_idx_on_or_after(&'a self, x: Self::X) -> Option { 45 | point_idx_on_or_after(self, x) 46 | } 47 | 48 | /// A reference to the first point that comes before the given `x`. 49 | #[inline] 50 | fn point_before(&'a self, x: Self::X) -> Option<&'a Self::Point> { 51 | self.point_idx_before(x).and_then(|i| self.points().nth(i)) 52 | } 53 | 54 | /// A reference to the first point that is equal to or comes before the given `x`. 55 | #[inline] 56 | fn point_on_or_before(&'a self, x: Self::X) -> Option<&'a Self::Point> { 57 | self.point_idx_on_or_before(x).and_then(|i| self.points().nth(i)) 58 | } 59 | 60 | /// A reference to the first point that comes before the given `x` along with its index. 61 | #[inline] 62 | fn point_before_with_idx(&'a self, x: Self::X) -> Option<(usize, &'a Self::Point)> { 63 | self.point_idx_before(x).and_then(|i| self.points().nth(i).map(|p| (i, p))) 64 | } 65 | 66 | /// A reference to the first point that is equal to or comes before the given `x` along with 67 | /// its index. 68 | #[inline] 69 | fn point_on_or_before_with_idx(&'a self, x: Self::X) -> Option<(usize, &'a Self::Point)> { 70 | self.point_idx_on_or_before(x).and_then(|i| self.points().nth(i).map(|p| (i, p))) 71 | } 72 | 73 | /// A reference to the first point that comes after the given `x`. 74 | #[inline] 75 | fn point_after(&'a self, x: Self::X) -> Option<&'a Self::Point> { 76 | self.point_idx_after(x).and_then(|i| self.points().nth(i)) 77 | } 78 | 79 | /// A reference to the first point that is equal to or comes after the given `x`. 80 | #[inline] 81 | fn point_on_or_after(&'a self, x: Self::X) -> Option<&'a Self::Point> { 82 | self.point_idx_on_or_after(x).and_then(|i| self.points().nth(i)) 83 | } 84 | 85 | /// A reference to the first point that comes after the given `x` along with its index. 86 | #[inline] 87 | fn point_after_with_idx(&'a self, x: Self::X) -> Option<(usize, &'a Self::Point)> { 88 | self.point_idx_after(x).and_then(|i| self.points().nth(i).map(|p| (i, p))) 89 | } 90 | 91 | /// A reference to the first point that is equal to or comes after the given `x` along with 92 | /// its index. 93 | #[inline] 94 | fn point_on_or_after_with_idx(&'a self, x: Self::X) -> Option<(usize, &'a Self::Point)> { 95 | self.point_idx_on_or_after(x).and_then(|i| self.points().nth(i).map(|p| (i, p))) 96 | } 97 | 98 | /// A reference to the first point lying directly on the given `x` if there is one. 99 | #[inline] 100 | fn point_at(&'a self, x: Self::X) -> Option<&'a Self::Point> { 101 | self.points().find(|p| p.x() == x) 102 | } 103 | 104 | /// A reference to the first point (along with it's index) lying directly on the given `x` if 105 | /// there is one. 106 | #[inline] 107 | fn point_at_with_idx(&'a self, x: Self::X) -> Option<(usize, &'a Self::Point)> { 108 | self.points().enumerate().find(|&(_, p)| p.x() == x) 109 | } 110 | 111 | /// The points that lie on either side of the given `x`. 112 | /// 113 | /// FIXME: This could be much faster. 114 | #[inline] 115 | fn surrounding_points(&'a self, x: Self::X) 116 | -> (Option<&'a Self::Point>, Option<&'a Self::Point>) 117 | { 118 | (self.point_on_or_before(x.clone()), self.point_after(x)) 119 | } 120 | 121 | /// A reference point that is closest to the given `x` if there is one. 122 | /// 123 | /// FIXME: This could be much faster. 124 | #[inline] 125 | fn closest_point(&'a self, x: Self::X) -> Option<&'a Self::Point> 126 | where >::X: std::ops::Sub>::X>, 127 | { 128 | match self.surrounding_points(x.clone()) { 129 | (Some(before), Some(after)) => 130 | if x.clone() - before.x() < after.x() - x { Some(before) } else { Some(after) }, 131 | (Some(point), None) | (None, Some(point)) => Some(point), 132 | (None, None) => None, 133 | } 134 | } 135 | 136 | /// Return `y` for the given `x`. 137 | /// 138 | /// If there is less than two points interpolation is not meaningful, 139 | /// thus we should just return None. 140 | /// 141 | /// Note: It is assumed that the points owned by the Envelope are sorted by `x`. 142 | #[inline] 143 | fn y(&'a self, x: Self::X) -> Option 144 | where ::Scalar: Scalar, 145 | { 146 | y(self, x) 147 | } 148 | 149 | /// Sample the `Envelope`'s `y` value for every given positive `x` step starting from the first 150 | /// point's `X` value. 151 | /// 152 | /// The envelope will yield `Some(Y)` until the first step is out of range of all points on the 153 | /// y axis. 154 | /// 155 | /// Returns `None` if `start` is outside the bounds of all points. 156 | /// 157 | /// Note: This method assumes that the envelope points are ordered. 158 | #[inline] 159 | fn steps(&'a self, start: Self::X, step: Self::X) -> Option> { 160 | let mut points = self.points(); 161 | points.next().and_then(|mut left| { 162 | let mut maybe_right = None; 163 | 164 | // Iterate through `points` until `start` is between `left` and `right` 165 | while let Some(point) = points.next() { 166 | maybe_right = Some(point); 167 | if point.x() < start { 168 | left = maybe_right.take().unwrap(); 169 | } else { 170 | break; 171 | } 172 | } 173 | 174 | // Check that the remaining points bound the `start`. 175 | match maybe_right { 176 | Some(right) => if right.x() < start { return None; }, 177 | None => if left.x() < start { return None; }, 178 | } 179 | 180 | Some(Steps { 181 | points: points, 182 | step: step, 183 | next_x: start, 184 | left: left, 185 | maybe_right: maybe_right, 186 | env: std::marker::PhantomData, 187 | }) 188 | }) 189 | } 190 | 191 | // /// An iterator yielding the X for each point at which the envelope intersects the given `y`. 192 | // /// 193 | // /// If there are any periods at which X is continuous, only the start X of the continuous 194 | // /// period will be returned. 195 | // fn xs_at_y(&self, y: Y) -> XsAtY { 196 | // unimplemented!(); 197 | // } 198 | 199 | } 200 | 201 | 202 | /// An iterator that interpolates the envelope `E` one `step` and yields the result. 203 | /// 204 | /// Returns `None` the first time `next` falls out of range of all points in `env`. 205 | #[derive(Clone)] 206 | pub struct Steps<'a, E> 207 | where E: Envelope<'a> + 'a, 208 | { 209 | points: E::Points, 210 | step: E::X, 211 | next_x: E::X, 212 | left: &'a E::Point, 213 | maybe_right: Option<&'a E::Point>, 214 | env: std::marker::PhantomData, 215 | } 216 | 217 | impl<'a, E> Steps<'a, E> 218 | where E: Envelope<'a>, 219 | { 220 | /// This is useful when the step size must change between steps. 221 | #[inline] 222 | pub fn set_step(&mut self, step: E::X) { 223 | self.step = step; 224 | } 225 | 226 | /// Yields the next step along with its position along the step. 227 | #[inline] 228 | pub fn next_xy(&mut self) -> Option<(E::X, E::Y)> 229 | where Self: Iterator, 230 | { 231 | let x = self.next_x.clone(); 232 | self.next().map(|y| (x, y)) 233 | } 234 | } 235 | 236 | impl<'a, E> Iterator for Steps<'a, E> 237 | where E: Envelope<'a>, 238 | >::X: std::ops::Add>::X>, 239 | <>::Y as Spatial>::Scalar: Scalar, 240 | { 241 | type Item = E::Y; 242 | #[inline] 243 | fn next(&mut self) -> Option { 244 | let Steps { 245 | ref step, 246 | ref mut points, 247 | ref mut next_x, 248 | ref mut left, 249 | ref mut maybe_right, 250 | .. 251 | } = *self; 252 | 253 | let x = next_x.clone(); 254 | *next_x = x.clone() + step.clone(); 255 | maybe_right.as_mut() 256 | .and_then(|right| { 257 | let x = x.clone(); 258 | while x > right.x() { 259 | *left = right; 260 | *right = match points.next() { 261 | Some(point) => point, 262 | None => return None, 263 | }; 264 | } 265 | Some(Point::interpolate(x, *left, *right)) 266 | }) 267 | .or_else(|| if x == left.x() { Some(left.y()) } else { None }) 268 | } 269 | } 270 | 271 | 272 | #[inline] 273 | fn point_idx_before<'a, E>(env: &'a E, x: E::X) -> Option 274 | where E: Envelope<'a>, 275 | { 276 | env.points().enumerate() 277 | .take_while(|&(_, point)| point.x() < x ) 278 | .last() 279 | .map(|(i, _)| i) 280 | } 281 | 282 | 283 | #[inline] 284 | fn point_idx_on_or_before<'a, E>(env: &'a E, x: E::X) -> Option 285 | where E: Envelope<'a>, 286 | { 287 | env.points().enumerate() 288 | .take_while(|&(_, point)| point.x() <= x ) 289 | .last() 290 | .map(|(i, _)| i) 291 | } 292 | 293 | 294 | #[inline] 295 | fn point_idx_after<'a, E>(env: &'a E, x: E::X) -> Option 296 | where E: Envelope<'a>, 297 | { 298 | env.points().enumerate().rev() 299 | .take_while(|&(_, point)| point.x() > x ) 300 | .last() 301 | .map(|(i, _)| i) 302 | } 303 | 304 | 305 | #[inline] 306 | fn point_idx_on_or_after<'a, E>(env: &'a E, x: E::X) -> Option 307 | where E: Envelope<'a>, 308 | { 309 | env.points().enumerate().rev() 310 | .take_while(|&(_, point)| point.x() >= x ) 311 | .last() 312 | .map(|(i, _)| i) 313 | } 314 | 315 | 316 | #[inline] 317 | fn y<'a, E>(env: &'a E, x: E::X) -> Option 318 | where E: Envelope<'a>, 319 | E::Y: Spatial + PartialEq + 'a, 320 | <>::Y as Spatial>::Scalar: Scalar, 321 | { 322 | let mut points = env.points(); 323 | points.next().and_then(|mut left| { 324 | let mut maybe_right = None; 325 | 326 | // Iterate through `points` until `x` is between `left` and `right` 327 | while let Some(point) = points.next() { 328 | maybe_right = Some(point); 329 | if point.x() < x { 330 | left = maybe_right.take().unwrap(); 331 | } else { 332 | break; 333 | } 334 | } 335 | 336 | // Check that the remaining points bound the `x`. 337 | match maybe_right { 338 | Some(right) => if right.x() < x { return None; }, 339 | None => if left.x() < x { return None; }, 340 | } 341 | 342 | maybe_right 343 | .and_then(|mut right| { 344 | let x = x.clone(); 345 | while x > right.x() { 346 | left = right; 347 | right = match points.next() { 348 | Some(point) => point, 349 | None => return None, 350 | }; 351 | } 352 | Some(Point::interpolate(x, left, right)) 353 | }) 354 | .or_else(|| if x == left.x() { Some(left.y()) } else { None }) 355 | }) 356 | } 357 | --------------------------------------------------------------------------------