diff options
author | Hans Goudey <h.goudey@me.com> | 2022-09-18 23:10:04 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2022-09-18 23:10:04 +0300 |
commit | ecf3435362cf2c1823b94a154c3f32a0c3412f53 (patch) | |
tree | 010bb7748534587ffcffca3518104648f6279a08 | |
parent | 7536abbe16bd8672ca38e2b866f9a9585d054713 (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.
-rw-r--r-- | source/blender/blenkernel/BKE_geometry_set.hh | 40 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_spline.hh | 688 | ||||
-rw-r--r-- | source/blender/blenkernel/CMakeLists.txt | 7 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/curve_eval.cc | 587 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/displist.cc | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/geometry_component_curve.cc | 1464 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/spline_base.cc | 526 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/spline_bezier.cc | 646 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/spline_nurbs.cc | 395 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/spline_poly.cc | 97 | ||||
-rw-r--r-- | source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc | 1 | ||||
-rw-r--r-- | source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc | 1 |
12 files changed, 1 insertions, 4453 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 ¤t_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; -} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc index c32600eccd1..799a65ec3d1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -7,7 +7,6 @@ #include "DNA_pointcloud_types.h" #include "BKE_pointcloud.h" -#include "BKE_spline.hh" #include "GEO_resample_curves.hh" diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index 0d3ae47e712..1576b573058 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "BKE_curves.hh" -#include "BKE_spline.hh" #include "BLI_task.hh" #include "UI_interface.h" |