autd3_rs_core/geometry/
mod.rs1mod autd3;
2mod device;
3mod groups;
4#[cfg(feature = "serde")]
5mod layout;
6mod mask;
7
8pub use autd3::Autd3;
9pub use device::Device;
10pub use groups::TransducerGroups;
11#[cfg(feature = "serde")]
12pub use layout::LayoutError;
13pub use mask::{TransducerMask, TransducerMaskError};
14pub use nalgebra::{Point3, Quaternion, UnitQuaternion, UnitVector3, Vector3};
15
16use crate::common::Length;
17use crate::value::Emission;
18
19#[must_use]
20pub fn point(x: Length, y: Length, z: Length) -> Point3<f32> {
21 Point3::new(x.mm(), y.mm(), z.mm())
22}
23
24#[must_use]
25pub fn offset(x: Length, y: Length, z: Length) -> Vector3<f32> {
26 Vector3::new(x.mm(), y.mm(), z.mm())
27}
28
29#[derive(Clone, Debug)]
30pub struct Geometry {
31 devices: Vec<Device>,
32}
33
34impl Geometry {
35 #[must_use]
36 pub fn new<D: Into<Device>>(devices: Vec<D>) -> Self {
37 Self {
38 devices: devices
39 .into_iter()
40 .enumerate()
41 .map(|(i, d)| {
42 let mut device = d.into();
43 device.set_idx(i);
44 device
45 })
46 .collect(),
47 }
48 }
49
50 #[must_use]
51 pub const fn num_devices(&self) -> usize {
52 self.devices.len()
53 }
54
55 #[must_use]
56 pub fn pattern_buffer(&self) -> Vec<Vec<Emission>> {
57 self.devices
58 .iter()
59 .map(|d| vec![Emission::default(); d.num_transducers()])
60 .collect()
61 }
62
63 #[must_use]
64 pub fn num_transducers(&self) -> usize {
65 self.devices.iter().map(Device::num_transducers).sum()
66 }
67
68 #[must_use]
69 pub const fn is_empty(&self) -> bool {
70 self.devices.is_empty()
71 }
72
73 pub fn iter(&self) -> core::slice::Iter<'_, Device> {
74 self.devices.iter()
75 }
76
77 #[must_use]
78 pub fn center(&self) -> Point3<f32> {
79 let n = self.devices.len() as f32;
80 let sum = self
81 .devices
82 .iter()
83 .fold(Vector3::zeros(), |acc, d| acc + d.center().coords);
84 Point3::from(sum / n)
85 }
86}
87
88impl core::ops::Index<usize> for Geometry {
89 type Output = Device;
90
91 fn index(&self, index: usize) -> &Device {
92 &self.devices[index]
93 }
94}
95
96impl<'a> IntoIterator for &'a Geometry {
97 type Item = &'a Device;
98 type IntoIter = core::slice::Iter<'a, Device>;
99
100 fn into_iter(self) -> Self::IntoIter {
101 self.iter()
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use approx::assert_abs_diff_eq;
108 use nalgebra::UnitQuaternion;
109
110 use super::*;
111 use crate::value::{Intensity, Phase};
112
113 #[test]
114 fn geometry_sets_device_idx_and_num_transducers() {
115 let g = Geometry::new(vec![Autd3::default(), Autd3::default()]);
116 assert_eq!(g[0].idx(), 0);
117 assert_eq!(g[1].idx(), 1);
118 assert_eq!(g.num_transducers(), 2 * Autd3::NUM_TRANSDUCERS);
119 }
120
121 #[test]
122 fn pattern_buffer_starts_at_zero_phase_max_intensity() {
123 let g = Geometry::new(vec![Autd3::default(), Autd3::default()]);
124 let buf = g.pattern_buffer();
125 assert_eq!(buf.len(), 2);
126 for dev in &buf {
127 assert_eq!(dev.len(), Autd3::NUM_TRANSDUCERS);
128 for &e in dev {
129 assert_eq!(e.phase, Phase::ZERO);
130 assert_eq!(e.intensity, Intensity::MAX);
131 }
132 }
133 }
134
135 #[test]
136 fn device_basis_directions_for_identity() {
137 let g = Geometry::new(vec![Autd3::default()]);
138 let dev = &g[0];
139 assert_abs_diff_eq!(dev.x_direction().into_inner(), Vector3::x(), epsilon = 1e-4);
140 assert_abs_diff_eq!(dev.y_direction().into_inner(), Vector3::y(), epsilon = 1e-4);
141 assert_abs_diff_eq!(
142 dev.axial_direction().into_inner(),
143 Vector3::z(),
144 epsilon = 1e-4
145 );
146 assert_abs_diff_eq!(
147 dev.rotation().angle_to(&UnitQuaternion::identity()),
148 0.0,
149 epsilon = 1e-4
150 );
151 }
152
153 #[test]
154 fn device_rotation_tracks_quarter_turn_about_x() {
155 let rot = UnitQuaternion::from_axis_angle(&Vector3::x_axis(), core::f32::consts::FRAC_PI_2);
156 let g = Geometry::new(vec![Autd3::new(Point3::origin(), rot)]);
157 let dev = &g[0];
158 assert_abs_diff_eq!(
159 dev.axial_direction().into_inner(),
160 (rot * Vector3::z_axis()).into_inner(),
161 epsilon = 1e-4
162 );
163 assert_abs_diff_eq!(dev.rotation().angle_to(&rot), 0.0, epsilon = 1e-4);
164 }
165}