diff options
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r-- | source/blender/blenkernel/BKE_curves.hh | 26 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/curves_geometry.cc | 60 |
2 files changed, 86 insertions, 0 deletions
diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh index 82f77d83bec..05a20917552 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -77,6 +77,15 @@ class CurvesGeometryRuntime { mutable std::mutex position_cache_mutex; mutable bool position_cache_dirty = true; + /** + * Cache of lengths along each evaluated curve for for each evaluated point. If a curve is + * cyclic, it needs one more length value to correspond to the last segment, so in order to + * make slicing this array for a curve fast, an extra float is stored for every curve. + */ + mutable Vector<float> evaluated_length_cache; + mutable std::mutex length_cache_mutex; + mutable bool length_cache_dirty = true; + /** Direction of the spline at each evaluated point. */ mutable Vector<float3> evaluated_tangents_cache; mutable std::mutex tangent_cache_mutex; @@ -267,6 +276,20 @@ class CurvesGeometry : public ::CurvesGeometry { Span<float3> evaluated_positions() const; /** + * Return a cache of accumulated lengths along the curve. Each item is the length of the + * subsequent segment (the first value is the length of the first segment rather than 0). + * This calculation is rather trivial, and only depends on the evaluated positions, but + * the results are used often, and it is necessarily single threaded per curve, so it is cached. + * + * \param cyclic: This argument is redundant with the data stored for the curve, + * but is passed for performance reasons to avoid looking up the attribute. + */ + Span<float> evaluated_lengths_for_curve(int curve_index, bool cyclic) const; + + /** Calculates the data described by #evaluated_lengths_for_curve if necessary. */ + void ensure_evaluated_lengths() const; + + /** * Evaluate a generic data to the standard evaluated points of a specific curve, * defined by the resolution attribute or other factors, depending on the curve type. * @@ -281,6 +304,9 @@ class CurvesGeometry : public ::CurvesGeometry { */ void ensure_nurbs_basis_cache() const; + /** Return the slice of #evaluated_length_cache that corresponds to this curve index. */ + IndexRange lengths_range_for_curve(int curve_index, bool cyclic) const; + /* -------------------------------------------------------------------- * Operations. */ diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index 7ceaa8f0f37..c4e9a06aad0 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -11,6 +11,7 @@ #include "BLI_bounds.hh" #include "BLI_index_mask_ops.hh" +#include "BLI_length_parameterize.hh" #include "DNA_curves_types.h" @@ -721,6 +722,63 @@ void CurvesGeometry::interpolate_to_evaluated(const int curve_index, BLI_assert_unreachable(); } +IndexRange CurvesGeometry::lengths_range_for_curve(const int curve_index, const bool cyclic) const +{ + BLI_assert(cyclic == this->cyclic()[curve_index]); + const IndexRange points = this->evaluated_points_for_curve(curve_index); + const int start = points.start() + curve_index; + const int size = curves::curve_segment_size(points.size(), cyclic); + return {start, size}; +} + +void CurvesGeometry::ensure_evaluated_lengths() const +{ + if (!this->runtime->length_cache_dirty) { + return; + } + + /* A double checked lock. */ + std::scoped_lock lock{this->runtime->length_cache_mutex}; + if (!this->runtime->length_cache_dirty) { + return; + } + + threading::isolate_task([&]() { + /* Use an extra length value for the final cyclic segment for a consistent size + * (see comment on #evaluated_length_cache). */ + const int total_size = this->evaluated_points_num() + this->curves_num(); + this->runtime->evaluated_length_cache.resize(total_size); + MutableSpan<float> evaluated_lengths = this->runtime->evaluated_length_cache; + + Span<float3> evaluated_positions = this->evaluated_positions(); + VArray<bool> curves_cyclic = this->cyclic(); + + threading::parallel_for(this->curves_range(), 128, [&](IndexRange curves_range) { + for (const int curve_index : curves_range) { + const bool cyclic = curves_cyclic[curve_index]; + const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index); + if (UNLIKELY(evaluated_points.is_empty())) { + continue; + } + const IndexRange lengths_range = this->lengths_range_for_curve(curve_index, cyclic); + length_parameterize::accumulate_lengths(evaluated_positions.slice(evaluated_points), + cyclic, + evaluated_lengths.slice(lengths_range)); + } + }); + }); + + this->runtime->length_cache_dirty = false; +} + +Span<float> CurvesGeometry::evaluated_lengths_for_curve(const int curve_index, + const bool cyclic) const +{ + BLI_assert(!this->runtime->length_cache_dirty); + const IndexRange range = this->lengths_range_for_curve(curve_index, cyclic); + return this->runtime->evaluated_length_cache.as_span().slice(range); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -747,6 +805,7 @@ void CurvesGeometry::tag_positions_changed() this->runtime->position_cache_dirty = true; this->runtime->tangent_cache_dirty = true; this->runtime->normal_cache_dirty = true; + this->runtime->length_cache_dirty = true; } void CurvesGeometry::tag_topology_changed() { @@ -755,6 +814,7 @@ void CurvesGeometry::tag_topology_changed() this->runtime->normal_cache_dirty = true; this->runtime->offsets_cache_dirty = true; this->runtime->nurbs_basis_cache_dirty = true; + this->runtime->length_cache_dirty = true; } void CurvesGeometry::tag_normals_changed() { |