From 584d26106a0595dbbbbee5b2558f285ee9456751 Mon Sep 17 00:00:00 2001 From: Falk David Date: Thu, 29 Feb 2024 12:51:15 +0100 Subject: [PATCH] Curves: Provide resample functions that don't depend on field evaluation This splits the core part of `resample_to_uniform` into it's own function, so that it doesn't depend on field evaluation. From there, new versions of `resample_to_count`, `resample_to_length`, and `resample_to_evaluated` are provided. Pull Request: https://projects.blender.org/blender/blender/pulls/118551 --- .../blender/geometry/GEO_resample_curves.hh | 11 ++ .../geometry/intern/resample_curves.cc | 147 ++++++++++++++---- 2 files changed, 130 insertions(+), 28 deletions(-) diff --git a/source/blender/geometry/GEO_resample_curves.hh b/source/blender/geometry/GEO_resample_curves.hh index dab10c75a06..4320947dd48 100644 --- a/source/blender/geometry/GEO_resample_curves.hh +++ b/source/blender/geometry/GEO_resample_curves.hh @@ -25,6 +25,10 @@ struct ResampleCurvesOutputAttributeIDs { * * \note The values provided by the #count_field are clamped to 1 or greater. */ +CurvesGeometry resample_to_count(const CurvesGeometry &src_curves, + const IndexMask &selection, + const VArray &counts, + const ResampleCurvesOutputAttributeIDs &output_ids = {}); CurvesGeometry resample_to_count(const CurvesGeometry &src_curves, const fn::FieldContext &field_context, const fn::Field &selection_field, @@ -36,6 +40,10 @@ CurvesGeometry resample_to_count(const CurvesGeometry &src_curves, * #segment_length field input, rounded to make the length of each segment the same. * The accuracy will depend on the curve's resolution parameter. */ +CurvesGeometry resample_to_length(const CurvesGeometry &src_curves, + const IndexMask &selection, + const VArray &sample_lengths, + const ResampleCurvesOutputAttributeIDs &output_ids = {}); CurvesGeometry resample_to_length(const CurvesGeometry &src_curves, const fn::FieldContext &field_context, const fn::Field &selection_field, @@ -45,6 +53,9 @@ CurvesGeometry resample_to_length(const CurvesGeometry &src_curves, /** * Evaluate each selected curve to its implicit evaluated points. */ +CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves, + const IndexMask &selection, + const ResampleCurvesOutputAttributeIDs &output_ids = {}); CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves, const fn::FieldContext &field_context, const fn::Field &selection_field, diff --git a/source/blender/geometry/intern/resample_curves.cc b/source/blender/geometry/intern/resample_curves.cc index 6c530fbab87..0f778f73e66 100644 --- a/source/blender/geometry/intern/resample_curves.cc +++ b/source/blender/geometry/intern/resample_curves.cc @@ -245,37 +245,21 @@ static void normalize_curve_point_data(const IndexMaskSegment curve_selection, } } -static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves, - const fn::FieldContext &field_context, - const fn::Field &selection_field, - const fn::Field &count_field, - const ResampleCurvesOutputAttributeIDs &output_ids) +static void resample_to_uniform(const CurvesGeometry &src_curves, + const IndexMask &selection, + const ResampleCurvesOutputAttributeIDs &output_ids, + CurvesGeometry &dst_curves) { if (src_curves.curves_range().is_empty()) { - return {}; + return; } + const OffsetIndices src_points_by_curve = src_curves.points_by_curve(); const OffsetIndices evaluated_points_by_curve = src_curves.evaluated_points_by_curve(); const VArray curves_cyclic = src_curves.cyclic(); const VArray curve_types = src_curves.curve_types(); const Span evaluated_positions = src_curves.evaluated_positions(); - CurvesGeometry dst_curves = bke::curves::copy_only_curve_domain(src_curves); - MutableSpan dst_offsets = dst_curves.offsets_for_write(); - - fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; - evaluator.set_selection(selection_field); - evaluator.add_with_destination(count_field, dst_offsets.drop_back(1)); - evaluator.evaluate(); - const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); - IndexMaskMemory memory; - const IndexMask unselected = selection.complement(src_curves.curves_range(), memory); - - /* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */ - offset_indices::copy_group_sizes(src_points_by_curve, unselected, dst_offsets); - offset_indices::accumulate_counts_to_offsets(dst_offsets); - dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); - /* All resampled curves are poly curves. */ dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY); @@ -385,11 +369,74 @@ static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves, } }); + IndexMaskMemory memory; + const IndexMask unselected = selection.complement(src_curves.curves_range(), memory); copy_or_defaults_for_unselected_curves(src_curves, unselected, attributes, dst_curves); for (bke::GSpanAttributeWriter &attribute : attributes.dst_attributes) { attribute.finish(); } +} + +static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves, + const fn::FieldContext &field_context, + const fn::Field &selection_field, + const fn::Field &count_field, + const ResampleCurvesOutputAttributeIDs &output_ids) +{ + if (src_curves.curves_range().is_empty()) { + return {}; + } + const OffsetIndices src_points_by_curve = src_curves.points_by_curve(); + + CurvesGeometry dst_curves = bke::curves::copy_only_curve_domain(src_curves); + MutableSpan dst_offsets = dst_curves.offsets_for_write(); + + fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; + evaluator.set_selection(selection_field); + evaluator.add_with_destination(count_field, dst_offsets.drop_back(1)); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + IndexMaskMemory memory; + const IndexMask unselected = selection.complement(src_curves.curves_range(), memory); + + /* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */ + offset_indices::copy_group_sizes(src_points_by_curve, unselected, dst_offsets); + offset_indices::accumulate_counts_to_offsets(dst_offsets); + dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); + + resample_to_uniform(src_curves, selection, output_ids, dst_curves); + + return dst_curves; +} + +CurvesGeometry resample_to_count(const CurvesGeometry &src_curves, + const IndexMask &selection, + const VArray &counts, + const ResampleCurvesOutputAttributeIDs &output_ids) +{ + if (src_curves.curves_range().is_empty()) { + return {}; + } + const OffsetIndices src_points_by_curve = src_curves.points_by_curve(); + + CurvesGeometry dst_curves = bke::curves::copy_only_curve_domain(src_curves); + MutableSpan dst_offsets = dst_curves.offsets_for_write(); + + array_utils::copy(counts, selection, dst_offsets); + + IndexMaskMemory memory; + const IndexMask unselected = selection.complement(src_curves.curves_range(), memory); + + /* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */ + offset_indices::copy_group_sizes(src_points_by_curve, unselected, dst_offsets); + /* We assume the counts are at least 1. */ + BLI_assert(std::all_of( + dst_offsets.begin(), dst_offsets.end(), [&](const int i) { return dst_offsets[i] > 0; })); + offset_indices::accumulate_counts_to_offsets(dst_offsets); + dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); + + resample_to_uniform(src_curves, selection, output_ids, dst_curves); return dst_curves; } @@ -407,6 +454,40 @@ CurvesGeometry resample_to_count(const CurvesGeometry &src_curves, output_ids); } +CurvesGeometry resample_to_length(const CurvesGeometry &src_curves, + const IndexMask &selection, + const VArray &sample_lengths, + const ResampleCurvesOutputAttributeIDs &output_ids) +{ + if (src_curves.curves_range().is_empty()) { + return {}; + } + const OffsetIndices src_points_by_curve = src_curves.points_by_curve(); + const VArray curves_cyclic = src_curves.cyclic(); + + CurvesGeometry dst_curves = bke::curves::copy_only_curve_domain(src_curves); + MutableSpan dst_offsets = dst_curves.offsets_for_write(); + + src_curves.ensure_evaluated_lengths(); + selection.foreach_index(GrainSize(1024), [&](const int curve_i) { + const float curve_length = src_curves.evaluated_length_total_for_curve(curve_i, + curves_cyclic[curve_i]); + dst_offsets[curve_i] = int(curve_length / sample_lengths[curve_i]) + 1; + }); + + IndexMaskMemory memory; + const IndexMask unselected = selection.complement(src_curves.curves_range(), memory); + + /* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */ + offset_indices::copy_group_sizes(src_points_by_curve, unselected, dst_offsets); + offset_indices::accumulate_counts_to_offsets(dst_offsets); + dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); + + resample_to_uniform(src_curves, selection, output_ids, dst_curves); + + return dst_curves; +} + CurvesGeometry resample_to_length(const CurvesGeometry &src_curves, const fn::FieldContext &field_context, const fn::Field &selection_field, @@ -421,8 +502,7 @@ CurvesGeometry resample_to_length(const CurvesGeometry &src_curves, } CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves, - const fn::FieldContext &field_context, - const fn::Field &selection_field, + const IndexMask &selection, const ResampleCurvesOutputAttributeIDs &output_ids) { if (src_curves.curves_range().is_empty()) { @@ -432,10 +512,6 @@ CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves, const OffsetIndices src_evaluated_points_by_curve = src_curves.evaluated_points_by_curve(); const Span evaluated_positions = src_curves.evaluated_positions(); - fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; - evaluator.set_selection(selection_field); - evaluator.evaluate(); - const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); IndexMaskMemory memory; const IndexMask unselected = selection.complement(src_curves.curves_range(), memory); @@ -511,4 +587,19 @@ CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves, return dst_curves; } +CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves, + const fn::FieldContext &field_context, + const fn::Field &selection_field, + const ResampleCurvesOutputAttributeIDs &output_ids) +{ + if (src_curves.curves_range().is_empty()) { + return {}; + } + fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; + evaluator.set_selection(selection_field); + evaluator.evaluate(); + return resample_to_evaluated( + src_curves, evaluator.get_evaluated_selection_as_mask(), output_ids); +} + } // namespace blender::geometry