1use super::{
2 Isometry3, Point3, Quaternion, Transducer, Translation3, UnitQuaternion, UnitVector3, Vector3,
3};
4
5pub struct Device {
7 pub(crate) idx: u16,
8 pub(crate) transducers: Vec<Transducer>,
9 rotation: UnitQuaternion,
10 center: Point3,
11 x_direction: UnitVector3,
12 y_direction: UnitVector3,
13 axial_direction: UnitVector3,
14 inv: Isometry3,
15}
16
17impl Device {
18 fn init(&mut self) {
19 self.center = Point3::from(
20 self.transducers
21 .iter()
22 .map(|tr| tr.position().coords)
23 .sum::<Vector3>()
24 / self.transducers.len() as f32,
25 );
26 self.x_direction = Self::get_direction(Vector3::x(), &self.rotation);
27 self.y_direction = Self::get_direction(Vector3::y(), &self.rotation);
28 self.axial_direction = if cfg!(feature = "left_handed") {
29 Self::get_direction(-Vector3::z(), &self.rotation) } else {
31 Self::get_direction(Vector3::z(), &self.rotation)
32 };
33 self.inv = (Translation3::from(self.transducers[0].position()) * self.rotation).inverse();
34 }
35
36 #[doc(hidden)]
37 #[must_use]
38 pub fn new(rot: UnitQuaternion, transducers: Vec<Transducer>) -> Self {
39 let mut transducers = transducers;
40 transducers.iter_mut().enumerate().for_each(|(tr_idx, tr)| {
41 tr.idx = tr_idx as _;
42 });
43 let mut dev = Self {
44 idx: 0,
45 transducers,
46 rotation: rot,
47 center: Point3::origin(),
48 x_direction: Vector3::x_axis(),
49 y_direction: Vector3::y_axis(),
50 axial_direction: Vector3::z_axis(),
51 inv: Isometry3::identity(),
52 };
53 dev.init();
54 dev
55 }
56
57 #[must_use]
59 pub const fn idx(&self) -> usize {
60 self.idx as _
61 }
62
63 #[must_use]
65 pub const fn num_transducers(&self) -> usize {
66 self.transducers.len()
67 }
68
69 #[must_use]
71 pub const fn rotation(&self) -> UnitQuaternion {
72 self.rotation
73 }
74
75 #[must_use]
77 pub const fn center(&self) -> Point3 {
78 self.center
79 }
80
81 #[must_use]
83 pub const fn x_direction(&self) -> UnitVector3 {
84 self.x_direction
85 }
86
87 #[must_use]
89 pub const fn y_direction(&self) -> UnitVector3 {
90 self.y_direction
91 }
92
93 #[must_use]
95 pub const fn axial_direction(&self) -> UnitVector3 {
96 self.axial_direction
97 }
98
99 #[doc(hidden)]
100 #[must_use]
101 pub const fn inv(&self) -> &Isometry3 {
102 &self.inv
103 }
104
105 #[must_use]
106 fn get_direction(dir: Vector3, rotation: &UnitQuaternion) -> UnitVector3 {
107 let dir: UnitQuaternion = UnitQuaternion::from_quaternion(Quaternion::from_imag(dir));
108 UnitVector3::new_normalize((rotation * dir * rotation.conjugate()).imag())
109 }
110}
111
112impl core::ops::Deref for Device {
113 type Target = [Transducer];
114
115 fn deref(&self) -> &Self::Target {
116 &self.transducers
117 }
118}
119
120impl core::iter::IntoIterator for Device {
121 type Item = Transducer;
122 type IntoIter = std::vec::IntoIter<Transducer>;
123
124 fn into_iter(self) -> Self::IntoIter {
125 self.transducers.into_iter()
126 }
127}
128
129impl<'a> IntoIterator for &'a Device {
130 type Item = &'a Transducer;
131 type IntoIter = core::slice::Iter<'a, Transducer>;
132
133 fn into_iter(self) -> Self::IntoIter {
134 self.transducers.iter()
135 }
136}
137
138#[cfg(test)]
139pub(crate) mod tests {
140 use super::*;
141 use crate::{
142 common::PI,
143 geometry::tests::{TestDevice, create_device},
144 };
145
146 macro_rules! assert_approx_eq_vec3 {
147 ($a:expr, $b:expr) => {
148 approx::assert_abs_diff_eq!($a.x, $b.x, epsilon = 1e-3);
149 approx::assert_abs_diff_eq!($a.y, $b.y, epsilon = 1e-3);
150 approx::assert_abs_diff_eq!($a.z, $b.z, epsilon = 1e-3);
151 };
152 }
153
154 #[test]
155 fn into_iter() {
156 let device = create_device(249);
157 let mut count = 0;
158 for tr in &device {
159 assert_eq!(count, tr.idx());
160 count += 1;
161 }
162 assert_eq!(249, count);
163
164 let mut count = 0;
165 device.into_iter().for_each(|tr| {
166 assert_eq!(count, tr.idx());
167 count += 1;
168 });
169 assert_eq!(249, count);
170 }
171
172 #[test]
173 fn idx() {
174 assert_eq!(0, create_device(249).idx());
175 }
176
177 #[rstest::rstest]
178 #[case(1)]
179 #[case(249)]
180 fn num_transducers(#[case] n: u8) {
181 assert_eq!(n, create_device(n).num_transducers() as u8);
182 }
183
184 #[test]
185 fn center() {
186 let device: Device = TestDevice::new_autd3(Point3::origin()).into();
187 let expected =
188 device.iter().map(|t| t.position().coords).sum::<Vector3>() / device.len() as f32;
189 assert_approx_eq_vec3!(expected, device.center().coords);
190 }
191
192 #[test]
193 fn direction() {
194 let device: Device = TestDevice::new_autd3(Point3::origin()).into();
195 assert_approx_eq_vec3!(Vector3::x(), device.x_direction().into_inner());
196 assert_approx_eq_vec3!(Vector3::y(), device.y_direction().into_inner());
197 if cfg!(feature = "left_handed") {
198 assert_approx_eq_vec3!(-Vector3::z(), device.axial_direction().into_inner()); } else {
200 assert_approx_eq_vec3!(Vector3::z(), device.axial_direction().into_inner());
201 }
202 }
203
204 #[rstest::rstest]
205 #[case(
206 Vector3::new(10., 20., 30.),
207 Vector3::new(10., 20., 30.),
208 Point3::origin(),
209 UnitQuaternion::identity()
210 )]
211 #[case(
212 Vector3::zeros(),
213 Vector3::new(10., 20., 30.),
214 Point3::new(10., 20., 30.),
215 UnitQuaternion::identity()
216 )]
217 #[case(
218 Vector3::new(20., -10., 30.),
219 Vector3::new(10., 20., 30.),
220 Point3::origin(),
221 UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.)
222 )]
223 #[case(
224 Vector3::new(30., 30., -30.),
225 Vector3::new(40., 50., 60.),
226 Point3::new(10., 20., 30.),
227 UnitQuaternion::from_axis_angle(&Vector3::x_axis(), PI / 2.)
228 )]
229 fn inv(
230 #[case] expected: Vector3,
231 #[case] target: Vector3,
232 #[case] origin: Point3,
233 #[case] rot: UnitQuaternion,
234 ) {
235 let device: Device = TestDevice::new_autd3_with_rot(origin, rot).into();
236 assert_approx_eq_vec3!(expected, device.inv * Point3::from(target));
237 }
238}