Skip to content

Commit 67781fa

Browse files
authored
[RSDK-732] Create Quaternion and corresponding FFI namespace (#10)
1 parent 70891e8 commit 67781fa

File tree

7 files changed

+860
-48
lines changed

7 files changed

+860
-48
lines changed

src/ffi/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
pub mod dial_ffi;
2-
pub mod spatialmath;
2+
pub mod spatialmath;

src/ffi/spatialmath/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
pub mod vector3;
1+
pub mod vector3;
2+
pub mod quaternion;

src/ffi/spatialmath/quaternion.rs

Lines changed: 351 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,351 @@
1+
use ffi_helpers::null_pointer_check;
2+
use libc::c_double;
3+
4+
use crate::spatialmath::{quaternion::Quaternion, vector3::Vector3};
5+
6+
/// The FFI interface for Quaternion functions and initialization. All public
7+
/// functions are meant to be called externally from other languages. Quaternions
8+
/// use the Real-I-J-K standard, so quaternions in other standards should be
9+
/// converted in the native language before being used to initialize quaternions
10+
/// from this library
11+
12+
/// Allocates a copy of the quaternion to the heap with a stable memory address and
13+
/// returns the raw pointer (for use by the FFI interface)
14+
fn to_raw_pointer(quat: &Quaternion) -> *mut Quaternion {
15+
Box::into_raw(Box::new(*quat))
16+
}
17+
18+
/// Initialize a quaternion from raw components and retrieve the C pointer
19+
/// to its address.
20+
///
21+
/// # Safety
22+
///
23+
/// When finished with the underlying quaternion initialized by this function
24+
/// the caller must remember to free the quaternion memory using the
25+
/// free_quaternion_memory FFI function
26+
#[no_mangle]
27+
pub extern "C" fn new_quaternion(real: f64, i: f64, j: f64, k: f64) -> *mut Quaternion {
28+
to_raw_pointer(&Quaternion::new(real, i, j, k))
29+
}
30+
31+
/// Initialize a quaternion from a real part and a C pointer to a Vector3
32+
/// and retrieve the C pointer to its address.
33+
///
34+
/// # Safety
35+
///
36+
/// When finished with the underlying quaternion initialized by this function
37+
/// the caller must remember to free the quaternion memory using the
38+
/// free_quaternion_memory FFI function
39+
#[no_mangle]
40+
pub unsafe extern "C" fn new_quaternion_from_vector(
41+
real: f64, imag_ptr: *const Vector3
42+
) -> *mut Quaternion {
43+
null_pointer_check!(imag_ptr);
44+
to_raw_pointer(&Quaternion::new_from_vector(real, *imag_ptr))
45+
}
46+
47+
/// Free memory at the address of the quaternion pointer. Outer processes
48+
/// that work with Quaternions via the FFI interface MUST remember
49+
/// to call this function when finished with a quaternion
50+
///
51+
/// # Safety
52+
#[no_mangle]
53+
pub unsafe extern "C" fn free_quaternion_memory(ptr: *mut Quaternion) {
54+
if ptr.is_null() {
55+
return;
56+
}
57+
let _ = Box::from_raw(ptr);
58+
}
59+
60+
/// Free memory at the address of the euler angles pointer. Outer processes
61+
/// that work with euler angles returned by this interface MUST remember
62+
/// to call this function when finished with the list of doubles
63+
///
64+
/// # Safety
65+
#[no_mangle]
66+
pub unsafe extern "C" fn free_euler_angles_memory(ptr: *mut c_double) {
67+
if ptr.is_null() {
68+
return;
69+
}
70+
let _ = Box::from_raw(ptr);
71+
}
72+
73+
/// Get the components of a quaternion as a list of C doubles, the order of the
74+
/// components will be (real, i, j, k).
75+
///
76+
/// # Safety
77+
///
78+
/// When finished with the underlying quaternion passed to this function
79+
/// the caller must remember to free the quaternion memory using the
80+
/// free_quaternion_memory FFI function
81+
#[no_mangle]
82+
pub unsafe extern "C" fn quaternion_get_components(quat_ptr: *const Quaternion) -> *const c_double {
83+
null_pointer_check!(quat_ptr);
84+
let components: [c_double;4] = [(*quat_ptr).real, (*quat_ptr).i, (*quat_ptr).j, (*quat_ptr).k];
85+
Box::into_raw(Box::new(components)) as *const _
86+
}
87+
88+
/// Set the real component of an existing quaternion stored at the address
89+
/// of a pointer.
90+
///
91+
/// # Safety
92+
///
93+
/// When finished with the underlying quaternion passed to this function
94+
/// the caller must remember to free the quaternion memory using the
95+
/// free_quaternion_memory FFI function
96+
#[no_mangle]
97+
pub unsafe extern "C" fn quaternion_set_real(quat_ptr: *mut Quaternion, real: f64) {
98+
null_pointer_check!(quat_ptr);
99+
(*quat_ptr).real = real;
100+
}
101+
102+
/// Set the i component of an existing quaternion stored at the address
103+
/// of a pointer.
104+
///
105+
/// # Safety
106+
///
107+
/// When finished with the underlying quaternion passed to this function
108+
/// the caller must remember to free the quaternion memory using the
109+
/// free_quaternion_memory FFI function
110+
#[no_mangle]
111+
pub unsafe extern "C" fn quaternion_set_i(quat_ptr: *mut Quaternion, i: f64) {
112+
null_pointer_check!(quat_ptr);
113+
(*quat_ptr).i = i;
114+
}
115+
116+
/// Set the j component of an existing quaternion stored at the address
117+
/// of a pointer.
118+
///
119+
/// # Safety
120+
///
121+
/// When finished with the underlying quaternion passed to this function
122+
/// the caller must remember to free the quaternion memory using the
123+
/// free_quaternion_memory FFI function
124+
#[no_mangle]
125+
pub unsafe extern "C" fn quaternion_set_j(quat_ptr: *mut Quaternion, j: f64) {
126+
null_pointer_check!(quat_ptr);
127+
(*quat_ptr).j = j;
128+
}
129+
130+
/// Set the k component of an existing quaternion stored at the address
131+
/// of a pointer.
132+
///
133+
/// # Safety
134+
///
135+
/// When finished with the underlying quaternion passed to this function
136+
/// the caller must remember to free the quaternion memory using the
137+
/// free_quaternion_memory FFI function
138+
#[no_mangle]
139+
pub unsafe extern "C" fn quaternion_set_k(quat_ptr: *mut Quaternion, k: f64) {
140+
null_pointer_check!(quat_ptr);
141+
(*quat_ptr).k = k;
142+
}
143+
144+
/// Set all of the components of an existing quaternion stored at the address
145+
/// of a pointer
146+
///
147+
/// # Safety
148+
///
149+
/// When finished with the underlying quaternion passed to this function
150+
/// the caller must remember to free the quaternion memory using the
151+
/// free_quaternion_memory FFI function
152+
#[no_mangle]
153+
pub unsafe extern "C" fn quaternion_set_components(
154+
quat_ptr: *mut Quaternion, real: f64, i: f64, j: f64, k: f64
155+
) {
156+
null_pointer_check!(quat_ptr);
157+
(*quat_ptr).real = real;
158+
(*quat_ptr).i = i;
159+
(*quat_ptr).j = j;
160+
(*quat_ptr).k = k;
161+
}
162+
163+
/// Set the imaginary components of an existing quaternion stored at
164+
/// the address of a pointer (quat_ptr) from the components of a 3-vector
165+
/// (stored at vec_ptr). The convention is x -> i, y -> j, z -> k
166+
///
167+
/// # Safety
168+
///
169+
/// When finished with the underlying quaternion passed to this function
170+
/// the caller must remember to free the quaternion memory using the
171+
/// free_quaternion_memory FFI function (the same applies for the vector
172+
/// stored at vec_ptr)
173+
#[no_mangle]
174+
pub unsafe extern "C" fn quaternion_set_imag_from_vector(quat_ptr: *mut Quaternion, vec_ptr: *const Vector3) {
175+
null_pointer_check!(quat_ptr);
176+
null_pointer_check!(vec_ptr);
177+
(*quat_ptr).set_imag_from_vector(*vec_ptr);
178+
}
179+
180+
/// Copies the imaginary components to a 3-vector (using x -> i, y -> j
181+
/// z -> k) and returns a pointer to the memory address of the resulting
182+
/// vector
183+
///
184+
/// # Safety
185+
///
186+
/// When finished with the underlying quaternion initialized by this function
187+
/// the caller must remember to free the quaternion memory using the
188+
/// free_quaternion_memory FFI function
189+
#[no_mangle]
190+
pub unsafe extern "C" fn quaternion_get_imaginary_vector(quat_ptr: *const Quaternion) -> *mut Vector3 {
191+
null_pointer_check!(quat_ptr);
192+
let imag = (*quat_ptr).imag();
193+
imag.to_raw_pointer()
194+
}
195+
196+
/// Normalizes an existing quaternion stored at the address of
197+
/// a pointer (quat_ptr)
198+
///
199+
/// # Safety
200+
///
201+
/// When finished with the underlying quaternion passed to this function
202+
/// the caller must remember to free the quaternion memory using the
203+
/// free_quaternion_memory FFI function
204+
#[no_mangle]
205+
pub unsafe extern "C" fn normalize_quaternion(quat_ptr: *mut Quaternion) {
206+
null_pointer_check!(quat_ptr);
207+
(*quat_ptr).normalize()
208+
}
209+
210+
/// Initializes a normalized copy of a quaternion stored at the
211+
/// address of a pointer (quat_ptr) and returns a pointer to the
212+
/// memory of the result
213+
///
214+
/// # Safety
215+
///
216+
/// The caller must remember to free the quaternion memory of
217+
/// *both* the input and output quaternions when finished with them
218+
/// using the free_quaternion_memory FFI function
219+
#[no_mangle]
220+
pub unsafe extern "C" fn quaternion_get_normalized(quat_ptr: *const Quaternion) -> *mut Quaternion {
221+
null_pointer_check!(quat_ptr);
222+
to_raw_pointer(&(*quat_ptr).get_normalized())
223+
}
224+
225+
/// Converts from euler angles (in radians) to a quaternion. The euler angles are expected to
226+
/// be represented according to the Tait-Bryan formalism and applied in the Z-Y'-X"
227+
/// order (where Z -> yaw, Y -> pitch, X -> roll)
228+
///
229+
/// # Safety
230+
///
231+
/// When finished with the underlying quaternion initialized by this function
232+
/// the caller must remember to free the quaternion memory using the
233+
/// free_quaternion_memory FFI function
234+
#[no_mangle]
235+
pub unsafe extern "C" fn quaternion_from_euler_angles(roll: f64, pitch: f64, yaw: f64) -> *mut Quaternion {
236+
let quat = Quaternion::from_euler_angles(roll, pitch, yaw);
237+
to_raw_pointer(&quat)
238+
}
239+
240+
/// Converts a quaternion into euler angles (in radians). The euler angles are
241+
/// represented according to the Tait-Bryan formalism and applied
242+
/// in the Z-Y'-X" order (where Z -> yaw, Y -> pitch, X -> roll).
243+
/// The return value is a pointer to a list of [roll, pitch, yaw]
244+
/// as C doubles
245+
///
246+
/// # Safety
247+
///
248+
/// When finished with the underlying quaternion passed to this function
249+
/// the caller must remember to free the quaternion memory using the
250+
/// free_quaternion_memory FFI function and the euler angles memory using
251+
/// the free_euler_angles memory function
252+
#[no_mangle]
253+
pub unsafe extern "C" fn quaternion_to_euler_angles(quat_ptr: *const Quaternion) -> *mut c_double {
254+
null_pointer_check!(quat_ptr);
255+
let euler_angles = (*quat_ptr).to_euler_angles();
256+
Box::into_raw(Box::new(euler_angles)) as *mut _
257+
}
258+
259+
/// Scales an existing quaternion stored at the address of
260+
/// a pointer (quat_ptr) by a factor (float)
261+
///
262+
/// # Safety
263+
///
264+
/// When finished with the underlying quaternion passed to this function
265+
/// the caller must remember to free the quaternion memory using the
266+
/// free_quaternion_memory FFI function
267+
#[no_mangle]
268+
pub unsafe extern "C" fn scale_quaternion(quat_ptr: *mut Quaternion, factor: f64) {
269+
null_pointer_check!(quat_ptr);
270+
(*quat_ptr).scale(factor);
271+
}
272+
273+
/// Initializes a copy of the quaternion stored at the address of a pointer (quat_ptr)
274+
/// scaled by a factor (float) and returns a pointer to the memory of the result
275+
///
276+
/// # Safety
277+
///
278+
/// The caller must remember to free the quaternion memory of
279+
/// *both* the input and output quaternions when finished with them
280+
/// using the free_quaternion_memory FFI function
281+
#[no_mangle]
282+
pub unsafe extern "C" fn quaternion_get_scaled(quat_ptr: *const Quaternion, factor: f64) -> *mut Quaternion {
283+
null_pointer_check!(quat_ptr);
284+
to_raw_pointer(&(*quat_ptr).get_scaled(factor))
285+
}
286+
287+
/// Initializes a quaternion that is the conjugate of one stored
288+
/// at the address of a pointer (quat_ptr)and returns a pointer
289+
/// to the memory of the result
290+
///
291+
/// # Safety
292+
///
293+
/// The caller must remember to free the quaternion memory of
294+
/// *both* the input and output quaternions when finished with them
295+
/// using the free_quaternion_memory FFI function
296+
#[no_mangle]
297+
pub unsafe extern "C" fn quaternion_get_conjugate(quat_ptr: *const Quaternion) -> *mut Quaternion {
298+
null_pointer_check!(quat_ptr);
299+
to_raw_pointer(&(*quat_ptr).conjugate())
300+
}
301+
302+
/// Adds two quaternions and returns a pointer to the
303+
/// memory of the result
304+
///
305+
/// # Safety
306+
///
307+
/// The caller must remember to free the quaternion memory of *both* the input and
308+
/// output quaternions when finished with them using the free_quaternion_memory FFI function
309+
#[no_mangle]
310+
pub unsafe extern "C" fn quaternion_add(
311+
quat_ptr_1: *const Quaternion,
312+
quat_ptr_2: *const Quaternion,
313+
) -> *mut Quaternion {
314+
null_pointer_check!(quat_ptr_1);
315+
null_pointer_check!(quat_ptr_2);
316+
to_raw_pointer(&((*quat_ptr_1) + (*quat_ptr_2)))
317+
}
318+
319+
/// Subtracts two quaternions and returns a pointer to the
320+
/// memory of the result
321+
///
322+
/// # Safety
323+
///
324+
/// The caller must remember to free the quaternion memory of *both* the input and
325+
/// output quaternions when finished with them using the free_quaternion_memory FFI function
326+
#[no_mangle]
327+
pub unsafe extern "C" fn quaternion_subtract(
328+
quat_ptr_1: *const Quaternion,
329+
quat_ptr_2: *const Quaternion,
330+
) -> *mut Quaternion {
331+
null_pointer_check!(quat_ptr_1);
332+
null_pointer_check!(quat_ptr_2);
333+
to_raw_pointer(&((*quat_ptr_1) - (*quat_ptr_2)))
334+
}
335+
336+
/// Computes the Hamiltonian product of two quaternions and
337+
/// returns a pointer to the memory of the result
338+
///
339+
/// # Safety
340+
///
341+
/// The caller must remember to free the quaternion memory of *both* the input and
342+
/// output quaternions when finished with them using the free_quaternion_memory FFI function
343+
#[no_mangle]
344+
pub unsafe extern "C" fn quaternion_hamiltonian_product(
345+
quat_ptr_1: *const Quaternion,
346+
quat_ptr_2: *const Quaternion,
347+
) -> *mut Quaternion {
348+
null_pointer_check!(quat_ptr_1);
349+
null_pointer_check!(quat_ptr_2);
350+
to_raw_pointer(&((*quat_ptr_1) * (*quat_ptr_2)))
351+
}

0 commit comments

Comments
 (0)