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
This commit is contained in:
Falk David 2024-02-29 12:51:15 +01:00 committed by Falk David
parent 1bff17cc99
commit 584d26106a
2 changed files with 130 additions and 28 deletions

View File

@ -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<int> &counts,
const ResampleCurvesOutputAttributeIDs &output_ids = {});
CurvesGeometry resample_to_count(const CurvesGeometry &src_curves,
const fn::FieldContext &field_context,
const fn::Field<bool> &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<float> &sample_lengths,
const ResampleCurvesOutputAttributeIDs &output_ids = {});
CurvesGeometry resample_to_length(const CurvesGeometry &src_curves,
const fn::FieldContext &field_context,
const fn::Field<bool> &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<bool> &selection_field,

View File

@ -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<bool> &selection_field,
const fn::Field<int> &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<bool> curves_cyclic = src_curves.cyclic();
const VArray<int8_t> curve_types = src_curves.curve_types();
const Span<float3> evaluated_positions = src_curves.evaluated_positions();
CurvesGeometry dst_curves = bke::curves::copy_only_curve_domain(src_curves);
MutableSpan<int> 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<bool> &selection_field,
const fn::Field<int> &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<int> 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<int> &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<int> 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<float> &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<bool> curves_cyclic = src_curves.cyclic();
CurvesGeometry dst_curves = bke::curves::copy_only_curve_domain(src_curves);
MutableSpan<int> 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<bool> &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<bool> &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<float3> 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<bool> &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