24 const IndexRange src_points = src_points_by_curve[curve_i];
25 const IndexRange dst_points = dst_points_by_curve[curve_i];
30 src.
slice(src_points),
31 dst.
slice(dst_points));
46 selection.foreach_index(
GrainSize(512), [&](
const int curve_i) {
47 const IndexRange src_points = src_points_by_curve[curve_i];
55 for (
int &
count : point_counts) {
59 if (!cyclic[curve_i]) {
61 point_counts.
first() = 1;
62 point_counts.
last() = 1;
67 if (radii[src_points[i]] == 0.0f) {
75 dst_curve_offsets[curve_i] = point_offsets.
last();
82 for (
const int i : positions.index_range().drop_back(1)) {
103 const float3 &position_next,
104 const float angle_prev,
106 const float angle_next,
107 const float radius_prev,
109 const float radius_next)
111 const float displacement = radius * std::tan(angle / 2.0f);
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(
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(
125 return radius * std::min(factor_prev, factor_next);
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;
171 const int i_last = positions.index_range().last();
173 radii_clamped.
first() = 0.0f;
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;
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];
193 radii_clamped.
last() = 0.0f;
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;
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;
223 dst[arc.first()] = arc_start;
224 dst[arc.last()] = arc_end;
226 const IndexRange middle = arc.drop_front(1).drop_back(1);
227 if (middle.is_empty()) {
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;
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));
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];
277 BLI_assert(arc.size() == 2);
278 const int i_dst_a = arc.first();
279 const int i_dst_b = arc.last();
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];
287 const float3 &arc_start = dst_positions[arc.first()];
288 const float3 &arc_end = dst_positions[arc.last()];
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;
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;
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];
340 dst_types_l.slice(arc).fill(BEZIER_HANDLE_VECTOR);
341 dst_types_r.slice(arc).fill(BEZIER_HANDLE_VECTOR);
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]);
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]);
369 const bool limit_radius,
370 const bool use_bezier_mode,
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,
430 const IndexRange dst_points = dst_points_by_curve[curve_i];
436 angles.reinitialize(src_points.
size());
439 radii.reinitialize(src_points.
size());
443 limit_radii(src_positions, angles, input_radii_buffer, cyclic[curve_i], radii);
454 dst_positions.
slice(dst_points));
457 if (use_bezier_mode) {
459 src_handles_r.
slice(src_points),
460 src_types_l.
slice(src_points),
461 src_types_r.
slice(src_points),
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));
474 src_handles_r.
slice(src_points),
475 src_types_l.
slice(src_points),
476 src_types_r.
slice(src_points),
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));
488 for (
auto &attribute : bke::retrieve_attributes_for_transfer(
492 bke::attribute_filter_with_skip_ref(attribute_filter,
505 attribute.dst.finish();
508 bke::copy_attributes_group_to_group(src_attributes,
509 bke::AttrDomain::Point,
510 bke::AttrDomain::Point,
524 const bool limit_radius,
528 src_curves, curve_selection, radius,
count,
limit_radius,
false, attribute_filter);
534 const bool limit_radius,
Low-level operations for curves.
Low-level operations for curves.
float angle_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
Span< T > as_span() const
void reinitialize(const int64_t new_size)
GMutableSpan slice(const int64_t start, int64_t size) const
GSpan slice(const int64_t start, int64_t size) 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
constexpr MutableSpan drop_back(const int64_t n) const
constexpr T & first() const
constexpr T & last(const int64_t n=0) const
constexpr Span slice(int64_t start, int64_t size) const
constexpr const T & first() const
constexpr const T & last(const int64_t n=0) const
constexpr IndexRange index_range() const
void materialize_compressed(const IndexMask &mask, MutableSpan< T > r_span) const
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< int > offsets() 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
AttributeAccessor attributes() 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
void gather_to_groups(OffsetIndices< int > dst_offsets, const IndexMask &src_selection, GSpan src, GMutableSpan dst)
IndexRange per_curve_point_offsets_range(const IndexRange points, const int curve_index)
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))
void devirtualize_varray(const VArray< T > &varray, const Func &func, bool enable=true)