use num_traits::{PrimInt, Float, NumCast};
#[cfg(any(test, feature = "arbitrary"))]
use quickcheck::{Arbitrary, Gen};
use nalgebra::{Vector2, Scalar};
use partition::{Partition, Subdivide};
#[cfg(any(test, feature = "arbitrary"))]
use rand::Rng;
#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)]
pub struct UnitQuad {
scale: u8,
offset: (u32, u32),
}
impl UnitQuad {
pub fn new(scale: u8, offset: (u32, u32)) -> UnitQuad {
assert!(scale < 32); let max_offset = 2.pow(scale as u32);
assert!(offset.0 < max_offset && offset.1 < max_offset);
UnitQuad { scale: scale, offset: offset }
}
pub fn scale(&self) -> u8 { self.scale }
pub fn offset(&self) -> (u32, u32) { self.offset }
pub fn coordinate<T: Scalar + NumCast + Float>(&self, coord: (T, T)) -> Vector2<T> {
let (u, v) = coord;
let width: T = self.width();
Vector2::new(
width * (<T as NumCast>::from(self.offset.0).unwrap() + u),
width * (<T as NumCast>::from(self.offset.1).unwrap() + v),
)
}
pub fn center<T: Scalar + NumCast + Float>(&self) -> Vector2<T> {
let half = NumCast::from(0.5).unwrap();
self.coordinate((half, half))
}
pub fn width<T: Scalar + NumCast + Float>(&self) -> T {
Float::powi(<T as NumCast>::from(0.5).unwrap(), self.scale as i32)
}
}
impl Subdivide for UnitQuad {
fn subdivide(&self) -> Vec<UnitQuad> {
[(0, 0), (0, 1), (1, 0), (1, 1)]
.iter()
.map(|&(di, dj)| {
let (i, j) = self.offset;
UnitQuad::new(self.scale + 1, (i * 2 + di, j * 2 + dj))
})
.collect()
}
}
impl<T: Scalar + NumCast + Float> Partition<Vector2<T>> for UnitQuad
{
fn contains(&self, elem: &Vector2<T>) -> bool {
let width: T = self.width();
let offset = Vector2::new(
width * NumCast::from(self.offset.0).unwrap(),
width * NumCast::from(self.offset.1).unwrap(),
);
(offset.x < elem.x) && (elem.x < offset.x + width) &&
(offset.y < elem.y) && (elem.y < offset.y + width)
}
}
#[cfg(any(test, feature = "arbitrary"))]
impl Arbitrary for UnitQuad {
fn arbitrary<G: Gen>(g: &mut G) -> UnitQuad {
use std::cmp;
let scale: u8 = {
let max_scale = cmp::min(31, g.size()) as u8;
g.gen_range(0, max_scale)
};
let max_offset = 2.pow(scale as u32);
UnitQuad::new(scale, (
g.gen_range(0, max_offset),
g.gen_range(0, max_offset),
))
}
}
#[cfg(test)]
mod test {
pub use nalgebra::Vector2;
pub use super::*;
use quickcheck::{quickcheck, TestResult};
use partition::Partition;
partition_quickcheck!(unitquad_vec2_f32, UnitQuad, Vector2<f32>);
partition_quickcheck!(unitquad_vec2_f64, UnitQuad, Vector2<f64>);
#[test]
fn unitquad_base_contains_region() {
fn check(v: Vector2<f64>) -> TestResult {
if v.x < 0.0 || v.x >= 1.0 || v.y < 0.0 || v.y >= 1.0 {
TestResult::discard()
} else {
TestResult::from_bool(UnitQuad::new(0, (0, 0)).contains(&v))
}
}
quickcheck(check as fn(Vector2<f64>) -> TestResult);
}
}