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