hoomd_microstate/property/oriented_point.rs
1// Copyright (c) 2024-2026 The Regents of the University of Michigan.
2// Part of hoomd-rs, released under the BSD 3-Clause License.
3
4//! Implement `OrientedPoint`
5
6use serde::{Deserialize, Serialize};
7
8use super::{Orientation, Point, Position};
9use crate::Transform;
10use hoomd_vector::{Rotate, Rotation, Vector};
11
12/// The position and orientation of an extended body.
13///
14/// Use [`OrientedPoint`] as a [`Body`](crate::Body) or [`Site`](crate::Site) property type.
15///
16/// # Example
17///
18/// ```
19/// use hoomd_microstate::property::OrientedPoint;
20/// use hoomd_vector::{Angle, Cartesian};
21///
22/// let point = OrientedPoint {
23/// position: Cartesian::from([1.0, -3.0]),
24/// orientation: Angle::from(1.2),
25/// };
26/// ```
27#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
28pub struct OrientedPoint<V, R> {
29 /// The location of the extended body in space.
30 pub position: V,
31 /// Rotate from the body's reference frame into another frame.
32 pub orientation: R,
33}
34
35/// Treat [`Point`] sites as constituents of oriented rigid bodies.
36impl<V, R> Transform<Point<V>> for OrientedPoint<V, R>
37where
38 V: Vector,
39 R: Rotate<V>,
40{
41 /// Move [`Point`] properties from the local body frame to the system frame.
42 ///
43 /// ```math
44 /// \vec{r} = \vec{r}_\mathrm{body} + R_\mathrm{body}(\vec{r}_\mathrm{site})
45 /// ```
46 ///
47 /// ```
48 /// use approxim::assert_relative_eq;
49 /// use hoomd_microstate::{
50 /// Transform,
51 /// property::{OrientedPoint, Point},
52 /// };
53 /// use hoomd_vector::{Angle, Cartesian};
54 /// use std::f64::consts::PI;
55 ///
56 /// let body_properties = OrientedPoint {
57 /// position: Cartesian::from([1.0, -2.0]),
58 /// orientation: Angle::from(PI / 2.0),
59 /// };
60 /// let site_properties = Point::new(Cartesian::from([-1.0, 0.0]));
61 ///
62 /// let system_site = body_properties.transform(&site_properties);
63 /// assert_relative_eq!(system_site.position, [1.0, -3.0].into());
64 /// ```
65 #[inline]
66 fn transform(&self, site_properties: &Point<V>) -> Point<V> {
67 Point {
68 position: self.position + self.orientation.rotate(&site_properties.position),
69 }
70 }
71}
72
73/// Treat [`OrientedPoint`] sites as constituents of oriented rigid bodies.
74impl<V, R> Transform<OrientedPoint<V, R>> for OrientedPoint<V, R>
75where
76 V: Vector,
77 R: Rotate<V> + Rotation,
78{
79 /// Move [`Point`] properties from the local body frame to the system frame.
80 ///
81 /// ```math
82 /// \vec{r} = \vec{r}_\mathrm{body} + R_\mathrm{body}(\vec{r}_\mathrm{site})
83 /// ```
84 /// ```math
85 /// R = R_\mathrm{body}(R_\mathrm{site})
86 /// ```
87 ///
88 /// ```
89 /// use approxim::assert_relative_eq;
90 /// use hoomd_microstate::{Transform, property::OrientedPoint};
91 /// use hoomd_vector::{Angle, Cartesian};
92 /// use std::f64::consts::PI;
93 ///
94 /// let body_properties = OrientedPoint {
95 /// position: Cartesian::from([1.0, -2.0]),
96 /// orientation: Angle::from(PI / 2.0),
97 /// };
98 /// let site_properties = OrientedPoint {
99 /// position: Cartesian::from([-1.0, 0.0]),
100 /// orientation: Angle::from(PI / 4.0),
101 /// };
102 ///
103 /// let system_site = body_properties.transform(&site_properties);
104 /// assert_relative_eq!(system_site.position, [1.0, -3.0].into());
105 /// assert_relative_eq!(system_site.orientation.theta, 3.0 * PI / 4.0);
106 /// ```
107 #[inline]
108 fn transform(&self, site_properties: &OrientedPoint<V, R>) -> OrientedPoint<V, R> {
109 OrientedPoint {
110 position: self.position + self.orientation.rotate(&site_properties.position),
111 orientation: self.orientation.combine(&site_properties.orientation),
112 }
113 }
114}
115
116impl<P, R> Position for OrientedPoint<P, R> {
117 type Position = P;
118
119 #[inline]
120 fn position(&self) -> &P {
121 &self.position
122 }
123
124 #[inline]
125 fn position_mut(&mut self) -> &mut P {
126 &mut self.position
127 }
128}
129
130impl<V, R> Orientation for OrientedPoint<V, R> {
131 type Rotation = R;
132
133 #[inline]
134 fn orientation(&self) -> &R {
135 &self.orientation
136 }
137
138 #[inline]
139 fn orientation_mut(&mut self) -> &mut R {
140 &mut self.orientation
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147 use approxim::assert_relative_eq;
148 use std::f64::consts::PI;
149
150 use hoomd_vector::{Cartesian, Versor};
151
152 #[test]
153 fn transform_point() {
154 let body = OrientedPoint {
155 position: Cartesian::from([3.0, -4.0, 5.0]),
156 orientation: Versor::from_axis_angle(
157 [0.0, 1.0, 0.0]
158 .try_into()
159 .expect("hard-coded vector should be non-zero"),
160 -PI / 2.0,
161 ),
162 };
163
164 let site = Point::new(Cartesian::from([-1.0, 2.0, -3.0]));
165 let transformed_site = body.transform(&site);
166 assert_relative_eq!(transformed_site.position, [6.0, -2.0, 4.0].into());
167 }
168
169 #[test]
170 fn transform_oriented_point() {
171 let body = OrientedPoint {
172 position: Cartesian::from([3.0, -4.0, 5.0]),
173 orientation: Versor::from_axis_angle(
174 [0.0, 1.0, 0.0]
175 .try_into()
176 .expect("hard-coded vector should be non-zero"),
177 -PI / 2.0,
178 ),
179 };
180
181 let site = OrientedPoint {
182 position: Cartesian::from([-1.0, 2.0, -3.0]),
183 orientation: Versor::from_axis_angle(
184 [1.0, 0.0, 0.0]
185 .try_into()
186 .expect("hard-coded vector should be non-zero"),
187 PI / 2.0,
188 ),
189 };
190 let transformed_site = body.transform(&site);
191 assert_relative_eq!(transformed_site.position, [6.0, -2.0, 4.0].into());
192 assert_relative_eq!(
193 transformed_site.orientation.get(),
194 &[0.5, 0.5, -0.5, 0.5].into()
195 );
196 }
197}