21 if (points_num < order) {
29 return (!cyclic || points_num % (order - 1) == 0);
50 return points_num + order * 2 - 1;
52 return points_num + order;
67 const int repeat_inner = is_bezier ? order - 1 : 1;
69 const int head = is_end_point ? (order - (cyclic ? 1 : 0)) :
70 (is_bezier ?
min_ii(2, repeat_inner) : 1);
73 const int tail = cyclic ? 2 * order - 1 : (is_end_point ? order : 0);
78 const int offset = is_end_point && cyclic ? 1 : 0;
84 for (
const int i :
IndexRange(offset, knots.
size() - offset - tail)) {
93 const int tail_index = knots.
size() - tail;
95 knots[tail_index + i] = current + (knots[i] - knots[0]);
100 const int points_num,
106 const int order = degree + 1;
110 for (
const int i :
IndexRange(points_num + degree)) {
111 const bool knots_equal = knots[i] == knots[i + 1];
112 if (knots_equal || parameter < knots[i] || parameter > knots[i + 1]) {
116 start = std::max(i - degree, 0);
123 buffer[end - start] = 1.0f;
125 for (
const int i_order :
IndexRange(2, degree)) {
126 if (end + i_order >= knots.
size()) {
127 end = points_num + degree - i_order;
129 for (
const int i :
IndexRange(end - start + 1)) {
130 const int knot_index = start + i;
132 float new_basis = 0.0f;
133 if (buffer[i] != 0.0f) {
134 new_basis += ((parameter - knots[knot_index]) * buffer[i]) /
135 (knots[knot_index + i_order - 1] - knots[knot_index]);
138 if (buffer[i + 1] != 0.0f) {
139 new_basis += ((knots[knot_index + i_order] - parameter) * buffer[i + 1]) /
140 (knots[knot_index + i_order] - knots[knot_index + 1]);
143 buffer[i] = new_basis;
149 r_start_index = start;
153 const int evaluated_num,
161 const int8_t degree = order - 1;
166 if (evaluated_num == 0) {
173 const int last_control_point_index = cyclic ? points_num + degree : points_num;
174 const int evaluated_segment_num =
segments_num(evaluated_num, cyclic);
176 const float start = knots[degree];
177 const float end = knots[last_control_point_index];
178 const float step = (end - start) / evaluated_segment_num;
179 for (
const int i :
IndexRange(evaluated_num)) {
181 const float parameter = std::clamp(start + step * i, knots[0], knots[points_num + degree]);
186 parameter, last_control_point_index, degree, knots, point_weights, basis_start_indices[i]);
199 for (const int i : range) {
200 Span<float> point_weights = basis_cache.weights.as_span().slice(i * order, order);
201 for (const int j : point_weights.index_range()) {
202 const int point_index = (basis_cache.start_indices[i] + j) % src.size();
203 mixer.mix_in(i, src[point_index], point_weights[j]);
206 mixer.finalize(range);
220 for (const int i : range) {
221 Span<float> point_weights = basis_cache.weights.as_span().slice(i * order, order);
223 for (const int j : point_weights.index_range()) {
224 const int point_index = (basis_cache.start_indices[i] + j) % src.size();
225 const float weight = point_weights[j] * control_weights[point_index];
226 mixer.mix_in(i, src[point_index], weight);
229 mixer.finalize(range);
245 attribute_math::convert_to_static_type(src.
type(), [&](
auto dummy) {
246 using T = decltype(dummy);
247 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
248 if (control_weights.is_empty()) {
249 interpolate_to_evaluated(basis_cache, order, src.typed<T>(), dst.typed<T>());
252 interpolate_to_evaluated_rational(
253 basis_cache, order, control_weights, src.typed<T>(), dst.typed<T>());
Low-level operations for curves.
MINLINE int min_ii(int a, int b)
#define UNUSED_VARS_NDEBUG(...)
@ NURBS_KNOT_MODE_ENDPOINT
@ NURBS_KNOT_MODE_ENDPOINT_BEZIER
Span< T > as_span() const
MutableSpan< T > as_mutable_span()
void copy_from(GSpan values)
const CPPType & type() const
constexpr int64_t size() const
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
constexpr IndexRange index_range() const
constexpr void copy_from(Span< T > values) const
constexpr int64_t size() const
void resize(const int64_t new_size)
typename DefaultMixerStruct< T >::type DefaultMixer
int calculate_evaluated_num(int points_num, int8_t order, bool cyclic, int resolution, KnotsMode knots_mode)
bool check_valid_num_and_order(int points_num, int8_t order, bool cyclic, KnotsMode knots_mode)
void calculate_knots(int points_num, KnotsMode mode, int8_t order, bool cyclic, MutableSpan< float > knots)
void calculate_basis_cache(int points_num, int evaluated_num, int8_t order, bool cyclic, Span< float > knots, BasisCache &basis_cache)
static void calculate_basis_for_point(const float parameter, const int points_num, const int degree, const Span< float > knots, MutableSpan< float > r_weights, int &r_start_index)
int knots_num(int points_num, int8_t order, bool cyclic)
static void interpolate_to_evaluated_rational(const BasisCache &basis_cache, const int8_t order, const Span< float > control_weights, const Span< T > src, MutableSpan< T > dst)
void interpolate_to_evaluated(const BasisCache &basis_cache, int8_t order, Span< float > control_weights, GSpan src, GMutableSpan dst)
int segments_num(const int points_num, const bool cyclic)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Vector< int > start_indices