Skip to main content

autd3_rs_core/geometry/
mod.rs

1mod autd3;
2mod autd3_unity;
3mod device;
4
5pub use autd3::Autd3;
6pub use autd3_unity::Autd3Unity;
7pub use device::Device;
8pub use nalgebra::{Point3, Quaternion, UnitQuaternion, UnitVector3, Vector3};
9
10use crate::common::Length;
11use crate::value::Emission;
12
13#[must_use]
14pub fn point(x: Length, y: Length, z: Length) -> Point3<f32> {
15    Point3::new(x.mm(), y.mm(), z.mm())
16}
17
18#[must_use]
19pub fn offset(x: Length, y: Length, z: Length) -> Vector3<f32> {
20    Vector3::new(x.mm(), y.mm(), z.mm())
21}
22
23#[derive(Clone, Debug)]
24pub struct Geometry {
25    devices: Vec<Device>,
26}
27
28impl Geometry {
29    #[must_use]
30    pub fn new<D: Into<Device>>(devices: Vec<D>) -> Self {
31        Self {
32            devices: devices
33                .into_iter()
34                .enumerate()
35                .map(|(i, d)| {
36                    let mut device = d.into();
37                    device.set_idx(i);
38                    device
39                })
40                .collect(),
41        }
42    }
43
44    #[must_use]
45    pub const fn num_devices(&self) -> usize {
46        self.devices.len()
47    }
48
49    #[must_use]
50    pub fn pattern_buffer(&self) -> Vec<Vec<Emission>> {
51        vec![vec![Emission::default(); Autd3::NUM_TRANSDUCERS]; self.num_devices()]
52    }
53
54    #[must_use]
55    pub fn num_transducers(&self) -> usize {
56        self.devices.iter().map(Device::num_transducers).sum()
57    }
58
59    #[must_use]
60    pub const fn is_empty(&self) -> bool {
61        self.devices.is_empty()
62    }
63
64    pub fn iter(&self) -> core::slice::Iter<'_, Device> {
65        self.devices.iter()
66    }
67
68    #[must_use]
69    pub fn center(&self) -> Point3<f32> {
70        let n = self.devices.len() as f32;
71        let sum = self
72            .devices
73            .iter()
74            .fold(Vector3::zeros(), |acc, d| acc + d.center().coords);
75        Point3::from(sum / n)
76    }
77}
78
79impl core::ops::Index<usize> for Geometry {
80    type Output = Device;
81
82    fn index(&self, index: usize) -> &Device {
83        &self.devices[index]
84    }
85}
86
87impl<'a> IntoIterator for &'a Geometry {
88    type Item = &'a Device;
89    type IntoIter = core::slice::Iter<'a, Device>;
90
91    fn into_iter(self) -> Self::IntoIter {
92        self.iter()
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use approx::assert_abs_diff_eq;
99    use nalgebra::UnitQuaternion;
100
101    use super::*;
102
103    #[test]
104    fn geometry_sets_device_idx_and_num_transducers() {
105        let g = Geometry::new(vec![Autd3::default(), Autd3::default()]);
106        assert_eq!(g[0].idx(), 0);
107        assert_eq!(g[1].idx(), 1);
108        assert_eq!(g.num_transducers(), 2 * Autd3::NUM_TRANSDUCERS);
109    }
110
111    #[test]
112    fn device_basis_directions_for_identity() {
113        let g = Geometry::new(vec![Autd3::default()]);
114        let dev = &g[0];
115        assert_abs_diff_eq!(dev.x_direction().into_inner(), Vector3::x(), epsilon = 1e-4);
116        assert_abs_diff_eq!(dev.y_direction().into_inner(), Vector3::y(), epsilon = 1e-4);
117        assert_abs_diff_eq!(
118            dev.axial_direction().into_inner(),
119            Vector3::z(),
120            epsilon = 1e-4
121        );
122        assert_abs_diff_eq!(
123            dev.rotation().angle_to(&UnitQuaternion::identity()),
124            0.0,
125            epsilon = 1e-4
126        );
127    }
128
129    #[test]
130    fn device_rotation_tracks_quarter_turn_about_x() {
131        let rot = UnitQuaternion::from_axis_angle(&Vector3::x_axis(), core::f32::consts::FRAC_PI_2);
132        let g = Geometry::new(vec![Autd3::new(Point3::origin(), rot)]);
133        let dev = &g[0];
134        assert_abs_diff_eq!(
135            dev.axial_direction().into_inner(),
136            (rot * Vector3::z_axis()).into_inner(),
137            epsilon = 1e-4
138        );
139        assert_abs_diff_eq!(dev.rotation().angle_to(&rot), 0.0, epsilon = 1e-4);
140    }
141}