/* SPDX-FileCopyrightText: 2023 Blender Authors * * SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once /** \file * \ingroup bke * \brief Low-level operations for curves. */ #include "BLI_bounds_types.hh" #include "BLI_generic_virtual_array.hh" #include "BLI_implicit_sharing_ptr.hh" #include "BLI_index_mask_fwd.hh" #include "BLI_math_matrix_types.hh" #include "BLI_math_vector_types.hh" #include "BLI_offset_indices.hh" #include "BLI_shared_cache.hh" #include "BLI_span.hh" #include "BLI_vector.hh" #include "BLI_virtual_array_fwd.hh" #include "BKE_attribute_math.hh" #include "BKE_curves.h" struct BlendDataReader; struct BlendWriter; struct MDeformVert; namespace blender::bke { class AnonymousAttributePropagationInfo; class AttributeAccessor; class MutableAttributeAccessor; enum class AttrDomain : int8_t; } // namespace blender::bke namespace blender::bke::bake { struct BakeMaterialsList; } namespace blender::bke { namespace curves::nurbs { 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 curve's order. */ Vector weights; /** * For each evaluated point, an offset into the curve's control points for the start of #weights. * In other words, the index of the first control point that influences this evaluated point. */ Vector start_indices; /** * The result of #check_valid_num_and_order, to avoid retrieving its inputs later on. * If this is true, the data above will be invalid, and original data should be copied * to the evaluated result. */ bool invalid = false; }; } // namespace curves::nurbs /** * Contains derived data, caches, and other information not saved in files. */ class CurvesGeometryRuntime { public: /** Implicit sharing user count for #CurvesGeometry::curve_offsets. */ const ImplicitSharingInfo *curve_offsets_sharing_info = nullptr; /** * The cached number of curves with each type. Unlike other caches here, this is not computed * lazily, since it is needed so often and types are not adjusted much anyway. */ std::array type_counts; /** * Cache of offsets into the evaluated array for each curve, accounting for all previous * evaluated points, Bezier curve vector segments, different resolutions per curve, etc. */ struct EvaluatedOffsets { Vector evaluated_offsets; Vector all_bezier_offsets; }; mutable SharedCache evaluated_offsets_cache; mutable SharedCache> nurbs_basis_cache; /** * Cache of evaluated positions for all curves. The positions span will * be used directly rather than the cache when all curves are poly type. */ mutable SharedCache> evaluated_position_cache; /** * A cache of bounds shared between data-blocks with unchanged positions. * When data changes affect the bounds, the cache is "un-shared" with other geometries. * See #SharedCache comments. */ mutable SharedCache> bounds_cache; /** * Cache of lengths along each evaluated curve for each evaluated point. If a curve is * cyclic, it needs one more length value to correspond to the last segment, so in order to * make slicing this array for a curve fast, an extra float is stored for every curve. */ mutable SharedCache> evaluated_length_cache; /** Direction of the curve at each evaluated point. */ mutable SharedCache> evaluated_tangent_cache; /** Normal direction vectors for each evaluated point. */ mutable SharedCache> evaluated_normal_cache; /** Stores weak references to material data blocks. */ std::unique_ptr bake_materials; }; /** * A C++ class that wraps the DNA struct for better encapsulation and ease of use. It inherits * directly from the struct rather than storing a pointer to avoid more complicated ownership * handling. */ class CurvesGeometry : public ::CurvesGeometry { public: CurvesGeometry(); /** * Create curves with the given size. Only the position attribute is created, along with the * offsets. */ CurvesGeometry(int point_num, int curve_num); CurvesGeometry(const CurvesGeometry &other); CurvesGeometry(CurvesGeometry &&other); CurvesGeometry &operator=(const CurvesGeometry &other); CurvesGeometry &operator=(CurvesGeometry &&other); ~CurvesGeometry(); /* -------------------------------------------------------------------- * Accessors. */ /** * The total number of control points in all curves. */ int points_num() const; /** * The number of curves in the data-block. */ int curves_num() const; IndexRange points_range() const; IndexRange curves_range() const; /** * The index of the first point in every curve. The size of this span is one larger than the * number of curves, but the spans will be empty if there are no curves/points. * * Consider using #points_by_curve rather than these offsets directly. */ Span offsets() const; MutableSpan offsets_for_write(); /** * The offsets of every curve into arrays on the points domain. */ OffsetIndices points_by_curve() const; /** The type (#CurveType) of each curve, or potentially a single if all are the same type. */ VArray curve_types() const; /** * Mutable access to curve types. Call #tag_topology_changed and #update_curve_types after * changing any type. Consider using the other methods to change types below. */ MutableSpan curve_types_for_write(); /** Set all curve types to the value and call #update_curve_types. */ void fill_curve_types(CurveType type); /** Set the types for the curves in the selection and call #update_curve_types. */ void fill_curve_types(const IndexMask &selection, CurveType type); /** Update the cached count of curves of each type, necessary after #curve_types_for_write. */ void update_curve_types(); bool has_curve_with_type(CurveType type) const; bool has_curve_with_type(Span types) const; /** Return true if all of the curves have the provided type. */ bool is_single_type(CurveType type) const; /** Return the number of curves with each type. */ const std::array &curve_type_counts() const; /** * All of the curve indices for curves with a specific type. */ IndexMask indices_for_curve_type(CurveType type, IndexMaskMemory &memory) const; IndexMask indices_for_curve_type(CurveType type, const IndexMask &selection, IndexMaskMemory &memory) const; Array point_to_curve_map() const; Span positions() const; MutableSpan positions_for_write(); /** Whether the curve loops around to connect to itself, on the curve domain. */ VArray cyclic() const; /** Mutable access to curve cyclic values. Call #tag_topology_changed after changes. */ MutableSpan cyclic_for_write(); /** * How many evaluated points to create for each segment when evaluating Bezier, * Catmull Rom, and NURBS curves. On the curve domain. Values must be one or greater. */ VArray resolution() const; /** Mutable access to curve resolution. Call #tag_topology_changed after changes. */ MutableSpan resolution_for_write(); /** * The angle used to rotate evaluated normals around the tangents after their calculation. * Call #tag_normals_changed after changes. */ VArray tilt() const; MutableSpan tilt_for_write(); /** * Which method to use for calculating the normals of evaluated points (#NormalMode). * Call #tag_normals_changed after changes. */ VArray normal_mode() const; MutableSpan normal_mode_for_write(); /** * Handle types for Bezier control points. Call #tag_topology_changed after changes. */ VArray handle_types_left() const; MutableSpan handle_types_left_for_write(); VArray handle_types_right() const; MutableSpan handle_types_right_for_write(); /** * The positions of Bezier curve handles. Though these are really control points for the Bezier * segments, they are stored in separate arrays to better reflect user expectations. Note that * values may be generated automatically based on the handle types. Call #tag_positions_changed * after changes. */ Span handle_positions_left() const; MutableSpan handle_positions_left_for_write(); Span handle_positions_right() const; MutableSpan handle_positions_right_for_write(); /** * The order (degree plus one) of each NURBS curve, on the curve domain. * Call #tag_topology_changed after changes. */ VArray nurbs_orders() const; MutableSpan nurbs_orders_for_write(); /** * The automatic generation mode for each NURBS curve's knots vector, on the curve domain. * Call #tag_topology_changed after changes. */ VArray nurbs_knots_modes() const; MutableSpan nurbs_knots_modes_for_write(); /** * The weight for each control point for NURBS curves. Call #tag_positions_changed after changes. */ Span nurbs_weights() const; MutableSpan nurbs_weights_for_write(); /** * UV coordinate for each curve that encodes where the curve is attached to the surface mesh. */ Span surface_uv_coords() const; MutableSpan surface_uv_coords_for_write(); /** * Vertex group data, encoded as an array of indices and weights for every vertex. * \warning: May be empty. */ Span deform_verts() const; MutableSpan deform_verts_for_write(); /** * The largest and smallest position values of evaluated points. */ std::optional> bounds_min_max() const; private: /* -------------------------------------------------------------------- * Evaluation. */ public: /** * The total number of points in the evaluated poly curve. * This can depend on the resolution attribute if it exists. */ int evaluated_points_num() const; /** * The offsets of every curve's evaluated points. */ OffsetIndices evaluated_points_by_curve() const; /** * Retrieve offsets into a Bezier curve's evaluated points for each control point. Stored in the * same format as #OffsetIndices. Call #evaluated_points_by_curve() first to ensure that the * evaluated offsets cache is current. */ Span bezier_evaluated_offsets_for_curve(int curve_index) const; Span evaluated_positions() const; Span evaluated_tangents() const; Span evaluated_normals() const; /** * Return a cache of accumulated lengths along the curve. Each item is the length of the * subsequent segment (the first value is the length of the first segment rather than 0). * This calculation is rather trivial, and only depends on the evaluated positions, but * the results are used often, and it is necessarily single threaded per curve, so it is cached. * * \param cyclic: This argument is redundant with the data stored for the curve, * but is passed for performance reasons to avoid looking up the attribute. */ Span evaluated_lengths_for_curve(int curve_index, bool cyclic) const; float evaluated_length_total_for_curve(int curve_index, bool cyclic) const; /** Calculates the data described by #evaluated_lengths_for_curve if necessary. */ void ensure_evaluated_lengths() const; void ensure_can_interpolate_to_evaluated() const; /** * Evaluate a generic data to the standard evaluated points of a specific curve, * defined by the resolution attribute or other factors, depending on the curve type. * * \warning This function expects offsets to the evaluated points for each curve to be * calculated. That can be ensured with #ensure_can_interpolate_to_evaluated. */ void interpolate_to_evaluated(int curve_index, GSpan src, GMutableSpan dst) const; /** * Evaluate generic data for curve control points to the standard evaluated points of the curves. */ void interpolate_to_evaluated(GSpan src, GMutableSpan dst) const; private: /** * Make sure the basis weights for NURBS curve's evaluated points are calculated. */ void ensure_nurbs_basis_cache() const; /** Return the slice of #evaluated_length_cache that corresponds to this curve index. */ IndexRange lengths_range_for_curve(int curve_index, bool cyclic) const; /* -------------------------------------------------------------------- * Operations. */ public: /** * Change the number of curves and/or points. * * \warning To avoid redundant writes, newly created attribute values are not initialized. * They must be initialized by the caller afterwards. */ void resize(int points_num, int curves_num); /** Call after deforming the position attribute. */ void tag_positions_changed(); /** * Call after any operation that changes the topology * (number of points, evaluated points, or the total count). */ void tag_topology_changed(); /** Call after changing the "tilt" or "up" attributes. */ void tag_normals_changed(); /** * Call when making manual changes to the "radius" attribute. The attribute API will also call * this in #finish() calls. */ void tag_radii_changed(); void translate(const float3 &translation); void transform(const float4x4 &matrix); void calculate_bezier_auto_handles(); void remove_points(const IndexMask &points_to_delete, const AnonymousAttributePropagationInfo &propagation_info); void remove_curves(const IndexMask &curves_to_delete, const AnonymousAttributePropagationInfo &propagation_info); /** * Change the direction of selected curves (switch the start and end) without changing their * shape. */ void reverse_curves(const IndexMask &curves_to_reverse); /** * Remove any attributes that are unused based on the types in the curves. */ void remove_attributes_based_on_types(); AttributeAccessor attributes() const; MutableAttributeAccessor attributes_for_write(); /* -------------------------------------------------------------------- * Attributes. */ GVArray adapt_domain(const GVArray &varray, AttrDomain from, AttrDomain to) const; template VArray adapt_domain(const VArray &varray, AttrDomain from, AttrDomain to) const { return this->adapt_domain(GVArray(varray), from, to).typed(); } /* -------------------------------------------------------------------- * File Read/Write. */ void blend_read(BlendDataReader &reader); /** * Helper struct for `CurvesGeometry::blend_write_*` functions. */ struct BlendWriteData { /* The point custom data layers to be written. */ Vector point_layers; /* The curve custom data layers to be written. */ Vector curve_layers; }; /** * This function needs to be called before `blend_write` and before the `CurvesGeometry` struct * is written because it can mutate the `CustomData` struct. */ BlendWriteData blend_write_prepare(); void blend_write(BlendWriter &writer, ID &id, const BlendWriteData &write_data); }; static_assert(sizeof(blender::bke::CurvesGeometry) == sizeof(::CurvesGeometry)); /** * Used to propagate deformation data through modifier evaluation so that sculpt tools can work on * evaluated data. */ class CurvesEditHints { public: /** * Original data that the edit hints below are meant to be used for. */ const Curves &curves_id_orig; /** * Evaluated positions for the points in #curves_orig. If this is empty, the positions from the * evaluated #Curves should be used if possible. */ ImplicitSharingPtrAndData positions_data; /** * Matrices which transform point movement vectors from original data to corresponding movements * of evaluated data. */ std::optional> deform_mats; CurvesEditHints(const Curves &curves_id_orig) : curves_id_orig(curves_id_orig) {} std::optional> positions() const; std::optional> positions_for_write(); /** * The edit hints have to correspond to the original curves, i.e. the number of deformed points * is the same as the number of original points. */ bool is_valid() const; }; void curves_normals_point_domain_calc(const CurvesGeometry &curves, MutableSpan normals); namespace curves { /* -------------------------------------------------------------------- */ /** \name Inline Curve Methods * \{ */ /** * The number of segments between control points, accounting for the last segment of cyclic * curves. The logic is simple, but this function should be used to make intentions clearer. */ inline int segments_num(const int points_num, const bool cyclic) { BLI_assert(points_num > 0); return (cyclic && points_num > 1) ? points_num : points_num - 1; } inline float2 encode_surface_bary_coord(const float3 &v) { BLI_assert(std::abs(v.x + v.y + v.z - 1.0f) < 0.00001f); return {v.x, v.y}; } inline float3 decode_surface_bary_coord(const float2 &v) { return {v.x, v.y, 1.0f - v.x - v.y}; } /** * Return a range used to retrieve values from an array of values stored per point, but with an * extra element at the end of each curve. This is useful for offsets within curves, where it is * convenient to store the first 0 and have the last offset be the total result curve size, using * the same rules as #OffsetIndices. */ inline IndexRange per_curve_point_offsets_range(const IndexRange points, const int curve_index) { return {curve_index + points.start(), points.size() + 1}; } /** \} */ /* -------------------------------------------------------------------- */ /** \name Curve Poly Methods * \{ */ namespace poly { /** * Calculate the direction at every point, defined as the normalized average of the two neighboring * segments (and if non-cyclic, the direction of the first and last segments). This is different * than evaluating the derivative of the basis functions for curve types like NURBS, Bezier, or * Catmull Rom, though the results may be similar. */ void calculate_tangents(Span positions, bool is_cyclic, MutableSpan tangents); /** * Calculate directions perpendicular to the tangent at every point by rotating an arbitrary * starting vector by the same rotation of each tangent. If the curve is cyclic, propagate a * correction through the entire to make sure the first and last normal align. */ void calculate_normals_minimum(Span tangents, bool cyclic, MutableSpan normals); /** * Calculate a vector perpendicular to every tangent on the X-Y plane (unless the tangent is * vertical, in that case use the X direction). */ void calculate_normals_z_up(Span tangents, MutableSpan normals); } // namespace poly /** \} */ /* -------------------------------------------------------------------- */ /** \name Curve Bezier Methods * \{ */ namespace bezier { /** * Return true if the handles that make up a segment both have a vector type. Vector segments for * Bezier curves have special behavior because they aren't divided into many evaluated points. */ bool segment_is_vector(const HandleType left, const HandleType right); bool segment_is_vector(const int8_t left, const int8_t right); bool segment_is_vector(Span handle_types_left, Span handle_types_right, int segment_index); /** * True if the Bezier curve contains polygonal segments of HandleType::BEZIER_HANDLE_VECTOR. * * \param num_curve_points: Number of points in the curve. * \param evaluated_size: Number of evaluated points in the curve. * \param cyclic: If curve is cyclic. * \param resolution: Curve resolution. */ bool has_vector_handles(int num_curve_points, int64_t evaluated_size, bool cyclic, int resolution); /** * Return true if the curve's last cyclic segment has a vector type. * This only makes a difference in the shape of cyclic curves. */ bool last_cyclic_segment_is_vector(Span handle_types_left, Span handle_types_right); /** * Return true if the handle types at the index are free (#BEZIER_HANDLE_FREE) or vector * (#BEZIER_HANDLE_VECTOR). In these cases, directional continuities from the previous and next * evaluated segments is assumed not to be desired. */ bool point_is_sharp(Span handle_types_left, Span handle_types_right, int index); /** * Calculate offsets into the curve's evaluated points for each control point. While most control * point edges generate the number of edges specified by the resolution, vector segments only * generate one edge. * * The expectations for the result \a evaluated_offsets are the same as for #OffsetIndices, so the * size must be one greater than the number of points. The value at each index is the evaluated * point at the start of that segment. */ void calculate_evaluated_offsets(Span handle_types_left, Span handle_types_right, bool cyclic, int resolution, MutableSpan evaluated_offsets); /** Knot insertion result, see #insert. */ struct Insertion { float3 handle_prev; float3 left_handle; float3 position; float3 right_handle; float3 handle_next; }; /** * Compute the insertion of a control point and handles in a Bezier segment without changing its * shape. * \param parameter: Factor in from 0 to 1 defining the insertion point within the segment. * \return Inserted point parameters including position, and both new and updated handles for * neighboring control points. * *
 *           handle_prev         handle_next
 *                x-----------------x
 *               /                   \
 *              /      x---O---x      \
 *             /        result         \
 *            /                         \
 *           O                           O
 *       point_prev                   point_next
 * 
*/ Insertion insert(const float3 &point_prev, const float3 &handle_prev, const float3 &handle_next, const float3 &point_next, float parameter); /** * Calculate the automatically defined positions for a vector handle (#BEZIER_HANDLE_VECTOR). While * this can be calculated automatically with #calculate_auto_handles, when more context is * available, it can be preferable for performance reasons to calculate it for a single segment * when necessary. */ float3 calculate_vector_handle(const float3 &point, const float3 &next_point); /** * Recalculate all auto (#BEZIER_HANDLE_AUTO) and vector (#BEZIER_HANDLE_VECTOR) handles with * positions automatically derived from the neighboring control points, and update aligned * (#BEZIER_HANDLE_ALIGN) handles to line up with neighboring non-aligned handles. The choices * made here are relatively arbitrary, but having standardized behavior is essential. */ void calculate_auto_handles(bool cyclic, Span types_left, Span types_right, Span positions, MutableSpan positions_left, MutableSpan positions_right); /** * Change the handles of a single control point, aligning any aligned (#BEZIER_HANDLE_ALIGN) * handles on the other side of the control point. * * \note This ignores the inputs if the handle types are automatically calculated, * so the types should be updated before-hand to be editable. */ void set_handle_position(const float3 &position, HandleType type, HandleType type_other, const float3 &new_handle, float3 &handle, float3 &handle_other); /** * Evaluate a cubic Bezier segment, using the "forward differencing" method. * A generic Bezier curve is made up by four points, but in many cases the first and last * points are referred to as the control points, and the middle points are the corresponding * handles. */ template void evaluate_segment( const T &point_0, const T &point_1, const T &point_2, const T &point_3, MutableSpan result); /** * Calculate all evaluated points for the Bezier curve. * * \param evaluated_offsets: The index in the evaluated points array for each control point, * including the points from the corresponding segment. Used to vary the number of evaluated * points per segment, i.e. to make vector segment only have one edge. This is expected to be * calculated by #calculate_evaluated_offsets, and is the reason why this function doesn't need * arguments like "cyclic" and "resolution". */ void calculate_evaluated_positions(Span positions, Span handles_left, Span handles_right, OffsetIndices evaluated_offsets, MutableSpan evaluated_positions); /** * Evaluate generic data to the evaluated points, with counts for each segment described by * #evaluated_offsets. Unlike other curve types, for Bezier curves generic data and positions * are treated separately, since attribute values aren't stored for the handle control points. */ void interpolate_to_evaluated(GSpan src, OffsetIndices evaluated_offsets, GMutableSpan dst); } // namespace bezier /** \} */ /* -------------------------------------------------------------------- */ /** \name Curve Catmull-Rom Methods * \{ */ namespace catmull_rom { /** * Calculate the number of evaluated points that #interpolate_to_evaluated is expected to produce. * \param points_num: The number of points in the curve. * \param resolution: The resolution for each segment. */ int calculate_evaluated_num(int points_num, bool cyclic, int resolution); /** * Evaluate the Catmull Rom curve. The length of the #dst span should be calculated with * #calculate_evaluated_num and is expected to divide evenly by the #src span's segment size. */ void interpolate_to_evaluated(GSpan src, bool cyclic, int resolution, GMutableSpan dst); /** * Evaluate the Catmull Rom curve. The placement of each segment in the #dst span is described by * #evaluated_offsets. */ void interpolate_to_evaluated(const GSpan src, const bool cyclic, const OffsetIndices evaluated_offsets, GMutableSpan dst); float4 calculate_basis(const float parameter); /** * Interpolate the control point values for the given parameter on the piecewise segment. * \param a: Value associated with the first control point influencing the segment. * \param d: Value associated with the fourth control point. * \param parameter: Parameter in range [0, 1] to compute the interpolation for. */ template T interpolate(const T &a, const T &b, const T &c, const T &d, const float parameter) { BLI_assert(0.0f <= parameter && parameter <= 1.0f); const float4 weights = calculate_basis(parameter); if constexpr (is_same_any_v) { /* Save multiplications by adjusting weights after mix. */ return 0.5f * attribute_math::mix4(weights, a, b, c, d); } else { return attribute_math::mix4(weights * 0.5f, a, b, c, d); } } } // namespace catmull_rom /** \} */ /* -------------------------------------------------------------------- */ /** \name Curve NURBS Methods * \{ */ namespace nurbs { /** * Checks the conditions that a NURBS curve needs to evaluate. */ bool check_valid_num_and_order(int points_num, int8_t order, bool cyclic, KnotsMode knots_mode); /** * Calculate the standard evaluated size for a NURBS curve, using the standard that * the resolution is multiplied by the number of segments between the control points. * * \note Though the number of evaluated points is rather arbitrary, it's useful to have a standard * for predictability and so that cached basis weights of NURBS curves with these properties can be * shared. */ int calculate_evaluated_num( int points_num, int8_t order, bool cyclic, int resolution, KnotsMode knots_mode); /** * Calculate the length of the knot vector for a NURBS curve with the given properties. * The knots must be longer for a cyclic curve, for example, in order to provide weights for the * last evaluated points that are also influenced by the first control points. */ int knots_num(int points_num, int8_t order, bool cyclic); /** * Calculate the knots for a curve given its properties, based on built-in standards defined by * #KnotsMode. * * \note Theoretically any sorted values can be used for NURBS knots, but calculating based * on standard modes allows useful presets, automatic recalculation when the number of points * changes, and is generally more intuitive than defining the knot vector manually. */ void calculate_knots( int points_num, KnotsMode mode, int8_t order, bool cyclic, MutableSpan knots); /** * Based on the knots, the order, and other properties of a NURBS curve, calculate a cache that can * be used to more simply interpolate attributes to the evaluated points later. The cache includes * two pieces of information for every evaluated point: the first control point that influences it, * and a weight for each control point. */ void calculate_basis_cache(int points_num, int evaluated_num, int8_t order, bool cyclic, Span knots, BasisCache &basis_cache); /** * Using a "basis cache" generated by #BasisCache, interpolate attribute values to the evaluated * points. The number of evaluated points is determined by the #basis_cache argument. * * \param control_weights: An optional span of control point weights, which must have the same size * as the number of control points in the curve if provided. Using this argument gives a NURBS * curve the "Rational" behavior that's part of its acronym; otherwise it is a NUBS. */ void interpolate_to_evaluated(const BasisCache &basis_cache, int8_t order, Span control_weights, GSpan src, GMutableSpan dst); } // namespace nurbs /** \} */ } // namespace curves Curves *curves_new_nomain(int points_num, int curves_num); Curves *curves_new_nomain(CurvesGeometry curves); /** * Create a new curves data-block containing a single curve with the given length and type. */ Curves *curves_new_nomain_single(int points_num, CurveType type); /** * Copy data from #src to #dst, except the geometry data in #CurvesGeometry. Typically used to * copy high-level parameters when a geometry-altering operation creates a new curves data-block. */ void curves_copy_parameters(const Curves &src, Curves &dst); CurvesGeometry curves_copy_point_selection( const CurvesGeometry &curves, const IndexMask &points_to_copy, const AnonymousAttributePropagationInfo &propagation_info); CurvesGeometry curves_copy_curve_selection( const CurvesGeometry &curves, const IndexMask &curves_to_copy, const AnonymousAttributePropagationInfo &propagation_info); std::array calculate_type_counts(const VArray &types); /* -------------------------------------------------------------------- */ /** \name #CurvesGeometry Inline Methods * \{ */ inline int CurvesGeometry::points_num() const { return this->point_num; } inline int CurvesGeometry::curves_num() const { return this->curve_num; } inline IndexRange CurvesGeometry::points_range() const { return IndexRange(this->points_num()); } inline IndexRange CurvesGeometry::curves_range() const { return IndexRange(this->curves_num()); } inline bool CurvesGeometry::is_single_type(const CurveType type) const { return this->curve_type_counts()[type] == this->curves_num(); } inline bool CurvesGeometry::has_curve_with_type(const CurveType type) const { return this->curve_type_counts()[type] > 0; } inline bool CurvesGeometry::has_curve_with_type(const Span types) const { return std::any_of( types.begin(), types.end(), [&](CurveType type) { return this->has_curve_with_type(type); }); } inline const std::array &CurvesGeometry::curve_type_counts() const { BLI_assert(this->runtime->type_counts == calculate_type_counts(this->curve_types())); return this->runtime->type_counts; } inline OffsetIndices CurvesGeometry::points_by_curve() const { return OffsetIndices({this->curve_offsets, this->curve_num + 1}); } inline int CurvesGeometry::evaluated_points_num() const { /* This could avoid calculating offsets in the future in simple circumstances. */ return this->evaluated_points_by_curve().total_size(); } inline Span CurvesGeometry::bezier_evaluated_offsets_for_curve(const int curve_index) const { const OffsetIndices points_by_curve = this->points_by_curve(); const IndexRange points = points_by_curve[curve_index]; const IndexRange range = curves::per_curve_point_offsets_range(points, curve_index); const Span offsets = this->runtime->evaluated_offsets_cache.data().all_bezier_offsets; return offsets.slice(range); } inline IndexRange CurvesGeometry::lengths_range_for_curve(const int curve_index, const bool cyclic) const { BLI_assert(cyclic == this->cyclic()[curve_index]); const IndexRange points = this->evaluated_points_by_curve()[curve_index]; const int start = points.start() + curve_index; return {start, curves::segments_num(points.size(), cyclic)}; } inline Span CurvesGeometry::evaluated_lengths_for_curve(const int curve_index, const bool cyclic) const { const IndexRange range = this->lengths_range_for_curve(curve_index, cyclic); return this->runtime->evaluated_length_cache.data().as_span().slice(range); } inline float CurvesGeometry::evaluated_length_total_for_curve(const int curve_index, const bool cyclic) const { const Span lengths = this->evaluated_lengths_for_curve(curve_index, cyclic); if (lengths.is_empty()) { return 0.0f; } return lengths.last(); } /** \} */ namespace curves { /* -------------------------------------------------------------------- */ /** \name Bezier Inline Methods * \{ */ namespace bezier { inline bool point_is_sharp(const Span handle_types_left, const Span handle_types_right, const int index) { return ELEM(handle_types_left[index], BEZIER_HANDLE_VECTOR, BEZIER_HANDLE_FREE) || ELEM(handle_types_right[index], BEZIER_HANDLE_VECTOR, BEZIER_HANDLE_FREE); } inline bool segment_is_vector(const HandleType left, const HandleType right) { return left == BEZIER_HANDLE_VECTOR && right == BEZIER_HANDLE_VECTOR; } inline bool segment_is_vector(const int8_t left, const int8_t right) { return segment_is_vector(HandleType(left), HandleType(right)); } inline bool has_vector_handles(const int num_curve_points, const int64_t evaluated_size, const bool cyclic, const int resolution) { return evaluated_size - !cyclic != int64_t(segments_num(num_curve_points, cyclic)) * resolution; } inline float3 calculate_vector_handle(const float3 &point, const float3 &next_point) { return math::interpolate(point, next_point, 1.0f / 3.0f); } } // namespace bezier /** \} */ } // namespace curves struct CurvesSurfaceTransforms { float4x4 curves_to_world; float4x4 curves_to_surface; float4x4 world_to_curves; float4x4 world_to_surface; float4x4 surface_to_world; float4x4 surface_to_curves; float4x4 surface_to_curves_normal; CurvesSurfaceTransforms() = default; CurvesSurfaceTransforms(const Object &curves_ob, const Object *surface_ob); }; } // namespace blender::bke inline blender::bke::CurvesGeometry &CurvesGeometry::wrap() { return *reinterpret_cast(this); } inline const blender::bke::CurvesGeometry &CurvesGeometry::wrap() const { return *reinterpret_cast(this); }