Create util module and move Interval struct to it
This commit is contained in:
parent
8c527d34fc
commit
65b5e3c45d
|
|
@ -8,6 +8,7 @@ pub mod mesh;
|
|||
pub mod raycasting;
|
||||
pub mod sampler;
|
||||
pub mod scene;
|
||||
pub mod util;
|
||||
|
||||
#[cfg(bench)]
|
||||
mod tests {
|
||||
|
|
|
|||
|
|
@ -1,90 +1,12 @@
|
|||
use nalgebra::{convert, Point3, RealField};
|
||||
use nalgebra::{Point3, RealField};
|
||||
|
||||
use crate::util::Interval;
|
||||
|
||||
use super::{IntersectP, Ray};
|
||||
|
||||
use itertools::izip;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Interval<T: RealField> {
|
||||
min: T,
|
||||
max: T,
|
||||
}
|
||||
|
||||
impl<T: RealField> Interval<T> {
|
||||
pub fn new(a: T, b: T) -> Self {
|
||||
if a > b {
|
||||
Interval { min: b, max: a }
|
||||
} else {
|
||||
Interval { min: a, max: b }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn empty() -> Self {
|
||||
Interval {
|
||||
min: convert(std::f64::INFINITY),
|
||||
max: convert(std::f64::NEG_INFINITY),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn infinite() -> Self {
|
||||
Interval {
|
||||
min: convert(std::f64::NEG_INFINITY),
|
||||
max: convert(std::f64::INFINITY),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn degenerate(value: T) -> Self {
|
||||
Interval {
|
||||
min: value,
|
||||
max: value,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_degenerate(self) -> bool {
|
||||
self.min == self.max
|
||||
}
|
||||
|
||||
pub fn is_empty(self) -> bool {
|
||||
self.min > self.max
|
||||
}
|
||||
|
||||
pub fn contains_value(&self, value: T) -> bool {
|
||||
value >= self.min && value <= self.max
|
||||
}
|
||||
|
||||
pub fn intersection(self, b: Self) -> Self {
|
||||
Interval {
|
||||
min: self.min.max(b.min),
|
||||
max: self.max.min(b.max),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn union(self, b: Self) -> Self {
|
||||
if self.is_empty() {
|
||||
b
|
||||
} else if b.is_empty() {
|
||||
self
|
||||
} else {
|
||||
Interval {
|
||||
min: self.min.min(b.min),
|
||||
max: self.max.max(b.max),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expand_to_value(self, v: T) -> Self {
|
||||
if self.is_empty() {
|
||||
Interval::degenerate(v)
|
||||
} else {
|
||||
Interval {
|
||||
min: self.min.min(v),
|
||||
max: self.max.max(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BoundingBox<T: RealField> {
|
||||
bounds: [Interval<T>; 3],
|
||||
}
|
||||
|
|
@ -160,8 +82,8 @@ impl<T: RealField> IntersectP<T> for BoundingBox<T> {
|
|||
izip!(ray.origin.iter(), ray.direction.iter(), self.bounds.iter())
|
||||
{
|
||||
t_interval_in_bounds = t_interval_in_bounds.intersection(Interval::new(
|
||||
(bounds.min - ray_origin) / ray_direction,
|
||||
(bounds.max - ray_origin) / ray_direction,
|
||||
(bounds.get_min() - ray_origin) / ray_direction,
|
||||
(bounds.get_max() - ray_origin) / ray_direction,
|
||||
));
|
||||
if t_interval_in_bounds.is_empty() {
|
||||
return false;
|
||||
|
|
@ -175,250 +97,9 @@ impl<T: RealField> IntersectP<T> for BoundingBox<T> {
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use itertools::{Itertools, MinMaxResult};
|
||||
|
||||
use quickcheck::TestResult;
|
||||
use quickcheck_macros::quickcheck;
|
||||
|
||||
mod interval {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn never_constructed_empty() {
|
||||
let target1 = Interval::new(5f64, 10f64);
|
||||
assert!(!target1.is_empty());
|
||||
let target2 = Interval::new(10f64, 5f64);
|
||||
assert!(!target2.is_empty());
|
||||
let target1 = Interval::new(5f64, -10f64);
|
||||
assert!(!target1.is_empty());
|
||||
let target2 = Interval::new(10f64, -5f64);
|
||||
assert!(!target2.is_empty());
|
||||
let target1 = Interval::new(-5f64, 10f64);
|
||||
assert!(!target1.is_empty());
|
||||
let target2 = Interval::new(-10f64, 5f64);
|
||||
assert!(!target2.is_empty());
|
||||
let target1 = Interval::new(-5f64, -10f64);
|
||||
assert!(!target1.is_empty());
|
||||
let target2 = Interval::new(-10f64, -5f64);
|
||||
assert!(!target2.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_is_empty() {
|
||||
let target: Interval<f64> = Interval::empty();
|
||||
assert!(target.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_when_min_greater_than_max() {
|
||||
let target = Interval {
|
||||
min: 10f64,
|
||||
max: 5f64,
|
||||
};
|
||||
assert!(target.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_when_min_greater_than_max_with_negative_values() {
|
||||
let target = Interval {
|
||||
min: -5f64,
|
||||
max: -10f64,
|
||||
};
|
||||
assert!(target.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_when_min_greater_than_max_with_mixed_signs() {
|
||||
let target = Interval {
|
||||
min: 5f64,
|
||||
max: -10f64,
|
||||
};
|
||||
assert!(target.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_is_not_degenerate() {
|
||||
let target = Interval {
|
||||
min: 10f64,
|
||||
max: 5f64,
|
||||
};
|
||||
assert!(!target.is_degenerate());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_is_not_degenerate_with_negative_values() {
|
||||
let target = Interval {
|
||||
min: -5f64,
|
||||
max: -10f64,
|
||||
};
|
||||
assert!(!target.is_degenerate());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_is_not_degenerate_with_mixed_signs() {
|
||||
let target = Interval {
|
||||
min: -5f64,
|
||||
max: 10f64,
|
||||
};
|
||||
assert!(!target.is_degenerate());
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn no_value_is_in_interval_returned_by_emtpy(value: f64) -> bool {
|
||||
!Interval::empty().contains_value(value)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn identical_min_max_yields_degenerate() {
|
||||
let target = Interval {
|
||||
min: 5f64,
|
||||
max: 5f64,
|
||||
};
|
||||
assert!(target.is_degenerate());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn degenerate_is_degenerate() {
|
||||
let target = Interval::degenerate(5f64);
|
||||
assert!(target.is_degenerate());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn degenerate_is_not_empty() {
|
||||
let target = Interval {
|
||||
min: 5f64,
|
||||
max: 5f64,
|
||||
};
|
||||
assert!(target.is_degenerate());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn degenerate_is_degenerate_with_negative_value() {
|
||||
let target = Interval {
|
||||
min: -5f64,
|
||||
max: -5f64,
|
||||
};
|
||||
assert!(target.is_degenerate());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn degenerate_is_not_empty_with_negative_value() {
|
||||
let target = Interval {
|
||||
min: -5f64,
|
||||
max: -5f64,
|
||||
};
|
||||
assert!(target.is_degenerate());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn degenerate_contains_expected_value() {
|
||||
let target = Interval::degenerate(5f64);
|
||||
assert!(target.contains_value(5.0));
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn degenerate_does_not_contain_any_values_othter_than_expected_value(value: f64) -> bool {
|
||||
let target_value = if value == 5f64 { 5.5 } else { 5f64 };
|
||||
let target = Interval::degenerate(target_value);
|
||||
!target.contains_value(value)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn intersection_with_infinite_is_self() {
|
||||
let target = Interval::new(5f32, 10f32);
|
||||
let result = target.intersection(Interval::infinite());
|
||||
assert!(target.min == result.min);
|
||||
assert!(target.max == result.max);
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn union_with_self_yields_self(a: f64, b: f64) -> bool {
|
||||
let target = Interval::new(a, b);
|
||||
let result = target.union(target);
|
||||
result.min == target.min && result.max == target.max
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn union_yields_min_and_max(a: f64, b: f64, c: f64, d: f64) -> bool {
|
||||
let values = vec![a, b, c, d];
|
||||
if let MinMaxResult::MinMax(&min, &max) =
|
||||
values.iter().minmax_by(|a, b| a.partial_cmp(b).unwrap())
|
||||
{
|
||||
let target1 = Interval::new(a, b);
|
||||
let target2 = Interval::new(c, d);
|
||||
let result = target1.union(target2);
|
||||
result.min == min && result.max == max
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn union_with_empty_interval_is_correct() {
|
||||
let empty = Interval {
|
||||
min: 1f64,
|
||||
max: -1f64,
|
||||
};
|
||||
let not_empty = Interval {
|
||||
min: 5f64,
|
||||
max: 10f64,
|
||||
};
|
||||
let union1 = not_empty.union(empty);
|
||||
assert!(union1.min == 5.0);
|
||||
assert!(union1.max == 10.0);
|
||||
let union2 = empty.union(not_empty);
|
||||
assert!(union2.min == 5.0);
|
||||
assert!(union2.max == 10.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn union_with_empty_interval_is_correct_when_empty_interval_produced_by_intersection() {
|
||||
let empty = Interval {
|
||||
min: 1f64,
|
||||
max: -1f64,
|
||||
};
|
||||
let not_empty = Interval {
|
||||
min: 5f64,
|
||||
max: 10f64,
|
||||
};
|
||||
let union1 = not_empty.union(empty);
|
||||
assert!(union1.min == 5.0);
|
||||
assert!(union1.max == 10.0);
|
||||
let union2 = empty.union(not_empty);
|
||||
assert!(union2.min == 5.0);
|
||||
assert!(union2.max == 10.0);
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
pub fn expand_to_value_creates_interval_that_includes_value(
|
||||
min: f64,
|
||||
max: f64,
|
||||
value: f64,
|
||||
) -> bool {
|
||||
// Don't check if min <= max, we want to test empty intervals too
|
||||
let interval1 = Interval { min, max };
|
||||
let interval2 = interval1.expand_to_value(value);
|
||||
interval2.contains_value(value)
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
pub fn expand_to_value_creates_interval_that_includes_original_interval(
|
||||
b: f64,
|
||||
a: f64,
|
||||
value: f64,
|
||||
) -> bool {
|
||||
let interval1 = Interval::new(a, b);
|
||||
let interval2 = interval1.expand_to_value(value);
|
||||
let interval3 = interval2.intersection(interval1);
|
||||
// If interval2 contains interval1, that the intersection of the two will
|
||||
// be equal to interval1
|
||||
interval1.min == interval3.min && interval1.max == interval3.max
|
||||
}
|
||||
}
|
||||
|
||||
mod bounding_box {
|
||||
use super::*;
|
||||
|
||||
use nalgebra::Vector3;
|
||||
|
||||
#[test]
|
||||
|
|
@ -454,15 +135,15 @@ mod tests {
|
|||
assert!(target
|
||||
.bounds
|
||||
.iter()
|
||||
.all(|bounds| bounds.min == 0.0 && bounds.max == 1.0));
|
||||
.all(|bounds| bounds.get_min() == 0.0 && bounds.get_max() == 1.0));
|
||||
}
|
||||
}
|
||||
|
||||
fn wrap_value_in_interval(value: f64, interval: Interval<f64>) -> f64 {
|
||||
let distance_from_start = (value - interval.min).abs();
|
||||
let range = interval.max - interval.min;
|
||||
let distance_from_start = (value - interval.get_min()).abs();
|
||||
let range = interval.get_max() - interval.get_min();
|
||||
let multiple_of_range = distance_from_start / range;
|
||||
return interval.min + multiple_of_range.fract() * range;
|
||||
return interval.get_min() + multiple_of_range.fract() * range;
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
|
|
@ -471,10 +152,7 @@ mod tests {
|
|||
interval.contains_value(wrap_value_in_interval(v, interval))
|
||||
}
|
||||
|
||||
fn wrap_point_into_bounding_box(
|
||||
point: Point3<f64>,
|
||||
bounds: &BoundingBox<f64>,
|
||||
) -> Point3<f64> {
|
||||
fn wrap_point_into_bounding_box(point: Point3<f64>, bounds: &BoundingBox<f64>) -> Point3<f64> {
|
||||
Point3::from(Vector3::from_iterator(
|
||||
point
|
||||
.iter()
|
||||
|
|
@ -527,10 +205,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn intersection_detected_when_ray_parallel_to_axis() {
|
||||
let target = BoundingBox::from_corners(
|
||||
Point3::new(1.0f64, 2.0, 3.0),
|
||||
Point3::new(4.0, 5.0, 6.0),
|
||||
);
|
||||
let target =
|
||||
BoundingBox::from_corners(Point3::new(1.0f64, 2.0, 3.0), Point3::new(4.0, 5.0, 6.0));
|
||||
let x_ray = Ray::new(Point3::new(0.0, 3.0, 4.0), Vector3::new(1.0, 0.0, 0.0));
|
||||
assert!(target.intersect(&x_ray));
|
||||
let y_ray = Ray::new(Point3::new(2.0, 0.0, 4.0), Vector3::new(0.0, 1.0, 0.0));
|
||||
|
|
@ -541,10 +217,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn intersection_missed_when_ray_parallel_to_axis() {
|
||||
let target = BoundingBox::from_corners(
|
||||
Point3::new(1.0f64, 2.0, 3.0),
|
||||
Point3::new(4.0, 5.0, 6.0),
|
||||
);
|
||||
let target =
|
||||
BoundingBox::from_corners(Point3::new(1.0f64, 2.0, 3.0), Point3::new(4.0, 5.0, 6.0));
|
||||
let x_ray = Ray::new(Point3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 0.0, 0.0));
|
||||
assert!(!target.intersect(&x_ray));
|
||||
let y_ray = Ray::new(Point3::new(0.0, 0.0, 0.0), Vector3::new(0.0, 1.0, 0.0));
|
||||
|
|
@ -561,7 +235,7 @@ mod tests {
|
|||
.bounds
|
||||
.iter()
|
||||
.zip(result.bounds.iter())
|
||||
.all(|(a, b)| a.min == b.min && a.max == b.max)
|
||||
.all(|(a, b)| a.get_min() == b.get_min() && a.get_max() == b.get_max())
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
|
|
@ -580,7 +254,10 @@ mod tests {
|
|||
target2.bounds.iter()
|
||||
)
|
||||
.all(|(r, t1, t2)| {
|
||||
r.min <= t1.min && r.min <= t2.min && r.max >= t1.max && r.max >= t2.max
|
||||
r.get_min() <= t1.get_min()
|
||||
&& r.get_min() <= t2.get_min()
|
||||
&& r.get_max() >= t1.get_max()
|
||||
&& r.get_max() >= t2.get_max()
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -609,5 +286,4 @@ mod tests {
|
|||
&& points.iter().any(|elem| elem.z <= p.z);
|
||||
target.contains_point(p) == is_in_bounds
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,330 @@
|
|||
use nalgebra::{convert, RealField};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Interval<T: RealField> {
|
||||
min: T,
|
||||
max: T,
|
||||
}
|
||||
|
||||
impl<T: RealField> Interval<T> {
|
||||
pub fn new(a: T, b: T) -> Self {
|
||||
if a > b {
|
||||
Interval { min: b, max: a }
|
||||
} else {
|
||||
Interval { min: a, max: b }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn empty() -> Self {
|
||||
Interval {
|
||||
min: convert(std::f64::INFINITY),
|
||||
max: convert(std::f64::NEG_INFINITY),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn infinite() -> Self {
|
||||
Interval {
|
||||
min: convert(std::f64::NEG_INFINITY),
|
||||
max: convert(std::f64::INFINITY),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn degenerate(value: T) -> Self {
|
||||
Interval {
|
||||
min: value,
|
||||
max: value,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_min(&self) -> T {
|
||||
return self.min;
|
||||
}
|
||||
|
||||
pub fn get_max(&self) -> T {
|
||||
return self.max;
|
||||
}
|
||||
|
||||
pub fn is_degenerate(self) -> bool {
|
||||
self.min == self.max
|
||||
}
|
||||
|
||||
pub fn is_empty(self) -> bool {
|
||||
self.min > self.max
|
||||
}
|
||||
|
||||
pub fn contains_value(&self, value: T) -> bool {
|
||||
value >= self.min && value <= self.max
|
||||
}
|
||||
|
||||
pub fn intersection(self, b: Self) -> Self {
|
||||
Interval {
|
||||
min: self.min.max(b.min),
|
||||
max: self.max.min(b.max),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn union(self, b: Self) -> Self {
|
||||
if self.is_empty() {
|
||||
b
|
||||
} else if b.is_empty() {
|
||||
self
|
||||
} else {
|
||||
Interval {
|
||||
min: self.min.min(b.min),
|
||||
max: self.max.max(b.max),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expand_to_value(self, v: T) -> Self {
|
||||
if self.is_empty() {
|
||||
Interval::degenerate(v)
|
||||
} else {
|
||||
Interval {
|
||||
min: self.min.min(v),
|
||||
max: self.max.max(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use itertools::{Itertools, MinMaxResult};
|
||||
|
||||
use quickcheck_macros::quickcheck;
|
||||
|
||||
#[test]
|
||||
fn never_constructed_empty() {
|
||||
let target1 = Interval::new(5f64, 10f64);
|
||||
assert!(!target1.is_empty());
|
||||
let target2 = Interval::new(10f64, 5f64);
|
||||
assert!(!target2.is_empty());
|
||||
let target1 = Interval::new(5f64, -10f64);
|
||||
assert!(!target1.is_empty());
|
||||
let target2 = Interval::new(10f64, -5f64);
|
||||
assert!(!target2.is_empty());
|
||||
let target1 = Interval::new(-5f64, 10f64);
|
||||
assert!(!target1.is_empty());
|
||||
let target2 = Interval::new(-10f64, 5f64);
|
||||
assert!(!target2.is_empty());
|
||||
let target1 = Interval::new(-5f64, -10f64);
|
||||
assert!(!target1.is_empty());
|
||||
let target2 = Interval::new(-10f64, -5f64);
|
||||
assert!(!target2.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_is_empty() {
|
||||
let target: Interval<f64> = Interval::empty();
|
||||
assert!(target.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_when_min_greater_than_max() {
|
||||
let target = Interval {
|
||||
min: 10f64,
|
||||
max: 5f64,
|
||||
};
|
||||
assert!(target.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_when_min_greater_than_max_with_negative_values() {
|
||||
let target = Interval {
|
||||
min: -5f64,
|
||||
max: -10f64,
|
||||
};
|
||||
assert!(target.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_when_min_greater_than_max_with_mixed_signs() {
|
||||
let target = Interval {
|
||||
min: 5f64,
|
||||
max: -10f64,
|
||||
};
|
||||
assert!(target.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_is_not_degenerate() {
|
||||
let target = Interval {
|
||||
min: 10f64,
|
||||
max: 5f64,
|
||||
};
|
||||
assert!(!target.is_degenerate());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_is_not_degenerate_with_negative_values() {
|
||||
let target = Interval {
|
||||
min: -5f64,
|
||||
max: -10f64,
|
||||
};
|
||||
assert!(!target.is_degenerate());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_is_not_degenerate_with_mixed_signs() {
|
||||
let target = Interval {
|
||||
min: -5f64,
|
||||
max: 10f64,
|
||||
};
|
||||
assert!(!target.is_degenerate());
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn no_value_is_in_interval_returned_by_emtpy(value: f64) -> bool {
|
||||
!Interval::empty().contains_value(value)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn identical_min_max_yields_degenerate() {
|
||||
let target = Interval {
|
||||
min: 5f64,
|
||||
max: 5f64,
|
||||
};
|
||||
assert!(target.is_degenerate());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn degenerate_is_degenerate() {
|
||||
let target = Interval::degenerate(5f64);
|
||||
assert!(target.is_degenerate());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn degenerate_is_not_empty() {
|
||||
let target = Interval {
|
||||
min: 5f64,
|
||||
max: 5f64,
|
||||
};
|
||||
assert!(target.is_degenerate());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn degenerate_is_degenerate_with_negative_value() {
|
||||
let target = Interval {
|
||||
min: -5f64,
|
||||
max: -5f64,
|
||||
};
|
||||
assert!(target.is_degenerate());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn degenerate_is_not_empty_with_negative_value() {
|
||||
let target = Interval {
|
||||
min: -5f64,
|
||||
max: -5f64,
|
||||
};
|
||||
assert!(target.is_degenerate());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn degenerate_contains_expected_value() {
|
||||
let target = Interval::degenerate(5f64);
|
||||
assert!(target.contains_value(5.0));
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn degenerate_does_not_contain_any_values_othter_than_expected_value(value: f64) -> bool {
|
||||
let target_value = if value == 5f64 { 5.5 } else { 5f64 };
|
||||
let target = Interval::degenerate(target_value);
|
||||
!target.contains_value(value)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn intersection_with_infinite_is_self() {
|
||||
let target = Interval::new(5f32, 10f32);
|
||||
let result = target.intersection(Interval::infinite());
|
||||
assert!(target.min == result.min);
|
||||
assert!(target.max == result.max);
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn union_with_self_yields_self(a: f64, b: f64) -> bool {
|
||||
let target = Interval::new(a, b);
|
||||
let result = target.union(target);
|
||||
result.min == target.min && result.max == target.max
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn union_yields_min_and_max(a: f64, b: f64, c: f64, d: f64) -> bool {
|
||||
let values = vec![a, b, c, d];
|
||||
if let MinMaxResult::MinMax(&min, &max) =
|
||||
values.iter().minmax_by(|a, b| a.partial_cmp(b).unwrap())
|
||||
{
|
||||
let target1 = Interval::new(a, b);
|
||||
let target2 = Interval::new(c, d);
|
||||
let result = target1.union(target2);
|
||||
result.min == min && result.max == max
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn union_with_empty_interval_is_correct() {
|
||||
let empty = Interval {
|
||||
min: 1f64,
|
||||
max: -1f64,
|
||||
};
|
||||
let not_empty = Interval {
|
||||
min: 5f64,
|
||||
max: 10f64,
|
||||
};
|
||||
let union1 = not_empty.union(empty);
|
||||
assert!(union1.min == 5.0);
|
||||
assert!(union1.max == 10.0);
|
||||
let union2 = empty.union(not_empty);
|
||||
assert!(union2.min == 5.0);
|
||||
assert!(union2.max == 10.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn union_with_empty_interval_is_correct_when_empty_interval_produced_by_intersection() {
|
||||
let empty = Interval {
|
||||
min: 1f64,
|
||||
max: -1f64,
|
||||
};
|
||||
let not_empty = Interval {
|
||||
min: 5f64,
|
||||
max: 10f64,
|
||||
};
|
||||
let union1 = not_empty.union(empty);
|
||||
assert!(union1.min == 5.0);
|
||||
assert!(union1.max == 10.0);
|
||||
let union2 = empty.union(not_empty);
|
||||
assert!(union2.min == 5.0);
|
||||
assert!(union2.max == 10.0);
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
pub fn expand_to_value_creates_interval_that_includes_value(
|
||||
min: f64,
|
||||
max: f64,
|
||||
value: f64,
|
||||
) -> bool {
|
||||
// Don't check if min <= max, we want to test empty intervals too
|
||||
let interval1 = Interval { min, max };
|
||||
let interval2 = interval1.expand_to_value(value);
|
||||
interval2.contains_value(value)
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
pub fn expand_to_value_creates_interval_that_includes_original_interval(
|
||||
b: f64,
|
||||
a: f64,
|
||||
value: f64,
|
||||
) -> bool {
|
||||
let interval1 = Interval::new(a, b);
|
||||
let interval2 = interval1.expand_to_value(value);
|
||||
let interval3 = interval2.intersection(interval1);
|
||||
// If interval2 contains interval1, that the intersection of the two will
|
||||
// be equal to interval1
|
||||
interval1.min == interval3.min && interval1.max == interval3.max
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
mod interval;
|
||||
|
||||
pub use interval::Interval;
|
||||
Loading…
Reference in New Issue