Blender  V3.3
curves_geometry.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
7 #include <mutex>
8 #include <utility>
9 
10 #include "MEM_guardedalloc.h"
11 
12 #include "BLI_bounds.hh"
13 #include "BLI_index_mask_ops.hh"
15 #include "BLI_math_rotation.hh"
16 
17 #include "DNA_curves_types.h"
18 
19 #include "BKE_attribute_math.hh"
20 #include "BKE_curves.hh"
21 #include "BKE_curves_utils.hh"
22 
23 namespace blender::bke {
24 
25 static const std::string ATTR_POSITION = "position";
26 static const std::string ATTR_RADIUS = "radius";
27 static const std::string ATTR_TILT = "tilt";
28 static const std::string ATTR_CURVE_TYPE = "curve_type";
29 static const std::string ATTR_CYCLIC = "cyclic";
30 static const std::string ATTR_RESOLUTION = "resolution";
31 static const std::string ATTR_NORMAL_MODE = "normal_mode";
32 static const std::string ATTR_HANDLE_TYPE_LEFT = "handle_type_left";
33 static const std::string ATTR_HANDLE_TYPE_RIGHT = "handle_type_right";
34 static const std::string ATTR_HANDLE_POSITION_LEFT = "handle_left";
35 static const std::string ATTR_HANDLE_POSITION_RIGHT = "handle_right";
36 static const std::string ATTR_NURBS_ORDER = "nurbs_order";
37 static const std::string ATTR_NURBS_WEIGHT = "nurbs_weight";
38 static const std::string ATTR_NURBS_KNOTS_MODE = "knots_mode";
39 static const std::string ATTR_SELECTION_POINT_FLOAT = ".selection_point_float";
40 static const std::string ATTR_SELECTION_CURVE_FLOAT = ".selection_curve_float";
41 static const std::string ATTR_SURFACE_UV_COORDINATE = "surface_uv_coordinate";
42 
43 /* -------------------------------------------------------------------- */
48 {
49 }
50 
51 CurvesGeometry::CurvesGeometry(const int point_num, const int curve_num)
52 {
53  this->point_num = point_num;
54  this->curve_num = curve_num;
57 
60  CD_DEFAULT,
61  nullptr,
62  this->point_num,
63  ATTR_POSITION.c_str());
64 
65  this->curve_offsets = (int *)MEM_malloc_arrayN(this->curve_num + 1, sizeof(int), __func__);
66 #ifdef DEBUG
67  this->offsets_for_write().fill(-1);
68 #endif
69  this->offsets_for_write().first() = 0;
70 
71  this->runtime = MEM_new<CurvesGeometryRuntime>(__func__);
72  /* Fill the type counts with the default so they're in a valid state. */
73  this->runtime->type_counts[CURVE_TYPE_CATMULL_ROM] = curve_num;
74 }
75 
80 {
83  dst.point_num = src.point_num;
84  dst.curve_num = src.curve_num;
87 
89  dst.curve_offsets = (int *)MEM_malloc_arrayN(dst.point_num + 1, sizeof(int), __func__);
90  dst.offsets_for_write().copy_from(src.offsets());
91 
93 
94  /* Though type counts are a cache, they must be copied because they are calculated eagerly. */
95  dst.runtime->type_counts = src.runtime->type_counts;
96 }
97 
99  : CurvesGeometry(other.point_num, other.curve_num)
100 {
101  copy_curves_geometry(*this, other);
102 }
103 
105 {
106  if (this != &other) {
107  copy_curves_geometry(*this, other);
108  }
109  return *this;
110 }
111 
112 /* The source should be empty, but in a valid state so that using it further will work. */
114 {
115  dst.point_num = src.point_num;
116  std::swap(dst.point_data, src.point_data);
117  CustomData_free(&src.point_data, src.point_num);
118  src.point_num = 0;
119 
120  dst.curve_num = src.curve_num;
121  std::swap(dst.curve_data, src.curve_data);
122  CustomData_free(&src.curve_data, src.curve_num);
123  src.curve_num = 0;
124 
125  std::swap(dst.curve_offsets, src.curve_offsets);
126  MEM_SAFE_FREE(src.curve_offsets);
127 
128  std::swap(dst.runtime, src.runtime);
129 }
130 
132  : CurvesGeometry(other.point_num, other.curve_num)
133 {
134  move_curves_geometry(*this, other);
135 }
136 
138 {
139  if (this != &other) {
140  move_curves_geometry(*this, other);
141  }
142  return *this;
143 }
144 
146 {
147  CustomData_free(&this->point_data, this->point_num);
148  CustomData_free(&this->curve_data, this->curve_num);
150  MEM_delete(this->runtime);
151  this->runtime = nullptr;
152 }
153 
156 /* -------------------------------------------------------------------- */
160 static int domain_num(const CurvesGeometry &curves, const eAttrDomain domain)
161 {
162  return domain == ATTR_DOMAIN_POINT ? curves.points_num() : curves.curves_num();
163 }
164 
166 {
167  return domain == ATTR_DOMAIN_POINT ? curves.point_data : curves.curve_data;
168 }
169 
170 static const CustomData &domain_custom_data(const CurvesGeometry &curves, const eAttrDomain domain)
171 {
172  return domain == ATTR_DOMAIN_POINT ? curves.point_data : curves.curve_data;
173 }
174 
175 template<typename T>
177  const eAttrDomain domain,
178  const StringRefNull name,
179  const T default_value)
180 {
181  const int num = domain_num(curves, domain);
182  const eCustomDataType type = cpp_type_to_custom_data_type(CPPType::get<T>());
183  const CustomData &custom_data = domain_custom_data(curves, domain);
184 
185  const T *data = (const T *)CustomData_get_layer_named(&custom_data, type, name.c_str());
186  if (data != nullptr) {
187  return VArray<T>::ForSpan(Span<T>(data, num));
188  }
189  return VArray<T>::ForSingle(default_value, num);
190 }
191 
192 template<typename T>
194  const eAttrDomain domain,
195  const StringRefNull name)
196 {
197  const int num = domain_num(curves, domain);
198  const CustomData &custom_data = domain_custom_data(curves, domain);
199  const eCustomDataType type = cpp_type_to_custom_data_type(CPPType::get<T>());
200 
201  T *data = (T *)CustomData_get_layer_named(&custom_data, type, name.c_str());
202  if (data == nullptr) {
203  return {};
204  }
205  return {data, num};
206 }
207 
208 template<typename T>
210  const eAttrDomain domain,
211  const StringRefNull name,
212  const T default_value = T())
213 {
214  const int num = domain_num(curves, domain);
215  const eCustomDataType type = cpp_type_to_custom_data_type(CPPType::get<T>());
216  CustomData &custom_data = domain_custom_data(curves, domain);
217 
219  &custom_data, type, name.c_str(), num);
220  if (data != nullptr) {
221  return {data, num};
222  }
224  &custom_data, type, CD_CALLOC, nullptr, num, name.c_str());
225  MutableSpan<T> span = {data, num};
226  if (num > 0 && span.first() != default_value) {
227  span.fill(default_value);
228  }
229  return span;
230 }
231 
233 {
234  return get_varray_attribute<int8_t>(
236 }
237 
239 {
240  return get_mutable_attribute<int8_t>(*this, ATTR_DOMAIN_CURVE, ATTR_CURVE_TYPE);
241 }
242 
244 {
245  this->curve_types_for_write().fill(type);
246  this->runtime->type_counts.fill(0);
247  this->runtime->type_counts[type] = this->curves_num();
248  this->tag_topology_changed();
249 }
250 
252 {
253  if (selection.size() == this->curves_num()) {
254  this->fill_curve_types(type);
255  return;
256  }
257  /* A potential performance optimization is only counting the changed indices. */
258  this->curve_types_for_write().fill_indices(selection, type);
259  this->update_curve_types();
260  this->tag_topology_changed();
261 }
262 
263 std::array<int, CURVE_TYPES_NUM> calculate_type_counts(const VArray<int8_t> &types)
264 {
265  using CountsType = std::array<int, CURVE_TYPES_NUM>;
266  CountsType counts;
267  counts.fill(0);
268 
269  if (types.is_single()) {
270  counts[types.get_internal_single()] = types.size();
271  return counts;
272  }
273 
274  Span<int8_t> types_span = types.get_internal_span();
276  types.index_range(),
277  2048,
278  counts,
279  [&](const IndexRange curves_range, const CountsType &init) {
280  CountsType result = init;
281  for (const int curve_index : curves_range) {
282  result[types_span[curve_index]]++;
283  }
284  return result;
285  },
286  [](const CountsType &a, const CountsType &b) {
287  CountsType result = a;
288  for (const int i : IndexRange(CURVE_TYPES_NUM)) {
289  result[i] += b[i];
290  }
291  return result;
292  });
293 }
294 
295 void CurvesGeometry::update_curve_types()
296 {
297  this->runtime->type_counts = calculate_type_counts(this->curve_types());
298 }
299 
301 {
302  return get_span_attribute<float3>(*this, ATTR_DOMAIN_POINT, ATTR_POSITION);
303 }
304 MutableSpan<float3> CurvesGeometry::positions_for_write()
305 {
306  return get_mutable_attribute<float3>(*this, ATTR_DOMAIN_POINT, ATTR_POSITION);
307 }
308 
309 Span<int> CurvesGeometry::offsets() const
310 {
311  return {this->curve_offsets, this->curve_num + 1};
312 }
313 MutableSpan<int> CurvesGeometry::offsets_for_write()
314 {
315  return {this->curve_offsets, this->curve_num + 1};
316 }
317 
318 VArray<bool> CurvesGeometry::cyclic() const
319 {
320  return get_varray_attribute<bool>(*this, ATTR_DOMAIN_CURVE, ATTR_CYCLIC, false);
321 }
322 MutableSpan<bool> CurvesGeometry::cyclic_for_write()
323 {
324  return get_mutable_attribute<bool>(*this, ATTR_DOMAIN_CURVE, ATTR_CYCLIC, false);
325 }
326 
327 VArray<int> CurvesGeometry::resolution() const
328 {
329  return get_varray_attribute<int>(*this, ATTR_DOMAIN_CURVE, ATTR_RESOLUTION, 12);
330 }
331 MutableSpan<int> CurvesGeometry::resolution_for_write()
332 {
333  return get_mutable_attribute<int>(*this, ATTR_DOMAIN_CURVE, ATTR_RESOLUTION, 12);
334 }
335 
336 VArray<int8_t> CurvesGeometry::normal_mode() const
337 {
338  return get_varray_attribute<int8_t>(*this, ATTR_DOMAIN_CURVE, ATTR_NORMAL_MODE, 0);
339 }
340 MutableSpan<int8_t> CurvesGeometry::normal_mode_for_write()
341 {
342  return get_mutable_attribute<int8_t>(*this, ATTR_DOMAIN_CURVE, ATTR_NORMAL_MODE);
343 }
344 
345 VArray<float> CurvesGeometry::tilt() const
346 {
347  return get_varray_attribute<float>(*this, ATTR_DOMAIN_POINT, ATTR_TILT, 0.0f);
348 }
349 MutableSpan<float> CurvesGeometry::tilt_for_write()
350 {
351  return get_mutable_attribute<float>(*this, ATTR_DOMAIN_POINT, ATTR_TILT);
352 }
353 
354 VArray<int8_t> CurvesGeometry::handle_types_left() const
355 {
356  return get_varray_attribute<int8_t>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_TYPE_LEFT, 0);
357 }
358 MutableSpan<int8_t> CurvesGeometry::handle_types_left_for_write()
359 {
360  return get_mutable_attribute<int8_t>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_TYPE_LEFT, 0);
361 }
362 
363 VArray<int8_t> CurvesGeometry::handle_types_right() const
364 {
365  return get_varray_attribute<int8_t>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_TYPE_RIGHT, 0);
366 }
367 MutableSpan<int8_t> CurvesGeometry::handle_types_right_for_write()
368 {
369  return get_mutable_attribute<int8_t>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_TYPE_RIGHT, 0);
370 }
371 
372 Span<float3> CurvesGeometry::handle_positions_left() const
373 {
374  return get_span_attribute<float3>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_POSITION_LEFT);
375 }
376 MutableSpan<float3> CurvesGeometry::handle_positions_left_for_write()
377 {
378  return get_mutable_attribute<float3>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_POSITION_LEFT);
379 }
380 
381 Span<float3> CurvesGeometry::handle_positions_right() const
382 {
383  return get_span_attribute<float3>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_POSITION_RIGHT);
384 }
385 MutableSpan<float3> CurvesGeometry::handle_positions_right_for_write()
386 {
387  return get_mutable_attribute<float3>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_POSITION_RIGHT);
388 }
389 
390 VArray<int8_t> CurvesGeometry::nurbs_orders() const
391 {
392  return get_varray_attribute<int8_t>(*this, ATTR_DOMAIN_CURVE, ATTR_NURBS_ORDER, 4);
393 }
394 MutableSpan<int8_t> CurvesGeometry::nurbs_orders_for_write()
395 {
396  return get_mutable_attribute<int8_t>(*this, ATTR_DOMAIN_CURVE, ATTR_NURBS_ORDER, 4);
397 }
398 
399 Span<float> CurvesGeometry::nurbs_weights() const
400 {
401  return get_span_attribute<float>(*this, ATTR_DOMAIN_POINT, ATTR_NURBS_WEIGHT);
402 }
403 MutableSpan<float> CurvesGeometry::nurbs_weights_for_write()
404 {
405  return get_mutable_attribute<float>(*this, ATTR_DOMAIN_POINT, ATTR_NURBS_WEIGHT);
406 }
407 
408 VArray<int8_t> CurvesGeometry::nurbs_knots_modes() const
409 {
410  return get_varray_attribute<int8_t>(*this, ATTR_DOMAIN_CURVE, ATTR_NURBS_KNOTS_MODE, 0);
411 }
412 MutableSpan<int8_t> CurvesGeometry::nurbs_knots_modes_for_write()
413 {
414  return get_mutable_attribute<int8_t>(*this, ATTR_DOMAIN_CURVE, ATTR_NURBS_KNOTS_MODE, 0);
415 }
416 
417 Span<float2> CurvesGeometry::surface_uv_coords() const
418 {
419  return get_span_attribute<float2>(*this, ATTR_DOMAIN_CURVE, ATTR_SURFACE_UV_COORDINATE);
420 }
421 
422 MutableSpan<float2> CurvesGeometry::surface_uv_coords_for_write()
423 {
424  return get_mutable_attribute<float2>(*this, ATTR_DOMAIN_CURVE, ATTR_SURFACE_UV_COORDINATE);
425 }
426 
427 VArray<float> CurvesGeometry::selection_point_float() const
428 {
429  return get_varray_attribute<float>(*this, ATTR_DOMAIN_POINT, ATTR_SELECTION_POINT_FLOAT, 1.0f);
430 }
431 
432 MutableSpan<float> CurvesGeometry::selection_point_float_for_write()
433 {
434  return get_mutable_attribute<float>(*this, ATTR_DOMAIN_POINT, ATTR_SELECTION_POINT_FLOAT, 1.0f);
435 }
436 
437 VArray<float> CurvesGeometry::selection_curve_float() const
438 {
439  return get_varray_attribute<float>(*this, ATTR_DOMAIN_CURVE, ATTR_SELECTION_CURVE_FLOAT, 1.0f);
440 }
441 
442 MutableSpan<float> CurvesGeometry::selection_curve_float_for_write()
443 {
444  return get_mutable_attribute<float>(*this, ATTR_DOMAIN_CURVE, ATTR_SELECTION_CURVE_FLOAT, 1.0f);
445 }
446 
449 /* -------------------------------------------------------------------- */
453 template<typename CountFn> void build_offsets(MutableSpan<int> offsets, const CountFn &count_fn)
454 {
455  int offset = 0;
456  for (const int i : offsets.drop_back(1).index_range()) {
457  offsets[i] = offset;
458  offset += count_fn(i);
459  }
460  offsets.last() = offset;
461 }
462 
464  MutableSpan<int> offsets,
465  MutableSpan<int> bezier_evaluated_offsets)
466 {
467  VArray<int8_t> types = curves.curve_types();
468  VArray<int> resolution = curves.resolution();
469  VArray<bool> cyclic = curves.cyclic();
470 
471  VArraySpan<int8_t> handle_types_left{curves.handle_types_left()};
472  VArraySpan<int8_t> handle_types_right{curves.handle_types_right()};
473 
474  VArray<int8_t> nurbs_orders = curves.nurbs_orders();
475  VArray<int8_t> nurbs_knots_modes = curves.nurbs_knots_modes();
476 
477  build_offsets(offsets, [&](const int curve_index) -> int {
478  const IndexRange points = curves.points_for_curve(curve_index);
479  switch (types[curve_index]) {
482  points.size(), cyclic[curve_index], resolution[curve_index]);
483  case CURVE_TYPE_POLY:
484  return points.size();
485  case CURVE_TYPE_BEZIER:
486  curves::bezier::calculate_evaluated_offsets(handle_types_left.slice(points),
487  handle_types_right.slice(points),
488  cyclic[curve_index],
489  resolution[curve_index],
490  bezier_evaluated_offsets.slice(points));
491  return bezier_evaluated_offsets[points.last()];
492  case CURVE_TYPE_NURBS:
494  nurbs_orders[curve_index],
495  cyclic[curve_index],
496  resolution[curve_index],
497  KnotsMode(nurbs_knots_modes[curve_index]));
498  }
500  return 0;
501  });
502 }
503 
504 void CurvesGeometry::ensure_evaluated_offsets() const
505 {
506  if (!this->runtime->offsets_cache_dirty) {
507  return;
508  }
509 
510  /* A double checked lock. */
511  std::scoped_lock lock{this->runtime->offsets_cache_mutex};
512  if (!this->runtime->offsets_cache_dirty) {
513  return;
514  }
515 
517  this->runtime->evaluated_offsets_cache.resize(this->curves_num() + 1);
518 
519  if (this->has_curve_with_type(CURVE_TYPE_BEZIER)) {
520  this->runtime->bezier_evaluated_offsets.resize(this->points_num());
521  }
522  else {
523  this->runtime->bezier_evaluated_offsets.clear_and_make_inline();
524  }
525 
527  *this, this->runtime->evaluated_offsets_cache, this->runtime->bezier_evaluated_offsets);
528  });
529 
530  this->runtime->offsets_cache_dirty = false;
531 }
532 
533 Span<int> CurvesGeometry::evaluated_offsets() const
534 {
535  this->ensure_evaluated_offsets();
536  return this->runtime->evaluated_offsets_cache;
537 }
538 
539 IndexMask CurvesGeometry::indices_for_curve_type(const CurveType type,
540  Vector<int64_t> &r_indices) const
541 {
542  return this->indices_for_curve_type(type, this->curves_range(), r_indices);
543 }
544 
545 IndexMask CurvesGeometry::indices_for_curve_type(const CurveType type,
546  const IndexMask selection,
547  Vector<int64_t> &r_indices) const
548 {
550  this->curve_types(), this->curve_type_counts(), type, selection, r_indices);
551 }
552 
553 void CurvesGeometry::ensure_nurbs_basis_cache() const
554 {
555  if (!this->runtime->nurbs_basis_cache_dirty) {
556  return;
557  }
558 
559  /* A double checked lock. */
560  std::scoped_lock lock{this->runtime->nurbs_basis_cache_mutex};
561  if (!this->runtime->nurbs_basis_cache_dirty) {
562  return;
563  }
564 
566  Vector<int64_t> nurbs_indices;
567  const IndexMask nurbs_mask = this->indices_for_curve_type(CURVE_TYPE_NURBS, nurbs_indices);
568  if (nurbs_mask.is_empty()) {
569  return;
570  }
571 
572  this->runtime->nurbs_basis_cache.resize(this->curves_num());
573  MutableSpan<curves::nurbs::BasisCache> basis_caches(this->runtime->nurbs_basis_cache);
574 
575  VArray<bool> cyclic = this->cyclic();
576  VArray<int8_t> orders = this->nurbs_orders();
577  VArray<int8_t> knots_modes = this->nurbs_knots_modes();
578 
579  threading::parallel_for(nurbs_mask.index_range(), 64, [&](const IndexRange range) {
580  for (const int curve_index : nurbs_mask.slice(range)) {
581  const IndexRange points = this->points_for_curve(curve_index);
582  const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
583 
584  const int8_t order = orders[curve_index];
585  const bool is_cyclic = cyclic[curve_index];
586  const KnotsMode mode = KnotsMode(knots_modes[curve_index]);
587 
588  if (!curves::nurbs::check_valid_num_and_order(points.size(), order, is_cyclic, mode)) {
589  basis_caches[curve_index].invalid = true;
590  continue;
591  }
592 
593  const int knots_num = curves::nurbs::knots_num(points.size(), order, is_cyclic);
594  Array<float> knots(knots_num);
595  curves::nurbs::calculate_knots(points.size(), mode, order, is_cyclic, knots);
596  curves::nurbs::calculate_basis_cache(points.size(),
597  evaluated_points.size(),
598  order,
599  is_cyclic,
600  knots,
601  basis_caches[curve_index]);
602  }
603  });
604  });
605 
606  this->runtime->nurbs_basis_cache_dirty = false;
607 }
608 
609 Span<float3> CurvesGeometry::evaluated_positions() const
610 {
611  if (!this->runtime->position_cache_dirty) {
612  return this->runtime->evaluated_positions_span;
613  }
614 
615  /* A double checked lock. */
616  std::scoped_lock lock{this->runtime->position_cache_mutex};
617  if (!this->runtime->position_cache_dirty) {
618  return this->runtime->evaluated_positions_span;
619  }
620 
622  if (this->is_single_type(CURVE_TYPE_POLY)) {
623  this->runtime->evaluated_positions_span = this->positions();
624  this->runtime->evaluated_position_cache.clear_and_make_inline();
625  return;
626  }
627 
628  this->runtime->evaluated_position_cache.resize(this->evaluated_points_num());
629  MutableSpan<float3> evaluated_positions = this->runtime->evaluated_position_cache;
630  this->runtime->evaluated_positions_span = evaluated_positions;
631 
632  VArray<int8_t> types = this->curve_types();
633  VArray<bool> cyclic = this->cyclic();
634  VArray<int> resolution = this->resolution();
635  Span<float3> positions = this->positions();
636 
637  Span<float3> handle_positions_left = this->handle_positions_left();
638  Span<float3> handle_positions_right = this->handle_positions_right();
639  Span<int> bezier_evaluated_offsets = this->runtime->bezier_evaluated_offsets;
640 
641  VArray<int8_t> nurbs_orders = this->nurbs_orders();
642  Span<float> nurbs_weights = this->nurbs_weights();
643 
644  this->ensure_nurbs_basis_cache();
645 
646  threading::parallel_for(this->curves_range(), 128, [&](IndexRange curves_range) {
647  for (const int curve_index : curves_range) {
648  const IndexRange points = this->points_for_curve(curve_index);
649  const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
650 
651  switch (types[curve_index]) {
654  positions.slice(points),
655  cyclic[curve_index],
656  resolution[curve_index],
657  evaluated_positions.slice(evaluated_points));
658  break;
659  case CURVE_TYPE_POLY:
660  evaluated_positions.slice(evaluated_points).copy_from(positions.slice(points));
661  break;
662  case CURVE_TYPE_BEZIER:
664  positions.slice(points),
665  handle_positions_left.slice(points),
666  handle_positions_right.slice(points),
667  bezier_evaluated_offsets.slice(points),
668  evaluated_positions.slice(evaluated_points));
669  break;
670  case CURVE_TYPE_NURBS: {
671  curves::nurbs::interpolate_to_evaluated(this->runtime->nurbs_basis_cache[curve_index],
672  nurbs_orders[curve_index],
673  nurbs_weights.slice(points),
674  positions.slice(points),
675  evaluated_positions.slice(evaluated_points));
676  break;
677  }
678  default:
680  break;
681  }
682  }
683  });
684  });
685 
686  this->runtime->position_cache_dirty = false;
687  return this->runtime->evaluated_positions_span;
688 }
689 
690 Span<float3> CurvesGeometry::evaluated_tangents() const
691 {
692  if (!this->runtime->tangent_cache_dirty) {
693  return this->runtime->evaluated_tangent_cache;
694  }
695 
696  /* A double checked lock. */
697  std::scoped_lock lock{this->runtime->tangent_cache_mutex};
698  if (!this->runtime->tangent_cache_dirty) {
699  return this->runtime->evaluated_tangent_cache;
700  }
701 
703  const Span<float3> evaluated_positions = this->evaluated_positions();
704  const VArray<bool> cyclic = this->cyclic();
705 
706  this->runtime->evaluated_tangent_cache.resize(this->evaluated_points_num());
707  MutableSpan<float3> tangents = this->runtime->evaluated_tangent_cache;
708 
709  threading::parallel_for(this->curves_range(), 128, [&](IndexRange curves_range) {
710  for (const int curve_index : curves_range) {
711  const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
712  curves::poly::calculate_tangents(evaluated_positions.slice(evaluated_points),
713  cyclic[curve_index],
714  tangents.slice(evaluated_points));
715  }
716  });
717 
718  /* Correct the first and last tangents of non-cyclic Bezier curves so that they align with the
719  * inner handles. This is a separate loop to avoid the cost when Bezier type curves are not
720  * used. */
721  Vector<int64_t> bezier_indices;
722  const IndexMask bezier_mask = this->indices_for_curve_type(CURVE_TYPE_BEZIER, bezier_indices);
723  if (!bezier_mask.is_empty()) {
724  const Span<float3> positions = this->positions();
725  const Span<float3> handles_left = this->handle_positions_left();
726  const Span<float3> handles_right = this->handle_positions_right();
727 
728  threading::parallel_for(bezier_mask.index_range(), 1024, [&](IndexRange range) {
729  for (const int curve_index : bezier_mask.slice(range)) {
730  if (cyclic[curve_index]) {
731  continue;
732  }
733  const IndexRange points = this->points_for_curve(curve_index);
734  const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
735 
736  const float epsilon = 1e-6f;
737  if (!math::almost_equal_relative(
738  handles_right[points.first()], positions[points.first()], epsilon)) {
739  tangents[evaluated_points.first()] = math::normalize(handles_right[points.first()] -
740  positions[points.first()]);
741  }
742  if (!math::almost_equal_relative(
743  handles_left[points.last()], positions[points.last()], epsilon)) {
744  tangents[evaluated_points.last()] = math::normalize(positions[points.last()] -
745  handles_left[points.last()]);
746  }
747  }
748  });
749  }
750  });
751 
752  this->runtime->tangent_cache_dirty = false;
753  return this->runtime->evaluated_tangent_cache;
754 }
755 
757  const Span<float3> axes,
758  const Span<float> angles)
759 {
760  for (const int i : directions.index_range()) {
761  directions[i] = math::rotate_direction_around_axis(directions[i], axes[i], angles[i]);
762  }
763 }
764 
765 Span<float3> CurvesGeometry::evaluated_normals() const
766 {
767  if (!this->runtime->normal_cache_dirty) {
768  return this->runtime->evaluated_normal_cache;
769  }
770 
771  /* A double checked lock. */
772  std::scoped_lock lock{this->runtime->normal_cache_mutex};
773  if (!this->runtime->normal_cache_dirty) {
774  return this->runtime->evaluated_normal_cache;
775  }
776 
778  const Span<float3> evaluated_tangents = this->evaluated_tangents();
779  const VArray<bool> cyclic = this->cyclic();
780  const VArray<int8_t> normal_mode = this->normal_mode();
781  const VArray<int8_t> types = this->curve_types();
782  const VArray<float> tilt = this->tilt();
783 
784  this->runtime->evaluated_normal_cache.resize(this->evaluated_points_num());
785  MutableSpan<float3> evaluated_normals = this->runtime->evaluated_normal_cache;
786 
787  threading::parallel_for(this->curves_range(), 128, [&](IndexRange curves_range) {
788  /* Reuse a buffer for the evaluated tilts. */
789  Vector<float> evaluated_tilts;
790 
791  for (const int curve_index : curves_range) {
792  const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
793  switch (normal_mode[curve_index]) {
794  case NORMAL_MODE_Z_UP:
795  curves::poly::calculate_normals_z_up(evaluated_tangents.slice(evaluated_points),
796  evaluated_normals.slice(evaluated_points));
797  break;
799  curves::poly::calculate_normals_minimum(evaluated_tangents.slice(evaluated_points),
800  cyclic[curve_index],
801  evaluated_normals.slice(evaluated_points));
802  break;
803  }
804 
805  /* If the "tilt" attribute exists, rotate the normals around the tangents by the
806  * evaluated angles. We can avoid copying the tilts to evaluate them for poly curves. */
807  if (!(tilt.is_single() && tilt.get_internal_single() == 0.0f)) {
808  const IndexRange points = this->points_for_curve(curve_index);
809  Span<float> curve_tilt = tilt.get_internal_span().slice(points);
810  if (types[curve_index] == CURVE_TYPE_POLY) {
811  rotate_directions_around_axes(evaluated_normals.slice(evaluated_points),
812  evaluated_tangents.slice(evaluated_points),
813  curve_tilt);
814  }
815  else {
816  evaluated_tilts.clear();
817  evaluated_tilts.resize(evaluated_points.size());
819  curve_index, curve_tilt, evaluated_tilts.as_mutable_span());
820  rotate_directions_around_axes(evaluated_normals.slice(evaluated_points),
821  evaluated_tangents.slice(evaluated_points),
822  evaluated_tilts.as_span());
823  }
824  }
825  }
826  });
827  });
828 
829  this->runtime->normal_cache_dirty = false;
830  return this->runtime->evaluated_normal_cache;
831 }
832 
833 void CurvesGeometry::interpolate_to_evaluated(const int curve_index,
834  const GSpan src,
835  GMutableSpan dst) const
836 {
837  BLI_assert(!this->runtime->offsets_cache_dirty);
838  BLI_assert(!this->runtime->nurbs_basis_cache_dirty);
839  const IndexRange points = this->points_for_curve(curve_index);
840  BLI_assert(src.size() == points.size());
841  BLI_assert(dst.size() == this->evaluated_points_for_curve(curve_index).size());
842  switch (this->curve_types()[curve_index]) {
845  src, this->cyclic()[curve_index], this->resolution()[curve_index], dst);
846  return;
847  case CURVE_TYPE_POLY:
848  dst.type().copy_assign_n(src.data(), dst.data(), src.size());
849  return;
850  case CURVE_TYPE_BEZIER:
852  src, this->runtime->bezier_evaluated_offsets.as_span().slice(points), dst);
853  return;
854  case CURVE_TYPE_NURBS:
855  curves::nurbs::interpolate_to_evaluated(this->runtime->nurbs_basis_cache[curve_index],
856  this->nurbs_orders()[curve_index],
857  this->nurbs_weights().slice(points),
858  src,
859  dst);
860  return;
861  }
863 }
864 
866 {
867  BLI_assert(!this->runtime->offsets_cache_dirty);
868  BLI_assert(!this->runtime->nurbs_basis_cache_dirty);
869  const VArray<int8_t> types = this->curve_types();
870  const VArray<int> resolution = this->resolution();
871  const VArray<bool> cyclic = this->cyclic();
872  const VArray<int8_t> nurbs_orders = this->nurbs_orders();
873  const Span<float> nurbs_weights = this->nurbs_weights();
874 
875  threading::parallel_for(this->curves_range(), 512, [&](IndexRange curves_range) {
876  for (const int curve_index : curves_range) {
877  const IndexRange points = this->points_for_curve(curve_index);
878  const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
879  switch (types[curve_index]) {
882  cyclic[curve_index],
883  resolution[curve_index],
884  dst.slice(evaluated_points));
885  continue;
886  case CURVE_TYPE_POLY:
887  dst.slice(evaluated_points).copy_from(src.slice(points));
888  continue;
889  case CURVE_TYPE_BEZIER:
891  src.slice(points),
892  this->runtime->bezier_evaluated_offsets.as_span().slice(points),
893  dst.slice(evaluated_points));
894  continue;
895  case CURVE_TYPE_NURBS:
896  curves::nurbs::interpolate_to_evaluated(this->runtime->nurbs_basis_cache[curve_index],
897  nurbs_orders[curve_index],
898  nurbs_weights.slice(points),
899  src.slice(points),
900  dst.slice(evaluated_points));
901  continue;
902  }
903  }
904  });
905 }
906 
907 void CurvesGeometry::ensure_evaluated_lengths() const
908 {
909  if (!this->runtime->length_cache_dirty) {
910  return;
911  }
912 
913  /* A double checked lock. */
914  std::scoped_lock lock{this->runtime->length_cache_mutex};
915  if (!this->runtime->length_cache_dirty) {
916  return;
917  }
918 
920  /* Use an extra length value for the final cyclic segment for a consistent size
921  * (see comment on #evaluated_length_cache). */
922  const int total_num = this->evaluated_points_num() + this->curves_num();
923  this->runtime->evaluated_length_cache.resize(total_num);
924  MutableSpan<float> evaluated_lengths = this->runtime->evaluated_length_cache;
925 
926  Span<float3> evaluated_positions = this->evaluated_positions();
927  VArray<bool> curves_cyclic = this->cyclic();
928 
929  threading::parallel_for(this->curves_range(), 128, [&](IndexRange curves_range) {
930  for (const int curve_index : curves_range) {
931  const bool cyclic = curves_cyclic[curve_index];
932  const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
933  const IndexRange lengths_range = this->lengths_range_for_curve(curve_index, cyclic);
934  length_parameterize::accumulate_lengths(evaluated_positions.slice(evaluated_points),
935  cyclic,
936  evaluated_lengths.slice(lengths_range));
937  }
938  });
939  });
940 
941  this->runtime->length_cache_dirty = false;
942 }
943 
944 void CurvesGeometry::ensure_can_interpolate_to_evaluated() const
945 {
946  this->ensure_evaluated_offsets();
947  this->ensure_nurbs_basis_cache();
948 }
949 
952 /* -------------------------------------------------------------------- */
956 void CurvesGeometry::resize(const int points_num, const int curves_num)
957 {
958  if (points_num != this->point_num) {
959  CustomData_realloc(&this->point_data, points_num);
960  this->point_num = points_num;
961  }
962  if (curves_num != this->curve_num) {
963  CustomData_realloc(&this->curve_data, curves_num);
964  this->curve_num = curves_num;
965  this->curve_offsets = (int *)MEM_reallocN(this->curve_offsets, sizeof(int) * (curves_num + 1));
966  }
967  this->tag_topology_changed();
968 }
969 
970 void CurvesGeometry::tag_positions_changed()
971 {
972  this->runtime->position_cache_dirty = true;
973  this->runtime->tangent_cache_dirty = true;
974  this->runtime->normal_cache_dirty = true;
975  this->runtime->length_cache_dirty = true;
976 }
977 void CurvesGeometry::tag_topology_changed()
978 {
979  this->runtime->position_cache_dirty = true;
980  this->runtime->tangent_cache_dirty = true;
981  this->runtime->normal_cache_dirty = true;
982  this->runtime->offsets_cache_dirty = true;
983  this->runtime->nurbs_basis_cache_dirty = true;
984  this->runtime->length_cache_dirty = true;
985 }
986 void CurvesGeometry::tag_normals_changed()
987 {
988  this->runtime->normal_cache_dirty = true;
989 }
990 
991 static void translate_positions(MutableSpan<float3> positions, const float3 &translation)
992 {
993  threading::parallel_for(positions.index_range(), 2048, [&](const IndexRange range) {
994  for (float3 &position : positions.slice(range)) {
995  position += translation;
996  }
997  });
998 }
999 
1001 {
1002  threading::parallel_for(positions.index_range(), 1024, [&](const IndexRange range) {
1003  for (float3 &position : positions.slice(range)) {
1004  position = matrix * position;
1005  }
1006  });
1007 }
1008 
1009 void CurvesGeometry::calculate_bezier_auto_handles()
1010 {
1011  const VArray<int8_t> types = this->curve_types();
1012  if (types.is_single() && types.get_internal_single() != CURVE_TYPE_BEZIER) {
1013  return;
1014  }
1015  if (this->handle_positions_left().is_empty() || this->handle_positions_right().is_empty()) {
1016  return;
1017  }
1018  const VArray<bool> cyclic = this->cyclic();
1019  const VArraySpan<int8_t> types_left{this->handle_types_left()};
1020  const VArraySpan<int8_t> types_right{this->handle_types_right()};
1021  const Span<float3> positions = this->positions();
1022  MutableSpan<float3> positions_left = this->handle_positions_left_for_write();
1023  MutableSpan<float3> positions_right = this->handle_positions_right_for_write();
1024 
1025  threading::parallel_for(this->curves_range(), 128, [&](IndexRange range) {
1026  for (const int i_curve : range) {
1027  if (types[i_curve] == CURVE_TYPE_BEZIER) {
1028  const IndexRange points = this->points_for_curve(i_curve);
1030  types_left.slice(points),
1031  types_right.slice(points),
1032  positions.slice(points),
1033  positions_left.slice(points),
1034  positions_right.slice(points));
1035  }
1036  }
1037  });
1038 }
1039 
1040 void CurvesGeometry::translate(const float3 &translation)
1041 {
1042  translate_positions(this->positions_for_write(), translation);
1043  if (!this->handle_positions_left().is_empty()) {
1044  translate_positions(this->handle_positions_left_for_write(), translation);
1045  }
1046  if (!this->handle_positions_right().is_empty()) {
1047  translate_positions(this->handle_positions_right_for_write(), translation);
1048  }
1049  this->tag_positions_changed();
1050 }
1051 
1053 {
1054  transform_positions(this->positions_for_write(), matrix);
1055  if (!this->handle_positions_left().is_empty()) {
1056  transform_positions(this->handle_positions_left_for_write(), matrix);
1057  }
1058  if (!this->handle_positions_right().is_empty()) {
1059  transform_positions(this->handle_positions_right_for_write(), matrix);
1060  }
1061  this->tag_positions_changed();
1062 }
1063 
1064 static std::optional<bounds::MinMaxResult<float3>> curves_bounds(const CurvesGeometry &curves)
1065 {
1066  const Span<float3> positions = curves.positions();
1067  const VArray<float> radii = curves.attributes().lookup_or_default<float>(
1069  if (!(radii.is_single() && radii.get_internal_single() == 0.0f)) {
1070  return bounds::min_max_with_radii(positions, radii.get_internal_span());
1071  }
1072  return bounds::min_max(positions);
1073 }
1074 
1075 bool CurvesGeometry::bounds_min_max(float3 &min, float3 &max) const
1076 {
1077  const std::optional<bounds::MinMaxResult<float3>> bounds = curves_bounds(*this);
1078  if (!bounds) {
1079  return false;
1080  }
1081  min = math::min(bounds->min, min);
1082  max = math::max(bounds->max, max);
1083  return true;
1084 }
1085 
1086 static void copy_between_buffers(const CPPType &type,
1087  const void *src_buffer,
1088  void *dst_buffer,
1089  const IndexRange src_range,
1090  const IndexRange dst_range)
1091 {
1092  BLI_assert(src_range.size() == dst_range.size());
1093  type.copy_construct_n(POINTER_OFFSET(src_buffer, type.size() * src_range.start()),
1094  POINTER_OFFSET(dst_buffer, type.size() * dst_range.start()),
1095  src_range.size());
1096 }
1097 
1098 template<typename T>
1099 static void copy_with_map(const Span<T> src, const Span<int> map, MutableSpan<T> dst)
1100 {
1101  threading::parallel_for(map.index_range(), 1024, [&](const IndexRange range) {
1102  for (const int i : range) {
1103  dst[i] = src[map[i]];
1104  }
1105  });
1106 }
1107 
1108 static void copy_with_map(const GSpan src, const Span<int> map, GMutableSpan dst)
1109 {
1110  attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
1111  using T = decltype(dummy);
1112  copy_with_map(src.typed<T>(), map, dst.typed<T>());
1113  });
1114 }
1115 
1120 {
1121  Array<int> point_to_curve_map(curves.points_num());
1122  threading::parallel_for(curves.curves_range(), 1024, [&](const IndexRange curves_range) {
1123  for (const int i_curve : curves_range) {
1124  point_to_curve_map.as_mutable_span().slice(curves.points_for_curve(i_curve)).fill(i_curve);
1125  }
1126  });
1127  return point_to_curve_map;
1128 }
1129 
1131  const IndexMask points_to_delete)
1132 {
1133  /* Use a map from points to curves to facilitate using an #IndexMask input. */
1134  const Array<int> point_to_curve_map = build_point_to_curve_map(curves);
1135 
1136  const Vector<IndexRange> copy_point_ranges = points_to_delete.extract_ranges_invert(
1137  curves.points_range());
1138 
1139  /* For every range of points to copy, find the offset in the result curves point layers. */
1140  int new_point_count = 0;
1141  Array<int> copy_point_range_dst_offsets(copy_point_ranges.size());
1142  for (const int i : copy_point_ranges.index_range()) {
1143  copy_point_range_dst_offsets[i] = new_point_count;
1144  new_point_count += copy_point_ranges[i].size();
1145  }
1146  BLI_assert(new_point_count == (curves.points_num() - points_to_delete.size()));
1147 
1148  /* Find out how many non-deleted points there are in every curve. */
1149  Array<int> curve_point_counts(curves.curves_num(), 0);
1150  for (const IndexRange range : copy_point_ranges) {
1151  for (const int point_i : range) {
1152  curve_point_counts[point_to_curve_map[point_i]]++;
1153  }
1154  }
1155 
1156  /* Build the offsets for the new curve points, skipping curves that had all points deleted.
1157  * Also store the original indices of the corresponding input curves, to facilitate parallel
1158  * copying of curve domain data. */
1159  int new_curve_count = 0;
1160  int curve_point_offset = 0;
1161  Vector<int> new_curve_offsets;
1162  Vector<int> new_curve_orig_indices;
1163  new_curve_offsets.append(0);
1164  for (const int i : curve_point_counts.index_range()) {
1165  if (curve_point_counts[i] > 0) {
1166  curve_point_offset += curve_point_counts[i];
1167  new_curve_offsets.append(curve_point_offset);
1168 
1169  new_curve_count++;
1170  new_curve_orig_indices.append(i);
1171  }
1172  }
1173 
1174  CurvesGeometry new_curves{new_point_count, new_curve_count};
1175 
1177  256 < new_point_count * new_curve_count,
1178  /* Initialize curve offsets. */
1179  [&]() { new_curves.offsets_for_write().copy_from(new_curve_offsets); },
1180  [&]() {
1181  /* Copy over point attributes. */
1183  curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT)) {
1184  threading::parallel_for(copy_point_ranges.index_range(), 128, [&](IndexRange range) {
1185  for (const int range_i : range) {
1186  const IndexRange src_range = copy_point_ranges[range_i];
1187  copy_between_buffers(attribute.src.type(),
1188  attribute.src.data(),
1189  attribute.dst.span.data(),
1190  src_range,
1191  {copy_point_range_dst_offsets[range_i], src_range.size()});
1192  }
1193  });
1194  attribute.dst.finish();
1195  }
1196 
1197  /* Copy over curve attributes.
1198  * In some cases points are just dissolved, so the the number of
1199  * curves will be the same. That could be optimized in the future. */
1201  curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE)) {
1202  if (new_curves.curves_num() == curves.curves_num()) {
1203  attribute.dst.span.copy_from(attribute.src);
1204  }
1205  else {
1206  copy_with_map(attribute.src, new_curve_orig_indices, attribute.dst.span);
1207  }
1208  attribute.dst.finish();
1209  }
1210  });
1211 
1212  return new_curves;
1213 }
1214 
1215 void CurvesGeometry::remove_points(const IndexMask points_to_delete)
1216 {
1217  if (points_to_delete.is_empty()) {
1218  return;
1219  }
1220  if (points_to_delete.size() == this->points_num()) {
1221  *this = {};
1222  }
1223  *this = copy_with_removed_points(*this, points_to_delete);
1224 }
1225 
1227  const IndexMask curves_to_delete)
1228 {
1229  const Span<int> old_offsets = curves.offsets();
1230  const Vector<IndexRange> old_curve_ranges = curves_to_delete.extract_ranges_invert(
1231  curves.curves_range(), nullptr);
1232  Vector<IndexRange> new_curve_ranges;
1233  Vector<IndexRange> old_point_ranges;
1234  Vector<IndexRange> new_point_ranges;
1235  int new_tot_points = 0;
1236  int new_tot_curves = 0;
1237  for (const IndexRange &curve_range : old_curve_ranges) {
1238  new_curve_ranges.append(IndexRange(new_tot_curves, curve_range.size()));
1239  new_tot_curves += curve_range.size();
1240 
1241  const IndexRange old_point_range = curves.points_for_curves(curve_range);
1242  old_point_ranges.append(old_point_range);
1243  new_point_ranges.append(IndexRange(new_tot_points, old_point_range.size()));
1244  new_tot_points += old_point_range.size();
1245  }
1246 
1247  CurvesGeometry new_curves{new_tot_points, new_tot_curves};
1248 
1250  256 < new_tot_points * new_tot_curves,
1251  /* Initialize curve offsets. */
1252  [&]() {
1253  MutableSpan<int> new_offsets = new_curves.offsets_for_write();
1254  new_offsets.last() = new_tot_points;
1256  old_curve_ranges.index_range(), 128, [&](const IndexRange ranges_range) {
1257  for (const int range_i : ranges_range) {
1258  const IndexRange old_curve_range = old_curve_ranges[range_i];
1259  const IndexRange new_curve_range = new_curve_ranges[range_i];
1260  const IndexRange old_point_range = old_point_ranges[range_i];
1261  const IndexRange new_point_range = new_point_ranges[range_i];
1262  const int offset_shift = new_point_range.start() - old_point_range.start();
1263  const int curves_in_range = old_curve_range.size();
1264  threading::parallel_for(
1265  IndexRange(curves_in_range), 512, [&](const IndexRange range) {
1266  for (const int i : range) {
1267  const int old_curve_i = old_curve_range[i];
1268  const int new_curve_i = new_curve_range[i];
1269  const int old_offset = old_offsets[old_curve_i];
1270  const int new_offset = old_offset + offset_shift;
1271  new_offsets[new_curve_i] = new_offset;
1272  }
1273  });
1274  }
1275  });
1276  },
1277  [&]() {
1278  /* Copy over point attributes. */
1280  curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT)) {
1281  threading::parallel_for(old_curve_ranges.index_range(), 128, [&](IndexRange range) {
1282  for (const int range_i : range) {
1283  copy_between_buffers(attribute.src.type(),
1284  attribute.src.data(),
1285  attribute.dst.span.data(),
1286  old_point_ranges[range_i],
1287  new_point_ranges[range_i]);
1288  }
1289  });
1290  attribute.dst.finish();
1291  }
1292  /* Copy over curve attributes. */
1294  curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE)) {
1295  threading::parallel_for(old_curve_ranges.index_range(), 128, [&](IndexRange range) {
1296  for (const int range_i : range) {
1297  copy_between_buffers(attribute.src.type(),
1298  attribute.src.data(),
1299  attribute.dst.span.data(),
1300  old_curve_ranges[range_i],
1301  new_curve_ranges[range_i]);
1302  }
1303  });
1304  attribute.dst.finish();
1305  }
1306  });
1307 
1308  return new_curves;
1309 }
1310 
1311 void CurvesGeometry::remove_curves(const IndexMask curves_to_delete)
1312 {
1313  if (curves_to_delete.is_empty()) {
1314  return;
1315  }
1316  if (curves_to_delete.size() == this->curves_num()) {
1317  *this = {};
1318  return;
1319  }
1320  *this = copy_with_removed_curves(*this, curves_to_delete);
1321 }
1322 
1323 template<typename T>
1325  const IndexMask curve_selection,
1327 {
1328  threading::parallel_for(curve_selection.index_range(), 256, [&](IndexRange range) {
1329  for (const int curve_i : curve_selection.slice(range)) {
1330  data.slice(curves.points_for_curve(curve_i)).reverse();
1331  }
1332  });
1333 }
1334 
1335 template<typename T>
1337  const IndexMask curve_selection,
1338  MutableSpan<T> data_a,
1339  MutableSpan<T> data_b)
1340 {
1341  threading::parallel_for(curve_selection.index_range(), 256, [&](IndexRange range) {
1342  for (const int curve_i : curve_selection.slice(range)) {
1343  const IndexRange points = curves.points_for_curve(curve_i);
1344  MutableSpan<T> a = data_a.slice(points);
1345  MutableSpan<T> b = data_b.slice(points);
1346  for (const int i : IndexRange(points.size() / 2)) {
1347  const int end_index = points.size() - 1 - i;
1348  std::swap(a[end_index], b[i]);
1349  std::swap(b[end_index], a[i]);
1350  }
1351  if (points.size() % 2) {
1352  const int64_t middle_index = points.size() / 2;
1353  std::swap(a[middle_index], b[middle_index]);
1354  }
1355  }
1356  });
1357 }
1358 
1360  const StringRef name,
1361  const eCustomDataType type)
1362 {
1363  if (layer.type != type) {
1364  return false;
1365  }
1366  return layer.name == name;
1367 }
1368 
1369 void CurvesGeometry::reverse_curves(const IndexMask curves_to_reverse)
1370 {
1371  CustomData_duplicate_referenced_layers(&this->point_data, this->points_num());
1372 
1373  /* Collect the Bezier handle attributes while iterating through the point custom data layers;
1374  * they need special treatment later. */
1375  MutableSpan<float3> positions_left;
1376  MutableSpan<float3> positions_right;
1377  MutableSpan<int8_t> types_left;
1378  MutableSpan<int8_t> types_right;
1379 
1380  for (const int layer_i : IndexRange(this->point_data.totlayer)) {
1381  CustomDataLayer &layer = this->point_data.layers[layer_i];
1382 
1383  if (positions_left.is_empty() &&
1385  positions_left = {static_cast<float3 *>(layer.data), this->points_num()};
1386  continue;
1387  }
1388  if (positions_right.is_empty() &&
1390  positions_right = {static_cast<float3 *>(layer.data), this->points_num()};
1391  continue;
1392  }
1393  if (types_left.is_empty() &&
1395  types_left = {static_cast<int8_t *>(layer.data), this->points_num()};
1396  continue;
1397  }
1398  if (types_right.is_empty() &&
1400  types_right = {static_cast<int8_t *>(layer.data), this->points_num()};
1401  continue;
1402  }
1403 
1404  const eCustomDataType data_type = static_cast<eCustomDataType>(layer.type);
1405  attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
1406  using T = decltype(dummy);
1407  reverse_curve_point_data<T>(
1408  *this, curves_to_reverse, {static_cast<T *>(layer.data), this->points_num()});
1409  });
1410  }
1411 
1412  /* In order to maintain the shape of Bezier curves, handle attributes must reverse, but also the
1413  * values for the left and right must swap. Use a utility to swap and reverse at the same time,
1414  * to avoid loading the attribute twice. Generally we can expect the right layer to exist when
1415  * the left does, but there's no need to count on it, so check for both attributes. */
1416 
1417  if (!positions_left.is_empty() && !positions_right.is_empty()) {
1418  reverse_swap_curve_point_data(*this, curves_to_reverse, positions_left, positions_right);
1419  }
1420  if (!types_left.is_empty() && !types_right.is_empty()) {
1421  reverse_swap_curve_point_data(*this, curves_to_reverse, types_left, types_right);
1422  }
1423 
1424  this->tag_topology_changed();
1425 }
1426 
1427 void CurvesGeometry::remove_attributes_based_on_types()
1428 {
1429  const int points_num = this->points_num();
1430  const int curves_num = this->curves_num();
1431  if (!this->has_curve_with_type(CURVE_TYPE_BEZIER)) {
1432  CustomData_free_layer_named(&this->point_data, ATTR_HANDLE_TYPE_LEFT.c_str(), points_num);
1433  CustomData_free_layer_named(&this->point_data, ATTR_HANDLE_TYPE_RIGHT.c_str(), points_num);
1434  CustomData_free_layer_named(&this->point_data, ATTR_HANDLE_POSITION_LEFT.c_str(), points_num);
1435  CustomData_free_layer_named(&this->point_data, ATTR_HANDLE_POSITION_RIGHT.c_str(), points_num);
1436  }
1437  if (!this->has_curve_with_type(CURVE_TYPE_NURBS)) {
1438  CustomData_free_layer_named(&this->point_data, ATTR_NURBS_WEIGHT.c_str(), points_num);
1439  CustomData_free_layer_named(&this->curve_data, ATTR_NURBS_ORDER.c_str(), curves_num);
1440  CustomData_free_layer_named(&this->curve_data, ATTR_NURBS_KNOTS_MODE.c_str(), curves_num);
1441  }
1442  if (!this->has_curve_with_type({CURVE_TYPE_BEZIER, CURVE_TYPE_CATMULL_ROM, CURVE_TYPE_NURBS})) {
1443  CustomData_free_layer_named(&this->curve_data, ATTR_RESOLUTION.c_str(), curves_num);
1444  }
1445 }
1446 
1449 /* -------------------------------------------------------------------- */
1460 template<typename T>
1462  const VArray<T> &old_values,
1463  MutableSpan<T> r_values)
1464 {
1465  attribute_math::DefaultMixer<T> mixer(r_values);
1466  for (const int i_curve : IndexRange(curves.curves_num())) {
1467  for (const int i_point : curves.points_for_curve(i_curve)) {
1468  mixer.mix_in(i_curve, old_values[i_point]);
1469  }
1470  }
1471  mixer.finalize();
1472 }
1473 
1481 template<>
1483  const VArray<bool> &old_values,
1484  MutableSpan<bool> r_values)
1485 {
1486  r_values.fill(true);
1487  for (const int i_curve : IndexRange(curves.curves_num())) {
1488  for (const int i_point : curves.points_for_curve(i_curve)) {
1489  if (!old_values[i_point]) {
1490  r_values[i_curve] = false;
1491  break;
1492  }
1493  }
1494  }
1495 }
1496 
1498  const GVArray &varray)
1499 {
1500  GVArray new_varray;
1501  attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
1502  using T = decltype(dummy);
1503  if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
1504  Array<T> values(curves.curves_num());
1505  adapt_curve_domain_point_to_curve_impl<T>(curves, varray.typed<T>(), values);
1506  new_varray = VArray<T>::ForContainer(std::move(values));
1507  }
1508  });
1509  return new_varray;
1510 }
1511 
1519 template<typename T>
1521  const VArray<T> &old_values,
1522  MutableSpan<T> r_values)
1523 {
1524  for (const int i_curve : IndexRange(curves.curves_num())) {
1525  r_values.slice(curves.points_for_curve(i_curve)).fill(old_values[i_curve]);
1526  }
1527 }
1528 
1530  const GVArray &varray)
1531 {
1532  GVArray new_varray;
1533  attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
1534  using T = decltype(dummy);
1535  Array<T> values(curves.points_num());
1536  adapt_curve_domain_curve_to_point_impl<T>(curves, varray.typed<T>(), values);
1537  new_varray = VArray<T>::ForContainer(std::move(values));
1538  });
1539  return new_varray;
1540 }
1541 
1542 GVArray CurvesGeometry::adapt_domain(const GVArray &varray,
1543  const eAttrDomain from,
1544  const eAttrDomain to) const
1545 {
1546  if (!varray) {
1547  return {};
1548  }
1549  if (varray.is_empty()) {
1550  return {};
1551  }
1552  if (from == to) {
1553  return varray;
1554  }
1555 
1556  if (from == ATTR_DOMAIN_POINT && to == ATTR_DOMAIN_CURVE) {
1557  return adapt_curve_domain_point_to_curve(*this, varray);
1558  }
1559  if (from == ATTR_DOMAIN_CURVE && to == ATTR_DOMAIN_POINT) {
1560  return adapt_curve_domain_curve_to_point(*this, varray);
1561  }
1562 
1564  return {};
1565 }
1566 
1569 } // namespace blender::bke
eAttrDomain
Definition: BKE_attribute.h:25
@ ATTR_DOMAIN_CURVE
Definition: BKE_attribute.h:31
@ ATTR_DOMAIN_POINT
Definition: BKE_attribute.h:27
@ ATTR_DOMAIN_MASK_POINT
Definition: BKE_attribute.h:37
@ ATTR_DOMAIN_MASK_CURVE
Definition: BKE_attribute.h:41
Low-level operations for curves.
Low-level operations for curves.
bool CustomData_free_layer_named(struct CustomData *data, const char *name, const int totelem)
Definition: customdata.cc:2883
void CustomData_free(struct CustomData *data, int totelem)
Definition: customdata.cc:2373
@ CD_CALLOC
@ CD_DUPLICATE
@ CD_DEFAULT
void CustomData_copy(const struct CustomData *source, struct CustomData *dest, eCustomDataMask mask, eCDAllocType alloctype, int totelem)
void * CustomData_duplicate_referenced_layer_named(struct CustomData *data, int type, const char *name, int totelem)
Definition: customdata.cc:2995
void * CustomData_add_layer_named(struct CustomData *data, int type, eCDAllocType alloctype, void *layer, int totelem, const char *name)
Definition: customdata.cc:2792
void * CustomData_get_layer_named(const struct CustomData *data, int type, const char *name)
void CustomData_realloc(struct CustomData *data, int totelem)
Definition: customdata.cc:2307
void CustomData_duplicate_referenced_layers(CustomData *data, int totelem)
Definition: customdata.cc:3020
void CustomData_reset(struct CustomData *data)
Definition: customdata.cc:2367
#define BLI_assert_unreachable()
Definition: BLI_assert.h:93
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define POINTER_OFFSET(v, ofs)
void swap(T &a, T &b)
Definition: Common.h:19
CurveType
@ CURVE_TYPE_BEZIER
@ CURVE_TYPE_NURBS
@ CURVE_TYPE_POLY
@ CURVE_TYPE_CATMULL_ROM
@ NORMAL_MODE_MINIMUM_TWIST
@ NORMAL_MODE_Z_UP
#define CURVE_TYPES_NUM
KnotsMode
#define CD_MASK_ALL
eCustomDataType
@ CD_PROP_FLOAT3
@ CD_PROP_INT8
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define MEM_reallocN(vmemh, len)
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to curves
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Sky Generate a procedural sky texture Noise Generate fractal Perlin noise Wave Generate procedural bands or rings with noise Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between and object coordinate space Combine Create a color from its and value channels Color Retrieve a color attribute
volatile int lock
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
static btDbvtVolume bounds(btDbvtNode **leaves, int count)
Definition: btDbvt.cpp:299
IndexRange index_range() const
Definition: BLI_array.hh:348
void copy_assign_n(const void *src, void *dst, int64_t n) const
const CPPType & type() const
void copy_from(GSpan values)
GMutableSpan slice(const int64_t start, int64_t size) const
const CPPType & type() const
int64_t size() const
bool is_empty() const
Vector< IndexRange > extract_ranges_invert(const IndexRange full_range, Vector< int64_t > *r_skip_amounts=nullptr) const
Definition: index_mask.cc:100
IndexRange index_range() const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
constexpr int64_t start() const
constexpr void fill(const T &value)
Definition: BLI_span.hh:527
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition: BLI_span.hh:581
constexpr bool is_empty() const
Definition: BLI_span.hh:519
constexpr MutableSpan drop_back(const int64_t n) const
Definition: BLI_span.hh:609
constexpr T & last(const int64_t n=0) const
Definition: BLI_span.hh:680
constexpr void copy_from(Span< T > values)
Definition: BLI_span.hh:707
constexpr IndexRange index_range() const
Definition: BLI_span.hh:661
constexpr T & first() const
Definition: BLI_span.hh:670
constexpr Span slice(int64_t start, int64_t size) const
Definition: BLI_span.hh:142
constexpr const char * c_str() const
Span< T > get_internal_span() const
static VArray ForSingle(T value, const int64_t size)
static VArray ForSpan(Span< T > values)
int64_t size() const
Definition: BLI_vector.hh:694
MutableSpan< T > as_mutable_span()
Definition: BLI_vector.hh:330
void append(const T &value)
Definition: BLI_vector.hh:433
Span< T > as_span() const
Definition: BLI_vector.hh:325
IndexRange index_range() const
Definition: BLI_vector.hh:920
void resize(const int64_t new_size)
Definition: BLI_vector.hh:353
MutableSpan< int8_t > curve_types_for_write()
void fill_curve_types(CurveType type)
MutableSpan< int > offsets_for_write()
VArray< int8_t > curve_types() const
CurvesGeometry & operator=(const CurvesGeometry &other)
static const char * ATTR_POSITION
Definition: curves.cc:54
StackEntry * from
SyclQueue void void * src
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
static char ** types
Definition: makesdna.c:67
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition: mallocn.c:34
#define T
static unsigned a[3]
Definition: RandGen.cpp:78
typename DefaultMixerStruct< T >::type DefaultMixer
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
void calculate_auto_handles(bool cyclic, Span< int8_t > types_left, Span< int8_t > types_right, Span< float3 > positions, MutableSpan< float3 > positions_left, MutableSpan< float3 > positions_right)
void calculate_evaluated_positions(Span< float3 > positions, Span< float3 > handles_left, Span< float3 > handles_right, Span< int > evaluated_offsets, MutableSpan< float3 > evaluated_positions)
void interpolate_to_evaluated(GSpan src, Span< int > evaluated_offsets, GMutableSpan dst)
int calculate_evaluated_num(int points_num, bool cyclic, int resolution)
IndexMask indices_for_type(const VArray< int8_t > &types, const std::array< int, CURVE_TYPES_NUM > &type_counts, const CurveType type, const IndexMask selection, Vector< int64_t > &r_indices)
Definition: curves_utils.cc:99
static int domain_num(const CurvesGeometry &curves, const eAttrDomain domain)
static void reverse_curve_point_data(const CurvesGeometry &curves, const IndexMask curve_selection, MutableSpan< T > data)
static void translate_positions(MutableSpan< float3 > positions, const float3 &translation)
static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves, const IndexMask curves_to_delete)
Vector< AttributeTransferData > retrieve_attributes_for_transfer(const bke::AttributeAccessor src_attributes, bke::MutableAttributeAccessor dst_attributes, eAttrDomainMask domain_mask, const Set< std::string > &skip={})
static const std::string ATTR_HANDLE_POSITION_RIGHT
static void copy_between_buffers(const CPPType &type, const void *src_buffer, void *dst_buffer, const IndexRange src_range, const IndexRange dst_range)
static void reverse_swap_curve_point_data(const CurvesGeometry &curves, const IndexMask curve_selection, MutableSpan< T > data_a, MutableSpan< T > data_b)
static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves, const IndexMask points_to_delete)
static Array< int > build_point_to_curve_map(const CurvesGeometry &curves)
static void move_curves_geometry(CurvesGeometry &dst, CurvesGeometry &src)
static VArray< T > get_varray_attribute(const CurvesGeometry &curves, const eAttrDomain domain, const StringRefNull name, const T default_value)
static CustomData & domain_custom_data(CurvesGeometry &curves, const eAttrDomain domain)
static const std::string ATTR_NURBS_WEIGHT
static const std::string ATTR_HANDLE_TYPE_RIGHT
static const std::string ATTR_NURBS_KNOTS_MODE
static const std::string ATTR_RADIUS
static MutableSpan< T > get_mutable_attribute(CurvesGeometry &curves, const eAttrDomain domain, const StringRefNull name, const T default_value=T())
static GVArray adapt_curve_domain_point_to_curve(const CurvesGeometry &curves, const GVArray &varray)
static Span< T > get_span_attribute(const CurvesGeometry &curves, const eAttrDomain domain, const StringRefNull name)
static bool layer_matches_name_and_type(const CustomDataLayer &layer, const StringRef name, const eCustomDataType type)
static const std::string ATTR_NORMAL_MODE
static void copy_curves_geometry(CurvesGeometry &dst, const CurvesGeometry &src)
static const std::string ATTR_POSITION
static const std::string ATTR_HANDLE_TYPE_LEFT
static const std::string ATTR_CYCLIC
static const std::string ATTR_SURFACE_UV_COORDINATE
static void copy_with_map(const GSpan src, const Span< int > map, GMutableSpan dst)
std::array< int, CURVE_TYPES_NUM > calculate_type_counts(const VArray< int8_t > &types)
static const std::string ATTR_RESOLUTION
static const std::string ATTR_TILT
static void calculate_evaluated_offsets(const CurvesGeometry &curves, MutableSpan< int > offsets, MutableSpan< int > bezier_evaluated_offsets)
static const std::string ATTR_CURVE_TYPE
void adapt_curve_domain_point_to_curve_impl(const CurvesGeometry &curves, const VArray< bool > &old_values, MutableSpan< bool > r_values)
static const std::string ATTR_HANDLE_POSITION_LEFT
void build_offsets(MutableSpan< int > offsets, const CountFn &count_fn)
static const std::string ATTR_NURBS_ORDER
static void transform_positions(MutableSpan< float3 > positions, const float4x4 &matrix)
static GVArray adapt_curve_domain_curve_to_point(const CurvesGeometry &curves, const GVArray &varray)
static void adapt_curve_domain_curve_to_point_impl(const CurvesGeometry &curves, const VArray< T > &old_values, MutableSpan< T > r_values)
static std::optional< bounds::MinMaxResult< float3 > > curves_bounds(const CurvesGeometry &curves)
static void rotate_directions_around_axes(MutableSpan< float3 > directions, const Span< float3 > axes, const Span< float > angles)
static const std::string ATTR_SELECTION_POINT_FLOAT
eCustomDataType cpp_type_to_custom_data_type(const blender::CPPType &type)
Definition: customdata.cc:5337
static const std::string ATTR_SELECTION_CURVE_FLOAT
static std::optional< MinMaxResult< T > > min_max(Span< T > values)
Definition: BLI_bounds.hh:26
static std::optional< MinMaxResult< T > > min_max_with_radii(Span< T > values, Span< RadiusT > radii)
Definition: BLI_bounds.hh:53
void accumulate_lengths(const Span< T > values, const bool cyclic, MutableSpan< float > lengths)
T min(const T &a, const T &b)
float3 rotate_direction_around_axis(const float3 &direction, const float3 &axis, float angle)
T max(const T &a, const T &b)
void isolate_task(const Function &function)
Definition: BLI_task.hh:125
void parallel_for(IndexRange range, int64_t grain_size, const Function &function)
Definition: BLI_task.hh:51
void parallel_invoke(Functions &&...functions)
Definition: BLI_task.hh:99
Value parallel_reduce(IndexRange range, int64_t grain_size, const Value &identity, const Function &function, const Reduction &reduction)
Definition: BLI_task.hh:73
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
MutableSpan< float3 > positions
MutableSpan< float > radii
MutableSpan< float3 > tangents
SocketIndexByIdentifierMap * map
#define min(a, b)
Definition: sort.c:35
static void calculate_normals_z_up(Span< float3 > tangents, MutableSpan< float3 > r_normals)
Definition: spline_base.cc:273
static void calculate_normals_minimum(Span< float3 > tangents, const bool cyclic, MutableSpan< float3 > r_normals)
Definition: spline_base.cc:312
static void calculate_tangents(Span< float3 > positions, const bool is_cyclic, MutableSpan< float3 > tangents)
Definition: spline_base.cc:205
signed char int8_t
Definition: stdint.h:75
CustomData point_data
CurvesGeometryRuntimeHandle * runtime
CustomData curve_data
float max