use num_traits::AsPrimitive;
use std::iter::{FusedIterator, Iterator};
use std::marker::PhantomData;
use super::coords::{Point, Vector};
type HexPointF = Point<f32>;
type HexVectorF = Vector<f32>;
pub struct Line<T> {
current: HexPointF,
step_size: HexVectorF,
steps: usize,
_phantom: PhantomData<T>,
}
impl<T> Line<T>
where
T: AsPrimitive<f32>,
f32: AsPrimitive<T>,
{
pub fn new(from: Point<T>, to: Point<T>, exclude_start: bool, exclude_end: bool) -> Self {
let mut current = from.cast_fix();
let delta = to.cast_fix() - current;
#[allow(clippy::cast_sign_loss)]
let mut steps = delta.length() as usize + 1;
let step_size = delta * (1.0 / ((steps - 1) as f32));
if exclude_start && steps > 0 {
current += step_size;
steps -= 1;
}
if exclude_end && steps > 0 {
steps -= 1;
}
Line {
current,
step_size,
steps,
_phantom: PhantomData,
}
}
}
impl<T> Iterator for Line<T>
where
T: 'static + Copy,
f32: AsPrimitive<T>,
{
type Item = Point<T>;
fn next(&mut self) -> Option<Self::Item> {
if self.steps > 0 {
let result = self.current;
self.current += self.step_size;
self.steps -= 1;
Some(result.cast_round())
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.steps, Some(self.steps))
}
}
impl<T> ExactSizeIterator for Line<T>
where
T: 'static + Copy,
f32: AsPrimitive<T>,
{
fn len(&self) -> usize {
self.steps
}
}
impl<T> FusedIterator for Line<T>
where
T: 'static + Copy,
f32: AsPrimitive<T>,
{
}
#[cfg(test)]
mod tests {
use super::*;
type H = super::super::coords::Point<i16>;
type V = super::super::coords::Vector<i16>;
#[test]
fn can_iter() {
fn test_endpoints(from: H, to: H, expected_steps: &[V]) {
let expected = expected_steps
.iter()
.scan(from, |s, &d| {
*s += d;
Some(*s)
})
.collect::<Vec<_>>();
itertools::assert_equal(Line::new(from, to, false, false), expected.iter().cloned());
itertools::assert_equal(
Line::new(from, to, true, false),
expected[1..].iter().cloned(),
);
itertools::assert_equal(
Line::new(from, to, false, true),
expected[..expected.len() - 1].iter().cloned(),
);
itertools::assert_equal(
Line::new(from, to, true, true),
expected[1..expected.len() - 1].iter().cloned(),
);
itertools::assert_equal(
Line::new(to, from, false, false),
expected.iter().cloned().rev(),
);
itertools::assert_equal(
Line::new(to, from, true, false),
expected[..expected.len() - 1].iter().cloned().rev(),
);
itertools::assert_equal(
Line::new(to, from, false, true),
expected[1..].iter().cloned().rev(),
);
itertools::assert_equal(
Line::new(to, from, true, true),
expected[1..expected.len() - 1].iter().cloned().rev(),
);
}
test_endpoints(
H::new(-1, 1, 0, 0),
H::new(0, -1, 1, 0),
&[V::zero(), V::zy(), V::xy()],
);
test_endpoints(
H::new(-1, 1, 0, 0),
H::new(1, -2, 1, 0),
&[V::zero(), V::xy(), V::zy(), V::xy()],
);
test_endpoints(
H::new(-1, 1, 0, 0),
H::new(0, 2, -2, 0),
&[V::zero(), V::yz(), V::xz()],
);
}
}