use std::ops::Range;
use crate::geometry::{
circ_bbox as raw_circle_bbox, circ_pixel_area, ellip_bbox as raw_ellipse_bbox,
ellip_pixel_area, extent_bbox, rect_pixel_area, Ellip, SQRT_HALF,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct BoundingBox {
pub ixmin: isize,
pub ixmax: isize,
pub iymin: isize,
pub iymax: isize,
}
impl BoundingBox {
#[inline]
pub fn x_range(self) -> Range<isize> {
self.ixmin..self.ixmax
}
#[inline]
pub fn y_range(self) -> Range<isize> {
self.iymin..self.iymax
}
#[inline]
pub fn shape(self) -> (usize, usize) {
(
extent_len(self.iymin, self.iymax),
extent_len(self.ixmin, self.ixmax),
)
}
}
impl From<(isize, isize, isize, isize)> for BoundingBox {
#[inline]
fn from((ixmin, ixmax, iymin, iymax): (isize, isize, isize, isize)) -> Self {
Self {
ixmin,
ixmax,
iymin,
iymax,
}
}
}
#[inline]
fn extent_len(start: isize, end: isize) -> usize {
end.checked_sub(start)
.and_then(|value| usize::try_from(value).ok())
.unwrap_or(0)
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Ellipse {
pub x0: f64,
pub y0: f64,
pub a: f64,
pub b: f64,
pub theta: f64,
}
impl Ellipse {
#[inline]
fn raw(self) -> Ellip {
Ellip {
x0: self.x0,
y0: self.y0,
a: self.a,
b: self.b,
cos_t: self.theta.cos(),
sin_t: self.theta.sin(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Rectangle {
pub x0: f64,
pub y0: f64,
pub width: f64,
pub height: f64,
pub theta: f64,
}
#[inline]
pub fn circle_bbox(x: f64, y: f64, radius: f64) -> BoundingBox {
raw_circle_bbox(x, y, radius).into()
}
#[inline]
pub fn circle_exact_weight_at_pixel(x: f64, y: f64, radius: f64, px: isize, py: isize) -> f64 {
let r_inner = (radius - SQRT_HALF).max(0.0);
let r_inner2 = r_inner * r_inner;
let r_outer = radius + SQRT_HALF;
let r_outer2 = r_outer * r_outer;
let dx = px as f64 - x;
let dy = py as f64 - y;
let rpix2 = dx * dx + dy * dy;
if rpix2 < r_inner2 {
1.0
} else if rpix2 > r_outer2 {
0.0
} else {
circ_pixel_area(x, y, px, py, radius)
}
}
#[inline]
pub fn ellipse_bbox(ellipse: Ellipse) -> BoundingBox {
raw_ellipse_bbox(ellipse.raw()).into()
}
#[inline]
pub fn ellipse_exact_weight_at_pixel(ellipse: Ellipse, px: isize, py: isize) -> f64 {
ellip_pixel_area(ellipse.raw(), px, py)
}
#[inline]
pub fn rectangle_bbox(rectangle: Rectangle) -> BoundingBox {
let half_w = 0.5 * rectangle.width;
let half_h = 0.5 * rectangle.height;
let cos_t = rectangle.theta.cos();
let sin_t = rectangle.theta.sin();
let dx = (half_w * cos_t).abs() + (half_h * sin_t).abs();
let dy = (half_w * sin_t).abs() + (half_h * cos_t).abs();
extent_bbox(rectangle.x0, rectangle.y0, dx, dy).into()
}
#[inline]
pub fn rectangle_exact_weight_at_pixel(rectangle: Rectangle, px: isize, py: isize) -> f64 {
rect_pixel_area(
rectangle.x0,
rectangle.y0,
px,
py,
0.5 * rectangle.width,
0.5 * rectangle.height,
rectangle.theta.cos(),
rectangle.theta.sin(),
)
}