autd3_core/geometry/
mod.rs

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