Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/blenkernel/BKE_curves.hh16
-rw-r--r--source/blender/blenkernel/intern/curve_bezier.cc40
-rw-r--r--source/blender/blenkernel/intern/curves_geometry.cc35
-rw-r--r--source/blender/blenkernel/intern/curves_geometry_test.cc73
4 files changed, 164 insertions, 0 deletions
diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh
index 96963dcbd8d..82f77d83bec 100644
--- a/source/blender/blenkernel/BKE_curves.hh
+++ b/source/blender/blenkernel/BKE_curves.hh
@@ -266,6 +266,15 @@ class CurvesGeometry : public ::CurvesGeometry {
Span<float3> evaluated_positions() 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.
+ *
+ * \warning This function expects offsets to the evaluated points for each curve to be
+ * calculated. That can be ensured with #ensure_evaluated_offsets.
+ */
+ void interpolate_to_evaluated(int curve_index, GSpan src, GMutableSpan dst) const;
+
private:
/**
* Make sure the basis weights for NURBS curve's evaluated points are calculated.
@@ -381,6 +390,13 @@ void calculate_evaluated_positions(Span<float3> positions,
Span<int> evaluated_offsets,
MutableSpan<float3> evaluated_positions);
+/**
+ * Evaluate generic data to the evaluated points, with counts for each segment described by
+ * #evaluated_offsets. Unlike other curve types, for Bezier curves generic data and positions
+ * are treated separately, since attribute values aren't stored for the handle control points.
+ */
+void interpolate_to_evaluated(GSpan src, Span<int> evaluated_offsets, GMutableSpan dst);
+
} // namespace bezier
namespace catmull_rom {
diff --git a/source/blender/blenkernel/intern/curve_bezier.cc b/source/blender/blenkernel/intern/curve_bezier.cc
index c02555dcf6a..8efe7a17a35 100644
--- a/source/blender/blenkernel/intern/curve_bezier.cc
+++ b/source/blender/blenkernel/intern/curve_bezier.cc
@@ -134,6 +134,46 @@ void calculate_evaluated_positions(const Span<float3> positions,
}
}
+template<typename T>
+static inline void linear_interpolation(const T &a, const T &b, MutableSpan<T> dst)
+{
+ dst.first() = a;
+ const float step = 1.0f / dst.size();
+ for (const int i : dst.index_range().drop_front(1)) {
+ dst[i] = attribute_math::mix2(i * step, a, b);
+ }
+}
+
+template<typename T>
+static void interpolate_to_evaluated(const Span<T> src,
+ const Span<int> evaluated_offsets,
+ MutableSpan<T> dst)
+{
+ linear_interpolation(src.first(), src[1], dst.take_front(evaluated_offsets.first()));
+
+ threading::parallel_for(
+ src.index_range().drop_back(1).drop_front(1), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const IndexRange segment_points = offsets_to_range(evaluated_offsets, i - 1);
+ linear_interpolation(src[i], src[i + 1], dst.slice(segment_points));
+ }
+ });
+
+ const IndexRange last_segment_points(evaluated_offsets.last(1),
+ evaluated_offsets.last() - evaluated_offsets.last(1));
+ linear_interpolation(src.last(), src.first(), dst.slice(last_segment_points));
+}
+
+void interpolate_to_evaluated(const GSpan src, const Span<int> evaluated_offsets, GMutableSpan dst)
+{
+ attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
+ interpolate_to_evaluated(src.typed<T>(), evaluated_offsets, dst.typed<T>());
+ }
+ });
+}
+
/** \} */
} // namespace blender::bke::curves::bezier
diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc
index 207d0d173ac..1dfd95ebb5b 100644
--- a/source/blender/blenkernel/intern/curves_geometry.cc
+++ b/source/blender/blenkernel/intern/curves_geometry.cc
@@ -689,6 +689,41 @@ Span<float3> CurvesGeometry::evaluated_positions() const
return this->runtime->evaluated_position_cache;
}
+void CurvesGeometry::interpolate_to_evaluated(const int curve_index,
+ const GSpan src,
+ GMutableSpan dst) const
+{
+ BLI_assert(!this->runtime->offsets_cache_dirty);
+ BLI_assert(!this->runtime->nurbs_basis_cache_dirty);
+ const IndexRange points = this->points_for_curve(curve_index);
+ BLI_assert(src.size() == points.size());
+ BLI_assert(dst.size() == this->evaluated_points_for_curve(curve_index).size());
+ switch (this->curve_types()[curve_index]) {
+ case CURVE_TYPE_CATMULL_ROM:
+ curves::catmull_rom::interpolate_to_evaluated(
+ src, this->cyclic()[curve_index], this->resolution()[curve_index], dst);
+ break;
+ case CURVE_TYPE_POLY:
+ dst.type().copy_assign_n(src.data(), dst.data(), src.size());
+ break;
+ case CURVE_TYPE_BEZIER:
+ curves::bezier::interpolate_to_evaluated(
+ src, this->runtime->bezier_evaluated_offsets.as_span().slice(points), dst);
+ break;
+ case CURVE_TYPE_NURBS:
+ curves::nurbs::interpolate_to_evaluated(this->runtime->nurbs_basis_cache[curve_index],
+ this->nurbs_orders()[curve_index],
+ this->nurbs_weights().slice(points),
+ src,
+ dst);
+ break;
+ default:
+ BLI_assert_unreachable();
+ break;
+ }
+}
+
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/blenkernel/intern/curves_geometry_test.cc b/source/blender/blenkernel/intern/curves_geometry_test.cc
index 574f90f2e51..e4dc9eead60 100644
--- a/source/blender/blenkernel/intern/curves_geometry_test.cc
+++ b/source/blender/blenkernel/intern/curves_geometry_test.cc
@@ -405,4 +405,77 @@ TEST(curves_geometry, NURBSEvaluation)
}
}
+TEST(curves_geometry, BezierGenericEvaluation)
+{
+ CurvesGeometry curves(3, 1);
+ curves.curve_types().fill(CURVE_TYPE_BEZIER);
+ curves.resolution().fill(8);
+ curves.offsets().last() = 3;
+
+ MutableSpan<float3> handles_left = curves.handle_positions_left();
+ MutableSpan<float3> handles_right = curves.handle_positions_right();
+ MutableSpan<float3> positions = curves.positions();
+ positions.first() = {-1, 0, 0};
+ handles_right.first() = {-1, 1, 0};
+ handles_left[1] = {0, 0, 0};
+ positions[1] = {1, 0, 0};
+ handles_right[1] = {2, 0, 0};
+ handles_left.last() = {1, 1, 0};
+ positions.last() = {2, 1, 0};
+
+ /* Dangling handles shouldn't be used in a non-cyclic curve. */
+ handles_left.first() = {100, 100, 100};
+ handles_right.last() = {100, 100, 100};
+
+ Span<float3> evaluated_positions = curves.evaluated_positions();
+ static const Array<float3> result_1{{
+ {-1.0f, 0.0f, 0.0f},
+ {-0.955078f, 0.287109f, 0.0f},
+ {-0.828125f, 0.421875f, 0.0f},
+ {-0.630859f, 0.439453f, 0.0f},
+ {-0.375f, 0.375f, 0.0f},
+ {-0.0722656f, 0.263672f, 0.0f},
+ {0.265625f, 0.140625f, 0.0f},
+ {0.626953f, 0.0410156f, 0.0f},
+ {1.0f, 0.0f, 0.0f},
+ {1.28906f, 0.0429688f, 0.0f},
+ {1.4375f, 0.15625f, 0.0f},
+ {1.49219f, 0.316406f, 0.0f},
+ {1.5f, 0.5f, 0.0f},
+ {1.50781f, 0.683594f, 0.0f},
+ {1.5625f, 0.84375f, 0.0f},
+ {1.71094f, 0.957031f, 0.0f},
+ {2.0f, 1.0f, 0.0f},
+ }};
+ for (const int i : evaluated_positions.index_range()) {
+ EXPECT_V3_NEAR(evaluated_positions[i], result_1[i], 1e-5f);
+ }
+
+ Array<float> radii{{0.0f, 1.0f, 2.0f}};
+ Array<float> evaluated_radii(17);
+ curves.interpolate_to_evaluated(0, radii.as_span(), evaluated_radii.as_mutable_span());
+ static const Array<float> result_2{{
+ 0.0f,
+ 0.125f,
+ 0.25f,
+ 0.375f,
+ 0.5f,
+ 0.625f,
+ 0.75f,
+ 0.875f,
+ 1.0f,
+ 1.125f,
+ 1.25f,
+ 1.375f,
+ 1.5f,
+ 1.625f,
+ 1.75f,
+ 1.875f,
+ 2.0f,
+ }};
+ for (const int i : evaluated_radii.index_range()) {
+ EXPECT_NEAR(evaluated_radii[i], result_2[i], 1e-6f);
+ }
+}
+
} // namespace blender::bke::tests