autd3-protobuf 32.0.0

gRPC protobuf for autd3
Documentation
use crate::{AUTDProtoBufError, pb::*, traits::FromMessage};

impl From<autd3_core::geometry::UnitVector3> for UnitVector3 {
    fn from(value: autd3_core::geometry::UnitVector3) -> Self {
        Self {
            x: value.x,
            y: value.y,
            z: value.z,
        }
    }
}

impl From<autd3_core::geometry::Point3> for Point3 {
    fn from(value: autd3_core::geometry::Point3) -> Self {
        Self {
            x: value.x,
            y: value.y,
            z: value.z,
        }
    }
}

impl From<autd3_core::geometry::UnitQuaternion> for Quaternion {
    fn from(value: autd3_core::geometry::UnitQuaternion) -> Self {
        Self {
            w: value.w,
            x: value.i,
            y: value.j,
            z: value.k,
        }
    }
}

impl From<&autd3_core::geometry::Geometry> for Geometry {
    fn from(value: &autd3_core::geometry::Geometry) -> Self {
        Self {
            devices: value
                .iter()
                .map(|dev| geometry::Autd3 {
                    pos: Some((*dev[0].position()).into()),
                    rot: Some((*dev.rotation()).into()),
                    sound_speed: Some(dev.sound_speed as _),
                })
                .collect(),
        }
    }
}

impl FromMessage<UnitVector3> for autd3_core::geometry::UnitVector3 {
    fn from_msg(msg: UnitVector3) -> Result<Self, AUTDProtoBufError> {
        Ok(autd3_core::geometry::UnitVector3::new_normalize(
            autd3_core::geometry::Vector3::new(msg.x as _, msg.y as _, msg.z as _),
        ))
    }
}

impl FromMessage<Point3> for autd3_core::geometry::Point3 {
    fn from_msg(msg: Point3) -> Result<Self, AUTDProtoBufError> {
        Ok(autd3_core::geometry::Point3::new(
            msg.x as _, msg.y as _, msg.z as _,
        ))
    }
}

impl FromMessage<Quaternion> for autd3_core::geometry::UnitQuaternion {
    fn from_msg(msg: Quaternion) -> Result<Self, AUTDProtoBufError> {
        Ok(autd3_core::geometry::UnitQuaternion::from_quaternion(
            autd3_core::geometry::Quaternion::new(msg.w as _, msg.x as _, msg.y as _, msg.z as _),
        ))
    }
}

impl FromMessage<Geometry> for autd3_core::geometry::Geometry {
    fn from_msg(msg: Geometry) -> Result<Self, AUTDProtoBufError> {
        Ok(autd3_core::geometry::Geometry::new(
            msg.devices
                .into_iter()
                .map(|dev_msg| {
                    let pos = dev_msg
                        .pos
                        .map(autd3_core::geometry::Point3::from_msg)
                        .transpose()?
                        .unwrap_or(autd3_core::geometry::Point3::origin());
                    let rot = dev_msg
                        .rot
                        .map(autd3_core::geometry::UnitQuaternion::from_msg)
                        .transpose()?
                        .unwrap_or(autd3_core::geometry::UnitQuaternion::identity());
                    let mut dev: autd3_core::geometry::Device =
                        autd3_driver::autd3_device::AUTD3 { pos, rot }.into();
                    if let Some(sound_speed) = dev_msg.sound_speed {
                        dev.sound_speed = sound_speed;
                    }
                    Ok(dev)
                })
                .collect::<Result<Vec<_>, AUTDProtoBufError>>()?,
        ))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use autd3_core::derive::Device;
    use autd3_driver::{
        autd3_device::AUTD3,
        geometry::{Geometry, Point3, Quaternion, UnitQuaternion, UnitVector3, Vector3},
    };
    use rand::Rng;

    #[test]
    fn point3() {
        let mut rng = rand::rng();
        let v = Point3::new(rng.random(), rng.random(), rng.random());
        let msg = v.into();
        let v2 = Point3::from_msg(msg).unwrap();
        approx::assert_abs_diff_eq!(v.x, v2.x);
        approx::assert_abs_diff_eq!(v.y, v2.y);
        approx::assert_abs_diff_eq!(v.z, v2.z);
    }

    #[test]
    fn unitvector3() {
        let mut rng = rand::rng();
        let v = UnitVector3::new_normalize(Vector3::new(rng.random(), rng.random(), rng.random()));
        let msg = v.into();
        let v2 = UnitVector3::from_msg(msg).unwrap();
        approx::assert_abs_diff_eq!(v.x, v2.x);
        approx::assert_abs_diff_eq!(v.y, v2.y);
        approx::assert_abs_diff_eq!(v.z, v2.z);
    }

    #[test]
    fn quaternion() {
        let mut rng = rand::rng();
        let q = UnitQuaternion::from_quaternion(Quaternion::new(
            rng.random(),
            rng.random(),
            rng.random(),
            rng.random(),
        ));
        let msg = q.into();
        let q2 = UnitQuaternion::from_msg(msg).unwrap();
        approx::assert_abs_diff_eq!(q.w, q2.w);
        approx::assert_abs_diff_eq!(q.i, q2.i);
        approx::assert_abs_diff_eq!(q.j, q2.j);
        approx::assert_abs_diff_eq!(q.k, q2.k);
    }

    #[test]
    fn geometry() {
        let mut rng = rand::rng();
        let mut dev: Device = AUTD3 {
            pos: Point3::new(rng.random(), rng.random(), rng.random()),
            rot: UnitQuaternion::identity(),
        }
        .into();
        dev.sound_speed = rng.random();
        let geometry = Geometry::new(vec![dev]);
        let msg = (&geometry).into();
        let geometry2 = Geometry::from_msg(msg).unwrap();
        geometry
            .into_iter()
            .zip(geometry2.iter())
            .for_each(|(dev, dev2)| {
                approx::assert_abs_diff_eq!(dev.sound_speed, dev2.sound_speed);
                approx::assert_abs_diff_eq!(dev.rotation().w, dev2.rotation().w);
                approx::assert_abs_diff_eq!(dev.rotation().i, dev2.rotation().i);
                approx::assert_abs_diff_eq!(dev.rotation().j, dev2.rotation().j);
                approx::assert_abs_diff_eq!(dev.rotation().k, dev2.rotation().k);
                dev.iter().zip(dev2.iter()).for_each(|(t, t2)| {
                    approx::assert_abs_diff_eq!(t.position().x, t2.position().x);
                    approx::assert_abs_diff_eq!(t.position().y, t2.position().y);
                    approx::assert_abs_diff_eq!(t.position().z, t2.position().z);
                });
            });
    }
}