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
22pub use bvh::aabb::Aabb;
23pub use device::*;
24use getset::CopyGetters;
25pub use rotation::*;
26pub use transducer::*;
27
28use derive_more::{Deref, IntoIterator};
29
30/// Geometry of the devices.
31#[derive(Deref, CopyGetters, IntoIterator)]
32pub struct Geometry {
33    #[deref]
34    #[into_iterator(ref)]
35    pub(crate) devices: Vec<Device>,
36    #[doc(hidden)]
37    #[getset(get_copy = "pub")]
38    version: usize,
39}
40
41impl Geometry {
42    /// Creates a new [`Geometry`].
43    #[must_use]
44    pub fn new(devices: Vec<Device>) -> Self {
45        Self {
46            devices,
47            version: 0,
48        }
49    }
50
51    /// Gets the number of enabled devices.
52    #[must_use]
53    pub fn num_devices(&self) -> usize {
54        self.devices().count()
55    }
56
57    /// Gets the number of enabled transducers.
58    #[must_use]
59    pub fn num_transducers(&self) -> usize {
60        self.devices().map(|dev| dev.num_transducers()).sum()
61    }
62
63    /// Gets the center of the enabled transducers.
64    #[must_use]
65    pub fn center(&self) -> Point3 {
66        Point3::from(
67            self.devices().map(|d| d.center().coords).sum::<Vector3>() / self.devices.len() as f32,
68        )
69    }
70
71    /// Gets the iterator of enabled devices.
72    pub fn devices(&self) -> impl Iterator<Item = &Device> {
73        self.iter().filter(|dev| dev.enable)
74    }
75
76    /// Gets the mutable iterator of enabled devices.
77    pub fn devices_mut(&mut self) -> impl Iterator<Item = &mut Device> {
78        self.iter_mut().filter(|dev| dev.enable)
79    }
80
81    /// Sets the sound speed of enabled devices.
82    pub fn set_sound_speed(&mut self, c: f32) {
83        self.devices_mut().for_each(|dev| dev.sound_speed = c);
84    }
85
86    /// Sets the sound speed of enabled devices from the temperature `t`.
87    ///
88    /// This is equivalent to `Self::set_sound_speed_from_temp_with(t, 1.4, 8.314_463, 28.9647e-3)`.
89    pub fn set_sound_speed_from_temp(&mut self, t: f32) {
90        self.set_sound_speed_from_temp_with(t, 1.4, 8.314_463, 28.9647e-3);
91    }
92
93    /// Sets the sound speed of enabled devices from the temperature `t`, heat capacity ratio `k`, gas constant `r`, and molar mass `m` [kg/mol].
94    pub fn set_sound_speed_from_temp_with(&mut self, t: f32, k: f32, r: f32, m: f32) {
95        self.devices_mut()
96            .for_each(|dev| dev.set_sound_speed_from_temp_with(t, k, r, m));
97    }
98
99    /// Axis Aligned Bounding Box of enabled devices.
100    #[must_use]
101    pub fn aabb(&self) -> Aabb<f32, 3> {
102        self.devices()
103            .fold(Aabb::empty(), |aabb, dev| aabb.join(dev.aabb()))
104    }
105}
106
107impl<'a> IntoIterator for &'a mut Geometry {
108    type Item = &'a mut Device;
109    type IntoIter = std::slice::IterMut<'a, Device>;
110
111    fn into_iter(self) -> Self::IntoIter {
112        self.version += 1;
113        self.devices.iter_mut()
114    }
115}
116
117impl std::ops::DerefMut for Geometry {
118    fn deref_mut(&mut self) -> &mut Self::Target {
119        self.version += 1;
120        &mut self.devices
121    }
122}
123
124#[cfg(test)]
125pub(crate) mod tests {
126    use crate::defined::{deg, mm};
127
128    use super::*;
129
130    macro_rules! assert_approx_eq_vec3 {
131        ($a:expr, $b:expr) => {
132            approx::assert_abs_diff_eq!($a.x, $b.x, epsilon = 1e-3);
133            approx::assert_abs_diff_eq!($a.y, $b.y, epsilon = 1e-3);
134            approx::assert_abs_diff_eq!($a.z, $b.z, epsilon = 1e-3);
135        };
136    }
137
138    pub struct TestDevice {
139        pub rotation: UnitQuaternion,
140        pub transducers: Vec<Transducer>,
141    }
142
143    impl TestDevice {
144        pub fn new_autd3(pos: Point3) -> Self {
145            Self::new_autd3_with_rot(pos, UnitQuaternion::identity())
146        }
147
148        pub fn new_autd3_with_rot(pos: Point3, rot: impl Into<UnitQuaternion>) -> Self {
149            let rotation = rot.into();
150            let isometry = Isometry {
151                rotation,
152                translation: Translation::from(pos),
153            };
154            Self {
155                rotation,
156                transducers: itertools::iproduct!(0..14, 0..18)
157                    .enumerate()
158                    .map(|(i, (y, x))| {
159                        Transducer::new(
160                            i as _,
161                            i as _,
162                            (isometry * (10.16 * mm * Point3::new(x as f32, y as f32, 0.))).xyz(),
163                        )
164                    })
165                    .collect(),
166            }
167        }
168    }
169
170    impl IntoDevice for TestDevice {
171        fn into_device(self, dev_idx: u16) -> Device {
172            Device::new(dev_idx, self.rotation, self.transducers)
173        }
174    }
175
176    pub fn create_device(idx: u16, n: u8) -> Device {
177        Device::new(
178            idx,
179            UnitQuaternion::identity(),
180            (0..n)
181                .map(|i| Transducer::new(i, idx, Point3::origin()))
182                .collect(),
183        )
184    }
185
186    pub fn create_geometry(n: u16, num_trans_in_unit: u8) -> Geometry {
187        Geometry::new(
188            (0..n)
189                .map(|i| create_device(i, num_trans_in_unit))
190                .collect(),
191        )
192    }
193
194    #[rstest::rstest]
195    #[test]
196    #[case(1, vec![create_device(0, 249)])]
197    #[case(2, vec![create_device(0, 249), create_device(0, 249)])]
198    fn test_num_devices(#[case] expected: usize, #[case] devices: Vec<Device>) {
199        let geometry = Geometry::new(devices);
200        assert_eq!(0, geometry.version());
201        assert_eq!(expected, geometry.num_devices());
202        assert_eq!(0, geometry.version());
203    }
204
205    #[rstest::rstest]
206    #[test]
207    #[case(249, vec![create_device(0, 249)])]
208    #[case(498, vec![create_device(0, 249), create_device(0, 249)])]
209    fn test_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 test_center() {
218        let geometry = Geometry::new(vec![
219            TestDevice::new_autd3(Point3::origin()).into_device(0),
220            TestDevice::new_autd3(Point3::new(10., 20., 30.)).into_device(1),
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    #[rstest::rstest]
233    #[test]
234    #[case(340.29525e3, 15.)]
235    #[case(343.23497e3, 20.)]
236    #[case(349.04013e3, 30.)]
237    fn test_set_sound_speed_from_temp(#[case] expected: f32, #[case] temp: f32) {
238        let mut geometry = create_geometry(2, 1);
239        assert_eq!(0, geometry.version());
240        geometry.set_sound_speed_from_temp(temp);
241        assert_eq!(1, geometry.version());
242        geometry.iter().for_each(|dev| {
243            approx::assert_abs_diff_eq!(expected * mm, dev.sound_speed, epsilon = 1e-3);
244        });
245    }
246
247    #[rstest::rstest]
248    #[test]
249    #[case(3.402_952_8e5)]
250    #[case(3.432_35e5)]
251    #[case(3.490_401_6e5)]
252    fn test_set_sound_speed(#[case] temp: f32) {
253        let mut geometry = create_geometry(2, 1);
254        assert_eq!(0, geometry.version());
255        geometry.set_sound_speed(temp * mm);
256        assert_eq!(1, geometry.version());
257        geometry.iter().for_each(|dev| {
258            assert_eq!(dev.sound_speed, temp * mm);
259        });
260    }
261
262    #[test]
263    fn into_iter() {
264        let mut geometry = create_geometry(1, 1);
265        assert_eq!(0, geometry.version());
266        for dev in &mut geometry {
267            dev.enable = true;
268        }
269        assert_eq!(1, geometry.version());
270    }
271
272    #[rstest::rstest]
273    #[test]
274    #[case(Aabb{min: Point3::origin(), max: Point3::new(172.72 * mm, 132.08 * mm, 0.)}, vec![TestDevice::new_autd3(Point3::origin())])]
275    #[case(Aabb{min: Point3::new(10. * mm, 20. * mm, 30. * mm), max: Point3::new(182.72 * mm, 152.08 * mm, 30. * mm)}, vec![TestDevice::new_autd3(Point3::new(10. * mm, 20. * mm, 30. * mm))])]
276    #[case(Aabb{min: Point3::new(-132.08 * mm, 0., 0.), max: Point3::new(0., 172.72 * mm, 0.)}, vec![TestDevice::new_autd3_with_rot(Point3::origin(), EulerAngle::ZYZ(90. * deg, 0. * deg, 0. * deg))])]
277    #[case(Aabb{min: Point3::new(-132.08 * mm, -10. * mm, 0.), max: Point3::new(172.72 * mm, 162.72 * mm, 10. * mm)}, vec![
278        TestDevice::new_autd3(Point3::origin()),
279        TestDevice::new_autd3_with_rot(Point3::new(0., -10. * mm, 10. * mm), EulerAngle::ZYZ(90. * deg, 0. * deg, 0. * deg))
280    ])]
281    fn aabb(#[case] expect: Aabb<f32, 3>, #[case] dev: Vec<TestDevice>) {
282        let geometry = Geometry::new(
283            dev.into_iter()
284                .enumerate()
285                .map(|(idx, d)| d.into_device(idx as _))
286                .collect(),
287        );
288        assert_approx_eq_vec3!(expect.min, geometry.aabb().min);
289        assert_approx_eq_vec3!(expect.max, geometry.aabb().max);
290    }
291}