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:
authorHans Goudey <h.goudey@me.com>2022-09-18 23:10:04 +0300
committerHans Goudey <h.goudey@me.com>2022-09-18 23:10:04 +0300
commitecf3435362cf2c1823b94a154c3f32a0c3412f53 (patch)
tree010bb7748534587ffcffca3518104648f6279a08 /source/blender/blenkernel
parent7536abbe16bd8672ca38e2b866f9a9585d054713 (diff)
Curves: Remove CurveEval and old Spline types
`CurveEval` was added for the first iteration of geometry nodes curve support. Since then, it has been replaced by the new `Curves` type which is designed to be much faster for many curves and better integrated with the rest of Blender. Now that all curve nodes have been moved to use `Curves` (T95443), the type can be removed, along with the corresponding geometry component.
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.hh40
-rw-r--r--source/blender/blenkernel/BKE_spline.hh688
-rw-r--r--source/blender/blenkernel/CMakeLists.txt7
-rw-r--r--source/blender/blenkernel/intern/curve_eval.cc587
-rw-r--r--source/blender/blenkernel/intern/displist.cc2
-rw-r--r--source/blender/blenkernel/intern/geometry_component_curve.cc1464
-rw-r--r--source/blender/blenkernel/intern/spline_base.cc526
-rw-r--r--source/blender/blenkernel/intern/spline_bezier.cc646
-rw-r--r--source/blender/blenkernel/intern/spline_nurbs.cc395
-rw-r--r--source/blender/blenkernel/intern/spline_poly.cc97
10 files changed, 1 insertions, 4451 deletions
diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh
index a5c4d5e1365..dce80373189 100644
--- a/source/blender/blenkernel/BKE_geometry_set.hh
+++ b/source/blender/blenkernel/BKE_geometry_set.hh
@@ -26,7 +26,6 @@
struct Curves;
struct Collection;
struct Curve;
-struct CurveEval;
struct Mesh;
struct Object;
struct PointCloud;
@@ -465,45 +464,6 @@ class PointCloudComponent : public GeometryComponent {
};
/**
- * Legacy runtime-only curves type.
- * These curves are stored differently than other geometry components, because the data structure
- * used here does not correspond exactly to the #Curve DNA data structure. A #CurveEval is stored
- * here instead, though the component does give access to a #Curve for interfacing with render
- * engines and other areas of Blender that expect to use a data-block with an #ID.
- */
-class CurveComponentLegacy : public GeometryComponent {
- private:
- CurveEval *curve_ = nullptr;
- GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
-
- public:
- CurveComponentLegacy();
- ~CurveComponentLegacy();
- GeometryComponent *copy() const override;
-
- void clear();
- bool has_curve() const;
- /**
- * Clear the component and replace it with the new curve.
- */
- void replace(CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
- CurveEval *release();
-
- const CurveEval *get_for_read() const;
- CurveEval *get_for_write();
-
- bool is_empty() const final;
-
- bool owns_direct_data() const override;
- void ensure_owns_direct_data() override;
-
- std::optional<blender::bke::AttributeAccessor> attributes() const final;
- std::optional<blender::bke::MutableAttributeAccessor> attributes_for_write() final;
-
- static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_CURVE;
-};
-
-/**
* A geometry component that stores a group of curves, corresponding the #Curves data-block type
* and the #CurvesGeometry type. Attributes are stored on the control point domain and the
* curve domain.
diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh
deleted file mode 100644
index 27542aa3586..00000000000
--- a/source/blender/blenkernel/BKE_spline.hh
+++ /dev/null
@@ -1,688 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#pragma once
-
-/** \file
- * \ingroup bke
- */
-
-#include <mutex>
-
-#include "DNA_curves_types.h"
-
-#include "BLI_float4x4.hh"
-#include "BLI_generic_virtual_array.hh"
-#include "BLI_math_vec_types.hh"
-#include "BLI_vector.hh"
-
-#include "BKE_attribute.hh"
-#include "BKE_attribute_math.hh"
-
-struct Curve;
-struct Curves;
-struct ListBase;
-
-class Spline;
-using SplinePtr = std::unique_ptr<Spline>;
-
-/**
- * A spline is an abstraction of a single branch-less curve section, its evaluation methods,
- * and data. The spline data itself is just control points and a set of attributes by the set
- * of "evaluated" data is often used instead. Conceptually, the derived vs. original data is
- * an essential distinction. Derived data is usually calculated lazily and cached on the spline.
- *
- * Any derived class of Spline has to manage two things:
- * 1. Interpolating arbitrary attribute data from the control points to evaluated points.
- * 2. Evaluating the positions based on the stored control point data.
- *
- * Beyond that, everything is the base class's responsibility, with minor exceptions. Further
- * evaluation happens in a layer on top of the evaluated points generated by the derived types.
- *
- * There are a few methods to evaluate a spline:
- * 1. #evaluated_positions and #interpolate_to_evaluated give data for the initial
- * evaluated points, depending on the resolution.
- * 2. #lookup_evaluated_factor and #lookup_evaluated_factor are meant for one-off lookups
- * along the length of a curve.
- * 3. #sample_uniform_index_factors returns an array that stores uniform-length samples
- * along the spline which can be used to interpolate data from method 1.
- *
- * Commonly used evaluated data is stored in caches on the spline itself so that operations on
- * splines don't need to worry about taking ownership of evaluated data when they don't need to.
- */
-class Spline {
- public:
- NormalMode normal_mode = NORMAL_MODE_MINIMUM_TWIST;
-
- blender::bke::CustomDataAttributes attributes;
-
- protected:
- CurveType type_;
- bool is_cyclic_ = false;
-
- /** Direction of the spline at each evaluated point. */
- mutable blender::Vector<blender::float3> evaluated_tangents_cache_;
- mutable std::mutex tangent_cache_mutex_;
- mutable bool tangent_cache_dirty_ = true;
-
- /** Normal direction vectors for each evaluated point. */
- mutable blender::Vector<blender::float3> evaluated_normals_cache_;
- mutable std::mutex normal_cache_mutex_;
- mutable bool normal_cache_dirty_ = true;
-
- /** Accumulated lengths along the evaluated points. */
- mutable blender::Vector<float> evaluated_lengths_cache_;
- mutable std::mutex length_cache_mutex_;
- mutable bool length_cache_dirty_ = true;
-
- public:
- virtual ~Spline() = default;
- Spline(const CurveType type) : type_(type)
- {
- }
- Spline(Spline &other) : attributes(other.attributes), type_(other.type_)
- {
- copy_base_settings(other, *this);
- }
-
- /**
- * Return a new spline with the same data, settings, and attributes.
- */
- SplinePtr copy() const;
- /**
- * Return a new spline with the same type and settings like "cyclic", but without any data.
- */
- SplinePtr copy_only_settings() const;
- /**
- * The same as #copy, but skips copying dynamic attributes to the new spline.
- */
- SplinePtr copy_without_attributes() const;
- static void copy_base_settings(const Spline &src, Spline &dst);
-
- CurveType type() const;
-
- /** Return the number of control points. */
- virtual int size() const = 0;
- int segments_num() const;
- bool is_cyclic() const;
- void set_cyclic(bool value);
-
- virtual void resize(int size) = 0;
- virtual blender::MutableSpan<blender::float3> positions() = 0;
- virtual blender::Span<blender::float3> positions() const = 0;
- virtual blender::MutableSpan<float> radii() = 0;
- virtual blender::Span<float> radii() const = 0;
- virtual blender::MutableSpan<float> tilts() = 0;
- virtual blender::Span<float> tilts() const = 0;
-
- virtual void translate(const blender::float3 &translation);
- virtual void transform(const blender::float4x4 &matrix);
-
- /**
- * Change the direction of the spline (switch the start and end) without changing its shape.
- */
- void reverse();
-
- /**
- * Mark all caches for re-computation. This must be called after any operation that would
- * change the generated positions, tangents, normals, mapping, etc. of the evaluated points.
- */
- virtual void mark_cache_invalid() = 0;
- virtual int evaluated_points_num() const = 0;
- int evaluated_edges_num() const;
-
- float length() const;
-
- virtual blender::Span<blender::float3> evaluated_positions() const = 0;
-
- /**
- * Return non-owning access to the cache of accumulated lengths along the spline. Each item is
- * the length of the subsequent segment, i.e. 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. However, the results are used often, and it is necessarily single threaded, so it
- * is cached.
- */
- blender::Span<float> evaluated_lengths() const;
- /**
- * Return non-owning access to the direction of the curve at each evaluated point.
- */
- blender::Span<blender::float3> evaluated_tangents() const;
- /**
- * Return non-owning access to the direction vectors perpendicular to the tangents at every
- * evaluated point. The method used to generate the normal vectors depends on Spline.normal_mode.
- */
- blender::Span<blender::float3> evaluated_normals() const;
-
- void bounds_min_max(blender::float3 &min, blender::float3 &max, bool use_evaluated) const;
-
- struct LookupResult {
- /**
- * The index of the evaluated point before the result location. In other words, the index of
- * the edge that the result lies on. If the sampled factor/length is the very end of the
- * spline, this will be the second to last index, if it's the very beginning, this will be 0.
- */
- int evaluated_index;
- /**
- * The index of the evaluated point after the result location, accounting for wrapping when
- * the spline is cyclic. If the sampled factor/length is the very end of the spline, this will
- * be the last index (#evaluated_points_num - 1).
- */
- int next_evaluated_index;
- /**
- * The portion of the way from the evaluated point at #evaluated_index to the next point.
- * If the sampled factor/length is the very end of the spline, this will be the 1.0f
- */
- float factor;
- };
- /**
- * Find the position on the evaluated spline at the given portion of the total length.
- * The return value is the indices of the two neighboring points at that location and the
- * factor between them, which can be used to look up any attribute on the evaluated points.
- * \note This does not support extrapolation.
- */
- LookupResult lookup_evaluated_factor(float factor) const;
- /**
- * The same as #lookup_evaluated_factor, but looks up a length directly instead of
- * a portion of the total.
- */
- LookupResult lookup_evaluated_length(float length) const;
-
- /**
- * Return an array of evenly spaced samples along the length of the spline. The samples are
- * indices and factors to the next index encoded in floats. The logic for converting from the
- * float values to interpolation data is in #lookup_data_from_index_factor.
- */
- blender::Array<float> sample_uniform_index_factors(int samples_num) const;
- LookupResult lookup_data_from_index_factor(float index_factor) const;
-
- /**
- * Sample any input data with a value for each evaluated point (already interpolated to evaluated
- * points) to arbitrary parameters in between the evaluated points. The interpolation is quite
- * simple, but this handles the cyclic and end point special cases.
- */
- void sample_with_index_factors(const blender::GVArray &src,
- blender::Span<float> index_factors,
- blender::GMutableSpan dst) const;
- template<typename T>
- void sample_with_index_factors(const blender::VArray<T> &src,
- blender::Span<float> index_factors,
- blender::MutableSpan<T> dst) const
- {
- this->sample_with_index_factors(
- blender::GVArray(src), index_factors, blender::GMutableSpan(dst));
- }
- template<typename T>
- void sample_with_index_factors(blender::Span<T> src,
- blender::Span<float> index_factors,
- blender::MutableSpan<T> dst) const
- {
- this->sample_with_index_factors(blender::VArray<T>::ForSpan(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
- * exceed the lifetime of the input data.
- */
- virtual blender::GVArray interpolate_to_evaluated(const blender::GVArray &src) const = 0;
- blender::GVArray interpolate_to_evaluated(blender::GSpan data) const;
- template<typename T> blender::VArray<T> interpolate_to_evaluated(blender::Span<T> data) const
- {
- return this->interpolate_to_evaluated(blender::GSpan(data)).typed<T>();
- }
-
- protected:
- virtual void correct_end_tangents() const = 0;
- virtual void copy_settings(Spline &dst) const = 0;
- virtual void copy_data(Spline &dst) const = 0;
- virtual void reverse_impl() = 0;
-};
-
-/**
- * A Bezier spline is made up of a many curve segments, possibly achieving continuity of curvature
- * by constraining the alignment of curve handles. Evaluation stores the positions and a map of
- * factors and indices in a list of floats, which is then used to interpolate any other data.
- */
-class BezierSpline final : public Spline {
- blender::Vector<blender::float3> positions_;
- blender::Vector<float> radii_;
- blender::Vector<float> tilts_;
- int resolution_;
-
- blender::Vector<int8_t> handle_types_left_;
- blender::Vector<int8_t> handle_types_right_;
-
- /* These are mutable to allow lazy recalculation of #Auto and #Vector handle positions. */
- mutable blender::Vector<blender::float3> handle_positions_left_;
- mutable blender::Vector<blender::float3> handle_positions_right_;
-
- mutable std::mutex auto_handle_mutex_;
- mutable bool auto_handles_dirty_ = true;
-
- /** Start index in evaluated points array for every control point. */
- mutable blender::Vector<int> offset_cache_;
- mutable std::mutex offset_cache_mutex_;
- mutable bool offset_cache_dirty_ = true;
-
- /** Cache of evaluated positions. */
- mutable blender::Vector<blender::float3> evaluated_position_cache_;
- mutable std::mutex position_cache_mutex_;
- mutable bool position_cache_dirty_ = true;
-
- /** Cache of "index factors" based calculated from the evaluated positions. */
- mutable blender::Vector<float> evaluated_mapping_cache_;
- mutable std::mutex mapping_cache_mutex_;
- mutable bool mapping_cache_dirty_ = true;
-
- public:
- BezierSpline() : Spline(CURVE_TYPE_BEZIER)
- {
- }
- BezierSpline(const BezierSpline &other)
- : Spline((Spline &)other),
- positions_(other.positions_),
- radii_(other.radii_),
- tilts_(other.tilts_),
- resolution_(other.resolution_),
- handle_types_left_(other.handle_types_left_),
- handle_types_right_(other.handle_types_right_),
- handle_positions_left_(other.handle_positions_left_),
- handle_positions_right_(other.handle_positions_right_)
- {
- }
-
- int size() const final;
- int resolution() const;
- void set_resolution(int value);
-
- void resize(int size) final;
- blender::MutableSpan<blender::float3> positions() final;
- blender::Span<blender::float3> positions() const final;
- blender::MutableSpan<float> radii() final;
- blender::Span<float> radii() const final;
- blender::MutableSpan<float> tilts() final;
- blender::Span<float> tilts() const final;
- blender::Span<int8_t> handle_types_left() const;
- blender::MutableSpan<int8_t> handle_types_left();
- blender::Span<blender::float3> handle_positions_left() const;
- /**
- * Get writable access to the handle position.
- *
- * \param write_only: pass true for an uninitialized spline, this prevents accessing
- * uninitialized memory while auto-generating handles.
- */
- blender::MutableSpan<blender::float3> handle_positions_left(bool write_only = false);
- blender::Span<int8_t> handle_types_right() const;
- blender::MutableSpan<int8_t> handle_types_right();
- blender::Span<blender::float3> handle_positions_right() const;
- /**
- * Get writable access to the handle position.
- *
- * \param write_only: pass true for an uninitialized spline, this prevents accessing
- * uninitialized memory while auto-generating handles.
- */
- blender::MutableSpan<blender::float3> handle_positions_right(bool write_only = false);
- /**
- * Recalculate all #Auto and #Vector handles with positions automatically
- * derived from the neighboring control points.
- */
- void ensure_auto_handles() const;
-
- void translate(const blender::float3 &translation) override;
- void transform(const blender::float4x4 &matrix) override;
-
- /**
- * Set positions for the right handle of the control point, ensuring that
- * aligned handles stay aligned. Has no effect for auto and vector type handles.
- */
- void set_handle_position_right(int index, const blender::float3 &value);
- /**
- * Set positions for the left handle of the control point, ensuring that
- * aligned handles stay aligned. Has no effect for auto and vector type handles.
- */
- void set_handle_position_left(int index, const blender::float3 &value);
-
- bool point_is_sharp(int index) const;
-
- void mark_cache_invalid() final;
- int evaluated_points_num() const final;
-
- /**
- * Returns access to a cache of offsets into the evaluated point array for each control point.
- * While most control point edges generate the number of edges specified by the resolution,
- * vector segments only generate one edge.
- *
- * \note The length of the result is one greater than the number of points, so that the last item
- * is the total number of evaluated points. This is useful to avoid recalculating the size of the
- * last segment everywhere.
- */
- blender::Span<int> control_point_offsets() const;
- /**
- * Returns non-owning access to an array of values containing the information necessary to
- * interpolate values from the original control points to evaluated points. The control point
- * index is the integer part of each value, and the factor used for interpolating to the next
- * control point is the remaining fractional part.
- */
- blender::Span<float> evaluated_mappings() const;
- blender::Span<blender::float3> evaluated_positions() const final;
- struct InterpolationData {
- int control_point_index;
- int next_control_point_index;
- /**
- * Linear interpolation weight between the two indices, from 0 to 1.
- * Higher means closer to next control point.
- */
- float factor;
- };
- /**
- * Convert the data encoded in #evaulated_mappings into its parts-- the information necessary
- * to interpolate data from control points to evaluated points between them. The next control
- * point index result will not overflow the size of the control point vectors.
- */
- InterpolationData interpolation_data_from_index_factor(float index_factor) const;
-
- virtual blender::GVArray interpolate_to_evaluated(const blender::GVArray &src) const override;
-
- void evaluate_segment(int index,
- int next_index,
- blender::MutableSpan<blender::float3> positions) const;
- /**
- * \warning This functional assumes that the spline has more than one point.
- */
- bool segment_is_vector(int start_index) const;
-
- /** See comment and diagram for #calculate_segment_insertion. */
- struct InsertResult {
- blender::float3 handle_prev;
- blender::float3 left_handle;
- blender::float3 position;
- blender::float3 right_handle;
- blender::float3 handle_next;
- };
- /**
- * De Casteljau Bezier subdivision.
- * \param index: The index of the segment's start control point.
- * \param next_index: The index of the control point at the end of the segment. Could be 0,
- * if the spline is cyclic.
- * \param parameter: The factor along the segment, between 0 and 1. Note that this is used
- * directly by the calculation, it doesn't correspond to a portion of the evaluated length.
- *
- * <pre>
- * handle_prev handle_next
- * x----------------x
- * / \
- * / x---O---x \
- * / result \
- * / \
- * O O
- * point_prev point_next
- * </pre>
- */
- InsertResult calculate_segment_insertion(int index, int next_index, float parameter);
-
- private:
- /**
- * If the spline is not cyclic, the direction for the first and last points is just the
- * direction formed by the corresponding handles and control points. In the unlikely situation
- * that the handles define a zero direction, fallback to using the direction defined by the
- * first and last evaluated segments already calculated in #Spline::evaluated_tangents().
- */
- void correct_end_tangents() const final;
- void copy_settings(Spline &dst) const final;
- void copy_data(Spline &dst) const final;
-
- protected:
- void reverse_impl() override;
-};
-
-/**
- * Data for Non-Uniform Rational B-Splines. The mapping from control points to evaluated points is
- * influenced by a vector of knots, weights for each point, and the order of the spline. Every
- * mapping of data to evaluated points is handled the same way, but the positions are cached in
- * the spline.
- */
-class NURBSpline final : public Spline {
- public:
- /** Method used to recalculate the knots vector when points are added or removed. */
- KnotsMode knots_mode;
-
- struct BasisCache {
- /**
- * For each evaluated point, the weight for all control points that influences it.
- * The vector's size is the evaluated point count multiplied by the spline's order.
- */
- blender::Vector<float> weights;
- /**
- * An offset for the start of #weights: the first control point index with a non-zero weight.
- */
- blender::Vector<int> start_indices;
- };
-
- private:
- blender::Vector<blender::float3> positions_;
- blender::Vector<float> radii_;
- blender::Vector<float> tilts_;
- blender::Vector<float> weights_;
- int resolution_;
- /**
- * Defines the number of nearby control points that influence a given evaluated point. Higher
- * orders give smoother results. The number of control points must be greater than or equal to
- * this value.
- */
- uint8_t order_;
-
- /**
- * Determines where and how the control points affect the evaluated points. The length should
- * always be the value returned by #knots_num(), and each value should be greater than or equal
- * to the previous. Only invalidated when a point is added or removed.
- */
- mutable blender::Vector<float> knots_;
- mutable std::mutex knots_mutex_;
- mutable bool knots_dirty_ = true;
-
- /** Cache of control point influences on each evaluated point. */
- mutable BasisCache basis_cache_;
- mutable std::mutex basis_cache_mutex_;
- mutable bool basis_cache_dirty_ = true;
-
- /**
- * Cache of position data calculated from the basis cache. Though it is interpolated
- * in the same way as any other attribute, it is stored to save unnecessary recalculation.
- */
- mutable blender::Vector<blender::float3> evaluated_position_cache_;
- mutable std::mutex position_cache_mutex_;
- mutable bool position_cache_dirty_ = true;
-
- public:
- NURBSpline() : Spline(CURVE_TYPE_NURBS)
- {
- }
- NURBSpline(const NURBSpline &other)
- : Spline((Spline &)other),
- knots_mode(other.knots_mode),
- positions_(other.positions_),
- radii_(other.radii_),
- tilts_(other.tilts_),
- weights_(other.weights_),
- resolution_(other.resolution_),
- order_(other.order_)
- {
- }
-
- int size() const final;
- int resolution() const;
- void set_resolution(int value);
- uint8_t order() const;
- void set_order(uint8_t value);
-
- bool check_valid_num_and_order() const;
- int knots_num() const;
-
- void resize(int size) final;
- blender::MutableSpan<blender::float3> positions() final;
- blender::Span<blender::float3> positions() const final;
- blender::MutableSpan<float> radii() final;
- blender::Span<float> radii() const final;
- blender::MutableSpan<float> tilts() final;
- blender::Span<float> tilts() const final;
- blender::Span<float> knots() const;
-
- blender::MutableSpan<float> weights();
- blender::Span<float> weights() const;
-
- void mark_cache_invalid() final;
- int evaluated_points_num() const final;
-
- blender::Span<blender::float3> evaluated_positions() const final;
-
- blender::GVArray interpolate_to_evaluated(const blender::GVArray &src) const final;
-
- protected:
- void correct_end_tangents() const final;
- void copy_settings(Spline &dst) const final;
- void copy_data(Spline &dst) const final;
- void reverse_impl() override;
-
- void calculate_knots() const;
- const BasisCache &calculate_basis_cache() const;
-};
-
-/**
- * A Poly spline is like a Bezier spline with a resolution of one. The main reason to distinguish
- * the two is for reduced complexity and increased performance, since interpolating data to control
- * points does not change it.
- *
- * Poly spline code is very simple, since it doesn't do anything that the base #Spline doesn't
- * handle. Mostly it just worries about storing the data used by the base class.
- */
-class PolySpline final : public Spline {
- blender::Vector<blender::float3> positions_;
- blender::Vector<float> radii_;
- blender::Vector<float> tilts_;
-
- public:
- PolySpline() : Spline(CURVE_TYPE_POLY)
- {
- }
- PolySpline(const PolySpline &other)
- : Spline((Spline &)other),
- positions_(other.positions_),
- radii_(other.radii_),
- tilts_(other.tilts_)
- {
- }
-
- int size() const final;
-
- void resize(int size) final;
- blender::MutableSpan<blender::float3> positions() final;
- blender::Span<blender::float3> positions() const final;
- blender::MutableSpan<float> radii() final;
- blender::Span<float> radii() const final;
- blender::MutableSpan<float> tilts() final;
- blender::Span<float> tilts() const final;
-
- void mark_cache_invalid() final;
- int evaluated_points_num() const final;
-
- blender::Span<blender::float3> evaluated_positions() const final;
-
- /**
- * Poly spline interpolation from control points to evaluated points is a special case, since
- * the result data is the same as the input data. This function returns a #GVArray that points to
- * the original data. Therefore the lifetime of the returned virtual array must not be longer
- * than the source data.
- */
- blender::GVArray interpolate_to_evaluated(const blender::GVArray &src) const final;
-
- protected:
- void correct_end_tangents() const final;
- void copy_settings(Spline &dst) const final;
- void copy_data(Spline &dst) const final;
- void reverse_impl() override;
-};
-
-/**
- * A collection of #Spline objects with the same attribute types and names. Most data and
- * functionality is in splines, but this contains some helpers for working with them as a group.
- *
- * \note A #CurveEval corresponds to the #Curve object data. The name is different for clarity,
- * since more of the data is stored in the splines, but also just to be different than the name in
- * DNA.
- */
-struct CurveEval {
- private:
- blender::Vector<SplinePtr> splines_;
-
- public:
- blender::bke::CustomDataAttributes attributes;
-
- CurveEval() = default;
- CurveEval(const CurveEval &other) : attributes(other.attributes)
- {
- for (const SplinePtr &spline : other.splines()) {
- this->add_spline(spline->copy());
- }
- }
-
- blender::Span<SplinePtr> splines() const;
- blender::MutableSpan<SplinePtr> splines();
- /**
- * \return True if the curve contains a spline with the given type.
- *
- * \note If you are looping over all of the splines in the same scope anyway,
- * it's better to avoid calling this function, in case there are many splines.
- */
- bool has_spline_with_type(const CurveType type) const;
-
- void resize(int size);
- /**
- * \warning Call #reallocate on the spline's attributes after adding all splines.
- */
- void add_spline(SplinePtr spline);
- void add_splines(blender::MutableSpan<SplinePtr> splines);
- void remove_splines(blender::IndexMask mask);
-
- void translate(const blender::float3 &translation);
- void transform(const blender::float4x4 &matrix);
- bool bounds_min_max(blender::float3 &min, blender::float3 &max, bool use_evaluated) const;
-
- blender::bke::MutableAttributeAccessor attributes_for_write();
-
- /**
- * Return the start indices for each of the curve spline's control points, if they were part
- * of a flattened array. This can be used to facilitate parallelism by avoiding the need to
- * accumulate an offset while doing more complex calculations.
- *
- * \note The result is one longer than the spline count; the last element is the total size.
- */
- blender::Array<int> control_point_offsets() const;
- /**
- * Exactly like #control_point_offsets, but uses the number of evaluated points instead.
- */
- blender::Array<int> evaluated_point_offsets() const;
- /**
- * Return the accumulated length at the start of every spline in the curve.
- * \note The result is one longer than the spline count; the last element is the total length.
- */
- blender::Array<float> accumulated_spline_lengths() const;
-
- float total_length() const;
- int total_control_point_num() const;
-
- void mark_cache_invalid();
-
- /**
- * Check the invariants that curve control point attributes should always uphold, necessary
- * because attributes are stored on splines rather than in a flat array on the curve:
- * - The same set of attributes exists on every spline.
- * - Attributes with the same name have the same type on every spline.
- * - Attributes are in the same order on every spline.
- */
- void assert_valid_point_attributes() const;
-};
-
-std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &curve,
- const ListBase &nurbs_list);
-std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve);
-std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves);
-Curves *curve_eval_to_curves(const CurveEval &curve_eval);
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 2f1e1897f8d..2e4b2508646 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -110,7 +110,6 @@ set(SRC
intern/curve_convert.c
intern/curve_decimate.c
intern/curve_deform.c
- intern/curve_eval.cc
intern/curve_legacy_convert.cc
intern/curve_nurbs.cc
intern/curve_poly.cc
@@ -137,7 +136,6 @@ set(SRC
intern/fluid.c
intern/fmodifier.c
intern/freestyle.c
- intern/geometry_component_curve.cc
intern/geometry_component_curves.cc
intern/geometry_component_edit_data.cc
intern/geometry_component_instances.cc
@@ -266,10 +264,6 @@ set(SRC
intern/softbody.c
intern/sound.c
intern/speaker.c
- intern/spline_base.cc
- intern/spline_bezier.cc
- intern/spline_nurbs.cc
- intern/spline_poly.cc
intern/studiolight.c
intern/subdiv.c
intern/subdiv_ccg.c
@@ -469,7 +463,6 @@ set(SRC
BKE_softbody.h
BKE_sound.h
BKE_speaker.h
- BKE_spline.hh
BKE_studiolight.h
BKE_subdiv.h
BKE_subdiv_ccg.h
diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc
deleted file mode 100644
index 3bee82fadab..00000000000
--- a/source/blender/blenkernel/intern/curve_eval.cc
+++ /dev/null
@@ -1,587 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "BLI_array.hh"
-#include "BLI_index_range.hh"
-#include "BLI_listbase.h"
-#include "BLI_map.hh"
-#include "BLI_span.hh"
-#include "BLI_string_ref.hh"
-#include "BLI_task.hh"
-#include "BLI_vector.hh"
-
-#include "DNA_curve_types.h"
-
-#include "BKE_anonymous_attribute.hh"
-#include "BKE_curve.h"
-#include "BKE_curves.hh"
-#include "BKE_geometry_set.hh"
-#include "BKE_spline.hh"
-
-using blender::Array;
-using blender::float3;
-using blender::float4x4;
-using blender::GVArray;
-using blender::GVArraySpan;
-using blender::IndexRange;
-using blender::Map;
-using blender::MutableSpan;
-using blender::Span;
-using blender::StringRefNull;
-using blender::VArray;
-using blender::VArraySpan;
-using blender::Vector;
-using blender::bke::AttributeIDRef;
-using blender::bke::AttributeMetaData;
-
-blender::Span<SplinePtr> CurveEval::splines() const
-{
- return splines_;
-}
-
-blender::MutableSpan<SplinePtr> CurveEval::splines()
-{
- return splines_;
-}
-
-bool CurveEval::has_spline_with_type(const CurveType type) const
-{
- for (const SplinePtr &spline : this->splines()) {
- if (spline->type() == type) {
- return true;
- }
- }
- return false;
-}
-
-void CurveEval::resize(const int size)
-{
- splines_.resize(size);
- attributes.reallocate(size);
-}
-
-void CurveEval::add_spline(SplinePtr spline)
-{
- splines_.append(std::move(spline));
-}
-
-void CurveEval::add_splines(MutableSpan<SplinePtr> splines)
-{
- for (SplinePtr &spline : splines) {
- this->add_spline(std::move(spline));
- }
-}
-
-void CurveEval::remove_splines(blender::IndexMask mask)
-{
- for (int i = mask.size() - 1; i >= 0; i--) {
- splines_.remove_and_reorder(mask.indices()[i]);
- }
-}
-
-void CurveEval::translate(const float3 &translation)
-{
- for (SplinePtr &spline : this->splines()) {
- spline->translate(translation);
- spline->mark_cache_invalid();
- }
-}
-
-void CurveEval::transform(const float4x4 &matrix)
-{
- for (SplinePtr &spline : this->splines()) {
- spline->transform(matrix);
- }
-}
-
-bool CurveEval::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const
-{
- bool have_minmax = false;
- for (const SplinePtr &spline : this->splines()) {
- if (spline->size()) {
- spline->bounds_min_max(min, max, use_evaluated);
- have_minmax = true;
- }
- }
-
- return have_minmax;
-}
-
-float CurveEval::total_length() const
-{
- float length = 0.0f;
- for (const SplinePtr &spline : this->splines()) {
- length += spline->length();
- }
- return length;
-}
-
-int CurveEval::total_control_point_num() const
-{
- int count = 0;
- for (const SplinePtr &spline : this->splines()) {
- count += spline->size();
- }
- return count;
-}
-
-blender::Array<int> CurveEval::control_point_offsets() const
-{
- Array<int> offsets(splines_.size() + 1);
- int offset = 0;
- for (const int i : splines_.index_range()) {
- offsets[i] = offset;
- offset += splines_[i]->size();
- }
- offsets.last() = offset;
- return offsets;
-}
-
-blender::Array<int> CurveEval::evaluated_point_offsets() const
-{
- Array<int> offsets(splines_.size() + 1);
- int offset = 0;
- for (const int i : splines_.index_range()) {
- offsets[i] = offset;
- offset += splines_[i]->evaluated_points_num();
- }
- offsets.last() = offset;
- return offsets;
-}
-
-blender::Array<float> CurveEval::accumulated_spline_lengths() const
-{
- Array<float> spline_lengths(splines_.size() + 1);
- float spline_length = 0.0f;
- for (const int i : splines_.index_range()) {
- spline_lengths[i] = spline_length;
- spline_length += splines_[i]->length();
- }
- spline_lengths.last() = spline_length;
- return spline_lengths;
-}
-
-void CurveEval::mark_cache_invalid()
-{
- for (SplinePtr &spline : splines_) {
- spline->mark_cache_invalid();
- }
-}
-
-static HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type)
-{
- switch (dna_handle_type) {
- case HD_FREE:
- return BEZIER_HANDLE_FREE;
- case HD_AUTO:
- return BEZIER_HANDLE_AUTO;
- case HD_VECT:
- return BEZIER_HANDLE_VECTOR;
- case HD_ALIGN:
- return BEZIER_HANDLE_ALIGN;
- case HD_AUTO_ANIM:
- return BEZIER_HANDLE_AUTO;
- case HD_ALIGN_DOUBLESIDE:
- return BEZIER_HANDLE_ALIGN;
- }
- BLI_assert_unreachable();
- return BEZIER_HANDLE_AUTO;
-}
-
-static NormalMode normal_mode_from_dna_curve(const int twist_mode)
-{
- switch (twist_mode) {
- case CU_TWIST_Z_UP:
- case CU_TWIST_TANGENT:
- return NORMAL_MODE_Z_UP;
- case CU_TWIST_MINIMUM:
- return NORMAL_MODE_MINIMUM_TWIST;
- }
- BLI_assert_unreachable();
- return NORMAL_MODE_MINIMUM_TWIST;
-}
-
-static KnotsMode knots_mode_from_dna_nurb(const short flag)
-{
- switch (flag & (CU_NURB_ENDPOINT | CU_NURB_BEZIER)) {
- case CU_NURB_ENDPOINT:
- return NURBS_KNOT_MODE_ENDPOINT;
- case CU_NURB_BEZIER:
- return NURBS_KNOT_MODE_BEZIER;
- case CU_NURB_ENDPOINT | CU_NURB_BEZIER:
- return NURBS_KNOT_MODE_ENDPOINT_BEZIER;
- default:
- return NURBS_KNOT_MODE_NORMAL;
- }
-
- BLI_assert_unreachable();
- return NURBS_KNOT_MODE_NORMAL;
-}
-
-static SplinePtr spline_from_dna_bezier(const Nurb &nurb)
-{
- std::unique_ptr<BezierSpline> spline = std::make_unique<BezierSpline>();
- spline->set_resolution(nurb.resolu);
- spline->set_cyclic(nurb.flagu & CU_NURB_CYCLIC);
-
- Span<const BezTriple> src_points{nurb.bezt, nurb.pntsu};
- spline->resize(src_points.size());
- MutableSpan<float3> positions = spline->positions();
- MutableSpan<float3> handle_positions_left = spline->handle_positions_left(true);
- MutableSpan<float3> handle_positions_right = spline->handle_positions_right(true);
- MutableSpan<int8_t> handle_types_left = spline->handle_types_left();
- MutableSpan<int8_t> handle_types_right = spline->handle_types_right();
- MutableSpan<float> radii = spline->radii();
- MutableSpan<float> tilts = spline->tilts();
-
- blender::threading::parallel_for(src_points.index_range(), 2048, [&](IndexRange range) {
- for (const int i : range) {
- const BezTriple &bezt = src_points[i];
- positions[i] = bezt.vec[1];
- handle_positions_left[i] = bezt.vec[0];
- handle_types_left[i] = handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h1);
- handle_positions_right[i] = bezt.vec[2];
- handle_types_right[i] = handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h2);
- radii[i] = bezt.radius;
- tilts[i] = bezt.tilt;
- }
- });
-
- return spline;
-}
-
-static SplinePtr spline_from_dna_nurbs(const Nurb &nurb)
-{
- std::unique_ptr<NURBSpline> spline = std::make_unique<NURBSpline>();
- spline->set_resolution(nurb.resolu);
- spline->set_cyclic(nurb.flagu & CU_NURB_CYCLIC);
- spline->set_order(nurb.orderu);
- spline->knots_mode = knots_mode_from_dna_nurb(nurb.flagu);
-
- Span<const BPoint> src_points{nurb.bp, nurb.pntsu};
- spline->resize(src_points.size());
- MutableSpan<float3> positions = spline->positions();
- MutableSpan<float> weights = spline->weights();
- MutableSpan<float> radii = spline->radii();
- MutableSpan<float> tilts = spline->tilts();
-
- blender::threading::parallel_for(src_points.index_range(), 2048, [&](IndexRange range) {
- for (const int i : range) {
- const BPoint &bp = src_points[i];
- positions[i] = bp.vec;
- weights[i] = bp.vec[3];
- radii[i] = bp.radius;
- tilts[i] = bp.tilt;
- }
- });
-
- return spline;
-}
-
-static SplinePtr spline_from_dna_poly(const Nurb &nurb)
-{
- std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
- spline->set_cyclic(nurb.flagu & CU_NURB_CYCLIC);
-
- Span<const BPoint> src_points{nurb.bp, nurb.pntsu};
- spline->resize(src_points.size());
- MutableSpan<float3> positions = spline->positions();
- MutableSpan<float> radii = spline->radii();
- MutableSpan<float> tilts = spline->tilts();
-
- blender::threading::parallel_for(src_points.index_range(), 2048, [&](IndexRange range) {
- for (const int i : range) {
- const BPoint &bp = src_points[i];
- positions[i] = bp.vec;
- radii[i] = bp.radius;
- tilts[i] = bp.tilt;
- }
- });
-
- return spline;
-}
-
-std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve,
- const ListBase &nurbs_list)
-{
- Vector<const Nurb *> nurbs(nurbs_list);
-
- std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
- curve->resize(nurbs.size());
- MutableSpan<SplinePtr> splines = curve->splines();
-
- blender::threading::parallel_for(nurbs.index_range(), 256, [&](IndexRange range) {
- for (const int i : range) {
- switch (nurbs[i]->type) {
- case CU_BEZIER:
- splines[i] = spline_from_dna_bezier(*nurbs[i]);
- break;
- case CU_NURBS:
- splines[i] = spline_from_dna_nurbs(*nurbs[i]);
- break;
- case CU_POLY:
- splines[i] = spline_from_dna_poly(*nurbs[i]);
- break;
- default:
- BLI_assert_unreachable();
- break;
- }
- }
- });
-
- /* Normal mode is stored separately in each spline to facilitate combining
- * splines from multiple curve objects, where the value may be different. */
- const NormalMode normal_mode = normal_mode_from_dna_curve(dna_curve.twist_mode);
- for (SplinePtr &spline : curve->splines()) {
- spline->normal_mode = normal_mode;
- }
-
- return curve;
-}
-
-std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
-{
- return curve_eval_from_dna_curve(dna_curve, *BKE_curve_nurbs_get_for_read(&dna_curve));
-}
-
-static void copy_attributes_between_components(
- const blender::bke::AttributeAccessor &src_attributes,
- blender::bke::MutableAttributeAccessor &dst_attributes,
- Span<std::string> skip)
-{
- src_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
- if (id.is_named() && skip.contains(id.name())) {
- return true;
- }
-
- GVArray src_attribute = src_attributes.lookup(id, meta_data.domain, meta_data.data_type);
- if (!src_attribute) {
- return true;
- }
- GVArraySpan src_attribute_data{src_attribute};
-
- blender::bke::GAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write(
- id, meta_data.domain, meta_data.data_type);
- if (!dst_attribute) {
- return true;
- }
- dst_attribute.varray.set_all(src_attribute_data.data());
- dst_attribute.finish();
- return true;
- });
-}
-
-std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves_id)
-{
- CurveComponent src_component;
- src_component.replace(&const_cast<Curves &>(curves_id), GeometryOwnershipType::ReadOnly);
- const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap(
- curves_id.geometry);
- const blender::bke::AttributeAccessor src_attributes = curves.attributes();
-
- VArray<int> resolution = curves.resolution();
- VArray<int8_t> normal_mode = curves.normal_mode();
-
- VArraySpan<float> nurbs_weights{
- src_attributes.lookup_or_default<float>("nurbs_weight", ATTR_DOMAIN_POINT, 1.0f)};
- VArraySpan<int8_t> nurbs_orders{
- src_attributes.lookup_or_default<int8_t>("nurbs_order", ATTR_DOMAIN_CURVE, 4)};
- VArraySpan<int8_t> nurbs_knots_modes{
- src_attributes.lookup_or_default<int8_t>("knots_mode", ATTR_DOMAIN_CURVE, 0)};
-
- VArraySpan<int8_t> handle_types_right{
- src_attributes.lookup_or_default<int8_t>("handle_type_right", ATTR_DOMAIN_POINT, 0)};
- VArraySpan<int8_t> handle_types_left{
- src_attributes.lookup_or_default<int8_t>("handle_type_left", ATTR_DOMAIN_POINT, 0)};
-
- /* Create splines with the correct size and type. */
- VArray<int8_t> curve_types = curves.curve_types();
- std::unique_ptr<CurveEval> curve_eval = std::make_unique<CurveEval>();
- for (const int curve_index : curve_types.index_range()) {
- const IndexRange points = curves.points_for_curve(curve_index);
-
- std::unique_ptr<Spline> spline;
- /* #CurveEval does not support catmull rom curves, so convert those to poly splines. */
- switch (std::max<int8_t>(1, curve_types[curve_index])) {
- case CURVE_TYPE_POLY: {
- spline = std::make_unique<PolySpline>();
- spline->resize(points.size());
- break;
- }
- case CURVE_TYPE_BEZIER: {
- std::unique_ptr<BezierSpline> bezier_spline = std::make_unique<BezierSpline>();
- bezier_spline->resize(points.size());
- bezier_spline->set_resolution(resolution[curve_index]);
- bezier_spline->handle_types_left().copy_from(handle_types_left.slice(points));
- bezier_spline->handle_types_right().copy_from(handle_types_right.slice(points));
-
- spline = std::move(bezier_spline);
- break;
- }
- case CURVE_TYPE_NURBS: {
- std::unique_ptr<NURBSpline> nurb_spline = std::make_unique<NURBSpline>();
- nurb_spline->resize(points.size());
- nurb_spline->set_resolution(resolution[curve_index]);
- nurb_spline->weights().copy_from(nurbs_weights.slice(points));
- nurb_spline->set_order(nurbs_orders[curve_index]);
- nurb_spline->knots_mode = static_cast<KnotsMode>(nurbs_knots_modes[curve_index]);
-
- spline = std::move(nurb_spline);
- break;
- }
- case CURVE_TYPE_CATMULL_ROM:
- /* Not supported yet. */
- BLI_assert_unreachable();
- continue;
- }
- spline->positions().fill(float3(0));
- spline->tilts().fill(0.0f);
- spline->radii().fill(1.0f);
- spline->normal_mode = static_cast<NormalMode>(normal_mode[curve_index]);
- curve_eval->add_spline(std::move(spline));
- }
-
- curve_eval->attributes.reallocate(curve_eval->splines().size());
-
- CurveComponentLegacy dst_component;
- dst_component.replace(curve_eval.get(), GeometryOwnershipType::Editable);
- blender::bke::MutableAttributeAccessor dst_attributes = *dst_component.attributes_for_write();
-
- copy_attributes_between_components(src_attributes,
- dst_attributes,
- {"curve_type",
- "resolution",
- "normal_mode",
- "nurbs_weight",
- "nurbs_order",
- "knots_mode",
- "handle_type_right",
- "handle_type_left"});
-
- return curve_eval;
-}
-
-Curves *curve_eval_to_curves(const CurveEval &curve_eval)
-{
- Curves *curves_id = blender::bke::curves_new_nomain(curve_eval.total_control_point_num(),
- curve_eval.splines().size());
- CurveComponent dst_component;
- dst_component.replace(curves_id, GeometryOwnershipType::Editable);
- blender::bke::MutableAttributeAccessor dst_attributes = *dst_component.attributes_for_write();
-
- blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap(curves_id->geometry);
- curves.offsets_for_write().copy_from(curve_eval.control_point_offsets());
- MutableSpan<int8_t> curve_types = curves.curve_types_for_write();
-
- blender::bke::SpanAttributeWriter<int8_t> normal_mode =
- dst_attributes.lookup_or_add_for_write_only_span<int8_t>("normal_mode", ATTR_DOMAIN_CURVE);
- blender::bke::SpanAttributeWriter<float> nurbs_weight;
- blender::bke::SpanAttributeWriter<int8_t> nurbs_order;
- blender::bke::SpanAttributeWriter<int8_t> nurbs_knots_mode;
- if (curve_eval.has_spline_with_type(CURVE_TYPE_NURBS)) {
- nurbs_weight = dst_attributes.lookup_or_add_for_write_only_span<float>("nurbs_weight",
- ATTR_DOMAIN_POINT);
- nurbs_order = dst_attributes.lookup_or_add_for_write_only_span<int8_t>("nurbs_order",
- ATTR_DOMAIN_CURVE);
- nurbs_knots_mode = dst_attributes.lookup_or_add_for_write_only_span<int8_t>("knots_mode",
- ATTR_DOMAIN_CURVE);
- }
- blender::bke::SpanAttributeWriter<int8_t> handle_type_right;
- blender::bke::SpanAttributeWriter<int8_t> handle_type_left;
- if (curve_eval.has_spline_with_type(CURVE_TYPE_BEZIER)) {
- handle_type_right = dst_attributes.lookup_or_add_for_write_only_span<int8_t>(
- "handle_type_right", ATTR_DOMAIN_POINT);
- handle_type_left = dst_attributes.lookup_or_add_for_write_only_span<int8_t>("handle_type_left",
- ATTR_DOMAIN_POINT);
- }
-
- for (const int curve_index : curve_eval.splines().index_range()) {
- const Spline &spline = *curve_eval.splines()[curve_index];
- curve_types[curve_index] = curve_eval.splines()[curve_index]->type();
- normal_mode.span[curve_index] = curve_eval.splines()[curve_index]->normal_mode;
- const IndexRange points = curves.points_for_curve(curve_index);
-
- switch (spline.type()) {
- case CURVE_TYPE_POLY:
- break;
- case CURVE_TYPE_BEZIER: {
- const BezierSpline &src = static_cast<const BezierSpline &>(spline);
- handle_type_right.span.slice(points).copy_from(src.handle_types_right());
- handle_type_left.span.slice(points).copy_from(src.handle_types_left());
- break;
- }
- case CURVE_TYPE_NURBS: {
- const NURBSpline &src = static_cast<const NURBSpline &>(spline);
- nurbs_knots_mode.span[curve_index] = static_cast<int8_t>(src.knots_mode);
- nurbs_order.span[curve_index] = src.order();
- nurbs_weight.span.slice(points).copy_from(src.weights());
- break;
- }
- case CURVE_TYPE_CATMULL_ROM: {
- BLI_assert_unreachable();
- break;
- }
- }
- }
-
- curves.update_curve_types();
-
- normal_mode.finish();
- nurbs_weight.finish();
- nurbs_order.finish();
- nurbs_knots_mode.finish();
- handle_type_right.finish();
- handle_type_left.finish();
-
- CurveComponentLegacy src_component;
- src_component.replace(&const_cast<CurveEval &>(curve_eval), GeometryOwnershipType::ReadOnly);
- const blender::bke::AttributeAccessor src_attributes = *src_component.attributes();
-
- copy_attributes_between_components(src_attributes, dst_attributes, {});
-
- return curves_id;
-}
-
-void CurveEval::assert_valid_point_attributes() const
-{
-#ifdef DEBUG
- if (splines_.size() == 0) {
- return;
- }
- const int layer_len = splines_.first()->attributes.data.totlayer;
-
- Array<AttributeIDRef> ids_in_order(layer_len);
- Array<AttributeMetaData> meta_data_in_order(layer_len);
-
- {
- int i = 0;
- splines_.first()->attributes.foreach_attribute(
- [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
- ids_in_order[i] = attribute_id;
- meta_data_in_order[i] = meta_data;
- i++;
- return true;
- },
- ATTR_DOMAIN_POINT);
- }
-
- for (const SplinePtr &spline : splines_) {
- /* All splines should have the same number of attributes. */
- BLI_assert(spline->attributes.data.totlayer == layer_len);
-
- int i = 0;
- spline->attributes.foreach_attribute(
- [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
- /* Attribute names and IDs should have the same order and exist on all splines. */
- BLI_assert(attribute_id == ids_in_order[i]);
-
- /* Attributes with the same ID different splines should all have the same type. */
- BLI_assert(meta_data == meta_data_in_order[i]);
-
- i++;
- return true;
- },
- ATTR_DOMAIN_POINT);
- }
-
-#endif
-}
diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc
index b87d675496c..279166297ec 100644
--- a/source/blender/blenkernel/intern/displist.cc
+++ b/source/blender/blenkernel/intern/displist.cc
@@ -634,7 +634,7 @@ void BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph,
/**
* \return True if the deformed curve control point data should be implicitly
- * converted directly to a mesh, or false if it can be left as curve data via #CurveEval.
+ * converted directly to a mesh, or false if it can be left as curve data via the #Curves type.
*/
static bool do_curve_implicit_mesh_conversion(const Curve *curve,
ModifierData *first_modifier,
diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc
deleted file mode 100644
index 8e482e4c691..00000000000
--- a/source/blender/blenkernel/intern/geometry_component_curve.cc
+++ /dev/null
@@ -1,1464 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "BLI_task.hh"
-
-#include "DNA_ID_enums.h"
-#include "DNA_curve_types.h"
-
-#include "BKE_attribute_math.hh"
-#include "BKE_curve.h"
-#include "BKE_geometry_set.hh"
-#include "BKE_lib_id.h"
-#include "BKE_spline.hh"
-
-#include "attribute_access_intern.hh"
-
-using blender::GMutableSpan;
-using blender::GSpan;
-using blender::GVArray;
-using blender::GVArraySpan;
-
-/* -------------------------------------------------------------------- */
-/** \name Geometry Component Implementation
- * \{ */
-
-CurveComponentLegacy::CurveComponentLegacy() : GeometryComponent(GEO_COMPONENT_TYPE_CURVE)
-{
-}
-
-CurveComponentLegacy::~CurveComponentLegacy()
-{
- this->clear();
-}
-
-GeometryComponent *CurveComponentLegacy::copy() const
-{
- CurveComponentLegacy *new_component = new CurveComponentLegacy();
- if (curve_ != nullptr) {
- new_component->curve_ = new CurveEval(*curve_);
- new_component->ownership_ = GeometryOwnershipType::Owned;
- }
- return new_component;
-}
-
-void CurveComponentLegacy::clear()
-{
- BLI_assert(this->is_mutable());
- if (curve_ != nullptr) {
- if (ownership_ == GeometryOwnershipType::Owned) {
- delete curve_;
- }
- curve_ = nullptr;
- }
-}
-
-bool CurveComponentLegacy::has_curve() const
-{
- return curve_ != nullptr;
-}
-
-void CurveComponentLegacy::replace(CurveEval *curve, GeometryOwnershipType ownership)
-{
- BLI_assert(this->is_mutable());
- this->clear();
- curve_ = curve;
- ownership_ = ownership;
-}
-
-CurveEval *CurveComponentLegacy::release()
-{
- BLI_assert(this->is_mutable());
- CurveEval *curve = curve_;
- curve_ = nullptr;
- return curve;
-}
-
-const CurveEval *CurveComponentLegacy::get_for_read() const
-{
- return curve_;
-}
-
-CurveEval *CurveComponentLegacy::get_for_write()
-{
- BLI_assert(this->is_mutable());
- if (ownership_ == GeometryOwnershipType::ReadOnly) {
- curve_ = new CurveEval(*curve_);
- ownership_ = GeometryOwnershipType::Owned;
- }
- return curve_;
-}
-
-bool CurveComponentLegacy::is_empty() const
-{
- return curve_ == nullptr;
-}
-
-bool CurveComponentLegacy::owns_direct_data() const
-{
- return ownership_ == GeometryOwnershipType::Owned;
-}
-
-void CurveComponentLegacy::ensure_owns_direct_data()
-{
- BLI_assert(this->is_mutable());
- if (ownership_ != GeometryOwnershipType::Owned) {
- curve_ = new CurveEval(*curve_);
- ownership_ = GeometryOwnershipType::Owned;
- }
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Attribute Access Helper Functions
- * \{ */
-
-namespace blender::bke {
-
-namespace {
-struct PointIndices {
- int spline_index;
- int point_index;
-};
-} // namespace
-static PointIndices lookup_point_indices(Span<int> offsets, const int index)
-{
- const int spline_index = std::upper_bound(offsets.begin(), offsets.end(), index) -
- offsets.begin() - 1;
- const int index_in_spline = index - offsets[spline_index];
- return {spline_index, index_in_spline};
-}
-
-/**
- * Mix together all of a spline's control point values.
- *
- * \note Theoretically this interpolation does not need to compute all values at once.
- * However, doing that makes the implementation simpler, and this can be optimized in the future if
- * only some values are required.
- */
-template<typename T>
-static void adapt_curve_domain_point_to_spline_impl(const CurveEval &curve,
- const VArray<T> &old_values,
- MutableSpan<T> r_values)
-{
- const int splines_len = curve.splines().size();
- Array<int> offsets = curve.control_point_offsets();
- BLI_assert(r_values.size() == splines_len);
- attribute_math::DefaultMixer<T> mixer(r_values);
-
- for (const int i_spline : IndexRange(splines_len)) {
- const int spline_offset = offsets[i_spline];
- const int spline_point_len = offsets[i_spline + 1] - spline_offset;
- for (const int i_point : IndexRange(spline_point_len)) {
- const T value = old_values[spline_offset + i_point];
- mixer.mix_in(i_spline, value);
- }
- }
-
- mixer.finalize();
-}
-
-/**
- * A spline is selected if all of its control points were selected.
- *
- * \note Theoretically this interpolation does not need to compute all values at once.
- * However, doing that makes the implementation simpler, and this can be optimized in the future if
- * only some values are required.
- */
-template<>
-void adapt_curve_domain_point_to_spline_impl(const CurveEval &curve,
- const VArray<bool> &old_values,
- MutableSpan<bool> r_values)
-{
- const int splines_len = curve.splines().size();
- Array<int> offsets = curve.control_point_offsets();
- BLI_assert(r_values.size() == splines_len);
-
- r_values.fill(true);
-
- for (const int i_spline : IndexRange(splines_len)) {
- const int spline_offset = offsets[i_spline];
- const int spline_point_len = offsets[i_spline + 1] - spline_offset;
-
- for (const int i_point : IndexRange(spline_point_len)) {
- if (!old_values[spline_offset + i_point]) {
- r_values[i_spline] = false;
- break;
- }
- }
- }
-}
-
-static GVArray adapt_curve_domain_point_to_spline(const CurveEval &curve, GVArray varray)
-{
- GVArray new_varray;
- attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
- using T = decltype(dummy);
- if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
- Array<T> values(curve.splines().size());
- adapt_curve_domain_point_to_spline_impl<T>(curve, varray.typed<T>(), values);
- new_varray = VArray<T>::ForContainer(std::move(values));
- }
- });
- return new_varray;
-}
-
-/**
- * A virtual array implementation for the conversion of spline attributes to control point
- * attributes. The goal is to avoid copying the spline value for every one of its control points
- * unless it is necessary (in that case the materialize functions will be called).
- */
-template<typename T> class VArray_For_SplineToPoint final : public VArrayImpl<T> {
- GVArray original_varray_;
- /* Store existing data materialized if it was not already a span. This is expected
- * to be worth it because a single spline's value will likely be accessed many times. */
- VArraySpan<T> original_data_;
- Array<int> offsets_;
-
- public:
- VArray_For_SplineToPoint(GVArray original_varray, Array<int> offsets)
- : VArrayImpl<T>(offsets.last()),
- original_varray_(std::move(original_varray)),
- original_data_(original_varray_.typed<T>()),
- offsets_(std::move(offsets))
- {
- }
-
- T get(const int64_t index) const final
- {
- const PointIndices indices = lookup_point_indices(offsets_, index);
- return original_data_[indices.spline_index];
- }
-
- void materialize(const IndexMask mask, MutableSpan<T> r_span) const final
- {
- const int total_num = offsets_.last();
- if (mask.is_range() && mask.as_range() == IndexRange(total_num)) {
- for (const int spline_index : original_data_.index_range()) {
- const int offset = offsets_[spline_index];
- const int next_offset = offsets_[spline_index + 1];
- r_span.slice(offset, next_offset - offset).fill(original_data_[spline_index]);
- }
- }
- else {
- int spline_index = 0;
- for (const int dst_index : mask) {
- while (offsets_[spline_index] < dst_index) {
- spline_index++;
- }
- r_span[dst_index] = original_data_[spline_index];
- }
- }
- }
-
- void materialize_to_uninitialized(const IndexMask mask, MutableSpan<T> r_span) const final
- {
- T *dst = r_span.data();
- const int total_num = offsets_.last();
- if (mask.is_range() && mask.as_range() == IndexRange(total_num)) {
- for (const int spline_index : original_data_.index_range()) {
- const int offset = offsets_[spline_index];
- const int next_offset = offsets_[spline_index + 1];
- uninitialized_fill_n(dst + offset, next_offset - offset, original_data_[spline_index]);
- }
- }
- else {
- int spline_index = 0;
- for (const int dst_index : mask) {
- while (offsets_[spline_index] < dst_index) {
- spline_index++;
- }
- new (dst + dst_index) T(original_data_[spline_index]);
- }
- }
- }
-};
-
-static GVArray adapt_curve_domain_spline_to_point(const CurveEval &curve, GVArray varray)
-{
- GVArray new_varray;
- attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
- using T = decltype(dummy);
-
- Array<int> offsets = curve.control_point_offsets();
- new_varray = VArray<T>::template For<VArray_For_SplineToPoint<T>>(std::move(varray),
- std::move(offsets));
- });
- return new_varray;
-}
-
-} // namespace blender::bke
-
-static GVArray adapt_curve_attribute_domain(const CurveEval &curve,
- const GVArray &varray,
- const eAttrDomain from_domain,
- const eAttrDomain to_domain)
-{
- if (!varray) {
- return {};
- }
- if (varray.is_empty()) {
- return {};
- }
- if (from_domain == to_domain) {
- return varray;
- }
-
- if (from_domain == ATTR_DOMAIN_POINT && to_domain == ATTR_DOMAIN_CURVE) {
- return blender::bke::adapt_curve_domain_point_to_spline(curve, std::move(varray));
- }
- if (from_domain == ATTR_DOMAIN_CURVE && to_domain == ATTR_DOMAIN_POINT) {
- return blender::bke::adapt_curve_domain_spline_to_point(curve, std::move(varray));
- }
-
- return {};
-}
-
-/** \} */
-
-namespace blender::bke {
-
-/* -------------------------------------------------------------------- */
-/** \name Builtin Spline Attributes
- *
- * Attributes with a value for every spline, stored contiguously or in every spline separately.
- * \{ */
-
-class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider {
- using AsReadAttribute = GVArray (*)(const CurveEval &data);
- using AsWriteAttribute = GVMutableArray (*)(CurveEval &data);
- const AsReadAttribute as_read_attribute_;
- const AsWriteAttribute as_write_attribute_;
-
- public:
- BuiltinSplineAttributeProvider(std::string attribute_name,
- const eCustomDataType attribute_type,
- const WritableEnum writable,
- const AsReadAttribute as_read_attribute,
- const AsWriteAttribute as_write_attribute)
- : BuiltinAttributeProvider(std::move(attribute_name),
- ATTR_DOMAIN_CURVE,
- attribute_type,
- BuiltinAttributeProvider::NonCreatable,
- writable,
- BuiltinAttributeProvider::NonDeletable),
- as_read_attribute_(as_read_attribute),
- as_write_attribute_(as_write_attribute)
- {
- }
-
- GVArray try_get_for_read(const void *owner) const final
- {
- const CurveEval *curve = static_cast<const CurveEval *>(owner);
- if (curve == nullptr) {
- return {};
- }
- return as_read_attribute_(*curve);
- }
-
- GAttributeWriter try_get_for_write(void *owner) const final
- {
- if (writable_ != Writable) {
- return {};
- }
- CurveEval *curve = static_cast<CurveEval *>(owner);
- if (curve == nullptr) {
- return {};
- }
- return {as_write_attribute_(*curve), domain_};
- }
-
- bool try_delete(void *UNUSED(owner)) const final
- {
- return false;
- }
-
- bool try_create(void *UNUSED(owner), const AttributeInit &UNUSED(initializer)) const final
- {
- return false;
- }
-
- bool exists(const void *owner) const final
- {
- const CurveEval *curve = static_cast<const CurveEval *>(owner);
- return !curve->splines().is_empty();
- }
-};
-
-static int get_spline_resolution(const SplinePtr &spline)
-{
- if (const BezierSpline *bezier_spline = dynamic_cast<const BezierSpline *>(spline.get())) {
- return bezier_spline->resolution();
- }
- if (const NURBSpline *nurb_spline = dynamic_cast<const NURBSpline *>(spline.get())) {
- return nurb_spline->resolution();
- }
- return 1;
-}
-
-static void set_spline_resolution(SplinePtr &spline, const int resolution)
-{
- if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(spline.get())) {
- bezier_spline->set_resolution(std::max(resolution, 1));
- }
- if (NURBSpline *nurb_spline = dynamic_cast<NURBSpline *>(spline.get())) {
- nurb_spline->set_resolution(std::max(resolution, 1));
- }
-}
-
-static GVArray make_resolution_read_attribute(const CurveEval &curve)
-{
- return VArray<int>::ForDerivedSpan<SplinePtr, get_spline_resolution>(curve.splines());
-}
-
-static GVMutableArray make_resolution_write_attribute(CurveEval &curve)
-{
- return VMutableArray<int>::
- ForDerivedSpan<SplinePtr, get_spline_resolution, set_spline_resolution>(curve.splines());
-}
-
-static bool get_cyclic_value(const SplinePtr &spline)
-{
- return spline->is_cyclic();
-}
-
-static void set_cyclic_value(SplinePtr &spline, const bool value)
-{
- if (spline->is_cyclic() != value) {
- spline->set_cyclic(value);
- spline->mark_cache_invalid();
- }
-}
-
-static GVArray make_cyclic_read_attribute(const CurveEval &curve)
-{
- return VArray<bool>::ForDerivedSpan<SplinePtr, get_cyclic_value>(curve.splines());
-}
-
-static GVMutableArray make_cyclic_write_attribute(CurveEval &curve)
-{
- return VMutableArray<bool>::ForDerivedSpan<SplinePtr, get_cyclic_value, set_cyclic_value>(
- curve.splines());
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Builtin Control Point Attributes
- *
- * Attributes with a value for every control point. Most of the complexity here is due to the fact
- * that we must provide access to the attribute data as if it was a contiguous array when it is
- * really stored separately on each spline. That will be inherently rather slow, but these virtual
- * array implementations try to make it workable in common situations.
- * \{ */
-
-/**
- * Individual spans in \a data may be empty if that spline contains no data for the attribute.
- */
-template<typename T>
-static void point_attribute_materialize(Span<Span<T>> data,
- Span<int> offsets,
- const IndexMask mask,
- MutableSpan<T> r_span)
-{
- const int total_num = offsets.last();
- if (mask.is_range() && mask.as_range() == IndexRange(total_num)) {
- for (const int spline_index : data.index_range()) {
- const int offset = offsets[spline_index];
- const int next_offset = offsets[spline_index + 1];
-
- Span<T> src = data[spline_index];
- MutableSpan<T> dst = r_span.slice(offset, next_offset - offset);
- if (src.is_empty()) {
- dst.fill(T());
- }
- else {
- dst.copy_from(src);
- }
- }
- }
- else {
- int spline_index = 0;
- for (const int dst_index : mask) {
- /* Skip splines that don't have any control points in the mask. */
- while (dst_index >= offsets[spline_index + 1]) {
- spline_index++;
- }
-
- const int index_in_spline = dst_index - offsets[spline_index];
- Span<T> src = data[spline_index];
- if (src.is_empty()) {
- r_span[dst_index] = T();
- }
- else {
- r_span[dst_index] = src[index_in_spline];
- }
- }
- }
-}
-
-/**
- * Individual spans in \a data may be empty if that spline contains no data for the attribute.
- */
-template<typename T>
-static void point_attribute_materialize_to_uninitialized(Span<Span<T>> data,
- Span<int> offsets,
- const IndexMask mask,
- MutableSpan<T> r_span)
-{
- T *dst = r_span.data();
- const int total_num = offsets.last();
- if (mask.is_range() && mask.as_range() == IndexRange(total_num)) {
- for (const int spline_index : data.index_range()) {
- const int offset = offsets[spline_index];
- const int next_offset = offsets[spline_index + 1];
-
- Span<T> src = data[spline_index];
- if (src.is_empty()) {
- uninitialized_fill_n(dst + offset, next_offset - offset, T());
- }
- else {
- uninitialized_copy_n(src.data(), next_offset - offset, dst + offset);
- }
- }
- }
- else {
- int spline_index = 0;
- for (const int dst_index : mask) {
- /* Skip splines that don't have any control points in the mask. */
- while (dst_index >= offsets[spline_index + 1]) {
- spline_index++;
- }
-
- const int index_in_spline = dst_index - offsets[spline_index];
- Span<T> src = data[spline_index];
- if (src.is_empty()) {
- new (dst + dst_index) T();
- }
- else {
- new (dst + dst_index) T(src[index_in_spline]);
- }
- }
- }
-}
-
-static GVArray varray_from_initializer(const AttributeInit &initializer,
- const eCustomDataType data_type,
- const Span<SplinePtr> splines)
-{
- switch (initializer.type) {
- case AttributeInit::Type::Construct:
- case AttributeInit::Type::DefaultValue:
- /* This function shouldn't be called in this case, since there
- * is no need to copy anything to the new custom data array. */
- BLI_assert_unreachable();
- return {};
- case AttributeInit::Type::VArray:
- return static_cast<const AttributeInitVArray &>(initializer).varray;
- case AttributeInit::Type::MoveArray:
- int total_num = 0;
- for (const SplinePtr &spline : splines) {
- total_num += spline->size();
- }
- return GVArray::ForSpan(GSpan(*bke::custom_data_type_to_cpp_type(data_type),
- static_cast<const AttributeInitMoveArray &>(initializer).data,
- total_num));
- }
- BLI_assert_unreachable();
- return {};
-}
-
-static bool create_point_attribute(CurveEval *curve,
- const AttributeIDRef &attribute_id,
- const AttributeInit &initializer,
- const eCustomDataType data_type)
-{
- if (curve == nullptr || curve->splines().size() == 0) {
- return false;
- }
-
- MutableSpan<SplinePtr> splines = curve->splines();
-
- /* First check the one case that allows us to avoid copying the input data. */
- if (splines.size() == 1 && initializer.type == AttributeInit::Type::MoveArray) {
- void *source_data = static_cast<const AttributeInitMoveArray &>(initializer).data;
- if (!splines.first()->attributes.create_by_move(attribute_id, data_type, source_data)) {
- MEM_freeN(source_data);
- return false;
- }
- return true;
- }
-
- /* Otherwise just create a custom data layer on each of the splines. */
- for (const int i : splines.index_range()) {
- if (!splines[i]->attributes.create(attribute_id, data_type)) {
- /* If attribute creation fails on one of the splines, we cannot leave the custom data
- * layers in the previous splines around, so delete them before returning. However,
- * this is not an expected case. */
- BLI_assert_unreachable();
- return false;
- }
- }
-
- /* With a default initializer type, we can keep the values at their initial values. */
- if (ELEM(initializer.type, AttributeInit::Type::DefaultValue, AttributeInit::Type::Construct)) {
- return true;
- }
-
- GAttributeWriter write_attribute = curve->attributes_for_write().lookup_for_write(attribute_id);
- /* We just created the attribute, it should exist. */
- BLI_assert(write_attribute);
-
- GVArray source_varray = varray_from_initializer(initializer, data_type, splines);
- /* TODO: When we can call a variant of #set_all with a virtual array argument,
- * this theoretically unnecessary materialize step could be removed. */
- GVArraySpan source_VArraySpan{source_varray};
- write_attribute.varray.set_all(source_VArraySpan.data());
- write_attribute.finish();
-
- if (initializer.type == AttributeInit::Type::MoveArray) {
- MEM_freeN(static_cast<const AttributeInitMoveArray &>(initializer).data);
- }
-
- return true;
-}
-
-static bool remove_point_attribute(CurveEval *curve, const AttributeIDRef &attribute_id)
-{
- if (curve == nullptr) {
- return false;
- }
-
- /* Reuse the boolean for all splines; we expect all splines to have the same attributes. */
- bool layer_freed = false;
- for (SplinePtr &spline : curve->splines()) {
- layer_freed = spline->attributes.remove(attribute_id);
- }
- return layer_freed;
-}
-
-/**
- * Mutable virtual array for any control point data accessed with spans and an offset array.
- */
-template<typename T> class VArrayImpl_For_SplinePoints final : public VMutableArrayImpl<T> {
- private:
- Array<MutableSpan<T>> data_;
- Array<int> offsets_;
-
- public:
- VArrayImpl_For_SplinePoints(Array<MutableSpan<T>> data, Array<int> offsets)
- : VMutableArrayImpl<T>(offsets.last()), data_(std::move(data)), offsets_(std::move(offsets))
- {
- }
-
- T get(const int64_t index) const final
- {
- const PointIndices indices = lookup_point_indices(offsets_, index);
- return data_[indices.spline_index][indices.point_index];
- }
-
- void set(const int64_t index, T value) final
- {
- const PointIndices indices = lookup_point_indices(offsets_, index);
- data_[indices.spline_index][indices.point_index] = value;
- }
-
- void set_all(Span<T> src) final
- {
- for (const int spline_index : data_.index_range()) {
- const int offset = offsets_[spline_index];
- const int next_offsets = offsets_[spline_index + 1];
- data_[spline_index].copy_from(src.slice(offset, next_offsets - offset));
- }
- }
-
- void materialize(const IndexMask mask, MutableSpan<T> r_span) const final
- {
- point_attribute_materialize({(Span<T> *)data_.data(), data_.size()}, offsets_, mask, r_span);
- }
-
- void materialize_to_uninitialized(const IndexMask mask, MutableSpan<T> r_span) const final
- {
- point_attribute_materialize_to_uninitialized(
- {(Span<T> *)data_.data(), data_.size()}, offsets_, mask, r_span);
- }
-};
-
-template<typename T> VArray<T> point_data_varray(Array<MutableSpan<T>> spans, Array<int> offsets)
-{
- return VArray<T>::template For<VArrayImpl_For_SplinePoints<T>>(std::move(spans),
- std::move(offsets));
-}
-
-template<typename T>
-VMutableArray<T> point_data_varray_mutable(Array<MutableSpan<T>> spans, Array<int> offsets)
-{
- return VMutableArray<T>::template For<VArrayImpl_For_SplinePoints<T>>(std::move(spans),
- std::move(offsets));
-}
-
-/**
- * Virtual array implementation specifically for control point positions. This is only needed for
- * Bezier splines, where adjusting the position also requires adjusting handle positions depending
- * on handle types. We pay a small price for this when other spline types are mixed with Bezier.
- *
- * \note There is no need to check the handle type to avoid changing auto handles, since
- * retrieving write access to the position data will mark them for recomputation anyway.
- */
-class VArrayImpl_For_SplinePosition final : public VMutableArrayImpl<float3> {
- private:
- MutableSpan<SplinePtr> splines_;
- Array<int> offsets_;
-
- public:
- VArrayImpl_For_SplinePosition(MutableSpan<SplinePtr> splines, Array<int> offsets)
- : VMutableArrayImpl<float3>(offsets.last()), splines_(splines), offsets_(std::move(offsets))
- {
- }
-
- float3 get(const int64_t index) const final
- {
- const PointIndices indices = lookup_point_indices(offsets_, index);
- return splines_[indices.spline_index]->positions()[indices.point_index];
- }
-
- void set(const int64_t index, float3 value) final
- {
- const PointIndices indices = lookup_point_indices(offsets_, index);
- Spline &spline = *splines_[indices.spline_index];
- spline.positions()[indices.point_index] = value;
- }
-
- void set_all(Span<float3> src) final
- {
- for (const int spline_index : splines_.index_range()) {
- Spline &spline = *splines_[spline_index];
- const int offset = offsets_[spline_index];
- const int next_offset = offsets_[spline_index + 1];
- spline.positions().copy_from(src.slice(offset, next_offset - offset));
- }
- }
-
- /** Utility so we can pass positions to the materialize functions above. */
- Array<Span<float3>> get_position_spans() const
- {
- Array<Span<float3>> spans(splines_.size());
- for (const int i : spans.index_range()) {
- spans[i] = splines_[i]->positions();
- }
- return spans;
- }
-
- void materialize(const IndexMask mask, MutableSpan<float3> r_span) const final
- {
- Array<Span<float3>> spans = this->get_position_spans();
- point_attribute_materialize(spans.as_span(), offsets_, mask, r_span);
- }
-
- void materialize_to_uninitialized(const IndexMask mask, MutableSpan<float3> r_span) const final
- {
- Array<Span<float3>> spans = this->get_position_spans();
- point_attribute_materialize_to_uninitialized(spans.as_span(), offsets_, mask, r_span);
- }
-};
-
-class VArrayImpl_For_BezierHandles final : public VMutableArrayImpl<float3> {
- private:
- MutableSpan<SplinePtr> splines_;
- Array<int> offsets_;
- bool is_right_;
-
- public:
- VArrayImpl_For_BezierHandles(MutableSpan<SplinePtr> splines,
- Array<int> offsets,
- const bool is_right)
- : VMutableArrayImpl<float3>(offsets.last()),
- splines_(splines),
- offsets_(std::move(offsets)),
- is_right_(is_right)
- {
- }
-
- float3 get(const int64_t index) const final
- {
- const PointIndices indices = lookup_point_indices(offsets_, index);
- const Spline &spline = *splines_[indices.spline_index];
- if (spline.type() == CURVE_TYPE_BEZIER) {
- const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(spline);
- return is_right_ ? bezier_spline.handle_positions_right()[indices.point_index] :
- bezier_spline.handle_positions_left()[indices.point_index];
- }
- return float3(0);
- }
-
- void set(const int64_t index, float3 value) final
- {
- const PointIndices indices = lookup_point_indices(offsets_, index);
- Spline &spline = *splines_[indices.spline_index];
- if (spline.type() == CURVE_TYPE_BEZIER) {
- BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline);
- if (is_right_) {
- bezier_spline.handle_positions_right()[indices.point_index] = value;
- }
- else {
- bezier_spline.handle_positions_left()[indices.point_index] = value;
- }
- bezier_spline.mark_cache_invalid();
- }
- }
-
- void set_all(Span<float3> src) final
- {
- for (const int spline_index : splines_.index_range()) {
- Spline &spline = *splines_[spline_index];
- if (spline.type() == CURVE_TYPE_BEZIER) {
- const int offset = offsets_[spline_index];
-
- BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline);
- if (is_right_) {
- for (const int i : IndexRange(bezier_spline.size())) {
- bezier_spline.handle_positions_right()[i] = src[offset + i];
- }
- }
- else {
- for (const int i : IndexRange(bezier_spline.size())) {
- bezier_spline.handle_positions_left()[i] = src[offset + i];
- }
- }
- bezier_spline.mark_cache_invalid();
- }
- }
- }
-
- void materialize(const IndexMask mask, MutableSpan<float3> r_span) const final
- {
- Array<Span<float3>> spans = get_handle_spans(splines_, is_right_);
- point_attribute_materialize(spans.as_span(), offsets_, mask, r_span);
- }
-
- void materialize_to_uninitialized(const IndexMask mask, MutableSpan<float3> r_span) const final
- {
- Array<Span<float3>> spans = get_handle_spans(splines_, is_right_);
- point_attribute_materialize_to_uninitialized(spans.as_span(), offsets_, mask, r_span);
- }
-
- /**
- * Utility so we can pass handle positions to the materialize functions above.
- *
- * \note This relies on the ability of the materialize implementations to
- * handle empty spans, since only Bezier splines have handles.
- */
- static Array<Span<float3>> get_handle_spans(Span<SplinePtr> splines, const bool is_right)
- {
- Array<Span<float3>> spans(splines.size());
- for (const int i : spans.index_range()) {
- if (splines[i]->type() == CURVE_TYPE_BEZIER) {
- BezierSpline &bezier_spline = static_cast<BezierSpline &>(*splines[i]);
- spans[i] = is_right ? bezier_spline.handle_positions_right() :
- bezier_spline.handle_positions_left();
- }
- else {
- spans[i] = {};
- }
- }
- return spans;
- }
-};
-
-/**
- * Provider for any builtin control point attribute that doesn't need
- * special handling like access to other arrays in the spline.
- */
-template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttributeProvider {
- protected:
- using GetSpan = Span<T> (*)(const Spline &spline);
- using GetMutableSpan = MutableSpan<T> (*)(Spline &spline);
- using UpdateOnWrite = void (*)(Spline &spline);
- const GetSpan get_span_;
- const GetMutableSpan get_mutable_span_;
- const UpdateOnWrite update_on_write_;
- bool stored_in_custom_data_;
-
- public:
- BuiltinPointAttributeProvider(std::string attribute_name,
- const CreatableEnum creatable,
- const DeletableEnum deletable,
- const GetSpan get_span,
- const GetMutableSpan get_mutable_span,
- const UpdateOnWrite update_on_write,
- const bool stored_in_custom_data)
- : BuiltinAttributeProvider(std::move(attribute_name),
- ATTR_DOMAIN_POINT,
- bke::cpp_type_to_custom_data_type(CPPType::get<T>()),
- creatable,
- WritableEnum::Writable,
- deletable),
- get_span_(get_span),
- get_mutable_span_(get_mutable_span),
- update_on_write_(update_on_write),
- stored_in_custom_data_(stored_in_custom_data)
- {
- }
-
- GVArray try_get_for_read(const void *owner) const override
- {
- const CurveEval *curve = static_cast<const CurveEval *>(owner);
- if (curve == nullptr) {
- return {};
- }
-
- if (!this->exists(owner)) {
- return {};
- }
-
- Span<SplinePtr> splines = curve->splines();
- if (splines.size() == 1) {
- return GVArray::ForSpan(get_span_(*splines.first()));
- }
-
- Array<int> offsets = curve->control_point_offsets();
- Array<MutableSpan<T>> spans(splines.size());
- for (const int i : splines.index_range()) {
- Span<T> span = get_span_(*splines[i]);
- /* Use const-cast because the underlying virtual array implementation is shared between const
- * and non const data. */
- spans[i] = MutableSpan<T>(const_cast<T *>(span.data()), span.size());
- }
-
- return point_data_varray(spans, offsets);
- }
-
- GAttributeWriter try_get_for_write(void *owner) const override
- {
- CurveEval *curve = static_cast<CurveEval *>(owner);
- if (curve == nullptr) {
- return {};
- }
-
- if (!this->exists(owner)) {
- return {};
- }
-
- std::function<void()> tag_modified_fn;
- if (update_on_write_ != nullptr) {
- tag_modified_fn = [curve, update = update_on_write_]() {
- for (SplinePtr &spline : curve->splines()) {
- update(*spline);
- }
- };
- }
-
- MutableSpan<SplinePtr> splines = curve->splines();
- if (splines.size() == 1) {
- return {GVMutableArray::ForSpan(get_mutable_span_(*splines.first())),
- domain_,
- std::move(tag_modified_fn)};
- }
-
- Array<int> offsets = curve->control_point_offsets();
- Array<MutableSpan<T>> spans(splines.size());
- for (const int i : splines.index_range()) {
- spans[i] = get_mutable_span_(*splines[i]);
- }
-
- return {point_data_varray_mutable(spans, offsets), domain_, tag_modified_fn};
- }
-
- bool try_delete(void *owner) const final
- {
- if (deletable_ == DeletableEnum::NonDeletable) {
- return false;
- }
- CurveEval *curve = static_cast<CurveEval *>(owner);
- return remove_point_attribute(curve, name_);
- }
-
- bool try_create(void *owner, const AttributeInit &initializer) const final
- {
- if (createable_ == CreatableEnum::NonCreatable) {
- return false;
- }
- CurveEval *curve = static_cast<CurveEval *>(owner);
- return create_point_attribute(curve, name_, initializer, CD_PROP_INT32);
- }
-
- bool exists(const void *owner) const final
- {
- const CurveEval *curve = static_cast<const CurveEval *>(owner);
- if (curve == nullptr) {
- return false;
- }
-
- Span<SplinePtr> splines = curve->splines();
- if (splines.size() == 0) {
- return false;
- }
-
- if (stored_in_custom_data_) {
- if (!curve->splines().first()->attributes.get_for_read(name_)) {
- return false;
- }
- }
-
- bool has_point = false;
- for (const SplinePtr &spline : curve->splines()) {
- if (spline->size() != 0) {
- has_point = true;
- break;
- }
- }
-
- if (!has_point) {
- return false;
- }
-
- return true;
- }
-};
-
-/**
- * Special attribute provider for the position attribute. Keeping this separate means we don't
- * need to make #BuiltinPointAttributeProvider overly generic, and the special handling for the
- * positions is more clear.
- */
-class PositionAttributeProvider final : public BuiltinPointAttributeProvider<float3> {
- public:
- PositionAttributeProvider()
- : BuiltinPointAttributeProvider(
- "position",
- BuiltinAttributeProvider::NonCreatable,
- BuiltinAttributeProvider::NonDeletable,
- [](const Spline &spline) { return spline.positions(); },
- [](Spline &spline) { return spline.positions(); },
- [](Spline &spline) { spline.mark_cache_invalid(); },
- false)
- {
- }
-
- GAttributeWriter try_get_for_write(void *owner) const final
- {
- CurveEval *curve = static_cast<CurveEval *>(owner);
- if (curve == nullptr) {
- return {};
- }
-
- /* Use the regular position virtual array when there aren't any Bezier splines
- * to avoid the overhead of checking the spline type for every point. */
- if (!curve->has_spline_with_type(CURVE_TYPE_BEZIER)) {
- return BuiltinPointAttributeProvider<float3>::try_get_for_write(owner);
- }
-
- auto tag_modified_fn = [curve]() {
- /* Changing the positions requires recalculation of cached evaluated data in many cases.
- * This could set more specific flags in the future to avoid unnecessary recomputation. */
- curve->mark_cache_invalid();
- };
-
- Array<int> offsets = curve->control_point_offsets();
- return {VMutableArray<float3>::For<VArrayImpl_For_SplinePosition>(curve->splines(),
- std::move(offsets)),
- domain_,
- tag_modified_fn};
- }
-};
-
-class BezierHandleAttributeProvider : public BuiltinAttributeProvider {
- private:
- bool is_right_;
-
- public:
- BezierHandleAttributeProvider(const bool is_right)
- : BuiltinAttributeProvider(is_right ? "handle_right" : "handle_left",
- ATTR_DOMAIN_POINT,
- CD_PROP_FLOAT3,
- BuiltinAttributeProvider::NonCreatable,
- BuiltinAttributeProvider::Writable,
- BuiltinAttributeProvider::NonDeletable),
- is_right_(is_right)
- {
- }
-
- GVArray try_get_for_read(const void *owner) const override
- {
- const CurveEval *curve = static_cast<const CurveEval *>(owner);
- if (curve == nullptr) {
- return {};
- }
-
- if (!curve->has_spline_with_type(CURVE_TYPE_BEZIER)) {
- return {};
- }
-
- Array<int> offsets = curve->control_point_offsets();
- /* Use const-cast because the underlying virtual array implementation is shared between const
- * and non const data. */
- return VArray<float3>::For<VArrayImpl_For_BezierHandles>(
- const_cast<CurveEval *>(curve)->splines(), std::move(offsets), is_right_);
- }
-
- GAttributeWriter try_get_for_write(void *owner) const override
- {
- CurveEval *curve = static_cast<CurveEval *>(owner);
- if (curve == nullptr) {
- return {};
- }
-
- if (!curve->has_spline_with_type(CURVE_TYPE_BEZIER)) {
- return {};
- }
-
- auto tag_modified_fn = [curve]() { curve->mark_cache_invalid(); };
-
- Array<int> offsets = curve->control_point_offsets();
- return {VMutableArray<float3>::For<VArrayImpl_For_BezierHandles>(
- curve->splines(), std::move(offsets), is_right_),
- domain_,
- tag_modified_fn};
- }
-
- bool try_delete(void *UNUSED(owner)) const final
- {
- return false;
- }
-
- bool try_create(void *UNUSED(owner), const AttributeInit &UNUSED(initializer)) const final
- {
- return false;
- }
-
- bool exists(const void *owner) const final
- {
- const CurveEval *curve = static_cast<const CurveEval *>(owner);
- if (curve == nullptr) {
- return false;
- }
-
- CurveComponentLegacy component;
- component.replace(const_cast<CurveEval *>(curve), GeometryOwnershipType::ReadOnly);
-
- return curve->has_spline_with_type(CURVE_TYPE_BEZIER) && !curve->splines().is_empty();
- }
-};
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Dynamic Control Point Attributes
- *
- * The dynamic control point attribute implementation is very similar to the builtin attribute
- * implementation-- it uses the same virtual array types. In order to work, this code depends on
- * the fact that all a curve's splines will have the same attributes and they all have the same
- * type.
- * \{ */
-
-class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
- private:
- static constexpr uint64_t supported_types_mask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 |
- CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_INT32 |
- CD_MASK_PROP_COLOR | CD_MASK_PROP_BOOL |
- CD_MASK_PROP_INT8;
-
- public:
- GAttributeReader try_get_for_read(const void *owner,
- const AttributeIDRef &attribute_id) const final
- {
- const CurveEval *curve = static_cast<const CurveEval *>(owner);
- if (curve == nullptr || curve->splines().size() == 0) {
- return {};
- }
-
- Span<SplinePtr> splines = curve->splines();
- Vector<GSpan> spans; /* GSpan has no default constructor. */
- spans.reserve(splines.size());
- std::optional<GSpan> first_span = splines[0]->attributes.get_for_read(attribute_id);
- if (!first_span) {
- return {};
- }
- spans.append(*first_span);
- for (const int i : IndexRange(1, splines.size() - 1)) {
- std::optional<GSpan> span = splines[i]->attributes.get_for_read(attribute_id);
- if (!span) {
- /* All splines should have the same set of data layers. It would be possible to recover
- * here and return partial data instead, but that would add a lot of complexity for a
- * situation we don't even expect to encounter. */
- BLI_assert_unreachable();
- return {};
- }
- if (span->type() != spans.last().type()) {
- /* Data layer types on separate splines do not match. */
- BLI_assert_unreachable();
- return {};
- }
- spans.append(*span);
- }
-
- /* First check for the simpler situation when we can return a simpler span virtual array. */
- if (spans.size() == 1) {
- return {GVArray::ForSpan(spans.first()), ATTR_DOMAIN_POINT};
- }
-
- GAttributeReader attribute = {};
- Array<int> offsets = curve->control_point_offsets();
- attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) {
- using T = decltype(dummy);
- Array<MutableSpan<T>> data(splines.size());
- for (const int i : splines.index_range()) {
- Span<T> span = spans[i].typed<T>();
- /* Use const-cast because the underlying virtual array implementation is shared between
- * const and non const data. */
- data[i] = MutableSpan<T>(const_cast<T *>(span.data()), span.size());
- BLI_assert(data[i].data() != nullptr);
- }
- attribute = {point_data_varray(data, offsets), ATTR_DOMAIN_POINT};
- });
- return attribute;
- }
-
- /* This function is almost the same as #try_get_for_read, but without const. */
- GAttributeWriter try_get_for_write(void *owner, const AttributeIDRef &attribute_id) const final
- {
- CurveEval *curve = static_cast<CurveEval *>(owner);
- if (curve == nullptr || curve->splines().size() == 0) {
- return {};
- }
-
- MutableSpan<SplinePtr> splines = curve->splines();
- Vector<GMutableSpan> spans; /* GMutableSpan has no default constructor. */
- spans.reserve(splines.size());
- std::optional<GMutableSpan> first_span = splines[0]->attributes.get_for_write(attribute_id);
- if (!first_span) {
- return {};
- }
- spans.append(*first_span);
- for (const int i : IndexRange(1, splines.size() - 1)) {
- std::optional<GMutableSpan> span = splines[i]->attributes.get_for_write(attribute_id);
- if (!span) {
- /* All splines should have the same set of data layers. It would be possible to recover
- * here and return partial data instead, but that would add a lot of complexity for a
- * situation we don't even expect to encounter. */
- BLI_assert_unreachable();
- return {};
- }
- if (span->type() != spans.last().type()) {
- /* Data layer types on separate splines do not match. */
- BLI_assert_unreachable();
- return {};
- }
- spans.append(*span);
- }
-
- /* First check for the simpler situation when we can return a simpler span virtual array. */
- if (spans.size() == 1) {
- return {GVMutableArray::ForSpan(spans.first()), ATTR_DOMAIN_POINT};
- }
-
- GAttributeWriter attribute = {};
- Array<int> offsets = curve->control_point_offsets();
- attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) {
- using T = decltype(dummy);
- Array<MutableSpan<T>> data(splines.size());
- for (const int i : splines.index_range()) {
- data[i] = spans[i].typed<T>();
- BLI_assert(data[i].data() != nullptr);
- }
- attribute = {point_data_varray_mutable(data, offsets), ATTR_DOMAIN_POINT};
- });
- return attribute;
- }
-
- bool try_delete(void *owner, const AttributeIDRef &attribute_id) const final
- {
- CurveEval *curve = static_cast<CurveEval *>(owner);
- return remove_point_attribute(curve, attribute_id);
- }
-
- bool try_create(void *owner,
- const AttributeIDRef &attribute_id,
- const eAttrDomain domain,
- const eCustomDataType data_type,
- const AttributeInit &initializer) const final
- {
- BLI_assert(this->type_is_supported(data_type));
- if (domain != ATTR_DOMAIN_POINT) {
- return false;
- }
- CurveEval *curve = static_cast<CurveEval *>(owner);
- return create_point_attribute(curve, attribute_id, initializer, data_type);
- }
-
- bool foreach_attribute(const void *owner, const AttributeForeachCallback callback) const final
- {
- const CurveEval *curve = static_cast<const CurveEval *>(owner);
- if (curve == nullptr || curve->splines().size() == 0) {
- return false;
- }
-
- Span<SplinePtr> splines = curve->splines();
-
- /* In a debug build, check that all corresponding custom data layers have the same type. */
- curve->assert_valid_point_attributes();
-
- /* Use the first spline as a representative for all the others. */
- splines.first()->attributes.foreach_attribute(callback, ATTR_DOMAIN_POINT);
-
- return true;
- }
-
- void foreach_domain(const FunctionRef<void(eAttrDomain)> callback) const final
- {
- callback(ATTR_DOMAIN_POINT);
- }
-
- bool type_is_supported(eCustomDataType data_type) const
- {
- return ((1ULL << data_type) & supported_types_mask) != 0;
- }
-};
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Attribute Provider Declaration
- * \{ */
-
-/**
- * In this function all the attribute providers for a curve component are created.
- * Most data in this function is statically allocated, because it does not change over time.
- */
-static ComponentAttributeProviders create_attribute_providers_for_curve()
-{
- static BuiltinSplineAttributeProvider resolution("resolution",
- CD_PROP_INT32,
- BuiltinAttributeProvider::Writable,
- make_resolution_read_attribute,
- make_resolution_write_attribute);
-
- static BuiltinSplineAttributeProvider cyclic("cyclic",
- CD_PROP_BOOL,
- BuiltinAttributeProvider::Writable,
- make_cyclic_read_attribute,
- make_cyclic_write_attribute);
-
- static CustomDataAccessInfo spline_custom_data_access = {
- [](void *owner) -> CustomData * {
- CurveEval *curve = static_cast<CurveEval *>(owner);
- return curve ? &curve->attributes.data : nullptr;
- },
- [](const void *owner) -> const CustomData * {
- const CurveEval *curve = static_cast<const CurveEval *>(owner);
- return curve ? &curve->attributes.data : nullptr;
- },
- [](const void *owner) -> int {
- const CurveEval *curve = static_cast<const CurveEval *>(owner);
- return curve->splines().size();
- }};
-
- static CustomDataAttributeProvider spline_custom_data(ATTR_DOMAIN_CURVE,
- spline_custom_data_access);
-
- static PositionAttributeProvider position;
- static BezierHandleAttributeProvider handles_start(false);
- static BezierHandleAttributeProvider handles_end(true);
-
- static BuiltinPointAttributeProvider<int> id(
- "id",
- BuiltinAttributeProvider::Creatable,
- BuiltinAttributeProvider::Deletable,
- [](const Spline &spline) {
- std::optional<GSpan> span = spline.attributes.get_for_read("id");
- return span ? span->typed<int>() : Span<int>();
- },
- [](Spline &spline) {
- std::optional<GMutableSpan> span = spline.attributes.get_for_write("id");
- return span ? span->typed<int>() : MutableSpan<int>();
- },
- {},
- true);
-
- static BuiltinPointAttributeProvider<float> radius(
- "radius",
- BuiltinAttributeProvider::NonCreatable,
- BuiltinAttributeProvider::NonDeletable,
- [](const Spline &spline) { return spline.radii(); },
- [](Spline &spline) { return spline.radii(); },
- nullptr,
- false);
-
- static BuiltinPointAttributeProvider<float> tilt(
- "tilt",
- BuiltinAttributeProvider::NonCreatable,
- BuiltinAttributeProvider::NonDeletable,
- [](const Spline &spline) { return spline.tilts(); },
- [](Spline &spline) { return spline.tilts(); },
- [](Spline &spline) { spline.mark_cache_invalid(); },
- false);
-
- static DynamicPointAttributeProvider point_custom_data;
-
- return ComponentAttributeProviders(
- {&position, &id, &radius, &tilt, &handles_start, &handles_end, &resolution, &cyclic},
- {&spline_custom_data, &point_custom_data});
-}
-
-/** \} */
-
-static AttributeAccessorFunctions get_curve_accessor_functions()
-{
- static const ComponentAttributeProviders providers = create_attribute_providers_for_curve();
- AttributeAccessorFunctions fn =
- attribute_accessor_functions::accessor_functions_for_providers<providers>();
- fn.domain_size = [](const void *owner, const eAttrDomain domain) -> int {
- if (owner == nullptr) {
- return 0;
- }
- const CurveEval &curve_eval = *static_cast<const CurveEval *>(owner);
- switch (domain) {
- case ATTR_DOMAIN_POINT:
- return curve_eval.total_control_point_num();
- case ATTR_DOMAIN_CURVE:
- return curve_eval.splines().size();
- default:
- return 0;
- }
- };
- fn.domain_supported = [](const void *UNUSED(owner), const eAttrDomain domain) {
- return ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
- };
- fn.adapt_domain = [](const void *owner,
- const blender::GVArray &varray,
- const eAttrDomain from_domain,
- const eAttrDomain to_domain) -> GVArray {
- if (owner == nullptr) {
- return {};
- }
- const CurveEval &curve_eval = *static_cast<const CurveEval *>(owner);
- return adapt_curve_attribute_domain(curve_eval, varray, from_domain, to_domain);
- };
- return fn;
-}
-
-static const AttributeAccessorFunctions &get_curve_accessor_functions_ref()
-{
- static const AttributeAccessorFunctions fn = get_curve_accessor_functions();
- return fn;
-}
-
-} // namespace blender::bke
-
-std::optional<blender::bke::AttributeAccessor> CurveComponentLegacy::attributes() const
-{
- return blender::bke::AttributeAccessor(curve_, blender::bke::get_curve_accessor_functions_ref());
-}
-
-std::optional<blender::bke::MutableAttributeAccessor> CurveComponentLegacy::attributes_for_write()
-{
- CurveEval *curve = this->get_for_write();
- return blender::bke::MutableAttributeAccessor(curve,
- blender::bke::get_curve_accessor_functions_ref());
-}
-
-blender::bke::MutableAttributeAccessor CurveEval::attributes_for_write()
-{
- return blender::bke::MutableAttributeAccessor(this,
- blender::bke::get_curve_accessor_functions_ref());
-}
diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc
deleted file mode 100644
index a674bf7800a..00000000000
--- a/source/blender/blenkernel/intern/spline_base.cc
+++ /dev/null
@@ -1,526 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "BLI_array.hh"
-#include "BLI_generic_virtual_array.hh"
-#include "BLI_span.hh"
-#include "BLI_task.hh"
-#include "BLI_timeit.hh"
-
-#include "BKE_attribute_math.hh"
-#include "BKE_spline.hh"
-
-using blender::Array;
-using blender::float3;
-using blender::GMutableSpan;
-using blender::GSpan;
-using blender::GVArray;
-using blender::IndexRange;
-using blender::MutableSpan;
-using blender::Span;
-using blender::VArray;
-using blender::attribute_math::convert_to_static_type;
-using blender::bke::AttributeIDRef;
-using blender::bke::AttributeMetaData;
-
-CurveType Spline::type() const
-{
- return type_;
-}
-
-void Spline::copy_base_settings(const Spline &src, Spline &dst)
-{
- dst.normal_mode = src.normal_mode;
- dst.is_cyclic_ = src.is_cyclic_;
-}
-
-static SplinePtr create_spline(const CurveType type)
-{
- switch (type) {
- case CURVE_TYPE_POLY:
- return std::make_unique<PolySpline>();
- case CURVE_TYPE_BEZIER:
- return std::make_unique<BezierSpline>();
- case CURVE_TYPE_NURBS:
- return std::make_unique<NURBSpline>();
- case CURVE_TYPE_CATMULL_ROM:
- BLI_assert_unreachable();
- return {};
- }
- BLI_assert_unreachable();
- return {};
-}
-
-SplinePtr Spline::copy() const
-{
- SplinePtr dst = this->copy_without_attributes();
- dst->attributes = this->attributes;
- return dst;
-}
-
-SplinePtr Spline::copy_only_settings() const
-{
- SplinePtr dst = create_spline(type_);
- this->copy_base_settings(*this, *dst);
- this->copy_settings(*dst);
- return dst;
-}
-
-SplinePtr Spline::copy_without_attributes() const
-{
- SplinePtr dst = this->copy_only_settings();
- this->copy_data(*dst);
-
- /* Though the attributes storage is empty, it still needs to know the correct size. */
- dst->attributes.reallocate(dst->size());
- return dst;
-}
-
-void Spline::translate(const blender::float3 &translation)
-{
- for (float3 &position : this->positions()) {
- position += translation;
- }
- this->mark_cache_invalid();
-}
-
-void Spline::transform(const blender::float4x4 &matrix)
-{
- for (float3 &position : this->positions()) {
- position = matrix * position;
- }
- this->mark_cache_invalid();
-}
-
-void Spline::reverse()
-{
- this->positions().reverse();
- this->radii().reverse();
- this->tilts().reverse();
-
- this->attributes.foreach_attribute(
- [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) {
- std::optional<blender::GMutableSpan> attribute = this->attributes.get_for_write(id);
- if (!attribute) {
- BLI_assert_unreachable();
- return false;
- }
- convert_to_static_type(meta_data.data_type, [&](auto dummy) {
- using T = decltype(dummy);
- attribute->typed<T>().reverse();
- });
- return true;
- },
- ATTR_DOMAIN_POINT);
-
- this->reverse_impl();
- this->mark_cache_invalid();
-}
-
-int Spline::evaluated_edges_num() const
-{
- const int eval_num = this->evaluated_points_num();
- if (eval_num < 2) {
- /* Two points are required for an edge. */
- return 0;
- }
-
- return this->is_cyclic_ ? eval_num : eval_num - 1;
-}
-
-float Spline::length() const
-{
- Span<float> lengths = this->evaluated_lengths();
- return lengths.is_empty() ? 0.0f : this->evaluated_lengths().last();
-}
-
-int Spline::segments_num() const
-{
- const int num = this->size();
-
- return is_cyclic_ ? num : num - 1;
-}
-
-bool Spline::is_cyclic() const
-{
- return is_cyclic_;
-}
-
-void Spline::set_cyclic(const bool value)
-{
- is_cyclic_ = value;
-}
-
-static void accumulate_lengths(Span<float3> positions,
- const bool is_cyclic,
- MutableSpan<float> lengths)
-{
- using namespace blender::math;
-
- float length = 0.0f;
- for (const int i : IndexRange(positions.size() - 1)) {
- length += distance(positions[i], positions[i + 1]);
- lengths[i] = length;
- }
- if (is_cyclic) {
- lengths.last() = length + distance(positions.last(), positions.first());
- }
-}
-
-Span<float> Spline::evaluated_lengths() const
-{
- if (!length_cache_dirty_) {
- return evaluated_lengths_cache_;
- }
-
- std::lock_guard lock{length_cache_mutex_};
- if (!length_cache_dirty_) {
- return evaluated_lengths_cache_;
- }
-
- const int total = evaluated_edges_num();
- evaluated_lengths_cache_.resize(total);
- if (total != 0) {
- Span<float3> positions = this->evaluated_positions();
- accumulate_lengths(positions, is_cyclic_, evaluated_lengths_cache_);
- }
-
- length_cache_dirty_ = false;
- return evaluated_lengths_cache_;
-}
-
-static float3 direction_bisect(const float3 &prev, const float3 &middle, const float3 &next)
-{
- using namespace blender::math;
-
- const float3 dir_prev = normalize(middle - prev);
- const float3 dir_next = normalize(next - middle);
-
- const float3 result = normalize(dir_prev + dir_next);
- if (UNLIKELY(is_zero(result))) {
- return float3(0.0f, 0.0f, 1.0f);
- }
- return result;
-}
-
-static void calculate_tangents(Span<float3> positions,
- const bool is_cyclic,
- MutableSpan<float3> tangents)
-{
- using namespace blender::math;
-
- if (positions.size() == 1) {
- tangents.first() = float3(0.0f, 0.0f, 1.0f);
- return;
- }
-
- for (const int i : IndexRange(1, positions.size() - 2)) {
- tangents[i] = direction_bisect(positions[i - 1], positions[i], positions[i + 1]);
- }
-
- if (is_cyclic) {
- const float3 &second_to_last = positions[positions.size() - 2];
- const float3 &last = positions.last();
- const float3 &first = positions.first();
- const float3 &second = positions[1];
- tangents.first() = direction_bisect(last, first, second);
- tangents.last() = direction_bisect(second_to_last, last, first);
- }
- else {
- tangents.first() = normalize(positions[1] - positions[0]);
- tangents.last() = normalize(positions.last() - positions[positions.size() - 2]);
- }
-}
-
-Span<float3> Spline::evaluated_tangents() const
-{
- if (!tangent_cache_dirty_) {
- return evaluated_tangents_cache_;
- }
-
- std::lock_guard lock{tangent_cache_mutex_};
- if (!tangent_cache_dirty_) {
- return evaluated_tangents_cache_;
- }
-
- const int eval_num = this->evaluated_points_num();
- evaluated_tangents_cache_.resize(eval_num);
-
- Span<float3> positions = this->evaluated_positions();
-
- calculate_tangents(positions, is_cyclic_, evaluated_tangents_cache_);
- this->correct_end_tangents();
-
- tangent_cache_dirty_ = false;
- return evaluated_tangents_cache_;
-}
-
-static float3 rotate_direction_around_axis(const float3 &direction,
- const float3 &axis,
- const float angle)
-{
- using namespace blender::math;
-
- BLI_ASSERT_UNIT_V3(direction);
- BLI_ASSERT_UNIT_V3(axis);
-
- const float3 axis_scaled = axis * dot(direction, axis);
- const float3 diff = direction - axis_scaled;
- const float3 cross = blender::math::cross(axis, diff);
-
- return axis_scaled + diff * std::cos(angle) + cross * std::sin(angle);
-}
-
-static void calculate_normals_z_up(Span<float3> tangents, MutableSpan<float3> r_normals)
-{
- using namespace blender::math;
-
- BLI_assert(r_normals.size() == tangents.size());
-
- /* Same as in `vec_to_quat`. */
- const float epsilon = 1e-4f;
- for (const int i : r_normals.index_range()) {
- const float3 &tangent = tangents[i];
- if (fabsf(tangent.x) + fabsf(tangent.y) < epsilon) {
- r_normals[i] = {1.0f, 0.0f, 0.0f};
- }
- else {
- r_normals[i] = normalize(float3(tangent.y, -tangent.x, 0.0f));
- }
- }
-}
-
-/**
- * Rotate the last normal in the same way the tangent has been rotated.
- */
-static float3 calculate_next_normal(const float3 &last_normal,
- const float3 &last_tangent,
- const float3 &current_tangent)
-{
- using namespace blender::math;
-
- if (is_zero(last_tangent) || is_zero(current_tangent)) {
- return last_normal;
- }
- const float angle = angle_normalized_v3v3(last_tangent, current_tangent);
- if (angle != 0.0) {
- const float3 axis = normalize(cross(last_tangent, current_tangent));
- return rotate_direction_around_axis(last_normal, axis, angle);
- }
- return last_normal;
-}
-
-static void calculate_normals_minimum(Span<float3> tangents,
- const bool cyclic,
- MutableSpan<float3> r_normals)
-{
- using namespace blender::math;
- BLI_assert(r_normals.size() == tangents.size());
-
- if (r_normals.is_empty()) {
- return;
- }
-
- const float epsilon = 1e-4f;
-
- /* Set initial normal. */
- const float3 &first_tangent = tangents[0];
- if (fabs(first_tangent.x) + fabs(first_tangent.y) < epsilon) {
- r_normals[0] = {1.0f, 0.0f, 0.0f};
- }
- else {
- r_normals[0] = normalize(float3(first_tangent.y, -first_tangent.x, 0.0f));
- }
-
- /* Forward normal with minimum twist along the entire spline. */
- for (const int i : IndexRange(1, r_normals.size() - 1)) {
- r_normals[i] = calculate_next_normal(r_normals[i - 1], tangents[i - 1], tangents[i]);
- }
-
- if (!cyclic) {
- return;
- }
-
- /* Compute how much the first normal deviates from the normal that has been forwarded along the
- * entire cyclic spline. */
- const float3 uncorrected_last_normal = calculate_next_normal(
- r_normals.last(), tangents.last(), tangents[0]);
- float correction_angle = angle_signed_on_axis_v3v3_v3(
- r_normals[0], uncorrected_last_normal, tangents[0]);
- if (correction_angle > M_PI) {
- correction_angle = correction_angle - 2 * M_PI;
- }
-
- /* Gradually apply correction by rotating all normals slightly. */
- const float angle_step = correction_angle / r_normals.size();
- for (const int i : r_normals.index_range()) {
- const float angle = angle_step * i;
- r_normals[i] = rotate_direction_around_axis(r_normals[i], tangents[i], angle);
- }
-}
-
-Span<float3> Spline::evaluated_normals() const
-{
- if (!normal_cache_dirty_) {
- return evaluated_normals_cache_;
- }
-
- std::lock_guard lock{normal_cache_mutex_};
- if (!normal_cache_dirty_) {
- return evaluated_normals_cache_;
- }
-
- const int eval_num = this->evaluated_points_num();
- evaluated_normals_cache_.resize(eval_num);
-
- Span<float3> tangents = this->evaluated_tangents();
- MutableSpan<float3> normals = evaluated_normals_cache_;
-
- /* Only Z up normals are supported at the moment. */
- switch (this->normal_mode) {
- case NORMAL_MODE_Z_UP: {
- calculate_normals_z_up(tangents, normals);
- break;
- }
- case NORMAL_MODE_MINIMUM_TWIST: {
- calculate_normals_minimum(tangents, is_cyclic_, normals);
- break;
- }
- }
-
- /* Rotate the generated normals with the interpolated tilt data. */
- VArray<float> tilts = this->interpolate_to_evaluated(this->tilts());
- for (const int i : normals.index_range()) {
- normals[i] = rotate_direction_around_axis(normals[i], tangents[i], tilts[i]);
- }
-
- normal_cache_dirty_ = false;
- return evaluated_normals_cache_;
-}
-
-Spline::LookupResult Spline::lookup_evaluated_factor(const float factor) const
-{
- return this->lookup_evaluated_length(this->length() * factor);
-}
-
-Spline::LookupResult Spline::lookup_evaluated_length(const float length) const
-{
- BLI_assert(length >= 0.0f && length <= this->length());
-
- Span<float> lengths = this->evaluated_lengths();
-
- const float *offset = std::lower_bound(lengths.begin(), lengths.end(), length);
- const int index = offset - lengths.begin();
- const int next_index = (index == this->evaluated_points_num() - 1) ? 0 : index + 1;
-
- const float previous_length = (index == 0) ? 0.0f : lengths[index - 1];
- const float length_in_segment = length - previous_length;
- const float segment_length = lengths[index] - previous_length;
- const float factor = segment_length == 0.0f ? 0.0f : length_in_segment / segment_length;
-
- return LookupResult{index, next_index, factor};
-}
-
-Array<float> Spline::sample_uniform_index_factors(const int samples_num) const
-{
- const Span<float> lengths = this->evaluated_lengths();
-
- BLI_assert(samples_num > 0);
- Array<float> samples(samples_num);
-
- samples[0] = 0.0f;
- if (samples_num == 1) {
- return samples;
- }
-
- const float total_length = this->length();
- const float sample_length = total_length / (samples_num - (is_cyclic_ ? 0 : 1));
-
- /* Store the length at the previous evaluated point in a variable so it can
- * start out at zero (the lengths array doesn't contain 0 for the first point). */
- float prev_length = 0.0f;
- int i_sample = 1;
- for (const int i_evaluated : IndexRange(this->evaluated_edges_num())) {
- const float length = lengths[i_evaluated];
-
- /* Add every sample that fits in this evaluated edge. */
- while ((sample_length * i_sample) < length && i_sample < samples_num) {
- const float factor = (sample_length * i_sample - prev_length) / (length - prev_length);
- samples[i_sample] = i_evaluated + factor;
- i_sample++;
- }
-
- prev_length = length;
- }
-
- /* Zero lengths or float inaccuracies can cause invalid values, or simply
- * skip some, so set the values that weren't completed in the main loop. */
- for (const int i : IndexRange(i_sample, samples_num - i_sample)) {
- samples[i] = float(samples_num);
- }
-
- if (!is_cyclic_) {
- /* In rare cases this can prevent overflow of the stored index. */
- samples.last() = lengths.size();
- }
-
- return samples;
-}
-
-Spline::LookupResult Spline::lookup_data_from_index_factor(const float index_factor) const
-{
- const int eval_num = this->evaluated_points_num();
-
- if (is_cyclic_) {
- if (index_factor < eval_num) {
- const int index = std::floor(index_factor);
- const int next_index = (index < eval_num - 1) ? index + 1 : 0;
- return LookupResult{index, next_index, index_factor - index};
- }
- return LookupResult{eval_num - 1, 0, 1.0f};
- }
-
- if (index_factor < eval_num - 1) {
- const int index = std::floor(index_factor);
- const int next_index = index + 1;
- return LookupResult{index, next_index, index_factor - index};
- }
- return LookupResult{eval_num - 2, eval_num - 1, 1.0f};
-}
-
-void Spline::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const
-{
- Span<float3> positions = use_evaluated ? this->evaluated_positions() : this->positions();
- for (const float3 &position : positions) {
- minmax_v3v3_v3(min, max, position);
- }
-}
-
-GVArray Spline::interpolate_to_evaluated(GSpan data) const
-{
- return this->interpolate_to_evaluated(GVArray::ForSpan(data));
-}
-
-void Spline::sample_with_index_factors(const GVArray &src,
- Span<float> index_factors,
- GMutableSpan dst) const
-{
- BLI_assert(src.size() == this->evaluated_points_num());
-
- blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
- using T = decltype(dummy);
- const VArray<T> src_typed = src.typed<T>();
- MutableSpan<T> dst_typed = dst.typed<T>();
- if (src.size() == 1) {
- dst_typed.fill(src_typed[0]);
- return;
- }
- blender::threading::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_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc
deleted file mode 100644
index 80515d0ef37..00000000000
--- a/source/blender/blenkernel/intern/spline_bezier.cc
+++ /dev/null
@@ -1,646 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "BLI_array.hh"
-#include "BLI_span.hh"
-#include "BLI_task.hh"
-
-#include "BKE_spline.hh"
-
-using blender::Array;
-using blender::float3;
-using blender::GVArray;
-using blender::IndexRange;
-using blender::MutableSpan;
-using blender::Span;
-using blender::VArray;
-
-void BezierSpline::copy_settings(Spline &dst) const
-{
- BezierSpline &bezier = static_cast<BezierSpline &>(dst);
- bezier.resolution_ = resolution_;
-}
-
-void BezierSpline::copy_data(Spline &dst) const
-{
- BezierSpline &bezier = static_cast<BezierSpline &>(dst);
- bezier.positions_ = positions_;
- bezier.handle_types_left_ = handle_types_left_;
- bezier.handle_positions_left_ = handle_positions_left_;
- bezier.handle_types_right_ = handle_types_right_;
- bezier.handle_positions_right_ = handle_positions_right_;
- bezier.radii_ = radii_;
- bezier.tilts_ = tilts_;
-}
-
-int BezierSpline::size() const
-{
- const int size = positions_.size();
- BLI_assert(size == handle_types_left_.size());
- BLI_assert(size == handle_positions_left_.size());
- BLI_assert(size == handle_types_right_.size());
- BLI_assert(size == handle_positions_right_.size());
- BLI_assert(size == radii_.size());
- BLI_assert(size == tilts_.size());
- return size;
-}
-
-int BezierSpline::resolution() const
-{
- return resolution_;
-}
-
-void BezierSpline::set_resolution(const int value)
-{
- BLI_assert(value > 0);
- resolution_ = value;
- this->mark_cache_invalid();
-}
-
-void BezierSpline::resize(const int size)
-{
- handle_types_left_.resize(size);
- handle_positions_left_.resize(size);
- positions_.resize(size);
- handle_types_right_.resize(size);
- handle_positions_right_.resize(size);
- radii_.resize(size);
- tilts_.resize(size);
- this->mark_cache_invalid();
- attributes.reallocate(size);
-}
-
-MutableSpan<float3> BezierSpline::positions()
-{
- return positions_;
-}
-Span<float3> BezierSpline::positions() const
-{
- return positions_;
-}
-MutableSpan<float> BezierSpline::radii()
-{
- return radii_;
-}
-Span<float> BezierSpline::radii() const
-{
- return radii_;
-}
-MutableSpan<float> BezierSpline::tilts()
-{
- return tilts_;
-}
-Span<float> BezierSpline::tilts() const
-{
- return tilts_;
-}
-Span<int8_t> BezierSpline::handle_types_left() const
-{
- return handle_types_left_;
-}
-MutableSpan<int8_t> BezierSpline::handle_types_left()
-{
- return handle_types_left_;
-}
-Span<float3> BezierSpline::handle_positions_left() const
-{
- this->ensure_auto_handles();
- return handle_positions_left_;
-}
-MutableSpan<float3> BezierSpline::handle_positions_left(const bool write_only)
-{
- if (!write_only) {
- this->ensure_auto_handles();
- }
- return handle_positions_left_;
-}
-
-Span<int8_t> BezierSpline::handle_types_right() const
-{
- return handle_types_right_;
-}
-MutableSpan<int8_t> BezierSpline::handle_types_right()
-{
- return handle_types_right_;
-}
-Span<float3> BezierSpline::handle_positions_right() const
-{
- this->ensure_auto_handles();
- return handle_positions_right_;
-}
-MutableSpan<float3> BezierSpline::handle_positions_right(const bool write_only)
-{
- if (!write_only) {
- this->ensure_auto_handles();
- }
- return handle_positions_right_;
-}
-
-void BezierSpline::reverse_impl()
-{
- this->handle_positions_left().reverse();
- this->handle_positions_right().reverse();
- std::swap(this->handle_positions_left_, this->handle_positions_right_);
-
- this->handle_types_left().reverse();
- this->handle_types_right().reverse();
- std::swap(this->handle_types_left_, this->handle_types_right_);
-}
-
-static float3 previous_position(Span<float3> positions, const bool cyclic, const int i)
-{
- if (i == 0) {
- if (cyclic) {
- return positions[positions.size() - 1];
- }
- return 2.0f * positions[i] - positions[i + 1];
- }
- return positions[i - 1];
-}
-
-static float3 next_position(Span<float3> positions, const bool cyclic, const int i)
-{
- if (i == positions.size() - 1) {
- if (cyclic) {
- return positions[0];
- }
- return 2.0f * positions[i] - positions[i - 1];
- }
- return positions[i + 1];
-}
-
-void BezierSpline::ensure_auto_handles() const
-{
- if (!auto_handles_dirty_) {
- return;
- }
-
- std::lock_guard lock{auto_handle_mutex_};
- if (!auto_handles_dirty_) {
- return;
- }
-
- if (this->size() == 1) {
- auto_handles_dirty_ = false;
- return;
- }
-
- for (const int i : IndexRange(this->size())) {
- using namespace blender;
-
- if (ELEM(BEZIER_HANDLE_AUTO, handle_types_left_[i], handle_types_right_[i])) {
- const float3 prev_diff = positions_[i] - previous_position(positions_, is_cyclic_, i);
- const float3 next_diff = next_position(positions_, is_cyclic_, i) - positions_[i];
- float prev_len = math::length(prev_diff);
- float next_len = math::length(next_diff);
- if (prev_len == 0.0f) {
- prev_len = 1.0f;
- }
- if (next_len == 0.0f) {
- next_len = 1.0f;
- }
- const float3 dir = next_diff / next_len + prev_diff / prev_len;
-
- /* This magic number is unfortunate, but comes from elsewhere in Blender. */
- const float len = math::length(dir) * 2.5614f;
- if (len != 0.0f) {
- if (handle_types_left_[i] == BEZIER_HANDLE_AUTO) {
- const float prev_len_clamped = std::min(prev_len, next_len * 5.0f);
- handle_positions_left_[i] = positions_[i] + dir * -(prev_len_clamped / len);
- }
- if (handle_types_right_[i] == BEZIER_HANDLE_AUTO) {
- const float next_len_clamped = std::min(next_len, prev_len * 5.0f);
- handle_positions_right_[i] = positions_[i] + dir * (next_len_clamped / len);
- }
- }
- }
-
- if (handle_types_left_[i] == BEZIER_HANDLE_VECTOR) {
- const float3 prev = previous_position(positions_, is_cyclic_, i);
- handle_positions_left_[i] = math::interpolate(positions_[i], prev, 1.0f / 3.0f);
- }
-
- if (handle_types_right_[i] == BEZIER_HANDLE_VECTOR) {
- const float3 next = next_position(positions_, is_cyclic_, i);
- handle_positions_right_[i] = math::interpolate(positions_[i], next, 1.0f / 3.0f);
- }
- }
-
- auto_handles_dirty_ = false;
-}
-
-void BezierSpline::translate(const blender::float3 &translation)
-{
- for (float3 &position : this->positions()) {
- position += translation;
- }
- for (float3 &handle_position : this->handle_positions_left()) {
- handle_position += translation;
- }
- for (float3 &handle_position : this->handle_positions_right()) {
- handle_position += translation;
- }
- this->mark_cache_invalid();
-}
-
-void BezierSpline::transform(const blender::float4x4 &matrix)
-{
- for (float3 &position : this->positions()) {
- position = matrix * position;
- }
- for (float3 &handle_position : this->handle_positions_left()) {
- handle_position = matrix * handle_position;
- }
- for (float3 &handle_position : this->handle_positions_right()) {
- handle_position = matrix * handle_position;
- }
- this->mark_cache_invalid();
-}
-
-static void set_handle_position(const float3 &position,
- const HandleType type,
- const HandleType type_other,
- const float3 &new_value,
- float3 &handle,
- float3 &handle_other)
-{
- using namespace blender::math;
-
- /* Don't bother when the handle positions are calculated automatically anyway. */
- if (ELEM(type, BEZIER_HANDLE_AUTO, BEZIER_HANDLE_VECTOR)) {
- return;
- }
-
- handle = new_value;
- if (type_other == BEZIER_HANDLE_ALIGN) {
- /* Keep track of the old length of the opposite handle. */
- const float length = distance(handle_other, position);
- /* Set the other handle to directly opposite from the current handle. */
- const float3 dir = normalize(handle - position);
- handle_other = position - dir * length;
- }
-}
-
-void BezierSpline::set_handle_position_right(const int index, const blender::float3 &value)
-{
- set_handle_position(positions_[index],
- static_cast<HandleType>(handle_types_right_[index]),
- static_cast<HandleType>(handle_types_left_[index]),
- value,
- handle_positions_right_[index],
- handle_positions_left_[index]);
-}
-
-void BezierSpline::set_handle_position_left(const int index, const blender::float3 &value)
-{
- set_handle_position(positions_[index],
- static_cast<HandleType>(handle_types_right_[index]),
- static_cast<HandleType>(handle_types_left_[index]),
- value,
- handle_positions_left_[index],
- handle_positions_right_[index]);
-}
-
-bool BezierSpline::point_is_sharp(const int index) const
-{
- return ELEM(handle_types_left_[index], BEZIER_HANDLE_VECTOR, BEZIER_HANDLE_FREE) ||
- ELEM(handle_types_right_[index], BEZIER_HANDLE_VECTOR, BEZIER_HANDLE_FREE);
-}
-
-bool BezierSpline::segment_is_vector(const int index) const
-{
- /* Two control points are necessary to form a segment, that should be checked by the caller. */
- BLI_assert(this->size() > 1);
-
- if (index == this->size() - 1) {
- if (is_cyclic_) {
- return handle_types_right_.last() == BEZIER_HANDLE_VECTOR &&
- handle_types_left_.first() == BEZIER_HANDLE_VECTOR;
- }
- /* There is actually no segment in this case, but it's nice to avoid
- * having a special case for the last segment in calling code. */
- return true;
- }
- return handle_types_right_[index] == BEZIER_HANDLE_VECTOR &&
- handle_types_left_[index + 1] == BEZIER_HANDLE_VECTOR;
-}
-
-void BezierSpline::mark_cache_invalid()
-{
- offset_cache_dirty_ = true;
- position_cache_dirty_ = true;
- mapping_cache_dirty_ = true;
- tangent_cache_dirty_ = true;
- normal_cache_dirty_ = true;
- length_cache_dirty_ = true;
- auto_handles_dirty_ = true;
-}
-
-int BezierSpline::evaluated_points_num() const
-{
- BLI_assert(this->size() > 0);
- return this->control_point_offsets().last();
-}
-
-void BezierSpline::correct_end_tangents() const
-{
- using namespace blender::math;
- if (is_cyclic_) {
- return;
- }
-
- MutableSpan<float3> tangents(evaluated_tangents_cache_);
-
- if (handle_positions_right_.first() != positions_.first()) {
- tangents.first() = normalize(handle_positions_right_.first() - positions_.first());
- }
- if (handle_positions_left_.last() != positions_.last()) {
- tangents.last() = normalize(positions_.last() - handle_positions_left_.last());
- }
-}
-
-BezierSpline::InsertResult BezierSpline::calculate_segment_insertion(const int index,
- const int next_index,
- const float parameter)
-{
- using namespace blender::math;
-
- BLI_assert(parameter <= 1.0f && parameter >= 0.0f);
- BLI_assert(ELEM(next_index, 0, index + 1));
- const float3 &point_prev = positions_[index];
- const float3 &handle_prev = handle_positions_right_[index];
- const float3 &handle_next = handle_positions_left_[next_index];
- const float3 &point_next = positions_[next_index];
- const float3 center_point = interpolate(handle_prev, handle_next, parameter);
-
- BezierSpline::InsertResult result;
- result.handle_prev = interpolate(point_prev, handle_prev, parameter);
- result.handle_next = interpolate(handle_next, point_next, parameter);
- result.left_handle = interpolate(result.handle_prev, center_point, parameter);
- result.right_handle = interpolate(center_point, result.handle_next, parameter);
- result.position = interpolate(result.left_handle, result.right_handle, parameter);
- return result;
-}
-
-static void bezier_forward_difference_3d(const float3 &point_0,
- const float3 &point_1,
- const float3 &point_2,
- const float3 &point_3,
- MutableSpan<float3> result)
-{
- BLI_assert(result.size() > 0);
- const float inv_len = 1.0f / static_cast<float>(result.size());
- const float inv_len_squared = inv_len * inv_len;
- const float inv_len_cubed = inv_len_squared * inv_len;
-
- const float3 rt1 = 3.0f * (point_1 - point_0) * inv_len;
- const float3 rt2 = 3.0f * (point_0 - 2.0f * point_1 + point_2) * inv_len_squared;
- const float3 rt3 = (point_3 - point_0 + 3.0f * (point_1 - point_2)) * inv_len_cubed;
-
- float3 q0 = point_0;
- float3 q1 = rt1 + rt2 + rt3;
- float3 q2 = 2.0f * rt2 + 6.0f * rt3;
- float3 q3 = 6.0f * rt3;
- for (const int i : result.index_range()) {
- result[i] = q0;
- q0 += q1;
- q1 += q2;
- q2 += q3;
- }
-}
-
-void BezierSpline::evaluate_segment(const int index,
- const int next_index,
- MutableSpan<float3> positions) const
-{
- if (this->segment_is_vector(index)) {
- BLI_assert(positions.size() == 1);
- positions.first() = positions_[index];
- }
- else {
- bezier_forward_difference_3d(positions_[index],
- handle_positions_right_[index],
- handle_positions_left_[next_index],
- positions_[next_index],
- positions);
- }
-}
-
-Span<int> BezierSpline::control_point_offsets() const
-{
- if (!offset_cache_dirty_) {
- return offset_cache_;
- }
-
- std::lock_guard lock{offset_cache_mutex_};
- if (!offset_cache_dirty_) {
- return offset_cache_;
- }
-
- const int size = this->size();
- offset_cache_.resize(size + 1);
-
- MutableSpan<int> offsets = offset_cache_;
- if (size == 1) {
- offsets.first() = 0;
- offsets.last() = 1;
- }
- else {
- int offset = 0;
- for (const int i : IndexRange(size)) {
- offsets[i] = offset;
- offset += this->segment_is_vector(i) ? 1 : resolution_;
- }
- offsets.last() = offset;
- }
-
- offset_cache_dirty_ = false;
- return offsets;
-}
-
-static void calculate_mappings_linear_resolution(Span<int> offsets,
- const int size,
- const int resolution,
- const bool is_cyclic,
- MutableSpan<float> r_mappings)
-{
- const float first_segment_len_inv = 1.0f / offsets[1];
- for (const int i : IndexRange(0, offsets[1])) {
- r_mappings[i] = i * first_segment_len_inv;
- }
-
- const int grain_size = std::max(2048 / resolution, 1);
- blender::threading::parallel_for(IndexRange(1, size - 2), grain_size, [&](IndexRange range) {
- for (const int i_control_point : range) {
- const int segment_len = offsets[i_control_point + 1] - offsets[i_control_point];
- const float segment_len_inv = 1.0f / segment_len;
- for (const int i : IndexRange(segment_len)) {
- r_mappings[offsets[i_control_point] + i] = i_control_point + i * segment_len_inv;
- }
- }
- });
-
- if (is_cyclic) {
- const int last_segment_len = offsets[size] - offsets[size - 1];
- const float last_segment_len_inv = 1.0f / last_segment_len;
- for (const int i : IndexRange(last_segment_len)) {
- r_mappings[offsets[size - 1] + i] = size - 1 + i * last_segment_len_inv;
- }
- }
- else {
- r_mappings.last() = size - 1;
- }
-}
-
-Span<float> BezierSpline::evaluated_mappings() const
-{
- if (!mapping_cache_dirty_) {
- return evaluated_mapping_cache_;
- }
-
- std::lock_guard lock{mapping_cache_mutex_};
- if (!mapping_cache_dirty_) {
- return evaluated_mapping_cache_;
- }
-
- const int num = this->size();
- const int eval_num = this->evaluated_points_num();
- evaluated_mapping_cache_.resize(eval_num);
- MutableSpan<float> mappings = evaluated_mapping_cache_;
-
- if (eval_num == 1) {
- mappings.first() = 0.0f;
- mapping_cache_dirty_ = false;
- return mappings;
- }
-
- Span<int> offsets = this->control_point_offsets();
-
- blender::threading::isolate_task([&]() {
- /* Isolate the task, since this is function is multi-threaded and holds a lock. */
- calculate_mappings_linear_resolution(offsets, num, resolution_, is_cyclic_, mappings);
- });
-
- mapping_cache_dirty_ = false;
- return mappings;
-}
-
-Span<float3> BezierSpline::evaluated_positions() const
-{
- if (!position_cache_dirty_) {
- return evaluated_position_cache_;
- }
-
- std::lock_guard lock{position_cache_mutex_};
- if (!position_cache_dirty_) {
- return evaluated_position_cache_;
- }
-
- const int num = this->size();
- const int eval_num = this->evaluated_points_num();
- evaluated_position_cache_.resize(eval_num);
-
- MutableSpan<float3> positions = evaluated_position_cache_;
-
- if (num == 1) {
- /* Use a special case for single point splines to avoid checking in #evaluate_segment. */
- BLI_assert(eval_num == 1);
- positions.first() = positions_.first();
- position_cache_dirty_ = false;
- return positions;
- }
-
- this->ensure_auto_handles();
-
- Span<int> offsets = this->control_point_offsets();
-
- const int grain_size = std::max(512 / resolution_, 1);
- blender::threading::isolate_task([&]() {
- /* Isolate the task, since this is function is multi-threaded and holds a lock. */
- blender::threading::parallel_for(IndexRange(num - 1), grain_size, [&](IndexRange range) {
- for (const int i : range) {
- this->evaluate_segment(i, i + 1, positions.slice(offsets[i], offsets[i + 1] - offsets[i]));
- }
- });
- });
- if (is_cyclic_) {
- this->evaluate_segment(
- num - 1, 0, positions.slice(offsets[num - 1], offsets[num] - offsets[num - 1]));
- }
- else {
- /* Since evaluating the bezier segment doesn't add the final point,
- * it must be added manually in the non-cyclic case. */
- positions.last() = positions_.last();
- }
-
- position_cache_dirty_ = false;
- return positions;
-}
-
-BezierSpline::InterpolationData BezierSpline::interpolation_data_from_index_factor(
- const float index_factor) const
-{
- const int num = this->size();
-
- if (is_cyclic_) {
- if (index_factor < num) {
- const int index = std::floor(index_factor);
- const int next_index = (index < num - 1) ? index + 1 : 0;
- return InterpolationData{index, next_index, index_factor - index};
- }
- return InterpolationData{num - 1, 0, 1.0f};
- }
-
- if (index_factor < num - 1) {
- const int index = std::floor(index_factor);
- const int next_index = index + 1;
- return InterpolationData{index, next_index, index_factor - index};
- }
- return InterpolationData{num - 2, num - 1, 1.0f};
-}
-
-/* Use a spline argument to avoid adding this to the header. */
-template<typename T>
-static void interpolate_to_evaluated_impl(const BezierSpline &spline,
- const blender::VArray<T> &src,
- MutableSpan<T> dst)
-{
- BLI_assert(src.size() == spline.size());
- BLI_assert(dst.size() == spline.evaluated_points_num());
- Span<float> mappings = spline.evaluated_mappings();
-
- for (const int i : dst.index_range()) {
- BezierSpline::InterpolationData interp = spline.interpolation_data_from_index_factor(
- mappings[i]);
-
- const T &value = src[interp.control_point_index];
- const T &next_value = src[interp.next_control_point_index];
-
- dst[i] = blender::attribute_math::mix2(interp.factor, value, next_value);
- }
-}
-
-GVArray BezierSpline::interpolate_to_evaluated(const GVArray &src) const
-{
- BLI_assert(src.size() == this->size());
-
- if (src.is_single()) {
- return src;
- }
-
- const int eval_num = this->evaluated_points_num();
- if (eval_num == 1) {
- return src;
- }
-
- GVArray new_varray;
- blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
- using T = decltype(dummy);
- if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) {
- Array<T> values(eval_num);
- interpolate_to_evaluated_impl<T>(*this, src.typed<T>(), values);
- new_varray = VArray<T>::ForContainer(std::move(values));
- }
- });
-
- return new_varray;
-}
diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc
deleted file mode 100644
index a7eeb82d854..00000000000
--- a/source/blender/blenkernel/intern/spline_nurbs.cc
+++ /dev/null
@@ -1,395 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "BLI_array.hh"
-#include "BLI_span.hh"
-#include "BLI_virtual_array.hh"
-
-#include "BKE_attribute_math.hh"
-#include "BKE_spline.hh"
-
-using blender::Array;
-using blender::float3;
-using blender::GVArray;
-using blender::IndexRange;
-using blender::MutableSpan;
-using blender::Span;
-using blender::VArray;
-
-void NURBSpline::copy_settings(Spline &dst) const
-{
- NURBSpline &nurbs = static_cast<NURBSpline &>(dst);
- nurbs.knots_mode = knots_mode;
- nurbs.resolution_ = resolution_;
- nurbs.order_ = order_;
-}
-
-void NURBSpline::copy_data(Spline &dst) const
-{
- NURBSpline &nurbs = static_cast<NURBSpline &>(dst);
- nurbs.positions_ = positions_;
- nurbs.weights_ = weights_;
- nurbs.knots_ = knots_;
- nurbs.knots_dirty_ = knots_dirty_;
- nurbs.radii_ = radii_;
- nurbs.tilts_ = tilts_;
-}
-
-int NURBSpline::size() const
-{
- const int size = positions_.size();
- BLI_assert(size == radii_.size());
- BLI_assert(size == tilts_.size());
- BLI_assert(size == weights_.size());
- return size;
-}
-
-int NURBSpline::resolution() const
-{
- return resolution_;
-}
-
-void NURBSpline::set_resolution(const int value)
-{
- BLI_assert(value > 0);
- resolution_ = value;
- this->mark_cache_invalid();
-}
-
-uint8_t NURBSpline::order() const
-{
- return order_;
-}
-
-void NURBSpline::set_order(const uint8_t value)
-{
- BLI_assert(value >= 2 && value <= 6);
- order_ = value;
- this->mark_cache_invalid();
-}
-
-void NURBSpline::resize(const int size)
-{
- positions_.resize(size);
- radii_.resize(size);
- tilts_.resize(size);
- weights_.resize(size);
- this->mark_cache_invalid();
- attributes.reallocate(size);
-}
-
-MutableSpan<float3> NURBSpline::positions()
-{
- return positions_;
-}
-Span<float3> NURBSpline::positions() const
-{
- return positions_;
-}
-MutableSpan<float> NURBSpline::radii()
-{
- return radii_;
-}
-Span<float> NURBSpline::radii() const
-{
- return radii_;
-}
-MutableSpan<float> NURBSpline::tilts()
-{
- return tilts_;
-}
-Span<float> NURBSpline::tilts() const
-{
- return tilts_;
-}
-MutableSpan<float> NURBSpline::weights()
-{
- return weights_;
-}
-Span<float> NURBSpline::weights() const
-{
- return weights_;
-}
-
-void NURBSpline::reverse_impl()
-{
- this->weights().reverse();
-}
-
-void NURBSpline::mark_cache_invalid()
-{
- basis_cache_dirty_ = true;
- position_cache_dirty_ = true;
- tangent_cache_dirty_ = true;
- normal_cache_dirty_ = true;
- length_cache_dirty_ = true;
-}
-
-int NURBSpline::evaluated_points_num() const
-{
- if (!this->check_valid_num_and_order()) {
- return 0;
- }
- return resolution_ * this->segments_num();
-}
-
-void NURBSpline::correct_end_tangents() const
-{
-}
-
-bool NURBSpline::check_valid_num_and_order() const
-{
- if (this->size() < order_) {
- return false;
- }
-
- if (ELEM(this->knots_mode, NURBS_KNOT_MODE_BEZIER, NURBS_KNOT_MODE_ENDPOINT_BEZIER)) {
- if (this->knots_mode == NURBS_KNOT_MODE_BEZIER && this->size() <= order_) {
- return false;
- }
- return (!is_cyclic_ || this->size() % (order_ - 1) == 0);
- }
-
- return true;
-}
-
-int NURBSpline::knots_num() const
-{
- const int num = this->size() + order_;
- return is_cyclic_ ? num + order_ - 1 : num;
-}
-
-void NURBSpline::calculate_knots() const
-{
- const KnotsMode mode = this->knots_mode;
- const int order = order_;
- const bool is_bezier = ELEM(mode, NURBS_KNOT_MODE_BEZIER, NURBS_KNOT_MODE_ENDPOINT_BEZIER);
- const bool is_end_point = ELEM(mode, NURBS_KNOT_MODE_ENDPOINT, NURBS_KNOT_MODE_ENDPOINT_BEZIER);
- /* Inner knots are always repeated once except on Bezier case. */
- const int repeat_inner = is_bezier ? order - 1 : 1;
- /* How many times to repeat 0.0 at the beginning of knot. */
- const int head = is_end_point ? (order - (is_cyclic_ ? 1 : 0)) :
- (is_bezier ? min_ii(2, repeat_inner) : 1);
- /* Number of knots replicating widths of the starting knots.
- * Covers both Cyclic and EndPoint cases. */
- const int tail = is_cyclic_ ? 2 * order - 1 : (is_end_point ? order : 0);
-
- knots_.resize(this->knots_num());
- MutableSpan<float> knots = knots_;
-
- int r = head;
- float current = 0.0f;
-
- const int offset = is_end_point && is_cyclic_ ? 1 : 0;
- if (offset) {
- knots[0] = current;
- current += 1.0f;
- }
-
- for (const int i : IndexRange(offset, knots.size() - offset - tail)) {
- knots[i] = current;
- r--;
- if (r == 0) {
- current += 1.0;
- r = repeat_inner;
- }
- }
-
- const int tail_index = knots.size() - tail;
- for (const int i : IndexRange(tail)) {
- knots[tail_index + i] = current + (knots[i] - knots[0]);
- }
-}
-
-Span<float> NURBSpline::knots() const
-{
- if (!knots_dirty_) {
- BLI_assert(knots_.size() == this->knots_num());
- return knots_;
- }
-
- std::lock_guard lock{knots_mutex_};
- if (!knots_dirty_) {
- BLI_assert(knots_.size() == this->knots_num());
- return knots_;
- }
-
- this->calculate_knots();
-
- knots_dirty_ = false;
-
- return knots_;
-}
-
-static void calculate_basis_for_point(const float parameter,
- const int num,
- const int degree,
- const Span<float> knots,
- MutableSpan<float> r_weights,
- int &r_start_index)
-{
- const int order = degree + 1;
-
- int start = 0;
- int end = 0;
- for (const int i : IndexRange(num + degree)) {
- const bool knots_equal = knots[i] == knots[i + 1];
- if (knots_equal || parameter < knots[i] || parameter > knots[i + 1]) {
- continue;
- }
-
- start = std::max(i - degree, 0);
- end = i;
- break;
- }
-
- Array<float, 12> buffer(order * 2, 0.0f);
-
- buffer[end - start] = 1.0f;
-
- for (const int i_order : IndexRange(2, degree)) {
- if (end + i_order >= knots.size()) {
- end = num + degree - i_order;
- }
- for (const int i : IndexRange(end - start + 1)) {
- const int knot_index = start + i;
-
- float new_basis = 0.0f;
- if (buffer[i] != 0.0f) {
- new_basis += ((parameter - knots[knot_index]) * buffer[i]) /
- (knots[knot_index + i_order - 1] - knots[knot_index]);
- }
-
- if (buffer[i + 1] != 0.0f) {
- new_basis += ((knots[knot_index + i_order] - parameter) * buffer[i + 1]) /
- (knots[knot_index + i_order] - knots[knot_index + 1]);
- }
-
- buffer[i] = new_basis;
- }
- }
-
- buffer.as_mutable_span().drop_front(end - start + 1).fill(0.0f);
- r_weights.copy_from(buffer.as_span().take_front(order));
- r_start_index = start;
-}
-
-const NURBSpline::BasisCache &NURBSpline::calculate_basis_cache() const
-{
- if (!basis_cache_dirty_) {
- return basis_cache_;
- }
-
- std::lock_guard lock{basis_cache_mutex_};
- if (!basis_cache_dirty_) {
- return basis_cache_;
- }
-
- const int num = this->size();
- const int eval_num = this->evaluated_points_num();
-
- const int order = this->order();
- const int degree = order - 1;
-
- basis_cache_.weights.resize(eval_num * order);
- basis_cache_.start_indices.resize(eval_num);
-
- if (eval_num == 0) {
- return basis_cache_;
- }
-
- MutableSpan<float> basis_weights(basis_cache_.weights);
- MutableSpan<int> basis_start_indices(basis_cache_.start_indices);
-
- const Span<float> control_weights = this->weights();
- const Span<float> knots = this->knots();
-
- const int last_control_point_index = is_cyclic_ ? num + degree : num;
-
- const float start = knots[degree];
- const float end = knots[last_control_point_index];
- const float step = (end - start) / this->evaluated_edges_num();
- for (const int i : IndexRange(eval_num)) {
- /* Clamp parameter due to floating point inaccuracy. */
- const float parameter = std::clamp(start + step * i, knots[0], knots[num + degree]);
-
- MutableSpan<float> point_weights = basis_weights.slice(i * order, order);
-
- calculate_basis_for_point(
- parameter, last_control_point_index, degree, knots, point_weights, basis_start_indices[i]);
-
- for (const int j : point_weights.index_range()) {
- const int point_index = (basis_start_indices[i] + j) % num;
- point_weights[j] *= control_weights[point_index];
- }
- }
-
- basis_cache_dirty_ = false;
- return basis_cache_;
-}
-
-template<typename T>
-void interpolate_to_evaluated_impl(const NURBSpline::BasisCache &basis_cache,
- const int order,
- const blender::VArray<T> &src,
- MutableSpan<T> dst)
-{
- const int num = src.size();
- blender::attribute_math::DefaultMixer<T> mixer(dst);
-
- for (const int i : dst.index_range()) {
- Span<float> point_weights = basis_cache.weights.as_span().slice(i * order, order);
- const int start_index = basis_cache.start_indices[i];
-
- for (const int j : point_weights.index_range()) {
- const int point_index = (start_index + j) % num;
- mixer.mix_in(i, src[point_index], point_weights[j]);
- }
- }
-
- mixer.finalize();
-}
-
-GVArray NURBSpline::interpolate_to_evaluated(const GVArray &src) const
-{
- BLI_assert(src.size() == this->size());
-
- if (src.is_single()) {
- return src;
- }
-
- const BasisCache &basis_cache = this->calculate_basis_cache();
-
- GVArray new_varray;
- blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
- using T = decltype(dummy);
- if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) {
- Array<T> values(this->evaluated_points_num());
- interpolate_to_evaluated_impl<T>(basis_cache, this->order(), src.typed<T>(), values);
- new_varray = VArray<T>::ForContainer(std::move(values));
- }
- });
-
- return new_varray;
-}
-
-Span<float3> NURBSpline::evaluated_positions() const
-{
- if (!position_cache_dirty_) {
- return evaluated_position_cache_;
- }
-
- std::lock_guard lock{position_cache_mutex_};
- if (!position_cache_dirty_) {
- return evaluated_position_cache_;
- }
-
- const int eval_num = this->evaluated_points_num();
- evaluated_position_cache_.resize(eval_num);
-
- /* TODO: Avoid copying the evaluated data from the temporary array. */
- VArray<float3> evaluated = Spline::interpolate_to_evaluated(positions_.as_span());
- evaluated.materialize(evaluated_position_cache_);
-
- position_cache_dirty_ = false;
- return evaluated_position_cache_;
-}
diff --git a/source/blender/blenkernel/intern/spline_poly.cc b/source/blender/blenkernel/intern/spline_poly.cc
deleted file mode 100644
index c3cc268c81c..00000000000
--- a/source/blender/blenkernel/intern/spline_poly.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "BLI_span.hh"
-#include "BLI_virtual_array.hh"
-
-#include "BKE_spline.hh"
-
-using blender::float3;
-using blender::GVArray;
-using blender::MutableSpan;
-using blender::Span;
-
-void PolySpline::copy_settings(Spline &UNUSED(dst)) const
-{
- /* Poly splines have no settings not covered by the base class. */
-}
-
-void PolySpline::copy_data(Spline &dst) const
-{
- PolySpline &poly = static_cast<PolySpline &>(dst);
- poly.positions_ = positions_;
- poly.radii_ = radii_;
- poly.tilts_ = tilts_;
-}
-
-int PolySpline::size() const
-{
- const int size = positions_.size();
- BLI_assert(size == radii_.size());
- BLI_assert(size == tilts_.size());
- return size;
-}
-
-void PolySpline::resize(const int size)
-{
- positions_.resize(size);
- radii_.resize(size);
- tilts_.resize(size);
- this->mark_cache_invalid();
- attributes.reallocate(size);
-}
-
-MutableSpan<float3> PolySpline::positions()
-{
- return positions_;
-}
-Span<float3> PolySpline::positions() const
-{
- return positions_;
-}
-MutableSpan<float> PolySpline::radii()
-{
- return radii_;
-}
-Span<float> PolySpline::radii() const
-{
- return radii_;
-}
-MutableSpan<float> PolySpline::tilts()
-{
- return tilts_;
-}
-Span<float> PolySpline::tilts() const
-{
- return tilts_;
-}
-
-void PolySpline::reverse_impl()
-{
-}
-
-void PolySpline::mark_cache_invalid()
-{
- tangent_cache_dirty_ = true;
- normal_cache_dirty_ = true;
- length_cache_dirty_ = true;
-}
-
-int PolySpline::evaluated_points_num() const
-{
- return this->size();
-}
-
-void PolySpline::correct_end_tangents() const
-{
-}
-
-Span<float3> PolySpline::evaluated_positions() const
-{
- return this->positions();
-}
-
-GVArray PolySpline::interpolate_to_evaluated(const GVArray &src) const
-{
- BLI_assert(src.size() == this->size());
- return src;
-}