24 const bool smooth_ends,
25 const bool keep_shape,
58 if (src.
size() == 1) {
63 const int n_half = keep_shape ? (iterations * iterations) / 8 + iterations :
64 (iterations * iterations) / 4 + 2 * iterations + 12;
65 double w = keep_shape ? 2.0 : 1.0;
66 double w2 = keep_shape ?
72 const int64_t last_pt = total_points - 1;
74 auto is_end_and_fixed = [smooth_ends,
is_cyclic, last_pt](
int index) {
80 for (const int64_t index : range) {
81 if (!is_end_and_fixed(index)) {
89 const int64_t offset = iterations - step;
91 for (const int64_t index : range) {
93 if (is_end_and_fixed(index)) {
97 double w_before = w - w2;
98 double w_after = w - w2;
101 int64_t before = index - offset;
102 int64_t after = index + offset;
104 before = (before % total_points + total_points) % total_points;
105 after = after % total_points;
108 if (!smooth_ends && (before < 0)) {
109 w_before *= -before / float(index);
111 before = math::max(before, int64_t(0));
113 if (!smooth_ends && (after > last_pt)) {
114 w_after *= (after - (total_points - 1)) / float(total_points - 1 - index);
116 after = math::min(after, last_pt);
120 const T bval = src[before];
121 const T aval = src[after];
122 const T cval = src[index];
124 dst[index] += (bval - cval) * w_before;
125 dst[index] += (aval - cval) * w_after;
128 total_weight[index] += w_before;
129 total_weight[index] += w_after;
133 w *= (n_half + offset) /
double(n_half + 1 - offset);
134 w2 *= (n_half * 3 + offset) /
double(n_half * 3 + 1 - offset);
139 threading::parallel_for(dst.index_range(), 1024, [&](
const IndexRange range) {
140 for (const int64_t index : range) {
141 if (!is_end_and_fixed(index)) {
142 total_weight[index] += w - w2;
143 dst[index] = src[index] + influence_by_point[index] * dst[index] / total_weight[index];
151 const int iterations,
153 const bool smooth_ends,
154 const bool keep_shape,
158 bke::attribute_math::convert_to_static_type(src.
type(), [&](
auto dummy) {
159 using T = decltype(dummy);
162 if constexpr (std::is_same_v<T, float> || std::is_same_v<T, float2> ||
163 std::is_same_v<T, float3>)
165 gaussian_blur_1D(src.typed<T>(),
239 const int iterations,
241 const bool smooth_ends,
242 const bool keep_shape)
245 const OffsetIndices points_by_curve = curves.points_by_curve();
247 const VArray<bool> point_selection = *curves.attributes().lookup_or_default<
bool>(
248 ".selection", bke::AttrDomain::Point,
true);
264 const IndexMask bezier_curves_to_smooth = curves.indices_for_curve_type(
270 Array<float3> all_positions = bke::curves::bezier::retrieve_all_positions(
271 curves, bezier_curves_to_smooth);
279 const IndexMask selection_mask = IndexMask::from_bools(points, point_selection, memory);
285 IndexRange positions_range(range.start() * 3, range.size() * 3);
287 if (!smooth_ends && !cyclic[curve]) {
297 if (!smooth_ends && !cyclic[curve]) {
298 threading::parallel_for(
300 for (const int index : influences_range) {
303 point_influences[index] = influences.slice(range)[(index + 1) / 3];
308 threading::parallel_for(
309 positions_range.index_range(), 4096, [&](
const IndexRange influences_range) {
310 for (const int index : influences_range) {
311 point_influences[index] = influences.slice(range)[index / 3];
328 bke::curves::bezier::write_all_positions(curves, bezier_curves_to_smooth, all_positions);
332 curves.curves_range(), memory);
333 if (!other_curves_to_smooth.
is_empty()) {
335 smooth_curve_attribute(other_curves_to_smooth,
347 curves.calculate_bezier_auto_handles();
350 curves.tag_positions_changed();
void smooth_curve_attribute(const IndexMask &curves_to_smooth, const OffsetIndices< int > points_by_curve, const VArray< bool > &point_selection, const VArray< bool > &cyclic, int iterations, float influence, bool smooth_ends, bool keep_shape, GMutableSpan attribute_data)
void smooth_curve_positions(bke::CurvesGeometry &curves, const IndexMask &curves_to_smooth, int iterations, const VArray< float > &influence_by_point, bool smooth_ends, bool keep_shape)
void gaussian_blur_1D(const GSpan src, int iterations, const VArray< float > &influence_by_point, const bool smooth_ends, const bool keep_shape, const bool is_cyclic, GMutableSpan dst)