use super::{Point, Rect};
#[inline]
pub fn to_screen_y(cartesian_y: f32, height: f32) -> f32 {
flip_y(cartesian_y, height)
}
#[inline]
pub fn to_screen_y_i32(cartesian_y: i32, height: i32) -> i32 {
height.saturating_sub(cartesian_y)
}
#[inline]
pub fn to_cartesian_y(screen_y: f32, height: f32) -> f32 {
flip_y(screen_y, height)
}
#[inline]
pub fn to_cartesian_y_i32(screen_y: i32, height: i32) -> i32 {
height.saturating_sub(screen_y)
}
#[inline]
pub fn to_pdf_y(screen_y: f32, height: f32) -> f32 {
flip_y(screen_y, height)
}
#[inline]
pub fn from_pdf_y(pdf_y: f32, height: f32) -> f32 {
flip_y(pdf_y, height)
}
#[inline]
pub fn point_to_screen(point: Point, height: i32) -> Point {
Point::new(point.x, height.saturating_sub(point.y))
}
#[inline]
pub fn point_to_screen_f32(x: f32, y: f32, height: f32) -> (f32, f32) {
(x, height - y)
}
#[inline]
pub fn point_to_cartesian(point: Point, height: i32) -> Point {
Point::new(point.x, height.saturating_sub(point.y))
}
#[inline]
pub fn point_to_cartesian_f32(x: f32, y: f32, height: f32) -> (f32, f32) {
(x, height - y)
}
#[inline]
pub fn rect_to_screen(rect: Rect, height: i32) -> Rect {
Rect::new(
rect.x,
height.saturating_sub(rect.y).saturating_sub_unsigned(rect.height),
rect.width,
rect.height,
)
}
#[inline]
pub fn rect_to_cartesian(rect: Rect, height: i32) -> Rect {
Rect::new(
rect.x,
height.saturating_sub(rect.y).saturating_sub_unsigned(rect.height),
rect.width,
rect.height,
)
}
#[inline]
pub fn flip_y(y: f32, height: f32) -> f32 {
height - y
}
#[inline]
pub fn flip_point_y(point: Point, height: i32) -> Point {
Point::new(point.x, height.saturating_sub(point.y))
}
#[inline]
pub fn flip_rect_y(rect: Rect, height: i32) -> Rect {
Rect::new(
rect.x,
height.saturating_sub(rect.y).saturating_sub_unsigned(rect.height),
rect.width,
rect.height,
)
}
#[inline]
pub fn rect_to_screen_f32(
x: f32,
y: f32,
width: f32,
height: f32,
container_height: f32,
) -> (f32, f32, f32, f32) {
(x, container_height - y - height, width, height)
}
#[inline]
pub fn rect_to_cartesian_f32(
x: f32,
y: f32,
width: f32,
height: f32,
container_height: f32,
) -> (f32, f32, f32, f32) {
(x, container_height - y - height, width, height)
}
#[inline]
pub fn rect_to_screen_f64(
x: f64,
y: f64,
width: f64,
height: f64,
container_height: f64,
) -> (f64, f64, f64, f64) {
(x, container_height - y - height, width, height)
}
#[inline]
pub fn rect_to_cartesian_f64(
x: f64,
y: f64,
width: f64,
height: f64,
container_height: f64,
) -> (f64, f64, f64, f64) {
(x, container_height - y - height, width, height)
}
#[inline]
pub fn point_to_screen_f64(x: f64, y: f64, height: f64) -> (f64, f64) {
(x, height - y)
}
#[inline]
pub fn point_to_cartesian_f64(x: f64, y: f64, height: f64) -> (f64, f64) {
(x, height - y)
}
#[inline]
pub fn to_screen_y_f64(cartesian_y: f64, height: f64) -> f64 {
height - cartesian_y
}
#[inline]
pub fn to_cartesian_y_f64(screen_y: f64, height: f64) -> f64 {
height - screen_y
}
#[inline]
pub fn to_screen_y_u32(cartesian_y: u32, height: u32) -> u32 {
height.saturating_sub(cartesian_y)
}
#[inline]
pub fn to_cartesian_y_u32(screen_y: u32, height: u32) -> u32 {
height.saturating_sub(screen_y)
}
#[inline]
pub fn normalize_coords(x: f32, y: f32, width: f32, height: f32) -> (f32, f32) {
let w = if width.is_finite() && width > 0.0 { width } else { 1.0 };
let h = if height.is_finite() && height > 0.0 { height } else { 1.0 };
let normalized_x = if x.is_finite() { (x / w).clamp(0.0, 1.0) } else { 0.0 };
let normalized_y = if y.is_finite() { (y / h).clamp(0.0, 1.0) } else { 0.0 };
(normalized_x, normalized_y)
}
#[inline]
pub fn denormalize_coords(norm_x: f32, norm_y: f32, width: f32, height: f32) -> (f32, f32) {
(norm_x * width, norm_y * height)
}
#[inline]
pub fn clamp_point_to_rect(point: Point, rect: Rect) -> Point {
let max_x = rect.x.saturating_add_unsigned(rect.width.max(1) - 1);
let max_y = rect.y.saturating_add_unsigned(rect.height.max(1) - 1);
Point::new(point.x.clamp(rect.x, max_x), point.y.clamp(rect.y, max_y))
}
#[inline]
pub fn clamp_point_to_rect_f32(
x: f32,
y: f32,
rect_x: f32,
rect_y: f32,
rect_width: f32,
rect_height: f32,
) -> (f32, f32) {
let max_x = rect_x + rect_width.max(1.0) - 1.0;
let max_y = rect_y + rect_height.max(1.0) - 1.0;
(x.clamp(rect_x, max_x), y.clamp(rect_y, max_y))
}
#[inline]
pub fn dpi_to_pixels(value: f32, dpi_scale: f32) -> f32 {
if !value.is_finite() || !dpi_scale.is_finite() || dpi_scale <= 0.0 {
0.0
} else {
(value as f64 * dpi_scale as f64).clamp(f32::MIN as f64, f32::MAX as f64) as f32
}
}
#[inline]
pub fn pixels_to_dpi(value: f32, dpi_scale: f32) -> f32 {
if !value.is_finite() || !dpi_scale.is_finite() || dpi_scale <= 0.0 {
0.0
} else {
(value as f64 / dpi_scale as f64).clamp(f32::MIN as f64, f32::MAX as f64) as f32
}
}
#[inline]
pub fn dpi_to_pixels_i32(value: i32, dpi_scale: f32) -> i32 {
if !dpi_scale.is_finite() || dpi_scale <= 0.0 {
0
} else {
(value as f64 * dpi_scale as f64).round().clamp(i32::MIN as f64, i32::MAX as f64) as i32
}
}
#[inline]
pub fn pixels_to_dpi_i32(value: i32, dpi_scale: f32) -> i32 {
if !dpi_scale.is_finite() || dpi_scale <= 0.0 {
0
} else {
(value as f64 / dpi_scale as f64).round().clamp(i32::MIN as f64, i32::MAX as f64) as i32
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_to_screen_y() {
assert_eq!(to_screen_y(0.0, 100.0), 100.0);
assert_eq!(to_screen_y(50.0, 100.0), 50.0);
assert_eq!(to_screen_y(100.0, 100.0), 0.0);
}
#[test]
fn test_to_cartesian_y() {
assert_eq!(to_cartesian_y(0.0, 100.0), 100.0);
assert_eq!(to_cartesian_y(50.0, 100.0), 50.0);
assert_eq!(to_cartesian_y(100.0, 100.0), 0.0);
}
#[test]
fn test_to_pdf_y() {
assert_eq!(to_pdf_y(0.0, 100.0), 100.0);
assert_eq!(to_pdf_y(50.0, 100.0), 50.0);
assert_eq!(to_pdf_y(100.0, 100.0), 0.0);
}
#[test]
fn test_from_pdf_y() {
assert_eq!(from_pdf_y(0.0, 100.0), 100.0);
assert_eq!(from_pdf_y(50.0, 100.0), 50.0);
assert_eq!(from_pdf_y(100.0, 100.0), 0.0);
}
#[test]
fn test_point_to_screen() {
assert_eq!(point_to_screen(Point::new(10, 0), 100), Point::new(10, 100));
assert_eq!(point_to_screen(Point::new(10, 50), 100), Point::new(10, 50));
assert_eq!(point_to_screen(Point::new(10, 100), 100), Point::new(10, 0));
}
#[test]
fn test_point_to_cartesian() {
assert_eq!(point_to_cartesian(Point::new(10, 0), 100), Point::new(10, 100));
assert_eq!(point_to_cartesian(Point::new(10, 50), 100), Point::new(10, 50));
assert_eq!(point_to_cartesian(Point::new(10, 100), 100), Point::new(10, 0));
}
#[test]
fn test_rect_to_screen() {
let rect = Rect::new(10, 0, 50, 30);
let screen_rect = rect_to_screen(rect, 100);
assert_eq!(screen_rect.x, 10);
assert_eq!(screen_rect.y, 70);
assert_eq!(screen_rect.width, 50);
assert_eq!(screen_rect.height, 30);
}
#[test]
fn test_rect_to_cartesian() {
let rect = Rect::new(10, 70, 50, 30);
let cartesian_rect = rect_to_cartesian(rect, 100);
assert_eq!(cartesian_rect.x, 10);
assert_eq!(cartesian_rect.y, 0);
assert_eq!(cartesian_rect.width, 50);
assert_eq!(cartesian_rect.height, 30);
}
#[test]
fn test_flip_y() {
assert_eq!(flip_y(0.0, 100.0), 100.0);
assert_eq!(flip_y(50.0, 100.0), 50.0);
assert_eq!(flip_y(100.0, 100.0), 0.0);
}
#[test]
fn test_flip_point_y() {
assert_eq!(flip_point_y(Point::new(10, 0), 100), Point::new(10, 100));
assert_eq!(flip_point_y(Point::new(10, 50), 100), Point::new(10, 50));
assert_eq!(flip_point_y(Point::new(10, 100), 100), Point::new(10, 0));
}
#[test]
fn test_flip_rect_y() {
let rect = Rect::new(10, 0, 50, 30);
let flipped = flip_rect_y(rect, 100);
assert_eq!(flipped.x, 10);
assert_eq!(flipped.y, 70);
assert_eq!(flipped.width, 50);
assert_eq!(flipped.height, 30);
}
#[test]
fn test_roundtrip_conversions() {
let y = 42.0;
let height = 100.0;
assert_eq!(to_cartesian_y(to_screen_y(y, height), height), y);
assert_eq!(to_screen_y(to_cartesian_y(y, height), height), y);
}
#[test]
fn zero_size_f32_rect_clamps_to_origin() {
assert_eq!(clamp_point_to_rect_f32(20.0, -20.0, 5.0, 7.0, 0.0, 0.0), (5.0, 7.0));
}
#[test]
fn non_positive_dpi_scale_returns_zero() {
assert_eq!(pixels_to_dpi(200.0, 0.0), 0.0);
assert_eq!(pixels_to_dpi_i32(200, -1.0), 0);
assert_eq!(dpi_to_pixels_i32(i32::MAX, f32::INFINITY), 0);
assert_eq!(dpi_to_pixels_i32(i32::MAX, 4.0), i32::MAX);
assert_eq!(pixels_to_dpi_i32(i32::MAX, f32::INFINITY), 0);
assert_eq!(pixels_to_dpi_i32(i32::MAX, 0.5), i32::MAX);
assert_eq!(dpi_to_pixels(f32::INFINITY, 2.0), 0.0);
assert_eq!(dpi_to_pixels(2.0, 0.0), 0.0);
assert_eq!(pixels_to_dpi(f32::NAN, 2.0), 0.0);
}
#[test]
fn i32_coordinate_flips_saturate_at_limits() {
assert_eq!(to_screen_y_i32(i32::MIN, i32::MAX), i32::MAX);
assert_eq!(point_to_screen(Point::new(0, i32::MIN), i32::MAX).y, i32::MAX);
let rect = Rect::new(i32::MIN, i32::MIN, u32::MAX, u32::MAX);
assert_eq!(rect_to_screen(rect, i32::MAX).y, i32::MIN);
assert_eq!(flip_rect_y(rect, i32::MAX).y, i32::MIN);
}
#[test]
fn normalize_coords_rejects_invalid_dimensions_and_clamps_range() {
assert_eq!(normalize_coords(-10.0, 200.0, -1.0, f32::NAN), (0.0, 1.0));
assert_eq!(normalize_coords(50.0, 25.0, 100.0, 50.0), (0.5, 0.5));
assert_eq!(normalize_coords(f32::NAN, f32::INFINITY, 100.0, 100.0), (0.0, 0.0));
}
}