use geometry_core::Rect;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Axis {
Horizontal,
Vertical,
}
impl Axis {
pub fn of(self, point: (f32, f32)) -> f32 {
match self {
Axis::Horizontal => point.0,
Axis::Vertical => point.1,
}
}
pub fn centre(self, rect: &Rect) -> f32 {
match self {
Axis::Horizontal => rect.x + rect.width / 2.0,
Axis::Vertical => rect.y + rect.height / 2.0,
}
}
}
pub fn insertion_index(rects: &[Rect], point: (f32, f32), axis: Axis) -> usize {
let along = axis.of(point);
rects
.iter()
.filter(|rect| axis.centre(rect) < along)
.count()
.min(rects.len())
}
pub fn apply_move<T>(items: &mut Vec<T>, from: usize, to: usize) -> bool {
if from >= items.len() {
return false;
}
let to = to.min(items.len());
let target = if to > from { to - 1 } else { to };
if target == from {
return false;
}
let item = items.remove(from);
items.insert(target, item);
true
}
#[cfg(test)]
mod tests {
use super::*;
fn strip(count: usize) -> Vec<Rect> {
(0..count)
.map(|i| Rect {
x: i as f32 * 100.0,
y: 0.0,
width: 100.0,
height: 40.0,
})
.collect()
}
#[test]
fn a_slot_turns_at_an_items_centre_not_at_its_edge() {
let rects = strip(3);
assert_eq!(insertion_index(&rects, (49.0, 20.0), Axis::Horizontal), 0);
assert_eq!(insertion_index(&rects, (51.0, 20.0), Axis::Horizontal), 1);
assert_eq!(insertion_index(&rects, (151.0, 20.0), Axis::Horizontal), 2);
}
#[test]
fn past_the_last_item_is_the_slot_after_it() {
let rects = strip(3);
assert_eq!(insertion_index(&rects, (999.0, 20.0), Axis::Horizontal), 3);
assert_eq!(insertion_index(&rects, (-999.0, 20.0), Axis::Horizontal), 0);
}
#[test]
fn a_vertical_strip_reads_the_other_coordinate() {
let rects: Vec<Rect> = (0..3)
.map(|i| Rect {
x: 0.0,
y: i as f32 * 40.0,
width: 100.0,
height: 40.0,
})
.collect();
assert_eq!(insertion_index(&rects, (50.0, 21.0), Axis::Vertical), 1);
assert_eq!(insertion_index(&rects, (50.0, 21.0), Axis::Horizontal), 0);
}
#[test]
fn moving_rightwards_accounts_for_the_hole_left_behind() {
let mut items = vec!['a', 'b', 'c', 'd'];
assert!(apply_move(&mut items, 0, 3));
assert_eq!(items, vec!['b', 'c', 'a', 'd']);
}
#[test]
fn moving_leftwards_lands_on_the_slot_as_counted() {
let mut items = vec!['a', 'b', 'c', 'd'];
assert!(apply_move(&mut items, 3, 1));
assert_eq!(items, vec!['a', 'd', 'b', 'c']);
}
#[test]
fn dropping_where_it_already_is_moves_nothing() {
let mut items = vec!['a', 'b', 'c'];
assert!(!apply_move(&mut items, 1, 1));
assert!(!apply_move(&mut items, 1, 2));
assert_eq!(items, vec!['a', 'b', 'c']);
}
#[test]
fn an_out_of_range_source_is_refused_rather_than_panicking() {
let mut items = vec!['a'];
assert!(!apply_move(&mut items, 5, 0));
assert_eq!(items, vec!['a']);
}
}