use num_traits::ToPrimitive;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::{Size, SubRect};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Slice {
Binary {
split: u32,
repeat: BinarySection,
},
Ternary {
split_first: u32,
split_last: u32,
},
}
impl Slice {
pub fn binary_first<S>(split: S) -> Self
where
S: ToPrimitive,
{
let split = split.to_u32().unwrap_or_default();
Self::Binary {
split,
repeat: BinarySection::First,
}
}
pub fn binary_last<S>(split: S) -> Self
where
S: ToPrimitive,
{
let split = split.to_u32().unwrap_or_default();
Self::Binary {
split,
repeat: BinarySection::Last,
}
}
pub fn ternary<S1, S2>(split_first: S1, split_last: S2) -> Self
where
S1: ToPrimitive,
S2: ToPrimitive,
{
let split_first = split_first.to_u32().unwrap_or_default();
let split_last = split_last.to_u32().unwrap_or_default();
Self::Ternary {
split_first,
split_last,
}
}
pub(crate) fn divide_area_iter(
&self,
source_length: u32,
target_length: u32,
) -> impl Iterator<Item = SliceProjection> {
match self {
Slice::Binary { split, repeat } => {
let middle = match repeat {
BinarySection::First => target_length.saturating_sub(*split),
BinarySection::Last => *split,
};
[
(0, middle, 0, *split),
(middle, target_length, *split, source_length),
(0, 0, 0, 0),
]
.into_iter()
}
Slice::Ternary {
split_first,
split_last,
} => {
let (middle_first, middle_second) = (
*split_first,
target_length.saturating_sub(source_length.saturating_sub(*split_last)),
);
let middle_first = middle_first.min(middle_second);
let middle_second = middle_second.clamp(middle_first, target_length);
[
(0, middle_first, 0, *split_first),
(middle_first, middle_second, *split_first, *split_last),
(middle_second, target_length, *split_last, source_length),
]
.into_iter()
}
}
.filter(|(target_start, target_end, source_start, source_end)| {
target_start < target_end && source_start < source_end
})
.map(|(target_start, target_end, source_start, source_end)| {
SliceProjection::new(source_start, source_end, target_start, target_end)
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum BinarySection {
First,
Last,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct SliceProjection {
pub source_start: u32,
pub source_end: u32,
pub target_start: u32,
pub target_end: u32,
}
impl SliceProjection {
pub fn new(source_start: u32, source_end: u32, target_start: u32, target_end: u32) -> Self {
Self {
source_start,
source_end,
target_start,
target_end,
}
}
pub fn source_amount(&self) -> u32 {
self.source_end - self.source_start
}
pub fn target_amount(&self) -> u32 {
self.target_end - self.target_start
}
pub fn into_sub_rects_static_y(self, y_size: u32) -> (SubRect, SubRect) {
let source = SubRect::from((self.source_start, 0, self.source_amount(), y_size));
let target = SubRect::from((self.target_start, 0, self.target_amount(), y_size));
(source, target)
}
pub fn into_sub_rects_static_x(self, x_size: u32) -> (SubRect, SubRect) {
let source = SubRect::from((0, self.source_start, x_size, self.source_amount()));
let target = SubRect::from((0, self.target_start, x_size, self.target_amount()));
(source, target)
}
pub fn combine_into_sub_rects(
horizontal: &SliceProjection,
vertical: &SliceProjection,
) -> (SubRect, SubRect) {
let source = SubRect::new(
horizontal.source_start,
vertical.source_start,
Size::new(horizontal.source_amount(), vertical.source_amount()),
);
let target = SubRect::new(
horizontal.target_start,
vertical.target_start,
Size::new(horizontal.target_amount(), vertical.target_amount()),
);
(source, target)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn slice9() {
let (horizontal_slice, vertical_slice) = (Slice::ternary(10, 20), Slice::ternary(25, 50));
let horizontal_projs = horizontal_slice
.divide_area_iter(30, 100)
.collect::<Vec<_>>();
let vertical_projs = vertical_slice.divide_area_iter(75, 150).collect::<Vec<_>>();
assert_eq!(
horizontal_projs,
[
SliceProjection::new(0, 10, 0, 10),
SliceProjection::new(10, 20, 10, 90),
SliceProjection::new(20, 30, 90, 100)
]
);
assert_eq!(
vertical_projs,
[
SliceProjection::new(0, 25, 0, 25),
SliceProjection::new(25, 50, 25, 125),
SliceProjection::new(50, 75, 125, 150)
]
);
assert_eq!(
SliceProjection::combine_into_sub_rects(&horizontal_projs[0], &vertical_projs[0]),
((0, 0, 10, 25).into(), (0, 0, 10, 25).into())
);
assert_eq!(
SliceProjection::combine_into_sub_rects(&horizontal_projs[1], &vertical_projs[1]),
((10, 25, 10, 25).into(), (10, 25, 80, 100).into())
);
assert_eq!(
SliceProjection::combine_into_sub_rects(&horizontal_projs[2], &vertical_projs[2]),
((20, 50, 10, 25).into(), (90, 125, 10, 25).into())
);
assert_eq!(
horizontal_projs[0].clone().into_sub_rects_static_y(25),
((0, 0, 10, 25).into(), (0, 0, 10, 25).into())
);
assert_eq!(
horizontal_projs[1].clone().into_sub_rects_static_y(25),
((10, 0, 10, 25).into(), (10, 0, 80, 25).into())
);
assert_eq!(
horizontal_projs[2].clone().into_sub_rects_static_y(25),
((20, 0, 10, 25).into(), (90, 0, 10, 25).into())
);
assert_eq!(
vertical_projs[0].clone().into_sub_rects_static_x(10),
((0, 0, 10, 25).into(), (0, 0, 10, 25).into())
);
assert_eq!(
vertical_projs[1].clone().into_sub_rects_static_x(10),
((0, 25, 10, 25).into(), (0, 25, 10, 100).into())
);
assert_eq!(
vertical_projs[2].clone().into_sub_rects_static_x(10),
((0, 50, 10, 25).into(), (0, 125, 10, 25).into())
);
}
}