forked from RobotLocomotion/drake
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathunit_vector.cc
87 lines (80 loc) · 3.82 KB
/
unit_vector.cc
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
#include "drake/math/unit_vector.h"
#include <cmath>
#include <stdexcept>
#include "drake/common/default_scalars.h"
#include "drake/common/fmt_eigen.h"
#include "drake/common/unused.h"
#include "drake/math/autodiff.h"
namespace drake {
namespace math {
namespace internal {
template <typename T>
Vector3<T> NormalizeOrThrow(const Vector3<T>& v,
std::string_view function_name) {
DRAKE_DEMAND(!function_name.empty());
const T norm = v.norm();
if constexpr (scalar_predicate<T>::is_bool) {
// Throw an exception if norm is non-finite (NaN or infinity) or too small.
// The threshold for "too small" is a heuristic (rule of thumb) guided by an
// expected small physical dimensions in a robotic systems. Numbers smaller
// than this are probably user or developer errors.
constexpr double kMinMagnitude = 1e-10;
using std::isfinite;
if (!(isfinite(norm) && norm >= kMinMagnitude)) {
throw std::logic_error(fmt::format(
"{}() cannot normalize the given vector v.\n"
" v = {}\n"
" |v| = {}\n"
" The measures must be finite and the vector must have a magnitude of"
" at least {} to normalize. If you are confident that v's direction"
" is meaningful, pass v.normalized() instead of v.",
function_name, fmt_eigen(DiscardGradient(v).transpose()),
ExtractDoubleOrThrow(norm), kMinMagnitude));
}
}
return v / norm;
}
template <typename T>
void ThrowIfNotUnitVector(const Vector3<T>& unit_vector,
std::string_view function_name,
const double tolerance_unit_vector_norm) {
DRAKE_DEMAND(!function_name.empty());
if constexpr (scalar_predicate<T>::is_bool) {
// A test that a unit vector's magnitude is within a very small ε of 1 is
// |√(𝐯⋅𝐯) − 1| ≤ ε. To avoid an unnecessary square-root, notice that this
// simple test is equivalent to ±(√(𝐯⋅𝐯) - 1) ≤ ε which means that both
// √(𝐯⋅𝐯) - 1 ≤ ε and √(𝐯⋅𝐯) - 1 ≥ -ε, or
// √(𝐯⋅𝐯) ≤ 1 + ε and √(𝐯⋅𝐯) ≥ 1 - ε. Squaring these two equations give
// 𝐯⋅𝐯 ≤ (1 + ε)² and 𝐯⋅𝐯 ≥ (1 - ε)². Distributing the square results in
// 𝐯⋅𝐯 ≤ 1 + 2 ε + ε² and 𝐯⋅𝐯 ≥ 1 - 2 ε + ε². Since ε² ≪ 2 ε, this gives
// 𝐯⋅𝐯 - 1 ≤ 2 ε and 𝐯⋅𝐯 - 1 ≥ -2 ε or |𝐯⋅𝐯 − 1| ≤ 2 ε
// -------------------------------------------------------------
// Hence the following test with norm() that uses an extra √, e.g., as
// sqrt(squaredNorm()), is replaced by one that only uses squaredNorm().
// is_ok_unit_vector =
// (abs(unit_vector.norm() - 1) <= tolerance_unit_vector_norm;
// -------------------------------------------------------------
const double tolerance2 = 2 * tolerance_unit_vector_norm;
const double uvec_squared = DiscardGradient(unit_vector).squaredNorm();
const bool is_ok_unit_vector = std::isfinite(uvec_squared) &&
std::abs(uvec_squared - 1.0) <= tolerance2;
if (!is_ok_unit_vector) {
using std::abs;
const T norm = unit_vector.norm();
const T norm_diff = abs(1.0 - norm);
throw std::logic_error(fmt::format(
"{}(): The unit_vector argument {} is not a unit vector.\n"
"|unit_vector| = {}\n"
"||unit_vector| - 1| = {} is greater than {}.",
function_name, fmt_eigen(unit_vector.transpose()), norm, norm_diff,
tolerance_unit_vector_norm));
}
} else {
unused(unit_vector, tolerance_unit_vector_norm);
}
}
DRAKE_DEFINE_FUNCTION_TEMPLATE_INSTANTIATIONS_ON_DEFAULT_SCALARS(
(&NormalizeOrThrow<T>, &ThrowIfNotUnitVector<T>));
} // namespace internal
} // namespace math
} // namespace drake