Blender  V3.3
fillet_curves.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #include "BKE_attribute_math.hh"
4 #include "BKE_curves.hh"
5 #include "BKE_curves_utils.hh"
6 #include "BKE_geometry_set.hh"
7 
9 #include "BLI_math_geom.h"
10 #include "BLI_math_rotation.hh"
11 #include "BLI_task.hh"
12 
13 #include "GEO_fillet_curves.hh"
14 
15 namespace blender::geometry {
16 
22 static IndexRange curve_dst_offsets(const IndexRange points, const int curve_index)
23 {
24  return {curve_index + points.start(), points.size() + 1};
25 }
26 
27 template<typename T>
28 static void threaded_slice_fill(const Span<T> src, const Span<int> offsets, MutableSpan<T> dst)
29 {
30  threading::parallel_for(src.index_range(), 512, [&](IndexRange range) {
31  for (const int i : range) {
32  dst.slice(bke::offsets_to_range(offsets, i)).fill(src[i]);
33  }
34  });
35 }
36 
37 template<typename T>
38 static void duplicate_fillet_point_data(const bke::CurvesGeometry &src_curves,
39  const bke::CurvesGeometry &dst_curves,
40  const IndexMask curve_selection,
41  const Span<int> point_offsets,
42  const Span<T> src,
43  MutableSpan<T> dst)
44 {
45  threading::parallel_for(curve_selection.index_range(), 512, [&](IndexRange range) {
46  for (const int curve_i : curve_selection.slice(range)) {
47  const IndexRange src_points = src_curves.points_for_curve(curve_i);
48  const IndexRange dst_points = dst_curves.points_for_curve(curve_i);
49  const Span<int> offsets = point_offsets.slice(curve_dst_offsets(src_points, curve_i));
50  threaded_slice_fill(src.slice(src_points), offsets, dst.slice(dst_points));
51  }
52  });
53 }
54 
55 static void duplicate_fillet_point_data(const bke::CurvesGeometry &src_curves,
56  const bke::CurvesGeometry &dst_curves,
57  const IndexMask selection,
58  const Span<int> point_offsets,
59  const GSpan src,
60  GMutableSpan dst)
61 {
62  attribute_math::convert_to_static_type(dst.type(), [&](auto dummy) {
63  using T = decltype(dummy);
64  duplicate_fillet_point_data(
65  src_curves, dst_curves, selection, point_offsets, src.typed<T>(), dst.typed<T>());
66  });
67 }
68 
69 static void calculate_result_offsets(const bke::CurvesGeometry &src_curves,
70  const IndexMask selection,
71  const Span<IndexRange> unselected_ranges,
72  const VArray<float> &radii,
73  const VArray<int> &counts,
74  const Span<bool> cyclic,
75  MutableSpan<int> dst_curve_offsets,
76  MutableSpan<int> dst_point_offsets)
77 {
78  /* Fill the offsets array with the curve point counts, then accumulate them to form offsets. */
79  bke::curves::fill_curve_counts(src_curves, unselected_ranges, dst_curve_offsets);
80  threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
81  for (const int curve_i : selection.slice(range)) {
82  const IndexRange src_points = src_curves.points_for_curve(curve_i);
83  const IndexRange offsets_range = curve_dst_offsets(src_points, curve_i);
84 
85  MutableSpan<int> point_offsets = dst_point_offsets.slice(offsets_range);
86  MutableSpan<int> point_counts = point_offsets.drop_back(1);
87 
88  counts.materialize_compressed(src_points, point_counts);
89  for (int &count : point_counts) {
90  /* Make sure the number of cuts is greater than zero and add one for the existing point. */
91  count = std::max(count, 0) + 1;
92  }
93  if (!cyclic[curve_i]) {
94  /* Endpoints on non-cyclic curves cannot be filleted. */
95  point_counts.first() = 1;
96  point_counts.last() = 1;
97  }
98  /* Implicitly "deselect" points with zero radius. */
99  devirtualize_varray(radii, [&](const auto radii) {
100  for (const int i : IndexRange(src_points.size())) {
101  if (radii[src_points[i]] == 0.0f) {
102  point_counts[i] = 1;
103  }
104  }
105  });
106 
107  bke::curves::accumulate_counts_to_offsets(point_offsets);
108 
109  dst_curve_offsets[curve_i] = point_offsets.last();
110  }
111  });
113 }
114 
116 {
117  for (const int i : positions.index_range().drop_back(1)) {
118  directions[i] = math::normalize(positions[i + 1] - positions[i]);
119  }
120  directions.last() = math::normalize(positions.first() - positions.last());
121 }
122 
123 static void calculate_angles(const Span<float3> directions, MutableSpan<float> angles)
124 {
125  angles.first() = M_PI - angle_v3v3(-directions.last(), directions.first());
126  for (const int i : directions.index_range().drop_front(1)) {
127  angles[i] = M_PI - angle_v3v3(-directions[i - 1], directions[i]);
128  }
129 }
130 
136 static float limit_radius(const float3 &position_prev,
137  const float3 &position,
138  const float3 &position_next,
139  const float angle_prev,
140  const float angle,
141  const float angle_next,
142  const float radius_prev,
143  const float radius,
144  const float radius_next)
145 {
146  const float displacement = radius * std::tan(angle / 2.0f);
147 
148  const float displacement_prev = radius_prev * std::tan(angle_prev / 2.0f);
149  const float segment_length_prev = math::distance(position, position_prev);
150  const float total_displacement_prev = displacement_prev + displacement;
151  const float factor_prev = std::clamp(segment_length_prev / total_displacement_prev, 0.0f, 1.0f);
152 
153  const float displacement_next = radius_next * std::tan(angle_next / 2.0f);
154  const float segment_length_next = math::distance(position, position_next);
155  const float total_displacement_next = displacement_next + displacement;
156  const float factor_next = std::clamp(segment_length_next / total_displacement_next, 0.0f, 1.0f);
157 
158  return radius * std::min(factor_prev, factor_next);
159 }
160 
162  const Span<float> angles,
163  const Span<float> radii,
164  const bool cyclic,
165  MutableSpan<float> radii_clamped)
166 {
167  if (cyclic) {
168  /* First point. */
169  radii_clamped.first() = limit_radius(positions.last(),
170  positions.first(),
171  positions[1],
172  angles.last(),
173  angles.first(),
174  angles[1],
175  radii.last(),
176  radii.first(),
177  radii[1]);
178  /* All middle points. */
179  for (const int i : positions.index_range().drop_back(1).drop_front(1)) {
180  const int i_prev = i - 1;
181  const int i_next = i + 1;
182  radii_clamped[i] = limit_radius(positions[i_prev],
183  positions[i],
184  positions[i_next],
185  angles[i_prev],
186  angles[i],
187  angles[i_next],
188  radii[i_prev],
189  radii[i],
190  radii[i_next]);
191  }
192  /* Last point. */
193  radii_clamped.last() = limit_radius(positions.last(1),
194  positions.last(),
195  positions.first(),
196  angles.last(1),
197  angles.last(),
198  angles.first(),
199  radii.last(1),
200  radii.last(),
201  radii.first());
202  }
203  else {
204  const int i_last = positions.index_range().last();
205  /* First point. */
206  radii_clamped.first() = 0.0f;
207  /* All middle points. */
208  for (const int i : positions.index_range().drop_back(1).drop_front(1)) {
209  const int i_prev = i - 1;
210  const int i_next = i + 1;
211  /* Use a zero radius for the first and last points, because they don't have fillets.
212  * This logic could potentially be unrolled, but it doesn't seem worth it. */
213  const float radius_prev = i_prev == 0 ? 0.0f : radii[i_prev];
214  const float radius_next = i_next == i_last ? 0.0f : radii[i_next];
215  radii_clamped[i] = limit_radius(positions[i_prev],
216  positions[i],
217  positions[i_next],
218  angles[i_prev],
219  angles[i],
220  angles[i_next],
221  radius_prev,
222  radii[i],
223  radius_next);
224  }
225  /* Last point. */
226  radii_clamped.last() = 0.0f;
227  }
228 }
229 
230 static void calculate_fillet_positions(const Span<float3> src_positions,
231  const Span<float> angles,
232  const Span<float> radii,
233  const Span<float3> directions,
234  const Span<int> dst_offsets,
236 {
237  const int i_src_last = src_positions.index_range().last();
238  threading::parallel_for(src_positions.index_range(), 512, [&](IndexRange range) {
239  for (const int i_src : range) {
240  const IndexRange arc = bke::offsets_to_range(dst_offsets, i_src);
241  const float3 &src = src_positions[i_src];
242  if (arc.size() == 1) {
243  dst[arc.first()] = src;
244  continue;
245  }
246 
247  const int i_src_prev = i_src == 0 ? i_src_last : i_src - 1;
248  const float angle = angles[i_src];
249  const float radius = radii[i_src];
250  const float displacement = radius * std::tan(angle / 2.0f);
251  const float3 prev_dir = -directions[i_src_prev];
252  const float3 &next_dir = directions[i_src];
253  const float3 arc_start = src + prev_dir * displacement;
254  const float3 arc_end = src + next_dir * displacement;
255 
256  dst[arc.first()] = arc_start;
257  dst[arc.last()] = arc_end;
258 
259  const IndexRange middle = arc.drop_front(1).drop_back(1);
260  if (middle.is_empty()) {
261  continue;
262  }
263 
264  const float3 axis = -math::normalize(math::cross(prev_dir, next_dir));
265  const float3 center_direction = math::normalize(math::midpoint(next_dir, prev_dir));
266  const float distance_to_center = std::sqrt(pow2f(radius) + pow2f(displacement));
267  const float3 center = src + center_direction * distance_to_center;
268 
269  /* Rotate each middle fillet point around the center. */
270  const float segment_angle = angle / (middle.size() + 1);
271  for (const int i : IndexRange(middle.size())) {
272  const int point_i = middle[i];
273  dst[point_i] = math::rotate_around_axis(arc_start, center, axis, segment_angle * (i + 1));
274  }
275  }
276  });
277 }
278 
284 static void calculate_bezier_handles_bezier_mode(const Span<float3> src_handles_l,
285  const Span<float3> src_handles_r,
286  const Span<int8_t> src_types_l,
287  const Span<int8_t> src_types_r,
288  const Span<float> angles,
289  const Span<float> radii,
290  const Span<float3> directions,
291  const Span<int> dst_offsets,
292  const Span<float3> dst_positions,
293  MutableSpan<float3> dst_handles_l,
294  MutableSpan<float3> dst_handles_r,
295  MutableSpan<int8_t> dst_types_l,
296  MutableSpan<int8_t> dst_types_r)
297 {
298  const int i_src_last = src_handles_l.index_range().last();
299  const int i_dst_last = dst_positions.index_range().last();
300  threading::parallel_for(src_handles_l.index_range(), 512, [&](IndexRange range) {
301  for (const int i_src : range) {
302  const IndexRange arc = bke::offsets_to_range(dst_offsets, i_src);
303  if (arc.size() == 1) {
304  dst_handles_l[arc.first()] = src_handles_l[i_src];
305  dst_handles_r[arc.first()] = src_handles_r[i_src];
306  dst_types_l[arc.first()] = src_types_l[i_src];
307  dst_types_r[arc.first()] = src_types_r[i_src];
308  continue;
309  }
310  BLI_assert(arc.size() == 2);
311  const int i_dst_a = arc.first();
312  const int i_dst_b = arc.last();
313 
314  const int i_src_prev = i_src == 0 ? i_src_last : i_src - 1;
315  const float angle = angles[i_src];
316  const float radius = radii[i_src];
317  const float3 prev_dir = -directions[i_src_prev];
318  const float3 &next_dir = directions[i_src];
319 
320  const float3 &arc_start = dst_positions[arc.first()];
321  const float3 &arc_end = dst_positions[arc.last()];
322 
323  /* Calculate the point's handles on the outside of the fillet segment,
324  * connecting to the next or previous result points. */
325  const int i_dst_prev = i_dst_a == 0 ? i_dst_last : i_dst_a - 1;
326  const int i_dst_next = i_dst_b == i_dst_last ? 0 : i_dst_b + 1;
327  dst_handles_l[i_dst_a] = bke::curves::bezier::calculate_vector_handle(
328  dst_positions[i_dst_a], dst_positions[i_dst_prev]);
329  dst_handles_r[i_dst_b] = bke::curves::bezier::calculate_vector_handle(
330  dst_positions[i_dst_b], dst_positions[i_dst_next]);
331  dst_types_l[i_dst_a] = BEZIER_HANDLE_VECTOR;
332  dst_types_r[i_dst_b] = BEZIER_HANDLE_VECTOR;
333 
334  /* The inner handles are aligned with the aligned with the outer vector
335  * handles, but have a specific length to best approximate a circle. */
336  const float handle_length = (4.0f / 3.0f) * radius * std::tan(angle / 4.0f);
337  dst_handles_r[i_dst_a] = arc_start - prev_dir * handle_length;
338  dst_handles_l[i_dst_b] = arc_end - next_dir * handle_length;
339  dst_types_r[i_dst_a] = BEZIER_HANDLE_ALIGN;
340  dst_types_l[i_dst_b] = BEZIER_HANDLE_ALIGN;
341  }
342  });
343 }
344 
349 static void calculate_bezier_handles_poly_mode(const Span<float3> src_handles_l,
350  const Span<float3> src_handles_r,
351  const Span<int8_t> src_types_l,
352  const Span<int8_t> src_types_r,
353  const Span<int> dst_offsets,
354  const Span<float3> dst_positions,
355  MutableSpan<float3> dst_handles_l,
356  MutableSpan<float3> dst_handles_r,
357  MutableSpan<int8_t> dst_types_l,
358  MutableSpan<int8_t> dst_types_r)
359 {
360  const int i_dst_last = dst_positions.index_range().last();
361  threading::parallel_for(src_handles_l.index_range(), 512, [&](IndexRange range) {
362  for (const int i_src : range) {
363  const IndexRange arc = bke::offsets_to_range(dst_offsets, i_src);
364  if (arc.size() == 1) {
365  dst_handles_l[arc.first()] = src_handles_l[i_src];
366  dst_handles_r[arc.first()] = src_handles_r[i_src];
367  dst_types_l[arc.first()] = src_types_l[i_src];
368  dst_types_r[arc.first()] = src_types_r[i_src];
369  continue;
370  }
371 
372  /* The fillet's next and previous handles are vector handles, as are the inner handles. */
373  dst_types_l.slice(arc).fill(BEZIER_HANDLE_VECTOR);
374  dst_types_r.slice(arc).fill(BEZIER_HANDLE_VECTOR);
375 
376  /* Calculate the point's handles on the outside of the fillet segment. This point
377  * won't be selected for a fillet if it is the first or last in a non-cyclic curve. */
378 
379  const int i_dst_prev = arc.first() == 0 ? i_dst_last : arc.one_before_start();
380  const int i_dst_next = arc.last() == i_dst_last ? 0 : arc.one_after_last();
381  dst_handles_l[arc.first()] = bke::curves::bezier::calculate_vector_handle(
382  dst_positions[arc.first()], dst_positions[i_dst_prev]);
383  dst_handles_r[arc.last()] = bke::curves::bezier::calculate_vector_handle(
384  dst_positions[arc.last()], dst_positions[i_dst_next]);
385 
386  /* Set the values for the inner handles. */
387  const IndexRange middle = arc.drop_front(1).drop_back(1);
388  for (const int i : middle) {
389  dst_handles_r[i] = bke::curves::bezier::calculate_vector_handle(dst_positions[i],
390  dst_positions[i - 1]);
391  dst_handles_l[i] = bke::curves::bezier::calculate_vector_handle(dst_positions[i],
392  dst_positions[i + 1]);
393  }
394  }
395  });
396 }
397 
399  const IndexMask curve_selection,
400  const VArray<float> &radius_input,
401  const VArray<int> &counts,
402  const bool limit_radius,
403  const bool use_bezier_mode)
404 {
405  const Vector<IndexRange> unselected_ranges = curve_selection.extract_ranges_invert(
406  src_curves.curves_range());
407 
408  const Span<float3> positions = src_curves.positions();
409  const VArraySpan<bool> cyclic{src_curves.cyclic()};
410  const bke::AttributeAccessor src_attributes = src_curves.attributes();
411 
413  /* Stores the offset of every result point for every original point.
414  * The extra length is used in order to store an extra zero for every curve. */
415  Array<int> dst_point_offsets(src_curves.points_num() + src_curves.curves_num());
416  calculate_result_offsets(src_curves,
417  curve_selection,
418  unselected_ranges,
419  radius_input,
420  counts,
421  cyclic,
422  dst_curves.offsets_for_write(),
423  dst_point_offsets);
424  const Span<int> point_offsets = dst_point_offsets.as_span();
425 
426  dst_curves.resize(dst_curves.offsets().last(), dst_curves.curves_num());
427  bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
428  MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
429 
430  VArraySpan<int8_t> src_types_l;
431  VArraySpan<int8_t> src_types_r;
432  Span<float3> src_handles_l;
433  Span<float3> src_handles_r;
434  MutableSpan<int8_t> dst_types_l;
435  MutableSpan<int8_t> dst_types_r;
436  MutableSpan<float3> dst_handles_l;
437  MutableSpan<float3> dst_handles_r;
438  if (src_curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
439  src_types_l = src_curves.handle_types_left();
440  src_types_r = src_curves.handle_types_right();
441  src_handles_l = src_curves.handle_positions_left();
442  src_handles_r = src_curves.handle_positions_right();
443 
444  dst_types_l = dst_curves.handle_types_left_for_write();
445  dst_types_r = dst_curves.handle_types_right_for_write();
446  dst_handles_l = dst_curves.handle_positions_left_for_write();
447  dst_handles_r = dst_curves.handle_positions_right_for_write();
448  }
449 
450  threading::parallel_for(curve_selection.index_range(), 512, [&](IndexRange range) {
451  Array<float3> directions;
452  Array<float> angles;
453  Array<float> radii;
454  Array<float> input_radii_buffer;
455 
456  for (const int curve_i : curve_selection.slice(range)) {
457  const IndexRange src_points = src_curves.points_for_curve(curve_i);
458  const Span<int> offsets = point_offsets.slice(curve_dst_offsets(src_points, curve_i));
459  const IndexRange dst_points = dst_curves.points_for_curve(curve_i);
460  const Span<float3> src_positions = positions.slice(src_points);
461 
462  directions.reinitialize(src_points.size());
463  calculate_directions(src_positions, directions);
464 
465  angles.reinitialize(src_points.size());
466  calculate_angles(directions, angles);
467 
468  radii.reinitialize(src_points.size());
469  if (limit_radius) {
470  input_radii_buffer.reinitialize(src_points.size());
471  radius_input.materialize_compressed(src_points, input_radii_buffer);
472  limit_radii(src_positions, angles, input_radii_buffer, cyclic[curve_i], radii);
473  }
474  else {
475  radius_input.materialize_compressed(src_points, radii);
476  }
477 
478  calculate_fillet_positions(positions.slice(src_points),
479  angles,
480  radii,
481  directions,
482  offsets,
483  dst_positions.slice(dst_points));
484 
485  if (src_curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
486  if (use_bezier_mode) {
487  calculate_bezier_handles_bezier_mode(src_handles_l.slice(src_points),
488  src_handles_r.slice(src_points),
489  src_types_l.slice(src_points),
490  src_types_r.slice(src_points),
491  angles,
492  radii,
493  directions,
494  offsets,
495  dst_positions.slice(dst_points),
496  dst_handles_l.slice(dst_points),
497  dst_handles_r.slice(dst_points),
498  dst_types_l.slice(dst_points),
499  dst_types_r.slice(dst_points));
500  }
501  else {
502  calculate_bezier_handles_poly_mode(src_handles_l.slice(src_points),
503  src_handles_r.slice(src_points),
504  src_types_l.slice(src_points),
505  src_types_r.slice(src_points),
506  offsets,
507  dst_positions.slice(dst_points),
508  dst_handles_l.slice(dst_points),
509  dst_handles_r.slice(dst_points),
510  dst_types_l.slice(dst_points),
511  dst_types_r.slice(dst_points));
512  }
513  }
514  }
515  });
516 
518  src_attributes,
519  dst_attributes,
521  {"position", "handle_type_left", "handle_type_right", "handle_right", "handle_left"})) {
523  src_curves, dst_curves, curve_selection, point_offsets, attribute.src, attribute.dst.span);
524  attribute.dst.finish();
525  }
526 
527  if (!unselected_ranges.is_empty()) {
529  src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) {
531  src_curves, dst_curves, unselected_ranges, attribute.src, attribute.dst.span);
532  attribute.dst.finish();
533  }
534  }
535 
536  return dst_curves;
537 }
538 
540  const IndexMask curve_selection,
541  const VArray<float> &radius,
542  const VArray<int> &count,
543  const bool limit_radius)
544 {
545  return fillet_curves(src_curves, curve_selection, radius, count, limit_radius, false);
546 }
547 
549  const IndexMask curve_selection,
550  const VArray<float> &radius,
551  const bool limit_radius)
552 {
553  return fillet_curves(src_curves,
554  curve_selection,
555  radius,
556  VArray<int>::ForSingle(1, src_curves.points_num()),
557  limit_radius,
558  true);
559 }
560 
561 } // namespace blender::geometry
@ ATTR_DOMAIN_MASK_POINT
Definition: BKE_attribute.h:37
Low-level operations for curves.
Low-level operations for curves.
#define M_PI
Definition: BLI_math_base.h:20
float angle_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
Definition: math_vector.c:385
@ CURVE_TYPE_BEZIER
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
SIMD_FORCE_INLINE btScalar angle(const btVector3 &v) const
Return the angle between this and another vector.
Definition: btVector3.h:356
Span< T > as_span() const
Definition: BLI_array.hh:231
const CPPType & type() 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 IndexRange drop_front(int64_t n) const
constexpr T & last(const int64_t n=0) const
Definition: BLI_span.hh:680
constexpr T & first() const
Definition: BLI_span.hh:670
constexpr const T & first() const
Definition: BLI_span.hh:303
constexpr const T & last(const int64_t n=0) const
Definition: BLI_span.hh:313
constexpr IndexRange index_range() const
Definition: BLI_span.hh:401
VArray< int8_t > handle_types_left() const
MutableSpan< float3 > positions_for_write()
MutableSpan< int8_t > handle_types_right_for_write()
VArray< int8_t > handle_types_right() const
IndexRange curves_range() const
Definition: BKE_curves.hh:795
MutableSpan< float3 > handle_positions_left_for_write()
MutableAttributeAccessor attributes_for_write()
MutableSpan< float3 > handle_positions_right_for_write()
Span< float3 > handle_positions_left() const
Span< float3 > positions() const
bool has_curve_with_type(CurveType type) const
Definition: BKE_curves.hh:805
void resize(int points_num, int curves_num)
Span< float3 > handle_positions_right() const
AttributeAccessor attributes() const
MutableSpan< int > offsets_for_write()
VArray< bool > cyclic() const
MutableSpan< int8_t > handle_types_left_for_write()
SyclQueue void void * src
int count
INLINE Rall1d< T, V, S > tan(const Rall1d< T, V, S > &arg)
Definition: rall1d.h:327
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
void fill_curve_counts(const bke::CurvesGeometry &curves, Span< IndexRange > curve_ranges, MutableSpan< int > counts)
Definition: curves_utils.cc:13
void accumulate_counts_to_offsets(MutableSpan< int > counts_to_offsets, int start_offset=0)
Definition: curves_utils.cc:28
bke::CurvesGeometry copy_only_curve_domain(const bke::CurvesGeometry &src_curves)
Definition: curves_utils.cc:87
void copy_point_data(const CurvesGeometry &src_curves, const CurvesGeometry &dst_curves, Span< IndexRange > curve_ranges, GSpan src, GMutableSpan dst)
Definition: curves_utils.cc:40
Vector< AttributeTransferData > retrieve_attributes_for_transfer(const bke::AttributeAccessor src_attributes, bke::MutableAttributeAccessor dst_attributes, eAttrDomainMask domain_mask, const Set< std::string > &skip={})
static void calculate_bezier_handles_bezier_mode(const Span< float3 > src_handles_l, const Span< float3 > src_handles_r, const Span< int8_t > src_types_l, const Span< int8_t > src_types_r, const Span< float > angles, const Span< float > radii, const Span< float3 > directions, const Span< int > dst_offsets, const Span< float3 > dst_positions, MutableSpan< float3 > dst_handles_l, MutableSpan< float3 > dst_handles_r, MutableSpan< int8_t > dst_types_l, MutableSpan< int8_t > dst_types_r)
static void calculate_bezier_handles_poly_mode(const Span< float3 > src_handles_l, const Span< float3 > src_handles_r, const Span< int8_t > src_types_l, const Span< int8_t > src_types_r, const Span< int > dst_offsets, const Span< float3 > dst_positions, MutableSpan< float3 > dst_handles_l, MutableSpan< float3 > dst_handles_r, MutableSpan< int8_t > dst_types_l, MutableSpan< int8_t > dst_types_r)
static void calculate_result_offsets(const bke::CurvesGeometry &src_curves, const IndexMask selection, const Span< IndexRange > unselected_ranges, const VArray< float > &radii, const VArray< int > &counts, const Span< bool > cyclic, MutableSpan< int > dst_curve_offsets, MutableSpan< int > dst_point_offsets)
static IndexRange curve_dst_offsets(const IndexRange points, const int curve_index)
static void limit_radii(const Span< float3 > positions, const Span< float > angles, const Span< float > radii, const bool cyclic, MutableSpan< float > radii_clamped)
static void calculate_angles(const Span< float3 > directions, MutableSpan< float > angles)
static float limit_radius(const float3 &position_prev, const float3 &position, const float3 &position_next, const float angle_prev, const float angle, const float angle_next, const float radius_prev, const float radius, const float radius_next)
static bke::CurvesGeometry fillet_curves(const bke::CurvesGeometry &src_curves, const IndexMask curve_selection, const VArray< float > &radius_input, const VArray< int > &counts, const bool limit_radius, const bool use_bezier_mode)
static void threaded_slice_fill(const Span< T > src, const Span< int > offsets, MutableSpan< T > dst)
static void calculate_directions(const Span< float3 > positions, MutableSpan< float3 > directions)
static void calculate_fillet_positions(const Span< float3 > src_positions, const Span< float > angles, const Span< float > radii, const Span< float3 > directions, const Span< int > dst_offsets, MutableSpan< float3 > dst)
bke::CurvesGeometry fillet_curves_bezier(const bke::CurvesGeometry &src_curves, IndexMask curve_selection, const VArray< float > &radius, bool limit_radius)
bke::CurvesGeometry fillet_curves_poly(const bke::CurvesGeometry &src_curves, IndexMask curve_selection, const VArray< float > &radius, const VArray< int > &counts, bool limit_radius)
static void duplicate_fillet_point_data(const bke::CurvesGeometry &src_curves, const bke::CurvesGeometry &dst_curves, const IndexMask selection, const Span< int > point_offsets, const GSpan src, GMutableSpan dst)
T clamp(const T &a, const T &min, const T &max)
T distance(const T &a, const T &b)
vec_base< T, Size > normalize(const vec_base< T, Size > &v)
void parallel_for(IndexRange range, int64_t grain_size, const Function &function)
Definition: BLI_task.hh:51
MutableSpan< float3 > positions
MutableSpan< float > radii
#define min(a, b)
Definition: sort.c:35