1pub(crate) mod device;
2mod rotation;
3mod transducer;
4
5pub type Complex = nalgebra::Complex<f32>;
7pub type Vector3 = nalgebra::Vector3<f32>;
9pub type UnitVector3 = nalgebra::UnitVector3<f32>;
11pub type Point3 = nalgebra::Point3<f32>;
13pub type Quaternion = nalgebra::Quaternion<f32>;
15pub type UnitQuaternion = nalgebra::UnitQuaternion<f32>;
17pub type Translation = nalgebra::Translation3<f32>;
19pub 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#[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 #[must_use]
44 pub fn new(devices: Vec<Device>) -> Self {
45 let mut geometry = Self {
46 devices,
47 version: 0,
48 };
49 geometry.assign_idx();
50 geometry
51 }
52
53 fn assign_idx(&mut self) {
54 self.devices
55 .iter_mut()
56 .enumerate()
57 .for_each(|(dev_idx, dev)| {
58 dev.idx = dev_idx as _;
59 dev.transducers.iter_mut().for_each(|tr| {
60 tr.dev_idx = dev_idx as _;
61 });
62 });
63 }
64
65 #[must_use]
67 pub fn num_devices(&self) -> usize {
68 self.devices().count()
69 }
70
71 #[must_use]
73 pub fn num_transducers(&self) -> usize {
74 self.devices().map(|dev| dev.num_transducers()).sum()
75 }
76
77 #[must_use]
79 pub fn center(&self) -> Point3 {
80 Point3::from(
81 self.devices().map(|d| d.center().coords).sum::<Vector3>() / self.devices.len() as f32,
82 )
83 }
84
85 pub fn devices(&self) -> impl Iterator<Item = &Device> {
87 self.iter().filter(|dev| dev.enable)
88 }
89
90 pub fn devices_mut(&mut self) -> impl Iterator<Item = &mut Device> {
92 self.iter_mut().filter(|dev| dev.enable)
93 }
94
95 pub fn set_sound_speed(&mut self, c: f32) {
97 self.devices_mut().for_each(|dev| dev.sound_speed = c);
98 }
99
100 pub fn set_sound_speed_from_temp(&mut self, t: f32) {
104 self.set_sound_speed_from_temp_with(t, 1.4, 8.314_463, 28.9647e-3);
105 }
106
107 pub fn set_sound_speed_from_temp_with(&mut self, t: f32, k: f32, r: f32, m: f32) {
109 self.devices_mut()
110 .for_each(|dev| dev.set_sound_speed_from_temp_with(t, k, r, m));
111 }
112
113 #[must_use]
115 pub fn aabb(&self) -> Aabb<f32, 3> {
116 self.devices()
117 .fold(Aabb::empty(), |aabb, dev| aabb.join(dev.aabb()))
118 }
119
120 pub fn reconfigure<D: Into<Device>, F: Fn(&Device) -> D>(&mut self, f: F) {
122 self.devices.iter_mut().for_each(|dev| {
123 let enable = dev.enable;
124 let sound_speed = dev.sound_speed;
125 *dev = f(dev).into();
126 dev.enable = enable;
127 dev.sound_speed = sound_speed;
128 });
129 self.assign_idx();
130 self.version += 1;
131 }
132}
133
134impl<'a> IntoIterator for &'a mut Geometry {
135 type Item = &'a mut Device;
136 type IntoIter = std::slice::IterMut<'a, Device>;
137
138 fn into_iter(self) -> Self::IntoIter {
139 self.version += 1;
140 self.devices.iter_mut()
141 }
142}
143
144impl std::ops::DerefMut for Geometry {
145 fn deref_mut(&mut self) -> &mut Self::Target {
146 self.version += 1;
147 &mut self.devices
148 }
149}
150
151#[cfg(test)]
152pub(crate) mod tests {
153 use rand::Rng;
154
155 use crate::common::{deg, mm};
156
157 use super::*;
158
159 macro_rules! assert_approx_eq_vec3 {
160 ($a:expr, $b:expr) => {
161 approx::assert_abs_diff_eq!($a.x, $b.x, epsilon = 1e-3);
162 approx::assert_abs_diff_eq!($a.y, $b.y, epsilon = 1e-3);
163 approx::assert_abs_diff_eq!($a.z, $b.z, epsilon = 1e-3);
164 };
165 }
166
167 pub struct TestDevice {
168 pub rotation: UnitQuaternion,
169 pub transducers: Vec<Transducer>,
170 }
171
172 impl TestDevice {
173 pub fn new_autd3(pos: Point3) -> Self {
174 Self::new_autd3_with_rot(pos, UnitQuaternion::identity())
175 }
176
177 pub fn new_autd3_with_rot(pos: Point3, rot: impl Into<UnitQuaternion>) -> Self {
178 let rotation = rot.into();
179 let isometry = Isometry {
180 rotation,
181 translation: Translation::from(pos),
182 };
183 Self {
184 rotation,
185 transducers: itertools::iproduct!(0..14, 0..18)
186 .map(|(y, x)| {
187 Transducer::new(
188 (isometry * (10.16 * mm * Point3::new(x as f32, y as f32, 0.))).xyz(),
189 )
190 })
191 .collect(),
192 }
193 }
194 }
195
196 impl From<TestDevice> for Device {
197 fn from(dev: TestDevice) -> Self {
198 Self::new(dev.rotation, dev.transducers)
199 }
200 }
201
202 pub fn create_device(n: u8) -> Device {
203 Device::new(
204 UnitQuaternion::identity(),
205 (0..n).map(|_| Transducer::new(Point3::origin())).collect(),
206 )
207 }
208
209 pub fn create_geometry(n: u16, num_trans_in_unit: u8) -> Geometry {
210 Geometry::new((0..n).map(|_| create_device(num_trans_in_unit)).collect())
211 }
212
213 #[rstest::rstest]
214 #[test]
215 #[case(1, vec![create_device(249)])]
216 #[case(2, vec![create_device(249), create_device(249)])]
217 fn test_num_devices(#[case] expected: usize, #[case] devices: Vec<Device>) {
218 let geometry = Geometry::new(devices);
219 assert_eq!(0, geometry.version());
220 assert_eq!(expected, geometry.num_devices());
221 assert_eq!(0, geometry.version());
222 }
223
224 #[rstest::rstest]
225 #[test]
226 #[case(249, vec![create_device(249)])]
227 #[case(498, vec![create_device(249), create_device(249)])]
228 fn test_num_transducers(#[case] expected: usize, #[case] devices: Vec<Device>) {
229 let geometry = Geometry::new(devices);
230 assert_eq!(0, geometry.version());
231 assert_eq!(expected, geometry.num_transducers());
232 assert_eq!(0, geometry.version());
233 }
234
235 #[test]
236 fn test_center() {
237 let geometry = Geometry::new(vec![
238 TestDevice::new_autd3(Point3::origin()).into(),
239 TestDevice::new_autd3(Point3::new(10., 20., 30.)).into(),
240 ]);
241 let expect = geometry
242 .iter()
243 .map(|dev| dev.center().coords)
244 .sum::<Vector3>()
245 / geometry.num_devices() as f32;
246 assert_eq!(0, geometry.version());
247 assert_approx_eq_vec3!(expect, geometry.center());
248 assert_eq!(0, geometry.version());
249 }
250
251 #[rstest::rstest]
252 #[test]
253 #[case(340.29525e3, 15.)]
254 #[case(343.23497e3, 20.)]
255 #[case(349.04013e3, 30.)]
256 fn test_set_sound_speed_from_temp(#[case] expected: f32, #[case] temp: f32) {
257 let mut geometry = create_geometry(2, 1);
258 assert_eq!(0, geometry.version());
259 geometry.set_sound_speed_from_temp(temp);
260 assert_eq!(1, geometry.version());
261 geometry.iter().for_each(|dev| {
262 approx::assert_abs_diff_eq!(expected * mm, dev.sound_speed, epsilon = 1e-3);
263 });
264 }
265
266 #[rstest::rstest]
267 #[test]
268 #[case(3.402_952_8e5)]
269 #[case(3.432_35e5)]
270 #[case(3.490_401_6e5)]
271 fn test_set_sound_speed(#[case] temp: f32) {
272 let mut geometry = create_geometry(2, 1);
273 assert_eq!(0, geometry.version());
274 geometry.set_sound_speed(temp * mm);
275 assert_eq!(1, geometry.version());
276 geometry.iter().for_each(|dev| {
277 assert_eq!(dev.sound_speed, temp * mm);
278 });
279 }
280
281 #[test]
282 fn into_iter() {
283 let mut geometry = create_geometry(1, 1);
284 assert_eq!(0, geometry.version());
285 for dev in &mut geometry {
286 dev.enable = true;
287 }
288 assert_eq!(1, geometry.version());
289 }
290
291 #[rstest::rstest]
292 #[test]
293 #[case(Aabb{min: Point3::origin(), max: Point3::new(172.72 * mm, 132.08 * mm, 0.)}, vec![TestDevice::new_autd3(Point3::origin())])]
294 #[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))])]
295 #[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))])]
296 #[case(Aabb{min: Point3::new(-132.08 * mm, -10. * mm, 0.), max: Point3::new(172.72 * mm, 162.72 * mm, 10. * mm)}, vec![
297 TestDevice::new_autd3(Point3::origin()),
298 TestDevice::new_autd3_with_rot(Point3::new(0., -10. * mm, 10. * mm), EulerAngle::ZYZ(90. * deg, 0. * deg, 0. * deg))
299 ])]
300 fn aabb(#[case] expect: Aabb<f32, 3>, #[case] dev: Vec<TestDevice>) {
301 let geometry = Geometry::new(dev.into_iter().map(|d| d.into()).collect());
302 assert_approx_eq_vec3!(expect.min, geometry.aabb().min);
303 assert_approx_eq_vec3!(expect.max, geometry.aabb().max);
304 }
305
306 #[test]
307 fn idx() {
308 let geometry = Geometry::new(vec![
309 TestDevice::new_autd3_with_rot(Point3::origin(), UnitQuaternion::identity()).into(),
310 TestDevice::new_autd3_with_rot(Point3::origin(), UnitQuaternion::identity()).into(),
311 ]);
312 (0..2).for_each(|dev_idx| {
313 assert_eq!(dev_idx, geometry[dev_idx].idx());
314 (0..14 * 18).for_each(|tr_idx| {
315 assert_eq!(tr_idx, geometry[dev_idx][tr_idx].idx());
316 assert_eq!(dev_idx, geometry[dev_idx][tr_idx].dev_idx());
317 });
318 });
319 }
320
321 #[test]
322 fn reconfigure() {
323 let mut geometry = Geometry::new(vec![
324 TestDevice::new_autd3_with_rot(Point3::origin(), UnitQuaternion::identity()).into(),
325 TestDevice::new_autd3_with_rot(Point3::origin(), UnitQuaternion::identity()).into(),
326 ]);
327
328 let mut rng = rand::rng();
329 let t = Point3::new(rng.random(), rng.random(), rng.random());
330 let rot = UnitQuaternion::new_normalize(Quaternion::new(
331 rng.random(),
332 rng.random(),
333 rng.random(),
334 rng.random(),
335 ));
336
337 geometry.reconfigure(|dev| match dev.idx() {
338 0 => TestDevice::new_autd3_with_rot(t, rot),
339 _ => TestDevice::new_autd3_with_rot(*dev[0].position(), *dev.rotation()),
340 });
341
342 assert_eq!(1, geometry.version());
343 assert_eq!(t, *geometry[0][0].position());
344 assert_eq!(rot, *geometry[0].rotation());
345 assert_eq!(Point3::origin(), *geometry[1][0].position());
346 assert_eq!(UnitQuaternion::identity(), *geometry[1].rotation());
347 }
348}