use crate::float::compatible::FloatPointCompatible;
use crate::float::number::FloatNumber;
use crate::float::rect::FloatRect;
use crate::int::number::int::IntNumber;
use crate::int::number::wide_int::WideIntNumber;
use crate::int::point::IntPoint;
use core::marker::PhantomData;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FloatPointAdapterScaleError {
ScaleTooLarge,
ScaleNonPositive,
ScaleNotFinite,
}
#[derive(Clone)]
pub struct FloatPointAdapter<P: FloatPointCompatible, I: IntNumber> {
dir_scale: P::Scalar,
inv_scale: P::Scalar,
offset: P,
rect: FloatRect<P::Scalar>,
int: PhantomData<I>,
}
impl<P: FloatPointCompatible, I: IntNumber> FloatPointAdapter<P, I> {
const SCALE_SAFETY_BITS: i32 = 3;
#[inline]
pub fn new(rect: FloatRect<P::Scalar>) -> Self {
let a = rect.width() * FloatNumber::from_float(0.5);
let b = rect.height() * FloatNumber::from_float(0.5);
let x = rect.min_x + a;
let y = rect.min_y + b;
let offset = P::from_xy(x, y);
let max = a.max(b);
if max == FloatNumber::from_float(0.0) {
return Self {
dir_scale: FloatNumber::from_float(1.0),
inv_scale: FloatNumber::from_float(1.0),
offset,
rect,
int: PhantomData,
};
}
let log2 = max.log2().to_i32();
let safe_bits = I::BITS as i32 - Self::SCALE_SAFETY_BITS;
let ie = safe_bits - log2;
let e = ie as f64;
let dir_scale = FloatNumber::from_float(libm::exp2(e));
let inv_scale = FloatNumber::from_float(libm::exp2(-e));
Self {
dir_scale,
inv_scale,
offset,
rect,
int: PhantomData,
}
}
#[inline]
pub fn with_scale(rect: FloatRect<P::Scalar>, scale: P::Scalar) -> Self {
let a = rect.width() * FloatNumber::from_float(0.5);
let b = rect.height() * FloatNumber::from_float(0.5);
let x = rect.min_x + a;
let y = rect.min_y + b;
let offset = P::from_xy(x, y);
let max = a.max(b);
if max == FloatNumber::from_float(0.0) {
return Self {
dir_scale: FloatNumber::from_float(1.0),
inv_scale: FloatNumber::from_float(1.0),
offset,
rect,
int: PhantomData,
};
}
let dir_scale = scale;
let inv_scale = <P::Scalar as FloatNumber>::from_float(1.0) / scale;
Self {
dir_scale,
inv_scale,
offset,
rect,
int: PhantomData,
}
}
#[inline]
pub fn try_with_scale(
rect: FloatRect<P::Scalar>,
scale: P::Scalar,
) -> Result<Self, FloatPointAdapterScaleError> {
let scale_f64 = Self::validate_scale(scale)?;
let mut adapter = Self::new(rect);
let zero = P::Scalar::from_float(0.0);
let is_degenerate = adapter.rect.width() == zero && adapter.rect.height() == zero;
if !is_degenerate && adapter.dir_scale < scale {
return Err(FloatPointAdapterScaleError::ScaleTooLarge);
}
adapter.dir_scale = scale;
adapter.inv_scale = P::Scalar::from_float(1.0 / scale_f64);
Ok(adapter)
}
#[inline]
pub fn with_iter<'a, Q>(iter: Q) -> Self
where
Q: Iterator<Item = &'a P>,
P: 'a,
{
Self::new(FloatRect::with_iter(iter).unwrap_or(FloatRect::zero()))
}
#[inline]
pub fn with_iter_and_scale_checked<'a, Q>(
iter: Q,
scale: P::Scalar,
) -> Result<Self, FloatPointAdapterScaleError>
where
Q: Iterator<Item = &'a P>,
P: 'a,
{
Self::try_with_scale(FloatRect::with_iter(iter).unwrap_or(FloatRect::zero()), scale)
}
#[inline]
fn validate_scale(scale: P::Scalar) -> Result<f64, FloatPointAdapterScaleError> {
let scale_f64 = scale.to_f64();
if !scale_f64.is_finite() {
return Err(FloatPointAdapterScaleError::ScaleNotFinite);
}
if scale_f64 <= 0.0 {
return Err(FloatPointAdapterScaleError::ScaleNonPositive);
}
Ok(scale_f64)
}
#[inline(always)]
pub fn dir_scale(&self) -> P::Scalar {
self.dir_scale
}
#[inline(always)]
pub fn inv_scale(&self) -> P::Scalar {
self.inv_scale
}
#[inline(always)]
pub fn offset(&self) -> P {
self.offset
}
#[inline(always)]
pub fn rect(&self) -> &FloatRect<P::Scalar> {
&self.rect
}
#[inline(always)]
pub fn int_to_float(&self, point: &IntPoint<I>) -> P {
let fx: P::Scalar = FloatNumber::from_int(point.x);
let fy: P::Scalar = FloatNumber::from_int(point.y);
let x = fx * self.inv_scale + self.offset.x();
let y = fy * self.inv_scale + self.offset.y();
let float = P::from_xy(x, y);
if cfg!(debug_assertions) {
let radius = self.rect.height().max(self.rect.width()) * P::Scalar::from_float(0.01);
if !self.rect.contains_with_radius(&float, radius) {
panic!(
"You are trying to convert a point[{}, {}] which is out of rect: {}",
x, y, self.rect
);
}
}
float
}
#[inline(always)]
pub fn float_to_int(&self, point: &P) -> IntPoint<I> {
if cfg!(debug_assertions) {
let radius = self.rect.height().max(self.rect.width()) * P::Scalar::from_float(0.01);
if !self.rect.contains_with_radius(point, radius) {
panic!(
"You are trying to convert a point[{}, {}] which is out of rect: {}",
point.x(),
point.y(),
self.rect
);
}
}
let sx = (point.x() - self.offset.x()) * self.dir_scale;
let sy = (point.y() - self.offset.y()) * self.dir_scale;
let x = I::from_rounded_float(sx);
let y = I::from_rounded_float(sy);
IntPoint { x, y }
}
#[inline(always)]
pub fn round_sqr_len_to_int(&self, value: P::Scalar) -> I::Wide {
let scale = self.dir_scale;
let sqr_scale = scale * scale;
I::Wide::from_rounded_float(sqr_scale * value)
}
#[inline(always)]
pub fn round_len_to_int(&self, value: P::Scalar) -> I {
I::from_rounded_float(self.dir_scale * value)
}
#[inline(always)]
pub fn len_to_float(&self, value: I) -> P::Scalar {
let f: P::Scalar = FloatNumber::from_int(value);
f * self.inv_scale
}
}
#[cfg(test)]
mod tests {
use crate::adapter::{FloatPointAdapter, FloatPointAdapterScaleError};
use crate::float::compatible::FloatPointCompatible;
use crate::float::point::FloatPoint;
use crate::float::rect::FloatRect;
use crate::int::point::IntPoint;
#[test]
fn test_0() {
let rect = FloatRect {
min_x: 1.0,
max_x: 1.0,
min_y: -2.0,
max_y: -2.0,
};
let adapter = FloatPointAdapter::<FloatPoint<f64>, i32>::new(rect);
assert_eq!(adapter.dir_scale(), 1.0);
assert_eq!(adapter.inv_scale(), 1.0);
}
#[test]
fn test_1() {
let rect = FloatRect {
min_x: 0.0,
max_x: 10.0,
min_y: 0.0,
max_y: 100.0,
};
let adapter = FloatPointAdapter::<[f64; 2], i32>::new(rect);
let f0 = [10.0, 2.0];
let p0 = adapter.float_to_int(&f0);
let f1: [f64; 2] = adapter.int_to_float(&p0);
assert_eq!((f0.x() - f1.x()).abs() < 0.000_0001, true);
assert_eq!((f0.y() - f1.y()).abs() < 0.000_0001, true);
}
#[test]
fn test_2() {
let points = [[-2.0, -4.0], [-2.0, 3.0], [5.0, 3.0], [5.0, -4.0]];
let adapter = FloatPointAdapter::<[f64; 2], i32>::with_iter(points.iter());
let f0 = [1.0, 2.0];
let p0 = adapter.float_to_int(&f0);
let f1: [f64; 2] = adapter.int_to_float(&p0);
assert_eq!((f0.x() - f1.x()).abs() < 0.000_0001, true);
assert_eq!((f0.y() - f1.y()).abs() < 0.000_0001, true);
}
#[test]
fn test_i64_scale() {
let rect = FloatRect {
min_x: -1000.0,
max_x: 1000.0,
min_y: -1000.0,
max_y: 1000.0,
};
let adapter = FloatPointAdapter::<[f64; 2], i64>::new(rect);
let p = adapter.float_to_int(&[1.25, -2.5]);
let f: [f64; 2] = adapter.int_to_float(&p);
assert_eq!(p.x.abs() > i32::MAX as i64, true);
assert_eq!((f.x() - 1.25).abs() < 0.000_0001, true);
assert_eq!((f.y() + 2.5).abs() < 0.000_0001, true);
}
#[test]
fn test_round_point_round_length() {
let adapter =
FloatPointAdapter::<[f64; 2], i32>::with_scale(FloatRect::new(-1.0, 1.0, -1.0, 1.0), 10.0);
assert_eq!(adapter.float_to_int(&[0.16, -0.16]), IntPoint::new(2, -2));
assert_eq!(adapter.round_len_to_int(0.16), 2);
}
#[test]
fn test_try_with_scale() {
let adapter =
FloatPointAdapter::<[f64; 2], i32>::try_with_scale(FloatRect::new(-1.0, 1.0, -1.0, 1.0), 10.0)
.unwrap();
assert_eq!(adapter.dir_scale(), 10.0);
assert_eq!(adapter.inv_scale(), 0.1);
assert_eq!(adapter.float_to_int(&[0.16, -0.16]), IntPoint::new(2, -2));
}
#[test]
fn test_try_with_scale_errors() {
let rect = FloatRect::new(-1.0, 1.0, -1.0, 1.0);
assert_eq!(
FloatPointAdapter::<[f64; 2], i32>::try_with_scale(rect.clone(), 0.0)
.err()
.unwrap(),
FloatPointAdapterScaleError::ScaleNonPositive
);
assert_eq!(
FloatPointAdapter::<[f64; 2], i32>::try_with_scale(rect.clone(), f64::INFINITY)
.err()
.unwrap(),
FloatPointAdapterScaleError::ScaleNotFinite
);
assert_eq!(
FloatPointAdapter::<[f64; 2], i32>::try_with_scale(rect, i32::MAX as f64)
.err()
.unwrap(),
FloatPointAdapterScaleError::ScaleTooLarge
);
}
#[test]
fn test_with_iter_and_scale_checked() {
let points = [[-2.0, -4.0], [-2.0, 3.0], [5.0, 3.0], [5.0, -4.0]];
let adapter =
FloatPointAdapter::<[f64; 2], i32>::with_iter_and_scale_checked(points.iter(), 100.0).unwrap();
assert_eq!(adapter.dir_scale(), 100.0);
assert_eq!(adapter.float_to_int(&[1.25, 2.25]), IntPoint::new(-25, 275));
}
#[test]
fn test_degenerate_try_with_scale_keeps_requested_scale() {
let adapter =
FloatPointAdapter::<[f64; 2], i32>::try_with_scale(FloatRect::new(1.0, 1.0, -2.0, -2.0), 1000.0)
.unwrap();
assert_eq!(adapter.dir_scale(), 1000.0);
assert_eq!(adapter.float_to_int(&[1.0, -2.0]), IntPoint::ZERO);
}
}