autd3_core/geometry/
mod.rs

1pub(crate) mod device;
2mod rotation;
3mod transducer;
4
5/// a complex number
6pub type Complex = nalgebra::Complex<f32>;
7/// 3-dimensional column vector.
8pub type Vector3 = nalgebra::Vector3<f32>;
9/// 3-dimensional unit vector.
10pub type UnitVector3 = nalgebra::UnitVector3<f32>;
11/// 3-dimensional point.
12pub type Point3 = nalgebra::Point3<f32>;
13/// A quaternion.
14pub type Quaternion = nalgebra::Quaternion<f32>;
15/// A unit quaternion.
16pub type UnitQuaternion = nalgebra::UnitQuaternion<f32>;
17/// A 3-dimensional translation.
18pub type Translation = nalgebra::Translation3<f32>;
19/// A 3-dimensional isometry.
20pub type Isometry = nalgebra::Isometry3<f32>;
21
22use alloc::vec::Vec;
23
24pub use device::*;
25pub use rotation::*;
26pub use transducer::*;
27
28/// Geometry of the devices.
29pub struct Geometry {
30    pub(crate) devices: Vec<Device>,
31    version: usize,
32}
33
34impl Geometry {
35    /// Creates a new [`Geometry`].
36    #[must_use]
37    pub fn new(devices: Vec<Device>) -> Self {
38        let mut geometry = Self {
39            devices,
40            version: 0,
41        };
42        geometry.assign_idx();
43        geometry
44    }
45
46    fn assign_idx(&mut self) {
47        self.devices
48            .iter_mut()
49            .enumerate()
50            .for_each(|(dev_idx, dev)| {
51                dev.idx = dev_idx as _;
52                dev.transducers.iter_mut().for_each(|tr| {
53                    tr.dev_idx = dev_idx as _;
54                });
55            });
56    }
57
58    /// Gets the number of devices.
59    #[must_use]
60    pub fn num_devices(&self) -> usize {
61        self.devices.len()
62    }
63
64    /// Gets the number of transducers.
65    #[must_use]
66    pub fn num_transducers(&self) -> usize {
67        self.iter().map(|dev| dev.num_transducers()).sum()
68    }
69
70    /// Gets the center of the transducers.
71    #[must_use]
72    pub fn center(&self) -> Point3 {
73        Point3::from(
74            self.iter().map(|d| d.center().coords).sum::<Vector3>() / self.devices.len() as f32,
75        )
76    }
77
78    #[doc(hidden)]
79    pub fn version(&self) -> usize {
80        self.version
81    }
82
83    /// Reconfigure the geometry.
84    pub fn reconfigure<D: Into<Device>, F: Fn(Device) -> D>(&mut self, f: F) {
85        self.devices = self.devices.drain(..).map(|dev| f(dev).into()).collect();
86        self.assign_idx();
87        self.version += 1;
88    }
89}
90
91impl<'a> IntoIterator for &'a Geometry {
92    type Item = &'a Device;
93    type IntoIter = core::slice::Iter<'a, Device>;
94
95    fn into_iter(self) -> Self::IntoIter {
96        self.devices.iter()
97    }
98}
99
100impl<'a> IntoIterator for &'a mut Geometry {
101    type Item = &'a mut Device;
102    type IntoIter = core::slice::IterMut<'a, Device>;
103
104    fn into_iter(self) -> Self::IntoIter {
105        self.version += 1;
106        self.devices.iter_mut()
107    }
108}
109
110impl core::ops::Deref for Geometry {
111    type Target = Vec<Device>;
112
113    fn deref(&self) -> &Self::Target {
114        &self.devices
115    }
116}
117
118impl core::ops::DerefMut for Geometry {
119    fn deref_mut(&mut self) -> &mut Self::Target {
120        self.version += 1;
121        &mut self.devices
122    }
123}
124
125#[cfg(test)]
126pub(crate) mod tests {
127    use alloc::vec::Vec;
128    use rand::Rng;
129
130    use crate::common::mm;
131
132    use super::*;
133
134    macro_rules! assert_approx_eq_vec3 {
135        ($a:expr, $b:expr) => {
136            approx::assert_abs_diff_eq!($a.x, $b.x, epsilon = 1e-3);
137            approx::assert_abs_diff_eq!($a.y, $b.y, epsilon = 1e-3);
138            approx::assert_abs_diff_eq!($a.z, $b.z, epsilon = 1e-3);
139        };
140    }
141
142    pub struct TestDevice {
143        pub rotation: UnitQuaternion,
144        pub transducers: Vec<Transducer>,
145    }
146
147    impl TestDevice {
148        pub fn new_autd3(pos: Point3) -> Self {
149            Self::new_autd3_with_rot(pos, UnitQuaternion::identity())
150        }
151
152        pub fn new_autd3_with_rot(pos: Point3, rot: impl Into<UnitQuaternion>) -> Self {
153            let rotation = rot.into();
154            let isometry = Isometry {
155                rotation,
156                translation: Translation::from(pos),
157            };
158            Self {
159                rotation,
160                transducers: itertools::iproduct!(0..14, 0..18)
161                    .map(|(y, x)| {
162                        Transducer::new(
163                            (isometry * (10.16 * mm * Point3::new(x as f32, y as f32, 0.))).xyz(),
164                        )
165                    })
166                    .collect(),
167            }
168        }
169    }
170
171    impl From<TestDevice> for Device {
172        fn from(dev: TestDevice) -> Self {
173            Self::new(dev.rotation, dev.transducers)
174        }
175    }
176
177    pub fn create_device(n: u8) -> Device {
178        Device::new(
179            UnitQuaternion::identity(),
180            (0..n).map(|_| Transducer::new(Point3::origin())).collect(),
181        )
182    }
183
184    pub fn create_geometry(n: u16, num_trans_in_unit: u8) -> Geometry {
185        Geometry::new((0..n).map(|_| create_device(num_trans_in_unit)).collect())
186    }
187
188    #[rstest::rstest]
189    #[case(1, alloc::vec![create_device(249)])]
190    #[case(2, alloc::vec![create_device(249), create_device(249)])]
191    fn test_num_devices(#[case] expected: usize, #[case] devices: Vec<Device>) {
192        let geometry = Geometry::new(devices);
193        assert_eq!(0, geometry.version());
194        assert_eq!(expected, geometry.num_devices());
195        assert_eq!(0, geometry.version());
196    }
197
198    #[rstest::rstest]
199    #[case(249, alloc::vec![create_device(249)])]
200    #[case(498, alloc::vec![create_device(249), create_device(249)])]
201    fn test_num_transducers(#[case] expected: usize, #[case] devices: Vec<Device>) {
202        let geometry = Geometry::new(devices);
203        assert_eq!(0, geometry.version());
204        assert_eq!(expected, geometry.num_transducers());
205        assert_eq!(0, geometry.version());
206    }
207
208    #[test]
209    fn test_center() {
210        let geometry = Geometry::new(alloc::vec![
211            TestDevice::new_autd3(Point3::origin()).into(),
212            TestDevice::new_autd3(Point3::new(10., 20., 30.)).into(),
213        ]);
214        let expect = geometry
215            .iter()
216            .map(|dev| dev.center().coords)
217            .sum::<Vector3>()
218            / geometry.num_devices() as f32;
219        assert_eq!(0, geometry.version());
220        assert_approx_eq_vec3!(expect, geometry.center());
221        assert_eq!(0, geometry.version());
222    }
223
224    #[test]
225    fn into_iter() {
226        let mut geometry = create_geometry(1, 1);
227        assert_eq!(0, geometry.version());
228        (&mut geometry).into_iter().for_each(|dev| {
229            _ = dev;
230        });
231        assert_eq!(1, geometry.version());
232    }
233
234    #[test]
235    fn idx() {
236        let geometry = Geometry::new(alloc::vec![
237            TestDevice::new_autd3_with_rot(Point3::origin(), UnitQuaternion::identity()).into(),
238            TestDevice::new_autd3_with_rot(Point3::origin(), UnitQuaternion::identity()).into(),
239        ]);
240        (0..2).for_each(|dev_idx| {
241            assert_eq!(dev_idx, geometry[dev_idx].idx());
242            (0..14 * 18).for_each(|tr_idx| {
243                assert_eq!(tr_idx, geometry[dev_idx][tr_idx].idx());
244                assert_eq!(dev_idx, geometry[dev_idx][tr_idx].dev_idx());
245            });
246        });
247    }
248
249    #[test]
250    fn reconfigure() {
251        let mut geometry = Geometry::new(alloc::vec![
252            TestDevice::new_autd3_with_rot(Point3::origin(), UnitQuaternion::identity()).into(),
253            TestDevice::new_autd3_with_rot(Point3::origin(), UnitQuaternion::identity()).into(),
254        ]);
255
256        let mut rng = rand::rng();
257        let t = Point3::new(rng.random(), rng.random(), rng.random());
258        let rot = UnitQuaternion::new_normalize(Quaternion::new(
259            rng.random(),
260            rng.random(),
261            rng.random(),
262            rng.random(),
263        ));
264
265        geometry.reconfigure(|dev| match dev.idx() {
266            0 => TestDevice::new_autd3_with_rot(t, rot),
267            _ => TestDevice::new_autd3_with_rot(dev[0].position(), dev.rotation()),
268        });
269
270        assert_eq!(1, geometry.version());
271        assert_eq!(t, geometry[0][0].position());
272        assert_eq!(rot, geometry[0].rotation());
273        assert_eq!(Point3::origin(), geometry[1][0].position());
274        assert_eq!(UnitQuaternion::identity(), geometry[1].rotation());
275    }
276}