plotpoint 0.1.9

A library to convert a point cloud into a (differentiable) image
Documentation
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(&center_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);
        }
    }
}