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 Self {
46 devices,
47 version: 0,
48 }
49 }
50
51 #[must_use]
53 pub fn num_devices(&self) -> usize {
54 self.devices().count()
55 }
56
57 #[must_use]
59 pub fn num_transducers(&self) -> usize {
60 self.devices().map(|dev| dev.num_transducers()).sum()
61 }
62
63 #[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 pub fn devices(&self) -> impl Iterator<Item = &Device> {
73 self.iter().filter(|dev| dev.enable)
74 }
75
76 pub fn devices_mut(&mut self) -> impl Iterator<Item = &mut Device> {
78 self.iter_mut().filter(|dev| dev.enable)
79 }
80
81 pub fn set_sound_speed(&mut self, c: f32) {
83 self.devices_mut().for_each(|dev| dev.sound_speed = c);
84 }
85
86 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 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 #[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}