-
-
Notifications
You must be signed in to change notification settings - Fork 4.5k
/
Copy pathvectors_3d.c
265 lines (234 loc) · 6.33 KB
/
vectors_3d.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
/**
* @file
* @brief Functions related to 3D vector operations.
* @author Krishna Vedala
*/
#include <stdio.h>
#ifdef __arm__ // if compiling for ARM-Cortex processors
#define LIBQUAT_ARM
#include <arm_math.h>
#else
#include <math.h>
#endif
#include <assert.h>
#include "geometry_datatypes.h"
/**
* @addtogroup vec_3d 3D Vector operations
* @{
*/
/**
* Subtract one vector from another. @f[
* \vec{c}=\vec{a}-\vec{b}=\left(a_x-b_x\right)\hat{i}+
* \left(a_y-b_y\right)\hat{j}+\left(a_z-b_z\right)\hat{k}@f]
* @param[in] a vector to subtract from
* @param[in] b vector to subtract
* @returns resultant vector
*/
vec_3d vector_sub(const vec_3d *a, const vec_3d *b)
{
vec_3d out;
#ifdef LIBQUAT_ARM
arm_sub_f32((float *)a, (float *)b, (float *)&out);
#else
out.x = a->x - b->x;
out.y = a->y - b->y;
out.z = a->z - b->z;
#endif
return out;
}
/**
* Add one vector to another. @f[
* \vec{c}=\vec{a}+\vec{b}=\left(a_x+b_x\right)\hat{i}+
* \left(a_y+b_y\right)\hat{j}+\left(a_z+b_z\right)\hat{k}@f]
* @param[in] a vector to add to
* @param[in] b vector to add
* @returns resultant vector
*/
vec_3d vector_add(const vec_3d *a, const vec_3d *b)
{
vec_3d out;
#ifdef LIBQUAT_ARM
arm_add_f32((float *)a, (float *)b, (float *)&out);
#else
out.x = a->x + b->x;
out.y = a->y + b->y;
out.z = a->z + b->z;
#endif
return out;
}
/**
* Obtain the dot product of two 3D vectors.
* @f[
* \vec{a}\cdot\vec{b}=a_xb_x + a_yb_y + a_zb_z
* @f]
* @param[in] a first vector
* @param[in] b second vector
* @returns resulting dot product
*/
float dot_prod(const vec_3d *a, const vec_3d *b)
{
float dot;
#ifdef LIBQUAT_ARM
arm_dot_prod_f32((float *)a, (float *)b, &dot);
#else
dot = a->x * b->x;
dot += a->y * b->y;
dot += a->z * b->z;
#endif
return dot;
}
/**
* Compute the vector product of two 3d vectors.
* @f[\begin{align*}
* \vec{a}\times\vec{b} &= \begin{vmatrix}
* \hat{i} & \hat{j} & \hat{k}\\
* a_x & a_y & a_z\\
* b_x & b_y & b_z
* \end{vmatrix}\\
* &= \left(a_yb_z-b_ya_z\right)\hat{i} - \left(a_xb_z-b_xa_z\right)\hat{j}
* + \left(a_xb_y-b_xa_y\right)\hat{k} \end{align*}
* @f]
* @param[in] a first vector @f$\vec{a}@f$
* @param[in] b second vector @f$\vec{b}@f$
* @returns resultant vector @f$\vec{o}=\vec{a}\times\vec{b}@f$
*/
vec_3d vector_prod(const vec_3d *a, const vec_3d *b)
{
vec_3d out; // better this way to avoid copying results to input
// vectors themselves
out.x = a->y * b->z - a->z * b->y;
out.y = -a->x * b->z + a->z * b->x;
out.z = a->x * b->y - a->y * b->x;
return out;
}
/**
* Print formatted vector on stdout.
* @param[in] a vector to print
* @param[in] name name of the vector
* @returns string representation of vector
*/
const char *print_vector(const vec_3d *a, const char *name)
{
static char vec_str[100]; // static to ensure the string life extends the
// life of function
snprintf(vec_str, 99, "vec(%s) = (%.3g)i + (%.3g)j + (%.3g)k\n", name, a->x,
a->y, a->z);
return vec_str;
}
/**
* Compute the norm a vector.
* @f[\lVert\vec{a}\rVert = \sqrt{\vec{a}\cdot\vec{a}} @f]
* @param[in] a input vector
* @returns norm of the given vector
*/
float vector_norm(const vec_3d *a)
{
float n = dot_prod(a, a);
#ifdef LIBQUAT_ARM
arm_sqrt_f32(*n, n);
#else
n = sqrtf(n);
#endif
return n;
}
/**
* Obtain unit vector in the same direction as given vector.
* @f[\hat{a}=\frac{\vec{a}}{\lVert\vec{a}\rVert}@f]
* @param[in] a input vector
* @returns n unit vector in the direction of @f$\vec{a}@f$
*/
vec_3d unit_vec(const vec_3d *a)
{
vec_3d n = {0};
float norm = vector_norm(a);
if (fabsf(norm) < EPSILON)
{ // detect possible divide by 0
return n;
}
if (norm != 1.F) // perform division only if needed
{
n.x = a->x / norm;
n.y = a->y / norm;
n.z = a->z / norm;
}
return n;
}
/**
* The cross product of vectors can be represented as a matrix
* multiplication operation. This function obtains the `3x3` matrix
* of the cross-product operator from the first vector.
* @f[\begin{align*}
* \left(\vec{a}\times\right)\vec{b} &= \tilde{A}_a\vec{b}\\
* \tilde{A}_a &=
* \begin{bmatrix}0&-a_z&a_y\\a_z&0&-a_x\\-a_y&a_x&0\end{bmatrix}
* \end{align*}@f]
* @param[in] a input vector
* @returns the `3x3` matrix for the cross product operator
* @f$\left(\vec{a}\times\right)@f$
*/
mat_3x3 get_cross_matrix(const vec_3d *a)
{
mat_3x3 A = {0., -a->z, a->y, a->z, 0., -a->x, -a->y, a->x, 0.};
return A;
}
/**
* Obtain the angle between two given vectors.
* @f[\alpha=acos\left(\frac{\vec{a} \cdot \vec{b}}{\lVert\vec{a}\rVert \cdot \lVert\vec{b}\rVert}\right)@f]
* @param[in] a first input vector
* @param[in] b second input vector
* @returns angle between @f$\vec{a}@f$ and @f$\vec{b}@f$ in radians
*/
double get_angle(const vec_3d *a, const vec_3d *b)
{
double alpha, cos_alpha;
float norm_a = vector_norm(a); ///< The norm of vector a
float norm_b = vector_norm(b); ///< The norm of vector b
if (fabsf(norm_a) < EPSILON || fabsf(norm_b) < EPSILON) /// detect possible division by 0 - the angle is not defined in this case
{
return NAN;
}
cos_alpha = dot_prod(a, b) / (norm_a * norm_b);
alpha = acos(cos_alpha); // delivers the radian
return alpha; // in range from -1 to 1
}
/** @} */
/**
* @brief Testing function
* @returns `void`
*/
static void test()
{
vec_3d a = {1., 2., 3.};
vec_3d b = {1., 1., 1.};
float d;
// printf("%s", print_vector(&a, "a"));
// printf("%s", print_vector(&b, "b"));
d = vector_norm(&a);
// printf("|a| = %.4g\n", d);
assert(fabsf(d - 3.742f) < 0.01);
d = vector_norm(&b);
// printf("|b| = %.4g\n", d);
assert(fabsf(d - 1.732f) < 0.01);
d = dot_prod(&a, &b);
// printf("Dot product: %f\n", d);
assert(fabsf(d - 6.f) < 0.01);
vec_3d c = vector_prod(&a, &b);
// printf("Vector product ");
// printf("%s", print_vector(&c, "c"));
assert(fabsf(c.x - (-1.f)) < 0.01);
assert(fabsf(c.y - (2.f)) < 0.01);
assert(fabsf(c.z - (-1.f)) < 0.01);
double alpha = get_angle(&a, &b);
// printf("The angle is %f\n", alpha);
assert(fabsf(alpha - 0.387597) < 0.01);
}
/**
* @brief Main function
*
* @return 0 on exit
*/
int main(void)
{
test();
return 0;
}