autd3_core/geometry/
mod.rs1pub(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
22use alloc::vec::Vec;
23
24pub use device::*;
25pub use rotation::*;
26pub use transducer::*;
27
28pub struct Geometry {
30 pub(crate) devices: Vec<Device>,
31 version: usize,
32}
33
34impl Geometry {
35 #[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 #[must_use]
60 pub fn num_devices(&self) -> usize {
61 self.devices.len()
62 }
63
64 #[must_use]
66 pub fn num_transducers(&self) -> usize {
67 self.iter().map(|dev| dev.num_transducers()).sum()
68 }
69
70 #[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 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}