use kurbo::Rect;
use pdfrum_object::Array;
use crate::geom;
#[must_use]
pub fn quad_point_count(array: Option<&Array>) -> usize {
array.map_or(0, |a| a.len() / 8)
}
#[must_use]
pub(crate) fn rect_from_quad_points_array(array: &Array, index: usize) -> Rect {
let base = index * 8;
let at = |offset: usize| array.number_at_or_zero(base + offset);
geom::rect(at(4), at(5), at(2), at(3))
}
#[must_use]
pub fn rect_from_quad_points(array: Option<&Array>, index: usize) -> Rect {
let array = array.filter(|a| index < a.len() / 8);
array.map_or(Rect::ZERO, |a| rect_from_quad_points_array(a, index))
}
#[must_use]
pub(crate) fn bounding_rect_from_quad_points(array: Option<&Array>) -> Rect {
let count = quad_point_count(array);
let Some(array) = array.filter(|_| count > 0) else {
return Rect::ZERO;
};
let mut bounds = rect_from_quad_points_array(array, 0);
for index in 1..count {
bounds = geom::union(bounds, rect_from_quad_points_array(array, index));
}
bounds
}
#[cfg(test)]
mod tests {
use super::{
bounding_rect_from_quad_points, quad_point_count, rect_from_quad_points,
rect_from_quad_points_array,
};
use crate::geom;
use kurbo::Rect;
use pdfrum_object::{Array, Object};
fn quads(count: usize) -> Array {
Array::of((0..count * 8).map(|n| Object::Int(i64::try_from(n).unwrap_or(0))))
}
#[test]
fn the_derived_rectangle_is_index_arithmetic_not_a_normalization() {
let array = quads(2);
assert_eq!(
rect_from_quad_points_array(&array, 0),
geom::rect(4.0, 5.0, 2.0, 3.0)
);
assert_eq!(
rect_from_quad_points_array(&array, 1),
geom::rect(12.0, 13.0, 10.0, 11.0)
);
}
#[test]
fn one_quadrilateral_is_never_normalized_but_two_are() {
let one = quads(1);
assert_eq!(
bounding_rect_from_quad_points(Some(&one)),
geom::rect(4.0, 5.0, 2.0, 3.0)
);
let three = quads(3);
assert_eq!(
bounding_rect_from_quad_points(Some(&three)),
geom::rect(2.0, 3.0, 20.0, 21.0)
);
}
#[test]
fn no_quadrilaterals_gives_the_zero_rectangle() {
assert_eq!(bounding_rect_from_quad_points(None), Rect::ZERO);
assert_eq!(
bounding_rect_from_quad_points(Some(&Array::of([Object::Int(1), Object::Int(2)]))),
Rect::ZERO
);
}
#[test]
fn an_out_of_range_index_gives_zero_rather_than_the_last_quadrilateral() {
let array = quads(2);
assert_eq!(rect_from_quad_points(Some(&array), 2), Rect::ZERO);
assert_eq!(
rect_from_quad_points(Some(&array), 1),
geom::rect(12.0, 13.0, 10.0, 11.0)
);
}
#[test]
fn the_count_ignores_a_trailing_partial_quadrilateral() {
for len in 0..8 {
let array = Array::of((0..len).map(Object::Int));
assert_eq!(quad_point_count(Some(&array)), 0, "len {len}");
}
for len in 8..16 {
let array = Array::of((0..len).map(Object::Int));
assert_eq!(quad_point_count(Some(&array)), 1, "len {len}");
}
let array = Array::of((0..65).map(Object::Int));
assert_eq!(quad_point_count(Some(&array)), 8);
}
}