use bitflags::bitflags;
use crate::types::LaserPoint;
#[derive(Debug, Clone, PartialEq)]
pub struct Frame {
pub pps: u32,
pub flags: WriteFrameFlags,
pub points: Vec<Point>,
}
impl Frame {
pub fn new(pps: u32, points: Vec<Point>) -> Self {
Frame {
pps,
points,
flags: WriteFrameFlags::SINGLE_MODE,
}
}
pub fn new_with_flags(pps: u32, points: Vec<Point>, flags: WriteFrameFlags) -> Self {
Frame { pps, points, flags }
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Point {
pub coordinate: Coordinate,
pub color: Color,
pub intensity: u8,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Coordinate {
pub x: u16,
pub y: u16,
}
impl From<(u16, u16)> for Coordinate {
fn from((x, y): (u16, u16)) -> Self {
Coordinate { x, y }
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Color {
pub r: u8,
pub g: u8,
pub b: u8,
}
impl Color {
pub fn new(r: u8, g: u8, b: u8) -> Self {
Color { r, g, b }
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct WriteFrameFlags: u8 {
const START_IMMEDIATELY = 0b0000_0001;
const SINGLE_MODE = 0b0000_0010;
const DONT_BLOCK = 0b0000_0100;
}
}
impl From<&LaserPoint> for Point {
fn from(p: &LaserPoint) -> Self {
Point {
coordinate: Coordinate {
x: LaserPoint::coord_to_u12_inverted(p.x),
y: LaserPoint::coord_to_u12_inverted(p.y),
},
color: Color::new(
LaserPoint::color_to_u8(p.r),
LaserPoint::color_to_u8(p.g),
LaserPoint::color_to_u8(p.b),
),
intensity: LaserPoint::color_to_u8(p.intensity),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_helios_conversion_center() {
let laser_point = LaserPoint::new(0.0, 0.0, 128 * 257, 64 * 257, 32 * 257, 200 * 257);
let helios_point: Point = (&laser_point).into();
assert_eq!(helios_point.coordinate.x, 2048);
assert_eq!(helios_point.coordinate.y, 2048);
assert_eq!(helios_point.color.r, 128);
assert_eq!(helios_point.color.g, 64);
assert_eq!(helios_point.color.b, 32);
assert_eq!(helios_point.intensity, 200);
}
#[test]
fn test_helios_conversion_boundaries() {
let min = LaserPoint::new(-1.0, -1.0, 0, 0, 0, 0);
let min_helios: Point = (&min).into();
assert_eq!(min_helios.coordinate.x, 4095);
assert_eq!(min_helios.coordinate.y, 4095);
let max = LaserPoint::new(1.0, 1.0, 0, 0, 0, 0);
let max_helios: Point = (&max).into();
assert_eq!(max_helios.coordinate.x, 0);
assert_eq!(max_helios.coordinate.y, 0);
}
#[test]
fn test_helios_conversion_asymmetric() {
let laser_point = LaserPoint::new(-0.5, 0.5, 0, 0, 0, 0);
let helios_point: Point = (&laser_point).into();
assert_eq!(helios_point.coordinate.x, 3071);
assert_eq!(helios_point.coordinate.y, 1024);
}
#[test]
fn test_helios_conversion_clamps_out_of_range() {
let positive = LaserPoint::new(2.0, 3.0, 0, 0, 0, 0);
let positive_helios: Point = (&positive).into();
assert_eq!(positive_helios.coordinate.x, 0);
assert_eq!(positive_helios.coordinate.y, 0);
let negative = LaserPoint::new(-2.0, -3.0, 0, 0, 0, 0);
let negative_helios: Point = (&negative).into();
assert_eq!(negative_helios.coordinate.x, 4095);
assert_eq!(negative_helios.coordinate.y, 4095);
}
#[test]
fn test_helios_inversion_symmetry() {
let p1 = LaserPoint::new(0.5, 0.0, 0, 0, 0, 0);
let p2 = LaserPoint::new(-0.5, 0.0, 0, 0, 0, 0);
let h1: Point = (&p1).into();
let h2: Point = (&p2).into();
let sum = h1.coordinate.x as i32 + h2.coordinate.x as i32;
assert!((sum - 4095).abs() <= 1, "Sum was {}, expected ~4095", sum);
}
#[test]
fn test_helios_conversion_infinity_clamps() {
let laser_point = LaserPoint::new(f32::INFINITY, f32::NEG_INFINITY, 0, 0, 0, 0);
let helios_point: Point = (&laser_point).into();
assert_eq!(helios_point.coordinate.x, 0);
assert_eq!(helios_point.coordinate.y, 4095);
}
#[test]
fn test_helios_conversion_nan_does_not_panic() {
let laser_point = LaserPoint::new(
f32::NAN,
f32::NAN,
100 * 257,
100 * 257,
100 * 257,
100 * 257,
);
let helios_point: Point = (&laser_point).into();
assert!(helios_point.coordinate.x <= 4095);
assert!(helios_point.coordinate.y <= 4095);
}
}