Blender V4.3
fillet_curves.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
6#include "BKE_curves.hh"
7#include "BKE_curves_utils.hh"
8
10#include "BLI_task.hh"
11
12#include "GEO_fillet_curves.hh"
13
14namespace blender::geometry {
15
16static void duplicate_fillet_point_data(const OffsetIndices<int> src_points_by_curve,
17 const OffsetIndices<int> dst_points_by_curve,
18 const IndexMask &curve_selection,
19 const Span<int> all_point_offsets,
20 const GSpan src,
21 GMutableSpan dst)
22{
23 curve_selection.foreach_index(GrainSize(512), [&](const int curve_i) {
24 const IndexRange src_points = src_points_by_curve[curve_i];
25 const IndexRange dst_points = dst_points_by_curve[curve_i];
26 const IndexRange offsets_range = bke::curves::per_curve_point_offsets_range(src_points,
27 curve_i);
28 bke::attribute_math::gather_to_groups(all_point_offsets.slice(offsets_range),
29 IndexRange(src_points.size()),
30 src.slice(src_points),
31 dst.slice(dst_points));
32 });
33}
34
35static void calculate_result_offsets(const OffsetIndices<int> src_points_by_curve,
36 const IndexMask &selection,
37 const IndexMask &unselected,
38 const VArray<float> &radii,
39 const VArray<int> &counts,
40 const Span<bool> cyclic,
41 MutableSpan<int> dst_curve_offsets,
42 MutableSpan<int> dst_point_offsets)
43{
44 /* Fill the offsets array with the curve point counts, then accumulate them to form offsets. */
45 offset_indices::copy_group_sizes(src_points_by_curve, unselected, dst_curve_offsets);
46 selection.foreach_index(GrainSize(512), [&](const int curve_i) {
47 const IndexRange src_points = src_points_by_curve[curve_i];
48 const IndexRange offsets_range = bke::curves::per_curve_point_offsets_range(src_points,
49 curve_i);
50
51 MutableSpan<int> point_offsets = dst_point_offsets.slice(offsets_range);
52 MutableSpan<int> point_counts = point_offsets.drop_back(1);
53
54 counts.materialize_compressed(src_points, point_counts);
55 for (int &count : point_counts) {
56 /* Make sure the number of cuts is greater than zero and add one for the existing point. */
57 count = std::max(count, 0) + 1;
58 }
59 if (!cyclic[curve_i]) {
60 /* Endpoints on non-cyclic curves cannot be filleted. */
61 point_counts.first() = 1;
62 point_counts.last() = 1;
63 }
64 /* Implicitly "deselect" points with zero radius. */
65 devirtualize_varray(radii, [&](const auto radii) {
66 for (const int i : IndexRange(src_points.size())) {
67 if (radii[src_points[i]] == 0.0f) {
68 point_counts[i] = 1;
69 }
70 }
71 });
72
74
75 dst_curve_offsets[curve_i] = point_offsets.last();
76 });
78}
79
80static void calculate_directions(const Span<float3> positions, MutableSpan<float3> directions)
81{
82 for (const int i : positions.index_range().drop_back(1)) {
83 directions[i] = math::normalize(positions[i + 1] - positions[i]);
84 }
85 directions.last() = math::normalize(positions.first() - positions.last());
86}
87
88static void calculate_angles(const Span<float3> directions, MutableSpan<float> angles)
89{
90 angles.first() = M_PI - angle_v3v3(-directions.last(), directions.first());
91 for (const int i : directions.index_range().drop_front(1)) {
92 angles[i] = M_PI - angle_v3v3(-directions[i - 1], directions[i]);
93 }
94}
95
101static float limit_radius(const float3 &position_prev,
102 const float3 &position,
103 const float3 &position_next,
104 const float angle_prev,
105 const float angle,
106 const float angle_next,
107 const float radius_prev,
108 const float radius,
109 const float radius_next)
110{
111 const float displacement = radius * std::tan(angle / 2.0f);
112
113 const float displacement_prev = radius_prev * std::tan(angle_prev / 2.0f);
114 const float segment_length_prev = math::distance(position, position_prev);
115 const float total_displacement_prev = displacement_prev + displacement;
116 const float factor_prev = std::clamp(
117 math::safe_divide(segment_length_prev, total_displacement_prev), 0.0f, 1.0f);
118
119 const float displacement_next = radius_next * std::tan(angle_next / 2.0f);
120 const float segment_length_next = math::distance(position, position_next);
121 const float total_displacement_next = displacement_next + displacement;
122 const float factor_next = std::clamp(
123 math::safe_divide(segment_length_next, total_displacement_next), 0.0f, 1.0f);
124
125 return radius * std::min(factor_prev, factor_next);
126}
127
128static void limit_radii(const Span<float3> positions,
129 const Span<float> angles,
130 const Span<float> radii,
131 const bool cyclic,
132 MutableSpan<float> radii_clamped)
133{
134 if (cyclic) {
135 /* First point. */
136 radii_clamped.first() = limit_radius(positions.last(),
137 positions.first(),
138 positions[1],
139 angles.last(),
140 angles.first(),
141 angles[1],
142 radii.last(),
143 radii.first(),
144 radii[1]);
145 /* All middle points. */
146 for (const int i : positions.index_range().drop_back(1).drop_front(1)) {
147 const int i_prev = i - 1;
148 const int i_next = i + 1;
149 radii_clamped[i] = limit_radius(positions[i_prev],
150 positions[i],
151 positions[i_next],
152 angles[i_prev],
153 angles[i],
154 angles[i_next],
155 radii[i_prev],
156 radii[i],
157 radii[i_next]);
158 }
159 /* Last point. */
160 radii_clamped.last() = limit_radius(positions.last(1),
161 positions.last(),
162 positions.first(),
163 angles.last(1),
164 angles.last(),
165 angles.first(),
166 radii.last(1),
167 radii.last(),
168 radii.first());
169 }
170 else {
171 const int i_last = positions.index_range().last();
172 /* First point. */
173 radii_clamped.first() = 0.0f;
174 /* All middle points. */
175 for (const int i : positions.index_range().drop_back(1).drop_front(1)) {
176 const int i_prev = i - 1;
177 const int i_next = i + 1;
178 /* Use a zero radius for the first and last points, because they don't have fillets.
179 * This logic could potentially be unrolled, but it doesn't seem worth it. */
180 const float radius_prev = i_prev == 0 ? 0.0f : radii[i_prev];
181 const float radius_next = i_next == i_last ? 0.0f : radii[i_next];
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 radius_prev,
189 radii[i],
190 radius_next);
191 }
192 /* Last point. */
193 radii_clamped.last() = 0.0f;
194 }
195}
196
197static void calculate_fillet_positions(const Span<float3> src_positions,
198 const Span<float> angles,
199 const Span<float> radii,
200 const Span<float3> directions,
201 const OffsetIndices<int> dst_offsets,
203{
204 const int i_src_last = src_positions.index_range().last();
205 threading::parallel_for(src_positions.index_range(), 512, [&](IndexRange range) {
206 for (const int i_src : range) {
207 const IndexRange arc = dst_offsets[i_src];
208 const float3 &src = src_positions[i_src];
209 if (arc.size() == 1) {
210 dst[arc.first()] = src;
211 continue;
212 }
213
214 const int i_src_prev = i_src == 0 ? i_src_last : i_src - 1;
215 const float angle = angles[i_src];
216 const float radius = radii[i_src];
217 const float displacement = radius * std::tan(angle / 2.0f);
218 const float3 prev_dir = -directions[i_src_prev];
219 const float3 &next_dir = directions[i_src];
220 const float3 arc_start = src + prev_dir * displacement;
221 const float3 arc_end = src + next_dir * displacement;
222
223 dst[arc.first()] = arc_start;
224 dst[arc.last()] = arc_end;
225
226 const IndexRange middle = arc.drop_front(1).drop_back(1);
227 if (middle.is_empty()) {
228 continue;
229 }
230
231 const float3 axis = -math::normalize(math::cross(prev_dir, next_dir));
232 const float3 center_direction = math::normalize(math::midpoint(next_dir, prev_dir));
233 const float distance_to_center = std::sqrt(pow2f(radius) + pow2f(displacement));
234 const float3 center = src + center_direction * distance_to_center;
235
236 /* Rotate each middle fillet point around the center. */
237 const float segment_angle = angle / (middle.size() + 1);
238 for (const int i : IndexRange(middle.size())) {
239 const int point_i = middle[i];
240 dst[point_i] = math::rotate_around_axis(arc_start, center, axis, segment_angle * (i + 1));
241 }
242 }
243 });
244}
245
252 const Span<float3> src_handles_r,
253 const Span<int8_t> src_types_l,
254 const Span<int8_t> src_types_r,
255 const Span<float> angles,
256 const Span<float> radii,
257 const Span<float3> directions,
258 const OffsetIndices<int> dst_offsets,
259 const Span<float3> dst_positions,
260 MutableSpan<float3> dst_handles_l,
261 MutableSpan<float3> dst_handles_r,
262 MutableSpan<int8_t> dst_types_l,
263 MutableSpan<int8_t> dst_types_r)
264{
265 const int i_src_last = src_handles_l.index_range().last();
266 const int i_dst_last = dst_positions.index_range().last();
267 threading::parallel_for(src_handles_l.index_range(), 512, [&](IndexRange range) {
268 for (const int i_src : range) {
269 const IndexRange arc = dst_offsets[i_src];
270 if (arc.size() == 1) {
271 dst_handles_l[arc.first()] = src_handles_l[i_src];
272 dst_handles_r[arc.first()] = src_handles_r[i_src];
273 dst_types_l[arc.first()] = src_types_l[i_src];
274 dst_types_r[arc.first()] = src_types_r[i_src];
275 continue;
276 }
277 BLI_assert(arc.size() == 2);
278 const int i_dst_a = arc.first();
279 const int i_dst_b = arc.last();
280
281 const int i_src_prev = i_src == 0 ? i_src_last : i_src - 1;
282 const float angle = angles[i_src];
283 const float radius = radii[i_src];
284 const float3 prev_dir = -directions[i_src_prev];
285 const float3 &next_dir = directions[i_src];
286
287 const float3 &arc_start = dst_positions[arc.first()];
288 const float3 &arc_end = dst_positions[arc.last()];
289
290 /* Calculate the point's handles on the outside of the fillet segment,
291 * connecting to the next or previous result points. */
292 const int i_dst_prev = i_dst_a == 0 ? i_dst_last : i_dst_a - 1;
293 const int i_dst_next = i_dst_b == i_dst_last ? 0 : i_dst_b + 1;
294 dst_handles_l[i_dst_a] = bke::curves::bezier::calculate_vector_handle(
295 dst_positions[i_dst_a], dst_positions[i_dst_prev]);
296 dst_handles_r[i_dst_b] = bke::curves::bezier::calculate_vector_handle(
297 dst_positions[i_dst_b], dst_positions[i_dst_next]);
298 dst_types_l[i_dst_a] = BEZIER_HANDLE_VECTOR;
299 dst_types_r[i_dst_b] = BEZIER_HANDLE_VECTOR;
300
301 /* The inner handles are aligned with the aligned with the outer vector
302 * handles, but have a specific length to best approximate a circle. */
303 const float handle_length = (4.0f / 3.0f) * radius * std::tan(angle / 4.0f);
304 dst_handles_r[i_dst_a] = arc_start - prev_dir * handle_length;
305 dst_handles_l[i_dst_b] = arc_end - next_dir * handle_length;
306 dst_types_r[i_dst_a] = BEZIER_HANDLE_ALIGN;
307 dst_types_l[i_dst_b] = BEZIER_HANDLE_ALIGN;
308 }
309 });
310}
311
316static void calculate_bezier_handles_poly_mode(const Span<float3> src_handles_l,
317 const Span<float3> src_handles_r,
318 const Span<int8_t> src_types_l,
319 const Span<int8_t> src_types_r,
320 const OffsetIndices<int> dst_offsets,
321 const Span<float3> dst_positions,
322 MutableSpan<float3> dst_handles_l,
323 MutableSpan<float3> dst_handles_r,
324 MutableSpan<int8_t> dst_types_l,
325 MutableSpan<int8_t> dst_types_r)
326{
327 const int i_dst_last = dst_positions.index_range().last();
328 threading::parallel_for(src_handles_l.index_range(), 512, [&](IndexRange range) {
329 for (const int i_src : range) {
330 const IndexRange arc = dst_offsets[i_src];
331 if (arc.size() == 1) {
332 dst_handles_l[arc.first()] = src_handles_l[i_src];
333 dst_handles_r[arc.first()] = src_handles_r[i_src];
334 dst_types_l[arc.first()] = src_types_l[i_src];
335 dst_types_r[arc.first()] = src_types_r[i_src];
336 continue;
337 }
338
339 /* The fillet's next and previous handles are vector handles, as are the inner handles. */
340 dst_types_l.slice(arc).fill(BEZIER_HANDLE_VECTOR);
341 dst_types_r.slice(arc).fill(BEZIER_HANDLE_VECTOR);
342
343 /* Calculate the point's handles on the outside of the fillet segment. This point
344 * won't be selected for a fillet if it is the first or last in a non-cyclic curve. */
345
346 const int i_dst_prev = arc.first() == 0 ? i_dst_last : arc.one_before_start();
347 const int i_dst_next = arc.last() == i_dst_last ? 0 : arc.one_after_last();
348 dst_handles_l[arc.first()] = bke::curves::bezier::calculate_vector_handle(
349 dst_positions[arc.first()], dst_positions[i_dst_prev]);
350 dst_handles_r[arc.last()] = bke::curves::bezier::calculate_vector_handle(
351 dst_positions[arc.last()], dst_positions[i_dst_next]);
352
353 /* Set the values for the inner handles. */
354 const IndexRange middle = arc.drop_front(1).drop_back(1);
355 for (const int i : middle) {
356 dst_handles_r[i] = bke::curves::bezier::calculate_vector_handle(dst_positions[i],
357 dst_positions[i - 1]);
358 dst_handles_l[i] = bke::curves::bezier::calculate_vector_handle(dst_positions[i],
359 dst_positions[i + 1]);
360 }
361 }
362 });
363}
364
366 const IndexMask &curve_selection,
367 const VArray<float> &radius_input,
368 const VArray<int> &counts,
369 const bool limit_radius,
370 const bool use_bezier_mode,
371 const bke::AttributeFilter &attribute_filter)
372{
373 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
374 const Span<float3> positions = src_curves.positions();
375 const VArraySpan<bool> cyclic{src_curves.cyclic()};
376 const bke::AttributeAccessor src_attributes = src_curves.attributes();
377 IndexMaskMemory memory;
378 const IndexMask unselected = curve_selection.complement(src_curves.curves_range(), memory);
379
381 /* Stores the offset of every result point for every original point.
382 * The extra length is used in order to store an extra zero for every curve. */
383 Array<int> dst_point_offsets(src_curves.points_num() + src_curves.curves_num());
384 calculate_result_offsets(src_points_by_curve,
385 curve_selection,
386 unselected,
387 radius_input,
388 counts,
389 cyclic,
390 dst_curves.offsets_for_write(),
391 dst_point_offsets);
392 const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
393 const Span<int> all_point_offsets = dst_point_offsets.as_span();
394
395 dst_curves.resize(dst_curves.offsets().last(), dst_curves.curves_num());
396 bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
397 MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
398
399 VArraySpan<int8_t> src_types_l;
400 VArraySpan<int8_t> src_types_r;
401 Span<float3> src_handles_l;
402 Span<float3> src_handles_r;
403 MutableSpan<int8_t> dst_types_l;
404 MutableSpan<int8_t> dst_types_r;
405 MutableSpan<float3> dst_handles_l;
406 MutableSpan<float3> dst_handles_r;
407 if (src_curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
408 src_types_l = src_curves.handle_types_left();
409 src_types_r = src_curves.handle_types_right();
410 src_handles_l = src_curves.handle_positions_left();
411 src_handles_r = src_curves.handle_positions_right();
412
413 dst_types_l = dst_curves.handle_types_left_for_write();
414 dst_types_r = dst_curves.handle_types_right_for_write();
415 dst_handles_l = dst_curves.handle_positions_left_for_write();
416 dst_handles_r = dst_curves.handle_positions_right_for_write();
417 }
418
419 curve_selection.foreach_segment(GrainSize(512), [&](const IndexMaskSegment segment) {
420 Array<float3> directions;
421 Array<float> angles;
422 Array<float> radii;
423 Array<float> input_radii_buffer;
424
425 for (const int curve_i : segment) {
426 const IndexRange src_points = src_points_by_curve[curve_i];
427 const IndexRange offsets_range = bke::curves::per_curve_point_offsets_range(src_points,
428 curve_i);
429 const OffsetIndices<int> offsets(all_point_offsets.slice(offsets_range));
430 const IndexRange dst_points = dst_points_by_curve[curve_i];
431 const Span<float3> src_positions = positions.slice(src_points);
432
433 directions.reinitialize(src_points.size());
434 calculate_directions(src_positions, directions);
435
436 angles.reinitialize(src_points.size());
437 calculate_angles(directions, angles);
438
439 radii.reinitialize(src_points.size());
440 if (limit_radius) {
441 input_radii_buffer.reinitialize(src_points.size());
442 radius_input.materialize_compressed(src_points, input_radii_buffer);
443 limit_radii(src_positions, angles, input_radii_buffer, cyclic[curve_i], radii);
444 }
445 else {
446 radius_input.materialize_compressed(src_points, radii);
447 }
448
449 calculate_fillet_positions(positions.slice(src_points),
450 angles,
451 radii,
452 directions,
453 offsets,
454 dst_positions.slice(dst_points));
455
456 if (src_curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
457 if (use_bezier_mode) {
458 calculate_bezier_handles_bezier_mode(src_handles_l.slice(src_points),
459 src_handles_r.slice(src_points),
460 src_types_l.slice(src_points),
461 src_types_r.slice(src_points),
462 angles,
463 radii,
464 directions,
465 offsets,
466 dst_positions.slice(dst_points),
467 dst_handles_l.slice(dst_points),
468 dst_handles_r.slice(dst_points),
469 dst_types_l.slice(dst_points),
470 dst_types_r.slice(dst_points));
471 }
472 else {
473 calculate_bezier_handles_poly_mode(src_handles_l.slice(src_points),
474 src_handles_r.slice(src_points),
475 src_types_l.slice(src_points),
476 src_types_r.slice(src_points),
477 offsets,
478 dst_positions.slice(dst_points),
479 dst_handles_l.slice(dst_points),
480 dst_handles_r.slice(dst_points),
481 dst_types_l.slice(dst_points),
482 dst_types_r.slice(dst_points));
483 }
484 }
485 }
486 });
487
489 src_attributes,
490 dst_attributes,
493 {"position",
494 "handle_type_left",
495 "handle_type_right",
496 "handle_right",
497 "handle_left"})))
498 {
499 duplicate_fillet_point_data(src_points_by_curve,
500 dst_points_by_curve,
501 curve_selection,
502 all_point_offsets,
503 attribute.src,
504 attribute.dst.span);
505 attribute.dst.finish();
506 }
507
511 attribute_filter,
512 src_points_by_curve,
513 dst_points_by_curve,
514 unselected,
515 dst_attributes);
516
517 return dst_curves;
518}
519
521 const IndexMask &curve_selection,
522 const VArray<float> &radius,
523 const VArray<int> &count,
524 const bool limit_radius,
525 const bke::AttributeFilter &attribute_filter)
526{
527 return fillet_curves(
528 src_curves, curve_selection, radius, count, limit_radius, false, attribute_filter);
529}
530
532 const IndexMask &curve_selection,
533 const VArray<float> &radius,
534 const bool limit_radius,
535 const bke::AttributeFilter &attribute_filter)
536{
537 return fillet_curves(src_curves,
538 curve_selection,
539 radius,
540 VArray<int>::ForSingle(1, src_curves.points_num()),
542 true,
543 attribute_filter);
544}
545
546} // namespace blender::geometry
@ ATTR_DOMAIN_MASK_POINT
Low-level operations for curves.
Low-level operations for curves.
#define M_PI
float angle_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
@ CURVE_TYPE_BEZIER
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:125
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 Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture 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
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:326
Span< T > as_span() const
Definition BLI_array.hh:232
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:388
GMutableSpan slice(const int64_t start, int64_t size) const
GSpan slice(const int64_t start, int64_t size) const
constexpr IndexRange drop_back(int64_t n) const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
constexpr IndexRange drop_front(int64_t n) const
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:574
constexpr MutableSpan drop_back(const int64_t n) const
Definition BLI_span.hh:619
constexpr T & first() const
Definition BLI_span.hh:680
constexpr T & last(const int64_t n=0) const
Definition BLI_span.hh:690
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:138
constexpr const T & first() const
Definition BLI_span.hh:316
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:326
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
void materialize_compressed(const IndexMask &mask, MutableSpan< T > r_span) const
static VArray ForSingle(T value, const int64_t size)
VArray< int8_t > handle_types_left() const
MutableSpan< float3 > positions_for_write()
OffsetIndices< int > points_by_curve() const
MutableSpan< int8_t > handle_types_right_for_write()
VArray< int8_t > handle_types_right() const
IndexRange curves_range() const
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
void resize(int points_num, int curves_num)
Span< float3 > handle_positions_right() const
MutableSpan< int > offsets_for_write()
VArray< bool > cyclic() const
MutableSpan< int8_t > handle_types_left_for_write()
IndexMask complement(const IndexMask &universe, IndexMaskMemory &memory) const
void foreach_index(Fn &&fn) const
void foreach_segment(Fn &&fn) const
int count
void gather_to_groups(OffsetIndices< int > dst_offsets, const IndexMask &src_selection, GSpan src, GMutableSpan dst)
bke::CurvesGeometry copy_only_curve_domain(const bke::CurvesGeometry &src_curves)
IndexRange per_curve_point_offsets_range(const IndexRange points, const int curve_index)
Vector< AttributeTransferData > retrieve_attributes_for_transfer(const AttributeAccessor src_attributes, MutableAttributeAccessor dst_attributes, AttrDomainMask domain_mask, const AttributeFilter &attribute_filter={})
auto attribute_filter_with_skip_ref(AttributeFilter filter, const Span< StringRef > skip)
void copy_attributes_group_to_group(AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, OffsetIndices< int > src_offsets, OffsetIndices< int > dst_offsets, const IndexMask &selection, MutableAttributeAccessor dst_attributes)
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 OffsetIndices< 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)
bke::CurvesGeometry fillet_curves_bezier(const bke::CurvesGeometry &src_curves, const IndexMask &curve_selection, const VArray< float > &radius, bool limit_radius, const bke::AttributeFilter &attribute_filter)
static void calculate_result_offsets(const OffsetIndices< int > src_points_by_curve, const IndexMask &selection, const IndexMask &unselected, const VArray< float > &radii, const VArray< int > &counts, const Span< bool > cyclic, MutableSpan< int > dst_curve_offsets, MutableSpan< int > dst_point_offsets)
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)
bke::CurvesGeometry fillet_curves_poly(const bke::CurvesGeometry &src_curves, const IndexMask &curve_selection, const VArray< float > &radius, const VArray< int > &counts, bool limit_radius, const bke::AttributeFilter &attribute_filter)
static void calculate_fillet_positions(const Span< float3 > src_positions, const Span< float > angles, const Span< float > radii, const Span< float3 > directions, const OffsetIndices< int > dst_offsets, MutableSpan< float3 > dst)
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, const bke::AttributeFilter &attribute_filter)
static void calculate_directions(const Span< float3 > positions, MutableSpan< float3 > directions)
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 OffsetIndices< 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 duplicate_fillet_point_data(const OffsetIndices< int > src_points_by_curve, const OffsetIndices< int > dst_points_by_curve, const IndexMask &curve_selection, const Span< int > all_point_offsets, const GSpan src, GMutableSpan dst)
T safe_divide(const T &a, const T &b)
T distance(const T &a, const T &b)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
void copy_group_sizes(OffsetIndices< int > offsets, const IndexMask &mask, MutableSpan< int > sizes)
OffsetIndices< int > accumulate_counts_to_offsets(MutableSpan< int > counts_to_offsets, int start_offset=0)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
void devirtualize_varray(const VArray< T > &varray, const Func &func, bool enable=true)
VecBase< float, 3 > float3
GPU_SHADER_INTERFACE_INFO(overlay_edit_curve_handle_iface, "vert").flat(Type pos vertex_in(1, Type::UINT, "data") .vertex_out(overlay_edit_curve_handle_iface) .geometry_layout(PrimitiveIn Frequency::GEOMETRY storage_buf(1, Qualifier::READ, "uint", "data[]", Frequency::GEOMETRY) .push_constant(Type Frequency::GEOMETRY selection[]