use std::ops::Mul;
use nalgebra::Vector2;
use num_dual::{DualNum, DualNumFloat};
#[derive(Clone, Copy, Debug, PartialEq)]
pub(super) struct Pixpos {
pub(super) x: isize,
pub(super) y: isize,
}
pub(super) struct AffectedSquare<F> {
val: F,
square: Pixpos,
}
impl<F> AffectedSquare<F> {
pub(super) fn val(&self) -> &F {
&self.val
}
pub(super) fn square(&self) -> Pixpos {
self.square
}
}
pub(super) fn affected<T: DualNum<F, Inner = F>, F: DualNumFloat>(
pix: &Vector2<T>,
) -> Vec<AffectedSquare<T>>
where
for<'a> &'a T: Mul<&'a T, Output = T>,
{
let center_topleft = pix
.map(|x| x.re())
.map(|f| {
let floor = f.floor();
let p5 = F::from(0.5).unwrap();
match f - floor < p5 {
true => floor - F::from(1.0).unwrap(),
false => floor,
}
})
.map(|f| f + F::from(0.5).unwrap());
let diff_center = pix.zip_map(¢er_topleft, |d, f| d - T::from_inner(f));
let wx0 = T::from_inner(F::one()) - &diff_center.x;
let wx1 = &diff_center.x;
let wy0 = T::from_inner(F::one()) - &diff_center.y;
let wy1 = &diff_center.y;
let wx0y0 = &wx0 * &wy0;
let wx0y1 = &wx0 * wy1;
let wx1y0 = wx1 * &wy0;
let wx1y1 = wx1 * wy1;
let topleftpix = Pixpos {
x: center_topleft.x.to_isize().unwrap(),
y: center_topleft.y.to_isize().unwrap(),
};
vec![
AffectedSquare {
val: wx0y0,
square: topleftpix,
},
AffectedSquare {
val: wx0y1,
square: Pixpos {
y: topleftpix.y + 1,
..topleftpix
},
},
AffectedSquare {
val: wx1y0,
square: Pixpos {
x: topleftpix.x + 1,
..topleftpix
},
},
AffectedSquare {
val: wx1y1,
square: Pixpos {
x: topleftpix.x + 1,
y: topleftpix.y + 1,
},
},
]
}
#[cfg(test)]
mod tests {
use approx::assert_abs_diff_eq;
use super::*;
mod float {
use super::*;
#[test]
fn test_affected_top_left() {
let output = affected(&Vector2::new(1.25, 1.1));
assert_eq!(output.len(), 4);
assert_eq!(output[0].square, Pixpos { x: 0, y: 0 });
assert_eq!(output[1].square, Pixpos { x: 0, y: 1 });
assert_eq!(output[2].square, Pixpos { x: 1, y: 0 });
assert_eq!(output[3].square, Pixpos { x: 1, y: 1 });
assert_abs_diff_eq!(output[0].val.re(), 0.25 * 4.0 / 10.0);
assert_abs_diff_eq!(output[1].val.re(), 0.25 * 6.0 / 10.0);
assert_abs_diff_eq!(output[2].val.re(), 0.75 * 4.0 / 10.0);
assert_abs_diff_eq!(output[3].val.re(), 0.75 * 6.0 / 10.0);
assert_eq!(output.iter().map(|o| o.val).sum::<f64>(), 1.0);
}
#[test]
fn test_affected_top_right() {
let output = affected(&Vector2::new(1.75, 1.1));
assert_eq!(output.len(), 4);
assert_eq!(output[0].square, Pixpos { x: 1, y: 0 });
assert_eq!(output[1].square, Pixpos { x: 1, y: 1 });
assert_eq!(output[2].square, Pixpos { x: 1 + 1, y: 0 });
assert_eq!(output[3].square, Pixpos { x: 1 + 1, y: 1 });
assert_abs_diff_eq!(output[0].val.re(), 0.75 * 4.0 / 10.0);
assert_abs_diff_eq!(output[1].val.re(), 0.75 * 6.0 / 10.0);
assert_abs_diff_eq!(output[2].val.re(), 0.25 * 4.0 / 10.0);
assert_abs_diff_eq!(output[3].val.re(), 0.25 * 6.0 / 10.0);
assert_eq!(output.iter().map(|o| o.val).sum::<f64>(), 1.0);
}
#[test]
fn test_affected_bot_left() {
let output = affected(&Vector2::new(1.25, 1.6));
assert_eq!(output.len(), 4);
assert_eq!(output[0].square, Pixpos { x: 0, y: 1 });
assert_eq!(output[1].square, Pixpos { x: 0, y: 2 });
assert_eq!(output[2].square, Pixpos { x: 1, y: 1 });
assert_eq!(output[3].square, Pixpos { x: 1, y: 2 });
assert_abs_diff_eq!(output[0].val.re(), 0.25 * 9.0 / 10.0);
assert_abs_diff_eq!(output[1].val.re(), 0.25 * 1.0 / 10.0);
assert_abs_diff_eq!(output[2].val.re(), 0.75 * 9.0 / 10.0);
assert_abs_diff_eq!(output[3].val.re(), 0.75 * 1.0 / 10.0);
assert_eq!(output.iter().map(|o| o.val).sum::<f64>(), 1.0);
}
#[test]
fn test_affected_bot_right() {
let output = affected(&Vector2::new(1.75, 1.6));
assert_eq!(output.len(), 4);
assert_eq!(output[0].square, Pixpos { x: 1, y: 1 });
assert_eq!(output[1].square, Pixpos { x: 1, y: 2 });
assert_eq!(output[2].square, Pixpos { x: 2, y: 1 });
assert_eq!(output[3].square, Pixpos { x: 2, y: 2 });
assert_abs_diff_eq!(output[0].val.re(), 0.75 * 9.0 / 10.0);
assert_abs_diff_eq!(output[1].val.re(), 0.75 * 1.0 / 10.0);
assert_abs_diff_eq!(output[2].val.re(), 0.25 * 9.0 / 10.0);
assert_abs_diff_eq!(output[3].val.re(), 0.25 * 1.0 / 10.0);
assert_eq!(output.iter().map(|o| o.val).sum::<f64>(), 1.0);
}
}
mod dual {
use num_dual::Dual64;
use super::*;
#[test]
fn test_affected_top_left() {
let output = affected(&Vector2::new(Dual64::new(1.25, 1.0), Dual64::new(1.1, 0.0)));
assert_eq!(output.len(), 4);
assert_eq!(output[0].square, Pixpos { x: 0, y: 0 });
assert_eq!(output[1].square, Pixpos { x: 0, y: 1 });
assert_eq!(output[2].square, Pixpos { x: 1, y: 0 });
assert_eq!(output[3].square, Pixpos { x: 1, y: 1 });
assert_abs_diff_eq!(output[0].val.re(), 0.25 * 4.0 / 10.0);
assert_abs_diff_eq!(output[1].val.re(), 0.25 * 6.0 / 10.0);
assert_abs_diff_eq!(output[2].val.re(), 0.75 * 4.0 / 10.0);
assert_abs_diff_eq!(output[3].val.re(), 0.75 * 6.0 / 10.0);
assert_abs_diff_eq!(output[0].val.eps, -0.4);
assert_eq!(output.iter().map(|o| o.val.re()).sum::<f64>(), 1.0);
}
}
}