Skip to main content

autd3_rs_core/geometry/
mod.rs

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