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