#![doc = include_str!("../README.md")]
#![no_std]
pub mod repr;
#[cfg(feature = "serde")]
mod serde;
#[cfg(feature = "reflectapi")]
mod reflectapi;
use core::marker::PhantomData;
use repr::*;
#[cfg(not(feature = "high_precision"))]
pub type CoordinateValue = u16;
#[cfg(feature = "high_precision")]
pub type CoordinateValue = u32;
#[cfg(not(feature = "high_precision"))]
#[doc(hidden)]
type InternalCalculationType = u32;
#[cfg(feature = "high_precision")]
#[doc(hidden)]
type InternalCalculationType = u64;
#[inline]
const fn div_round_closest(
dividend: InternalCalculationType,
divider: InternalCalculationType,
) -> InternalCalculationType {
(dividend + (divider / 2)) / divider
}
macro_rules! min {
($a:expr, $b:expr) => {{ if $a < $b { $a } else { $b } }};
}
macro_rules! max {
($a:expr, $b:expr) => {{ if $a > $b { $a } else { $b } }};
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Coordinate {
pub x: CoordinateValue,
pub y: CoordinateValue,
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct ImageDimensions {
pub width: CoordinateValue,
pub height: CoordinateValue,
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Hotspot<R = PixelRepr> {
upper_right: Coordinate,
lower_left: Coordinate,
_repr: core::marker::PhantomData<R>,
}
impl Hotspot<PixelRepr> {
#[inline]
pub const fn upper_right(&self) -> Coordinate {
self.upper_right
}
#[inline]
pub const fn upper_left(&self) -> Coordinate {
Coordinate {
x: self.upper_right.x,
y: self.lower_left.y,
}
}
#[inline]
pub const fn lower_left(&self) -> Coordinate {
self.lower_left
}
#[inline]
pub const fn lower_right(&self) -> Coordinate {
Coordinate {
x: self.lower_left.x,
y: self.upper_right.y,
}
}
#[inline]
pub const fn as_percentage(
this: Self,
image_dimensions: ImageDimensions,
) -> Hotspot<PercentageRepr> {
let Self {
upper_right,
lower_left,
_repr: _,
} = this;
Hotspot::builder()
.with_repr::<PercentageRepr>()
.from_percentage((upper_right, lower_left), image_dimensions)
}
}
impl Hotspot<PercentageRepr> {
#[inline]
pub const fn as_pixels(this: Self, image_dimensions: ImageDimensions) -> Hotspot<PixelRepr> {
Hotspot {
upper_right: this.upper_right(image_dimensions),
lower_left: this.lower_left(image_dimensions),
_repr: PhantomData,
}
}
}
impl Hotspot<PercentageRepr> {
#[inline]
pub const fn upper_right(
&self,
ImageDimensions { height, width }: ImageDimensions,
) -> Coordinate {
let Coordinate { x, y } = self.upper_right;
let x: CoordinateValue = div_round_closest(
x as InternalCalculationType * width as InternalCalculationType,
CoordinateValue::MAX as InternalCalculationType,
) as CoordinateValue;
let y: CoordinateValue = div_round_closest(
y as InternalCalculationType * height as InternalCalculationType,
CoordinateValue::MAX as InternalCalculationType,
) as CoordinateValue;
Coordinate { x, y }
}
#[inline]
pub const fn upper_left(
&self,
ImageDimensions { height, width }: ImageDimensions,
) -> Coordinate {
let Coordinate { x, y } = Coordinate {
x: self.upper_right.x,
y: self.lower_left.y,
};
let x: CoordinateValue = div_round_closest(
x as InternalCalculationType * width as InternalCalculationType,
CoordinateValue::MAX as InternalCalculationType,
) as CoordinateValue;
let y: CoordinateValue = div_round_closest(
y as InternalCalculationType * height as InternalCalculationType,
CoordinateValue::MAX as InternalCalculationType,
) as CoordinateValue;
Coordinate { x, y }
}
#[inline]
pub const fn lower_left(
&self,
ImageDimensions { height, width }: ImageDimensions,
) -> Coordinate {
let Coordinate { x, y } = self.lower_left;
let x: CoordinateValue = div_round_closest(
x as InternalCalculationType * width as InternalCalculationType,
CoordinateValue::MAX as InternalCalculationType,
) as CoordinateValue;
let y: CoordinateValue = div_round_closest(
y as InternalCalculationType * height as InternalCalculationType,
CoordinateValue::MAX as InternalCalculationType,
) as CoordinateValue;
Coordinate { x, y }
}
#[inline]
pub const fn lower_right(
&self,
ImageDimensions { height, width }: ImageDimensions,
) -> Coordinate {
let Coordinate { x, y } = Coordinate {
x: self.lower_left.x,
y: self.upper_right.y,
};
let x: CoordinateValue = div_round_closest(
x as InternalCalculationType * width as InternalCalculationType,
CoordinateValue::MAX as InternalCalculationType,
) as CoordinateValue;
let y: CoordinateValue = div_round_closest(
y as InternalCalculationType * height as InternalCalculationType,
CoordinateValue::MAX as InternalCalculationType,
) as CoordinateValue;
Coordinate { x, y }
}
}
impl<R> Hotspot<R> {
pub const fn overlap(&self, other: &Self) -> f32 {
let Coordinate { x: xa2, y: ya2 } = self.upper_right;
let Coordinate { x: xa1, y: ya1 } = self.lower_left;
let Coordinate { x: xb2, y: yb2 } = other.upper_right;
let Coordinate { x: xb1, y: yb1 } = other.lower_left;
let xa1 = xa1 as InternalCalculationType;
let xa2 = xa2 as InternalCalculationType;
let ya1 = ya1 as InternalCalculationType;
let ya2 = ya2 as InternalCalculationType;
let xb1 = xb1 as InternalCalculationType;
let xb2 = xb2 as InternalCalculationType;
let yb1 = yb1 as InternalCalculationType;
let yb2 = yb2 as InternalCalculationType;
let sa = (xa2 - xa1) * (ya2 - ya1);
let sb = (xb2 - xb1) * (yb2 - yb1);
let intersection_w = min!(xa2, xb2).saturating_sub(max!(xa1, xb1));
let intersection_h = min!(ya2, yb2).saturating_sub(max!(ya1, yb1));
let si = intersection_w * intersection_h;
let su = sa as f32 + sb as f32 - si as f32;
if su == 0.0 {
return 0.0;
}
si as f32 / su
}
pub const fn overlap_in(&self, other: &Self) -> f32 {
let Coordinate { x: xa2, y: ya2 } = self.upper_right;
let Coordinate { x: xa1, y: ya1 } = self.lower_left;
let Coordinate { x: xb2, y: yb2 } = other.upper_right;
let Coordinate { x: xb1, y: yb1 } = other.lower_left;
let xa1 = xa1 as InternalCalculationType;
let xa2 = xa2 as InternalCalculationType;
let ya1 = ya1 as InternalCalculationType;
let ya2 = ya2 as InternalCalculationType;
let xb1 = xb1 as InternalCalculationType;
let xb2 = xb2 as InternalCalculationType;
let yb1 = yb1 as InternalCalculationType;
let yb2 = yb2 as InternalCalculationType;
let sa = (xa2 - xa1) * (ya2 - ya1);
let intersection_w = min!(xa2, xb2).saturating_sub(max!(xa1, xb1));
let intersection_h = min!(ya2, yb2).saturating_sub(max!(ya1, yb1));
let si = intersection_w * intersection_h;
if sa == 0 {
return 0.0;
}
si as f32 / sa as f32
}
#[inline]
pub const fn max_overlap(&self, other: &Self) -> f32 {
self.overlap_in(other).max(other.overlap_in(self))
}
#[inline]
pub const fn combine_hotspots(this: Self, other: Self) -> Self {
Self {
upper_right: Coordinate {
x: max!(this.upper_right.x, other.upper_right.x),
y: max!(this.upper_right.y, other.upper_right.y),
},
lower_left: Coordinate {
x: min!(this.lower_left.x, other.lower_left.x),
y: min!(this.lower_left.y, other.lower_left.y),
},
_repr: PhantomData,
}
}
}
#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct HotspotBuilder<R> {
_marker: PhantomData<R>,
}
impl core::fmt::Debug for HotspotBuilder<PixelRepr> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("HotspotBuilder")
.field("kind", &"Pixel Representation")
.finish()
}
}
impl core::fmt::Debug for HotspotBuilder<PercentageRepr> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("HotspotBuilder")
.field("kind", &"Percentage Representation")
.finish()
}
}
impl Hotspot {
#[inline]
pub const fn builder() -> HotspotBuilder<PixelRepr> {
HotspotBuilder {
_marker: core::marker::PhantomData,
}
}
}
impl<R: InternalRepr> HotspotBuilder<R> {
#[inline]
pub const fn with_repr<NewR: InternalRepr>(self) -> HotspotBuilder<NewR> {
HotspotBuilder {
_marker: core::marker::PhantomData,
}
}
}
impl HotspotBuilder<PixelRepr> {
#[inline]
pub const fn from_pixels(
self,
(Coordinate { x: x1, y: y1 }, Coordinate { x: x2, y: y2 }): (Coordinate, Coordinate),
) -> Hotspot<PixelRepr> {
let upper_right = Coordinate {
x: max!(x1, x2),
y: max!(y1, y2),
};
let lower_left = Coordinate {
x: min!(x1, x2),
y: min!(y1, y2),
};
Hotspot {
upper_right,
lower_left,
_repr: core::marker::PhantomData,
}
}
}
impl HotspotBuilder<PercentageRepr> {
#[inline]
pub const fn from_percentage(
self,
input: (Coordinate, Coordinate),
ImageDimensions { height, width }: ImageDimensions,
) -> Hotspot<PercentageRepr> {
let Hotspot {
upper_right,
lower_left,
_repr: _,
} = Hotspot::<PixelRepr>::builder().from_pixels(input);
let height = height as InternalCalculationType;
let width = width as InternalCalculationType;
let upper_right = Coordinate {
x: div_round_closest(
upper_right.x as InternalCalculationType
* CoordinateValue::MAX as InternalCalculationType,
width,
) as CoordinateValue,
y: div_round_closest(
upper_right.y as InternalCalculationType
* CoordinateValue::MAX as InternalCalculationType,
height,
) as CoordinateValue,
};
let lower_left = Coordinate {
x: div_round_closest(
lower_left.x as InternalCalculationType
* CoordinateValue::MAX as InternalCalculationType,
width,
) as CoordinateValue,
y: div_round_closest(
lower_left.y as InternalCalculationType
* CoordinateValue::MAX as InternalCalculationType,
height,
) as CoordinateValue,
};
Hotspot {
upper_right,
lower_left,
_repr: core::marker::PhantomData,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
extern crate alloc;
use alloc::format;
#[cfg(not(feature = "high_precision"))]
#[test]
fn test_percentage_repr() {
let hotspot = Hotspot::builder()
.with_repr::<PercentageRepr>()
.from_percentage(
(Coordinate { x: 50, y: 50 }, Coordinate { x: 2622, y: 2622 }),
crate::ImageDimensions {
height: 5000,
width: 5000,
},
);
assert_eq!(hotspot.upper_right, Coordinate { x: 34367, y: 34367 });
assert_eq!(hotspot.lower_left, Coordinate { x: 655, y: 655 });
assert_eq!(
hotspot.upper_right(crate::ImageDimensions {
height: 5000,
width: 5000,
}),
Coordinate { x: 2622, y: 2622 }
);
assert_eq!(
hotspot.lower_right(crate::ImageDimensions {
height: 10000,
width: 5000,
}),
Coordinate { x: 50, y: 5244 }
);
assert_eq!(
hotspot.upper_left(crate::ImageDimensions {
height: 5000,
width: 5000,
}),
Coordinate { x: 2622, y: 50 }
);
assert_eq!(
hotspot.upper_right(crate::ImageDimensions {
height: 10000,
width: 5000,
}),
Coordinate { x: 2622, y: 5244 }
);
}
#[cfg(feature = "high_precision")]
#[test]
fn test_percentage_repr() {
let hotspot = Hotspot::builder()
.with_repr::<PercentageRepr>()
.from_percentage(
(Coordinate { x: 50, y: 50 }, Coordinate { x: 2622, y: 2622 }),
crate::ImageDimensions {
height: 5000,
width: 5000,
},
);
assert_eq!(
hotspot.upper_right,
Coordinate {
x: 2252280849,
y: 2252280849
}
);
assert_eq!(
hotspot.lower_left,
Coordinate {
x: 42949673,
y: 42949673
}
);
assert_eq!(
hotspot.upper_right(crate::ImageDimensions {
height: 5000,
width: 5000,
}),
Coordinate { x: 2622, y: 2622 }
);
assert_eq!(
hotspot.lower_right(crate::ImageDimensions {
height: 10000,
width: 5000,
}),
Coordinate { x: 50, y: 5244 }
);
assert_eq!(
hotspot.upper_left(crate::ImageDimensions {
height: 5000,
width: 5000,
}),
Coordinate { x: 2622, y: 50 }
);
assert_eq!(
hotspot.upper_right(crate::ImageDimensions {
height: 10000,
width: 5000,
}),
Coordinate { x: 2622, y: 5244 }
);
}
fn make_hotspot(x1: u16, y1: u16, x2: u16, y2: u16) -> Hotspot<PixelRepr> {
Hotspot::builder().from_pixels((
Coordinate {
x: x1 as CoordinateValue,
y: y1 as CoordinateValue,
},
Coordinate {
x: x2 as CoordinateValue,
y: y2 as CoordinateValue,
},
))
}
#[test]
fn test_no_overlap() {
let h1 = make_hotspot(0, 0, 10, 10);
let h2 = make_hotspot(20, 20, 30, 30);
assert_eq!(h1.overlap(&h2), 0.0);
}
#[test]
fn test_complete_overlap() {
let h1 = make_hotspot(0, 0, 10, 10);
let h2 = make_hotspot(0, 0, 10, 10);
assert_eq!(h1.overlap(&h2), 1.0);
}
#[test]
fn test_partial_overlap() {
let h1 = make_hotspot(0, 0, 10, 10);
let h2 = make_hotspot(5, 0, 15, 10);
assert!((h1.overlap(&h2) - (1.0 / 3.0)).abs() < f32::EPSILON);
}
#[test]
fn test_contained_overlap() {
let h1 = make_hotspot(0, 0, 20, 20);
let h2 = make_hotspot(5, 5, 15, 15);
assert_eq!(h1.overlap(&h2), 0.25);
assert_eq!(h2.overlap_in(&h1), 1.0);
assert_eq!(h1.overlap_in(&h2), 0.25);
}
#[test]
fn test_corner_overlap() {
let h1 = make_hotspot(0, 0, 10, 10);
let h2 = make_hotspot(5, 5, 15, 15);
assert!((h1.overlap(&h2) - (1.0 / 7.0)).abs() < f32::EPSILON);
}
#[test]
fn test_zero_area() {
let h1 = make_hotspot(0, 0, 0, 0);
let h2 = make_hotspot(0, 0, 10, 10);
assert_eq!(h1.overlap(&h2), 0.0);
let h1 = make_hotspot(0, 0, 0, 0);
let h2 = make_hotspot(0, 0, 0, 0);
assert_eq!(h1.overlap(&h2), 0.0);
}
#[test]
fn test_overflow() {
let h1 = make_hotspot(0, 0, u16::MAX, u16::MAX);
let h2 = make_hotspot(0, 0, u16::MAX, u16::MAX);
assert_eq!(h1.overlap(&h2), 1.0);
}
#[test]
fn test_from_pixels_equal_coordinates() {
let h1 = make_hotspot(5, 5, 5, 5);
assert_eq!(h1.lower_left, Coordinate { x: 5, y: 5 });
assert_eq!(h1.upper_right, Coordinate { x: 5, y: 5 });
let h2 = make_hotspot(5, 0, 5, 10);
assert_eq!(h2.lower_left, Coordinate { x: 5, y: 0 });
assert_eq!(h2.upper_right, Coordinate { x: 5, y: 10 });
let h3 = make_hotspot(0, 5, 10, 5);
assert_eq!(h3.lower_left, Coordinate { x: 0, y: 5 });
assert_eq!(h3.upper_right, Coordinate { x: 10, y: 5 });
let h4 = make_hotspot(10, 10, 0, 0);
assert_eq!(h4.lower_left, Coordinate { x: 0, y: 0 });
assert_eq!(h4.upper_right, Coordinate { x: 10, y: 10 });
}
#[test]
fn test_from_pixels_strict_inequality_logic() {
let h = make_hotspot(5, 7, 6, 8);
assert_eq!(h.lower_left.x, 5);
assert_eq!(h.lower_left.y, 7);
assert_eq!(h.upper_right.x, 6);
assert_eq!(h.upper_right.y, 8);
let h = make_hotspot(10, 20, 9, 19);
assert_eq!(h.lower_left.x, 9);
assert_eq!(h.lower_left.y, 19);
assert_eq!(h.upper_right.x, 10);
assert_eq!(h.upper_right.y, 20);
for x1 in 0u16..15 {
for x2 in 0u16..15 {
let h = make_hotspot(x1, 0, x2, 0);
if x1 < x2 {
assert_eq!(h.lower_left.x, x1 as CoordinateValue);
assert_eq!(h.upper_right.x, x2 as CoordinateValue);
} else {
assert_eq!(h.lower_left.x, x2 as CoordinateValue);
assert_eq!(h.upper_right.x, x1 as CoordinateValue);
}
}
}
for y1 in 0u16..15 {
for y2 in 0u16..15 {
let h = make_hotspot(0, y1, 0, y2);
if y1 < y2 {
assert_eq!(h.lower_left.y, y1 as CoordinateValue);
assert_eq!(h.upper_right.y, y2 as CoordinateValue);
} else {
assert_eq!(h.lower_left.y, y2 as CoordinateValue);
assert_eq!(h.upper_right.y, y1 as CoordinateValue);
}
}
}
}
#[test]
fn test_max_overlap_symmetric() {
let h1 = make_hotspot(0, 0, 10, 10);
let h2 = make_hotspot(0, 0, 10, 10);
assert_eq!(h1.max_overlap(&h2), 1.0);
}
#[test]
fn test_max_overlap_no_overlap() {
let h1 = make_hotspot(0, 0, 10, 10);
let h2 = make_hotspot(20, 20, 30, 30);
assert_eq!(h1.max_overlap(&h2), 0.0);
}
#[test]
fn test_max_overlap_contained() {
let h1 = make_hotspot(0, 0, 20, 20);
let h2 = make_hotspot(5, 5, 15, 15);
assert_eq!(h1.max_overlap(&h2), 1.0);
}
#[test]
fn test_max_overlap_partial() {
let h1 = make_hotspot(0, 0, 10, 10);
let h2 = make_hotspot(5, 0, 15, 10);
assert_eq!(h1.max_overlap(&h2), 0.5);
}
#[test]
fn test_debug_impls() {
let pixel_builder = Hotspot::builder();
assert_eq!(
format!("{:?}", pixel_builder),
"HotspotBuilder { kind: \"Pixel Representation\" }"
);
let percentage_builder = Hotspot::builder().with_repr::<PercentageRepr>();
assert_eq!(
format!("{:?}", percentage_builder),
"HotspotBuilder { kind: \"Percentage Representation\" }"
);
}
#[cfg(not(miri))]
mod fuzz_tests {
use super::*;
use proptest::prelude::*;
prop_compose! {
fn arb_coordinate()(x in 0..CoordinateValue::MAX, y in 0..CoordinateValue::MAX) -> Coordinate {
Coordinate { x, y }
}
}
prop_compose! {
fn arb_hotspot()(c1 in arb_coordinate(), c2 in arb_coordinate()) -> Hotspot<PixelRepr> {
Hotspot::builder().from_pixels((c1, c2))
}
}
prop_compose! {
fn arb_dimensions()(
width in 1..CoordinateValue::MAX,
height in 1..CoordinateValue::MAX
) -> ImageDimensions {
ImageDimensions { width, height }
}
}
proptest! {
#[test]
fn fuzz_from_pixels_invariants(c1 in arb_coordinate(), c2 in arb_coordinate()) {
let h = Hotspot::builder().from_pixels((c1, c2));
prop_assert!(h.lower_left.x <= h.upper_right.x);
prop_assert!(h.lower_left.y <= h.upper_right.y);
let expected_lower_x = if c1.x < c2.x { c1.x } else { c2.x };
let expected_lower_y = if c1.y < c2.y { c1.y } else { c2.y };
prop_assert_eq!(h.lower_left.x, expected_lower_x);
prop_assert_eq!(h.lower_left.y, expected_lower_y);
let expected_top_x = if c1.x > c2.x { c1.x } else { c2.x };
let expected_top_y = if c1.y > c2.y { c1.y } else { c2.y };
prop_assert_eq!(h.upper_right.x, expected_top_x);
prop_assert_eq!(h.upper_right.y, expected_top_y);
}
#[test]
fn fuzz_overlap_symmetry(h1 in arb_hotspot(), h2 in arb_hotspot()) {
let o1 = h1.overlap(&h2);
let o2 = h2.overlap(&h1);
prop_assert!((o1 - o2).abs() < f32::EPSILON);
}
#[test]
fn fuzz_overlap_bounds(h1 in arb_hotspot(), h2 in arb_hotspot()) {
let o = h1.overlap(&h2);
prop_assert!(o >= 0.0);
prop_assert!(o <= 1.0);
}
#[test]
fn fuzz_overlap_in_bounds(h1 in arb_hotspot(), h2 in arb_hotspot()) {
let o = h1.overlap_in(&h2);
prop_assert!(o >= 0.0);
prop_assert!(o <= 1.0);
}
#[test]
fn fuzz_combine_hotspots_containment(h1 in arb_hotspot(), h2 in arb_hotspot()) {
let combined = Hotspot::combine_hotspots(h1, h2);
prop_assert!(combined.upper_right.x >= h1.upper_right.x);
prop_assert!(combined.upper_right.y >= h1.upper_right.y);
prop_assert!(combined.lower_left.x <= h1.lower_left.x);
prop_assert!(combined.lower_left.y <= h1.lower_left.y);
prop_assert!(combined.upper_right.x >= h2.upper_right.x);
prop_assert!(combined.upper_right.y >= h2.upper_right.y);
prop_assert!(combined.lower_left.x <= h2.lower_left.x);
prop_assert!(combined.lower_left.y <= h2.lower_left.y);
}
#[test]
fn fuzz_combine_hotspots_overlap_in(h1 in arb_hotspot(), h2 in arb_hotspot()) {
let combined = Hotspot::combine_hotspots(h1, h2);
let h1_area = (h1.upper_right.x - h1.lower_left.x) as InternalCalculationType * (h1.upper_right.y - h1.lower_left.y) as InternalCalculationType;
if h1_area > 0 {
prop_assert!((h1.overlap_in(&combined) - 1.0).abs() < 1e-5);
}
let h2_area = (h2.upper_right.x - h2.lower_left.x) as InternalCalculationType * (h2.upper_right.y - h2.lower_left.y) as InternalCalculationType;
if h2_area > 0 {
prop_assert!((h2.overlap_in(&combined) - 1.0).abs() < 1e-5);
}
}
#[test]
fn fuzz_percentage_roundtrip(
h in arb_hotspot(),
dims in arb_dimensions()
) {
let h_constrained = Hotspot::builder().from_pixels((
Coordinate {
x: h.lower_left.x % dims.width,
y: h.lower_left.y % dims.height
},
Coordinate {
x: h.upper_right.x % dims.width,
y: h.upper_right.y % dims.height
}
));
let p = Hotspot::as_percentage(h_constrained, dims);
let back = Hotspot::as_pixels(p, dims);
let tolerance_x = (dims.width as f64 / u16::MAX as f64).ceil() as CoordinateValue + 1;
let tolerance_y = (dims.height as f64 / u16::MAX as f64).ceil() as CoordinateValue + 1;
let diff_x1 = back.lower_left.x.abs_diff(h_constrained.lower_left.x);
let diff_y1 = back.lower_left.y.abs_diff(h_constrained.lower_left.y);
let diff_x2 = back.upper_right.x.abs_diff(h_constrained.upper_right.x);
let diff_y2 = back.upper_right.y.abs_diff(h_constrained.upper_right.y);
prop_assert!(diff_x1 <= tolerance_x);
prop_assert!(diff_y1 <= tolerance_y);
prop_assert!(diff_x2 <= tolerance_x);
prop_assert!(diff_y2 <= tolerance_y);
}
}
}
}