forked from RobotLocomotion/drake
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbspline_basis.cc
154 lines (137 loc) · 5.54 KB
/
bspline_basis.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
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
#include "drake/math/bspline_basis.h"
#include <algorithm>
#include <functional>
#include <set>
#include <utility>
#include <fmt/format.h>
#include "drake/common/default_scalars.h"
namespace drake {
namespace math {
namespace {
template <typename T>
std::vector<T> MakeKnotVector(int order, int num_basis_functions,
KnotVectorType type,
const T& initial_parameter_value,
const T& final_parameter_value) {
if (num_basis_functions < order) {
throw std::invalid_argument(fmt::format(
"The number of basis functions ({}) should be greater than or "
"equal to the order ({}).",
num_basis_functions, order));
}
DRAKE_DEMAND(initial_parameter_value <= final_parameter_value);
const int num_knots{num_basis_functions + order};
std::vector<T> knots(num_knots);
const T knot_interval = (final_parameter_value - initial_parameter_value) /
(num_basis_functions - order + 1.0);
for (int i = 0; i < num_knots; ++i) {
if (i < order && type == KnotVectorType::kClampedUniform) {
knots.at(i) = initial_parameter_value;
} else if (i >= num_basis_functions &&
type == KnotVectorType::kClampedUniform) {
knots.at(i) = final_parameter_value;
} else {
knots.at(i) = initial_parameter_value + knot_interval * (i - (order - 1));
}
}
return knots;
}
// This custom comparator is needed to explicitly convert Formula to bool when
// T == Expression.
template <typename T>
bool less_than_with_cast(const T& val, const T& other) {
return static_cast<bool>(val < other);
}
} // namespace
template <typename T>
BsplineBasis<T>::BsplineBasis(int order, std::vector<T> knots)
: order_(order),
knots_(std::move(knots)) {
if (static_cast<int>(knots_.size()) < 2 * order) {
throw std::invalid_argument(
fmt::format("The number of knots ({}) should be greater than or "
"equal to twice the order ({}).",
knots_.size(), 2 * order));
}
DRAKE_ASSERT(CheckInvariants());
}
template <typename T>
BsplineBasis<T>::BsplineBasis(int order, int num_basis_functions,
KnotVectorType type,
const T& initial_parameter_value,
const T& final_parameter_value)
: BsplineBasis<T>(order, MakeKnotVector<T>(order, num_basis_functions, type,
initial_parameter_value,
final_parameter_value)) {}
template <typename T>
std::vector<int> BsplineBasis<T>::ComputeActiveBasisFunctionIndices(
const std::array<T, 2>& parameter_interval) const {
DRAKE_ASSERT(parameter_interval[0] <= parameter_interval[1]);
DRAKE_ASSERT(parameter_interval[0] >= initial_parameter_value());
DRAKE_ASSERT(parameter_interval[1] <= final_parameter_value());
const int first_active_index =
FindContainingInterval(parameter_interval[0]) - order() + 1;
const int final_active_index = FindContainingInterval(parameter_interval[1]);
std::vector<int> active_control_point_indices{};
active_control_point_indices.reserve(final_active_index - first_active_index);
for (int i = first_active_index; i <= final_active_index; ++i) {
active_control_point_indices.push_back(i);
}
return active_control_point_indices;
}
template <typename T>
std::vector<int> BsplineBasis<T>::ComputeActiveBasisFunctionIndices(
const T& parameter_value) const {
return ComputeActiveBasisFunctionIndices(
{{parameter_value, parameter_value}});
}
template <typename T>
T BsplineBasis<T>::EvaluateBasisFunctionI(int index,
const T& parameter_value) const {
std::vector<T> delta(num_basis_functions(), 0.0);
delta[index] = 1.0;
return EvaluateCurve(delta, parameter_value);
}
template <typename T>
int BsplineBasis<T>::FindContainingInterval(const T& parameter_value) const {
DRAKE_ASSERT(parameter_value >= initial_parameter_value());
DRAKE_ASSERT(parameter_value <= final_parameter_value());
const std::vector<T>& t = knots();
const T& t_bar = parameter_value;
return std::distance(
t.begin(), std::prev(t_bar < final_parameter_value()
? std::upper_bound(t.begin(), t.end(), t_bar,
less_than_with_cast<T>)
: std::lower_bound(t.begin(), t.end(), t_bar,
less_than_with_cast<T>)));
}
template <typename T>
boolean<T> BsplineBasis<T>::operator==(const BsplineBasis<T>& other) const {
if (this->order() == other.order() &&
this->num_basis_functions() == other.num_basis_functions()) {
boolean<T> result{true};
const int num_knots{num_basis_functions() + order()};
for (int i = 0; i < num_knots; ++i) {
result = result && (this->knots()[i] == other.knots()[i]);
if (std::equal_to<boolean<T>>{}(result, boolean<T>{false})) {
break;
}
}
return result;
} else {
return boolean<T>{false};
}
}
template <typename T>
boolean<T> BsplineBasis<T>::operator!=(const BsplineBasis<T>& other) const {
return !this->operator==(other);
}
template<typename T>
bool BsplineBasis<T>::CheckInvariants() const {
return std::is_sorted(knots_.begin(), knots_.end(), less_than_with_cast<T>) &&
static_cast<int>(knots_.size()) >= 2 * order_;
}
} // namespace math
} // namespace drake
DRAKE_DEFINE_CLASS_TEMPLATE_INSTANTIATIONS_ON_DEFAULT_SCALARS(
class ::drake::math::BsplineBasis)