From 675677ec67c6b922b3a5e602c8bf3dbb562bd687 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 9 Jun 2021 14:53:39 -0500 Subject: Splines: Add API functions for interpolating data First, expand on the interpolation to evaluated points with a templated helper function, and a function that takes a GSPan. Next, add a set of functions to `Spline` for interpolating at arbitrary intervals between the evaluated points. The code for doing that isn't that complicated anyway, but it's nice to avoid repeating, and it might make it easier to unroll the special cases for the first and last points if we require the index factors to be sorted. --- source/blender/blenkernel/BKE_spline.hh | 26 ++++++++ source/blender/blenkernel/intern/spline_base.cc | 41 +++++++++++- source/blender/blenkernel/intern/spline_nurbs.cc | 8 +-- .../geometry/nodes/node_geo_curve_resample.cc | 77 +++++++--------------- .../nodes/geometry/nodes/node_geo_curve_to_mesh.cc | 3 +- 5 files changed, 94 insertions(+), 61 deletions(-) diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index fc1292e3620..dfbe82f31fd 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -172,6 +172,25 @@ class Spline { blender::Array sample_uniform_index_factors(const int samples_size) const; LookupResult lookup_data_from_index_factor(const float index_factor) const; + void sample_based_on_index_factors(const blender::fn::GVArray &src, + blender::Span index_factors, + blender::fn::GMutableSpan dst) const; + template + void sample_based_on_index_factors(const blender::VArray &src, + blender::Span index_factors, + blender::MutableSpan dst) const + { + this->sample_based_on_index_factors( + blender::fn::GVArray_For_VArray(src), index_factors, blender::fn::GMutableSpan(dst)); + } + template + void sample_based_on_index_factors(blender::Span src, + blender::Span index_factors, + blender::MutableSpan dst) const + { + this->sample_based_on_index_factors(blender::VArray_For_Span(src), index_factors, dst); + } + /** * Interpolate a virtual array of data with the size of the number of control points to the * evaluated points. For poly splines, the lifetime of the returned virtual array must not @@ -179,6 +198,13 @@ class Spline { */ virtual blender::fn::GVArrayPtr interpolate_to_evaluated_points( const blender::fn::GVArray &source_data) const = 0; + blender::fn::GVArrayPtr interpolate_to_evaluated_points(blender::fn::GSpan data) const; + template + blender::fn::GVArray_Typed interpolate_to_evaluated_points(blender::Span data) const + { + return blender::fn::GVArray_Typed( + this->interpolate_to_evaluated_points(blender::fn::GSpan(data))); + } protected: virtual void correct_end_tangents() const = 0; diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc index 11620a30948..9f8c215b31f 100644 --- a/source/blender/blenkernel/intern/spline_base.cc +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -16,6 +16,7 @@ #include "BLI_array.hh" #include "BLI_span.hh" +#include "BLI_task.hh" #include "BLI_timeit.hh" #include "BKE_spline.hh" @@ -27,6 +28,12 @@ using blender::float3; using blender::IndexRange; using blender::MutableSpan; using blender::Span; +using blender::fn::GMutableSpan; +using blender::fn::GSpan; +using blender::fn::GVArray; +using blender::fn::GVArray_For_GSpan; +using blender::fn::GVArray_Typed; +using blender::fn::GVArrayPtr; Spline::Type Spline::type() const { @@ -234,8 +241,7 @@ Span Spline::evaluated_normals() const calculate_normals_z_up(tangents, normals); /* Rotate the generated normals with the interpolated tilt data. */ - blender::fn::GVArray_Typed tilts{ - this->interpolate_to_evaluated_points(blender::fn::GVArray_For_Span(this->tilts()))}; + GVArray_Typed tilts = this->interpolate_to_evaluated_points(this->tilts()); for (const int i : normals.index_range()) { normals[i] = rotate_direction_around_axis(normals[i], tangents[i], tilts[i]); } @@ -341,3 +347,34 @@ void Spline::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) minmax_v3v3_v3(min, max, position); } } + +GVArrayPtr Spline::interpolate_to_evaluated_points(GSpan data) const +{ + return this->interpolate_to_evaluated_points(GVArray_For_GSpan(data)); +} + +/** + * Sample any input data with a value for each evaluated point (already interpolated to evaluated + * points) to arbitrary parameters in betwen the evaluated points. The interpolation is quite + * simple, but this handles the cyclic and end point special cases. + */ +void Spline::sample_based_on_index_factors(const GVArray &src, + Span index_factors, + GMutableSpan dst) const +{ + BLI_assert(src.size() == this->evaluated_points_size()); + + blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + const GVArray_Typed src_typed = src.typed(); + MutableSpan dst_typed = dst.typed(); + blender::parallel_for(dst_typed.index_range(), 1024, [&](IndexRange range) { + for (const int i : range) { + const LookupResult interp = this->lookup_data_from_index_factor(index_factors[i]); + dst_typed[i] = blender::attribute_math::mix2(interp.factor, + src_typed[interp.evaluated_index], + src_typed[interp.next_evaluated_index]); + } + }); + }); +} diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc index cae206341a0..bfb0d652b1a 100644 --- a/source/blender/blenkernel/intern/spline_nurbs.cc +++ b/source/blender/blenkernel/intern/spline_nurbs.cc @@ -26,6 +26,7 @@ using blender::float3; using blender::IndexRange; using blender::MutableSpan; using blender::Span; +using blender::fn::GVArray_Typed; SplinePtr NURBSpline::copy() const { @@ -434,10 +435,9 @@ Span NURBSpline::evaluated_positions() const const int eval_size = this->evaluated_points_size(); evaluated_position_cache_.resize(eval_size); - blender::fn::GVArray_Typed evaluated_positions{ - this->interpolate_to_evaluated_points(blender::fn::GVArray_For_Span(positions_))}; - - evaluated_positions->materialize(evaluated_position_cache_); + /* TODO: Avoid copying the evaluated data from the temporary array. */ + GVArray_Typed evaluated = Spline::interpolate_to_evaluated_points(positions_.as_span()); + evaluated->materialize(evaluated_position_cache_); position_cache_dirty_ = false; return evaluated_position_cache_; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index 684f7d6c702..9d0820eb0b0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -76,34 +76,17 @@ struct SampleModeParam { std::optional count; }; -template -static void sample_span_to_output_spline(const Spline &input_spline, - Span index_factors, - const VArray &input_data, - MutableSpan output_data) -{ - BLI_assert(input_data.size() == input_spline.evaluated_points_size()); - - parallel_for(output_data.index_range(), 1024, [&](IndexRange range) { - for (const int i : range) { - const Spline::LookupResult interp = input_spline.lookup_data_from_index_factor( - index_factors[i]); - output_data[i] = blender::attribute_math::mix2(interp.factor, - input_data[interp.evaluated_index], - input_data[interp.next_evaluated_index]); - } - }); -} - static SplinePtr resample_spline(const Spline &input_spline, const int count) { std::unique_ptr output_spline = std::make_unique(); output_spline->set_cyclic(input_spline.is_cyclic()); output_spline->normal_mode = input_spline.normal_mode; - if (input_spline.evaluated_edges_size() < 1) { - output_spline->resize(1); - output_spline->positions().first() = input_spline.positions().first(); + if (input_spline.evaluated_edges_size() < 1 || count == 1) { + output_spline->add_point(input_spline.positions().first(), + input_spline.tilts().first(), + input_spline.radii().first()); + output_spline->attributes.reallocate(1); return output_spline; } @@ -111,26 +94,18 @@ static SplinePtr resample_spline(const Spline &input_spline, const int count) Array uniform_samples = input_spline.sample_uniform_index_factors(count); - { - GVArray_For_Span positions(input_spline.evaluated_positions()); - GVArray_Typed positions_typed(positions); - sample_span_to_output_spline( - input_spline, uniform_samples, positions_typed, output_spline->positions()); - } - { - GVArrayPtr interpolated_data = input_spline.interpolate_to_evaluated_points( - GVArray_For_Span(input_spline.radii())); - GVArray_Typed interpolated_data_typed{*interpolated_data}; - sample_span_to_output_spline( - input_spline, uniform_samples, interpolated_data_typed, output_spline->radii()); - } - { - GVArrayPtr interpolated_data = input_spline.interpolate_to_evaluated_points( - GVArray_For_Span(input_spline.tilts())); - GVArray_Typed interpolated_data_typed{*interpolated_data}; - sample_span_to_output_spline( - input_spline, uniform_samples, interpolated_data_typed, output_spline->tilts()); - } + input_spline.sample_based_on_index_factors( + input_spline.evaluated_positions(), uniform_samples, output_spline->positions()); + + input_spline.sample_based_on_index_factors( + input_spline.interpolate_to_evaluated_points(input_spline.radii()), + uniform_samples, + output_spline->radii()); + + input_spline.sample_based_on_index_factors( + input_spline.interpolate_to_evaluated_points(input_spline.tilts()), + uniform_samples, + output_spline->tilts()); output_spline->attributes.reallocate(count); input_spline.attributes.foreach_attribute( @@ -147,16 +122,12 @@ static SplinePtr resample_spline(const Spline &input_spline, const int count) BLI_assert_unreachable(); return false; } - GVArrayPtr interpolated_attribute = input_spline.interpolate_to_evaluated_points( - GVArray_For_GSpan(*input_attribute)); - attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) { - using T = decltype(dummy); - GVArray_Typed interpolated_attribute_typed{*interpolated_attribute}; - sample_span_to_output_spline(input_spline, - uniform_samples, - interpolated_attribute_typed, - (*output_attribute).typed()); - }); + + input_spline.sample_based_on_index_factors( + *input_spline.interpolate_to_evaluated_points(*input_attribute), + uniform_samples, + *output_attribute); + return true; }, ATTR_DOMAIN_POINT); @@ -177,7 +148,7 @@ static std::unique_ptr resample_curve(const CurveEval &input_curve, else if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { BLI_assert(mode_param.length); const float length = spline->length(); - const int count = length / *mode_param.length; + const int count = std::max(int(length / *mode_param.length), 1); output_curve->add_spline(resample_spline(*spline, count)); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index fe1b23bf6d7..b6f04352929 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -181,8 +181,7 @@ static void spline_extrude_to_mesh_data(const Spline &spline, Span normals = spline.evaluated_normals(); Span profile_positions = profile_spline.evaluated_positions(); - GVArray_Typed radii{ - spline.interpolate_to_evaluated_points(blender::fn::GVArray_For_Span(spline.radii()))}; + GVArray_Typed radii = spline.interpolate_to_evaluated_points(spline.radii()); for (const int i_ring : IndexRange(spline_vert_len)) { float4x4 point_matrix = float4x4::from_normalized_axis_data( positions[i_ring], normals[i_ring], tangents[i_ring]); -- cgit v1.2.3