Changeset View
Standalone View
source/blender/geometry/intern/trim_curves.cc
| Show All 12 Lines | |||||||||||
| #include "BKE_curves_utils.hh" | #include "BKE_curves_utils.hh" | ||||||||||
| #include "BKE_geometry_set.hh" | #include "BKE_geometry_set.hh" | ||||||||||
| #include "GEO_trim_curves.hh" | #include "GEO_trim_curves.hh" | ||||||||||
| namespace blender::geometry { | namespace blender::geometry { | ||||||||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||||||||
| /** \name Curve Enums | |||||||||||
| * \{ */ | |||||||||||
| #define CURVE_TYPE_AS_MASK(curve_type) ((CurveTypeMask)(1 << int(curve_type))) | |||||||||||
| enum CurveTypeMask { | |||||||||||
| CURVE_TYPE_MASK_CATMULL_ROM = (1 << 0), | |||||||||||
| CURVE_TYPE_MASK_POLY = (1 << 1), | |||||||||||
| CURVE_TYPE_MASK_BEZIER = (1 << 2), | |||||||||||
| CURVE_TYPE_MASK_NURBS = (1 << 3), | |||||||||||
| CURVE_TYPE_MASK_ALL = (1 << 4) - 1 | |||||||||||
| }; | |||||||||||
| /** \} */ | |||||||||||
| /* -------------------------------------------------------------------- */ | |||||||||||
| /** \name #IndexRangeCyclic Utilities | |||||||||||
| * \{ */ | |||||||||||
| /** | |||||||||||
| * Create a cyclical iterator for all control points within the interval [start_point, end_point] | |||||||||||
| * including any control point at the start or end point. | |||||||||||
| * | |||||||||||
| * \param start_point: Point on the curve that define the starting point of the interval. | |||||||||||
| * \param end_point: Point on the curve that define the end point of the interval (included). | |||||||||||
| * \param points: #IndexRange for the curve points. | |||||||||||
| */ | |||||||||||
| static bke::curves::IndexRangeCyclic get_range_between_endpoints( | |||||||||||
| const bke::curves::CurvePoint start_point, | |||||||||||
| const bke::curves::CurvePoint end_point, | |||||||||||
| const IndexRange points) | |||||||||||
| { | |||||||||||
| const int64_t start_index = start_point.parameter == 0.0 ? start_point.index : | |||||||||||
| start_point.next_index; | |||||||||||
| int64_t end_index = end_point.parameter == 0.0 ? end_point.index : end_point.next_index; | |||||||||||
| int64_t cycles; | |||||||||||
| if (end_point.is_controlpoint()) { | |||||||||||
| ++end_index; | |||||||||||
| if (end_index > points.last()) { | |||||||||||
| end_index = points.one_after_last(); | |||||||||||
| } | |||||||||||
| /* end_point < start_point but parameter is irrelevant (end_point is controlpoint), and loop | |||||||||||
| * when equal due to increment. */ | |||||||||||
| cycles = end_index <= start_index; | |||||||||||
| } | |||||||||||
| else { | |||||||||||
| cycles = end_point < start_point || end_index < start_index; | |||||||||||
| } | |||||||||||
| return bke::curves::IndexRangeCyclic(start_index, end_index, points, cycles); | |||||||||||
| } | |||||||||||
| /** \} */ | |||||||||||
| /* -------------------------------------------------------------------- */ | |||||||||||
| /** \name Lookup Curve Points | /** \name Lookup Curve Points | ||||||||||
| * \{ */ | * \{ */ | ||||||||||
| /** | /** | ||||||||||
| * Find the point on the curve defined by the distance along the curve. Assumes curve resolution is | * Find the point on the curve defined by the distance along the curve. Assumes curve resolution is | ||||||||||
| * constant for all curve segments and evaluated curve points are uniformly spaced between the | * constant for all curve segments and evaluated curve points are uniformly spaced between the | ||||||||||
| * segment endpoints in relation to the curve parameter. | * segment endpoints in relation to the curve parameter. | ||||||||||
| * | * | ||||||||||
| * \param lengths: Accumulated length for the evaluated curve. | * \param lengths: Accumulated length for the evaluated curve. | ||||||||||
| * \param sample_length: Distance along the curve to determine the #CurvePoint for. | * \param sample_length: Distance along the curve to determine the #CurvePoint for. | ||||||||||
| * \param cyclic: If curve is cyclic. | * \param cyclic: If curve is cyclic. | ||||||||||
| * \param resolution: Curve resolution (number of evaluated points per segment). | * \param resolution: Curve resolution (number of evaluated points per segment). | ||||||||||
| * \param num_curve_points: Total number of control points in the curve. | * \param num_curve_points: Total number of control points in the curve. | ||||||||||
| * \return: Point on the piecewise segment matching the given distance. | * \return: Point on the piecewise segment matching the given distance. | ||||||||||
| */ | */ | ||||||||||
| static bke::curves::CurvePoint lookup_curve_point(const Span<float> lengths, | static bke::curves::CurvePoint lookup_point_uniform_spacing(const Span<float> lengths, | ||||||||||
| const float sample_length, | const float sample_length, | ||||||||||
| const bool cyclic, | const bool cyclic, | ||||||||||
| const int resolution, | const int resolution, | ||||||||||
| const int num_curve_points) | const int num_curve_points) | ||||||||||
| { | { | ||||||||||
| BLI_assert(!cyclic || lengths.size() / resolution >= 2); | BLI_assert(!cyclic || lengths.size() / resolution >= 2); | ||||||||||
| const int last_index = num_curve_points - 1; | const int last_index = num_curve_points - 1; | ||||||||||
| if (sample_length <= 0.0f) { | if (sample_length <= 0.0f) { | ||||||||||
| return {{0, 1}, 0.0f}; | return {{0, 1}, 0.0f}; | ||||||||||
| } | } | ||||||||||
| if (sample_length >= lengths.last()) { | if (sample_length >= lengths.last()) { | ||||||||||
| return cyclic ? bke::curves::CurvePoint{{last_index, 0}, 1.0} : | return cyclic ? bke::curves::CurvePoint{{last_index, 0}, 1.0} : | ||||||||||
| bke::curves::CurvePoint{{last_index - 1, last_index}, 1.0}; | bke::curves::CurvePoint{{last_index - 1, last_index}, 1.0}; | ||||||||||
| } | } | ||||||||||
| int eval_index; | int eval_index; | ||||||||||
| float eval_factor; | float eval_factor; | ||||||||||
| length_parameterize::sample_at_length(lengths, sample_length, eval_index, eval_factor); | length_parameterize::sample_at_length(lengths, sample_length, eval_index, eval_factor); | ||||||||||
| const int index = eval_index / resolution; | const int index = eval_index / resolution; | ||||||||||
| const int next_index = (index == last_index) ? 0 : index + 1; | const int next_index = (index == last_index) ? 0 : index + 1; | ||||||||||
| const float parameter = (eval_factor + eval_index) / resolution - index; | const float parameter = (eval_factor + eval_index) / resolution - index; | ||||||||||
| return bke::curves::CurvePoint{{index, next_index}, parameter}; | return bke::curves::CurvePoint{{index, next_index}, parameter}; | ||||||||||
| } | } | ||||||||||
| /** | /** | ||||||||||
| * Find the point on the 'evaluated' polygonal curve. | * Find the point on the 'evaluated' polygonal curve. | ||||||||||
| */ | */ | ||||||||||
| static bke::curves::CurvePoint lookup_evaluated_point(const Span<float> lengths, | static bke::curves::CurvePoint lookup_point_polygonal(const Span<float> lengths, | ||||||||||
| const float sample_length, | const float sample_length, | ||||||||||
| const bool cyclic, | const bool cyclic, | ||||||||||
| const int evaluated_size) | const int evaluated_size) | ||||||||||
| { | { | ||||||||||
| const int last_index = evaluated_size - 1; | const int last_index = evaluated_size - 1; | ||||||||||
| if (sample_length <= 0.0f) { | if (sample_length <= 0.0f) { | ||||||||||
| return {{0, 1}, 0.0f}; | return {{0, 1}, 0.0f}; | ||||||||||
| } | } | ||||||||||
| if (sample_length >= lengths.last()) { | if (sample_length >= lengths.last()) { | ||||||||||
| return cyclic ? bke::curves::CurvePoint{{last_index, 0}, 1.0} : | return cyclic ? bke::curves::CurvePoint{{last_index, 0}, 1.0} : | ||||||||||
| bke::curves::CurvePoint{{last_index - 1, last_index}, 1.0}; | bke::curves::CurvePoint{{last_index - 1, last_index}, 1.0}; | ||||||||||
| } | } | ||||||||||
| int eval_index; | int eval_index; | ||||||||||
| float eval_factor; | float eval_factor; | ||||||||||
| length_parameterize::sample_at_length(lengths, sample_length, eval_index, eval_factor); | length_parameterize::sample_at_length(lengths, sample_length, eval_index, eval_factor); | ||||||||||
| const int next_eval_index = (eval_index == last_index) ? 0 : eval_index + 1; | const int next_eval_index = (eval_index == last_index) ? 0 : eval_index + 1; | ||||||||||
| return bke::curves::CurvePoint{{eval_index, next_eval_index}, eval_factor}; | return bke::curves::CurvePoint{{eval_index, next_eval_index}, eval_factor}; | ||||||||||
| } | } | ||||||||||
| /** | /** | ||||||||||
| * Find the point on a Bezier curve using the 'bezier_offsets' cache. | * Find the point on a Bezier curve using the 'bezier_offsets' cache. | ||||||||||
| */ | */ | ||||||||||
| static bke::curves::CurvePoint lookup_bezier_point(const Span<int> bezier_offsets, | static bke::curves::CurvePoint lookup_point_bezier(const Span<int> bezier_offsets, | ||||||||||
| const Span<float> lengths, | const Span<float> lengths, | ||||||||||
| const float sample_length, | const float sample_length, | ||||||||||
| const bool cyclic, | const bool cyclic, | ||||||||||
| const int num_curve_points) | const int num_curve_points) | ||||||||||
| { | { | ||||||||||
| const int last_index = num_curve_points - 1; | const int last_index = num_curve_points - 1; | ||||||||||
| if (sample_length <= 0.0f) { | if (sample_length <= 0.0f) { | ||||||||||
| return {{0, 1}, 0.0f}; | return {{0, 1}, 0.0f}; | ||||||||||
| } | } | ||||||||||
| if (sample_length >= lengths.last()) { | if (sample_length >= lengths.last()) { | ||||||||||
| return cyclic ? bke::curves::CurvePoint{{last_index, 0}, 1.0} : | return cyclic ? bke::curves::CurvePoint{{last_index, 0}, 1.0} : | ||||||||||
| bke::curves::CurvePoint{{last_index - 1, last_index}, 1.0}; | bke::curves::CurvePoint{{last_index - 1, last_index}, 1.0}; | ||||||||||
| } | } | ||||||||||
| int eval_index; | int eval_index; | ||||||||||
| float eval_factor; | float eval_factor; | ||||||||||
| length_parameterize::sample_at_length(lengths, sample_length, eval_index, eval_factor); | length_parameterize::sample_at_length(lengths, sample_length, eval_index, eval_factor); | ||||||||||
| /* Find the segment index from the offset mapping. */ | /* Find the segment index from the offset mapping. */ | ||||||||||
| const int *offset = std::upper_bound(bezier_offsets.begin(), bezier_offsets.end(), eval_index); | const int *offset = std::upper_bound(bezier_offsets.begin(), bezier_offsets.end(), eval_index); | ||||||||||
| const int left = offset - bezier_offsets.begin(); | const int left = offset - bezier_offsets.begin(); | ||||||||||
| const int right = left == last_index ? 0 : left + 1; | const int right = left == last_index ? 0 : left + 1; | ||||||||||
| const int prev_offset = left == 0 ? 0 : bezier_offsets[int64_t(left) - 1]; | const int prev_offset = left == 0 ? 0 : bezier_offsets[int64_t(left) - 1]; | ||||||||||
| const float offset_in_segment = eval_factor + eval_index - prev_offset; | const float offset_in_segment = eval_factor + (eval_index - prev_offset); | ||||||||||
| const int segment_resolution = bezier_offsets[left] - prev_offset; | const int segment_resolution = bezier_offsets[left] - prev_offset; | ||||||||||
| const float parameter = std::clamp(offset_in_segment / segment_resolution, 0.0f, 1.0f); | const float parameter = std::clamp(offset_in_segment / segment_resolution, 0.0f, 1.0f); | ||||||||||
| return {{left, right}, parameter}; | return {{left, right}, parameter}; | ||||||||||
| } | } | ||||||||||
| Array<bke::curves::CurvePoint, 12> lookup_curve_points(const bke::CurvesGeometry &curves, | static bke::curves::CurvePoint lookup_point_bezier(const bke::CurvesGeometry &src_curves, | ||||||||||
| const Span<float> lengths, | const int64_t curve_index, | ||||||||||
| const Span<int64_t> curve_indices, | const Span<float> accumulated_lengths, | ||||||||||
| const bool normalized_factors) | const float sample_length, | ||||||||||
| const bool cyclic, | |||||||||||
| const int resolution, | |||||||||||
| const int num_curve_points) | |||||||||||
| { | { | ||||||||||
| BLI_assert(lengths.size() == curve_indices.size()); | |||||||||||
| BLI_assert(*std::max_element(curve_indices.begin(), curve_indices.end()) < curves.curves_num()); | |||||||||||
| const VArray<bool> cyclic = curves.cyclic(); | |||||||||||
| const VArray<int> resolution = curves.resolution(); | |||||||||||
| const VArray<int8_t> curve_types = curves.curve_types(); | |||||||||||
| /* Compute curve lengths! */ | |||||||||||
| curves.ensure_evaluated_lengths(); | |||||||||||
| curves.ensure_evaluated_offsets(); | |||||||||||
| /* Find the curve points referenced by the input! */ | |||||||||||
| Array<bke::curves::CurvePoint, 12> lookups(curve_indices.size()); | |||||||||||
| threading::parallel_for(curve_indices.index_range(), 128, [&](const IndexRange range) { | |||||||||||
| for (const int64_t lookup_index : range) { | |||||||||||
| const int64_t curve_i = curve_indices[lookup_index]; | |||||||||||
| const int point_count = curves.points_num_for_curve(curve_i); | |||||||||||
| if (curve_i < 0 || point_count == 1) { | |||||||||||
| lookups[lookup_index] = {{0, 0}, 0.0f}; | |||||||||||
| continue; | |||||||||||
| } | |||||||||||
| const Span<float> accumulated_lengths = curves.evaluated_lengths_for_curve(curve_i, | |||||||||||
| cyclic[curve_i]); | |||||||||||
| BLI_assert(accumulated_lengths.size() > 0); | |||||||||||
| const float sample_length = normalized_factors ? | |||||||||||
| lengths[lookup_index] * accumulated_lengths.last() : | |||||||||||
| lengths[lookup_index]; | |||||||||||
| const CurveType curve_type = (CurveType)curve_types[curve_i]; | |||||||||||
| switch (curve_type) { | |||||||||||
| case CURVE_TYPE_BEZIER: { | |||||||||||
| if (bke::curves::bezier::has_vector_handles( | if (bke::curves::bezier::has_vector_handles( | ||||||||||
| point_count, | num_curve_points, | ||||||||||
| curves.evaluated_points_for_curve(curve_i).size(), | src_curves.evaluated_points_for_curve(curve_index).size(), | ||||||||||
| cyclic[curve_i], | cyclic, | ||||||||||
| resolution[curve_i])) { | resolution)) { | ||||||||||
| const Span<int> bezier_offsets = curves.bezier_evaluated_offsets_for_curve(curve_i); | const Span<int> bezier_offsets = src_curves.bezier_evaluated_offsets_for_curve(curve_index); | ||||||||||
| lookups[lookup_index] = lookup_bezier_point( | return lookup_point_bezier( | ||||||||||
| bezier_offsets, accumulated_lengths, sample_length, cyclic[curve_i], point_count); | bezier_offsets, accumulated_lengths, sample_length, cyclic, num_curve_points); | ||||||||||
| } | } | ||||||||||
| else { | else { | ||||||||||
| lookups[lookup_index] = lookup_curve_point(accumulated_lengths, | return lookup_point_uniform_spacing( | ||||||||||
| sample_length, | accumulated_lengths, sample_length, cyclic, resolution, num_curve_points); | ||||||||||
| cyclic[curve_i], | |||||||||||
| resolution[curve_i], | |||||||||||
| point_count); | |||||||||||
| } | |||||||||||
| break; | |||||||||||
| } | } | ||||||||||
| case CURVE_TYPE_CATMULL_ROM: { | |||||||||||
| lookups[lookup_index] = lookup_curve_point(accumulated_lengths, | |||||||||||
| sample_length, | |||||||||||
| cyclic[curve_i], | |||||||||||
| resolution[curve_i], | |||||||||||
| point_count); | |||||||||||
| break; | |||||||||||
| } | } | ||||||||||
| case CURVE_TYPE_NURBS: | |||||||||||
| case CURVE_TYPE_POLY: | |||||||||||
| default: { | |||||||||||
| /* Handle general case as an "evaluated" or polygonal curve. */ | |||||||||||
| BLI_assert(resolution[curve_i] > 0); | |||||||||||
| lookups[lookup_index] = lookup_evaluated_point( | |||||||||||
| accumulated_lengths, | |||||||||||
| sample_length, | |||||||||||
| cyclic[curve_i], | |||||||||||
| curves.evaluated_points_for_curve(curve_i).size()); | |||||||||||
| break; | |||||||||||
| } | |||||||||||
| } | |||||||||||
| } | |||||||||||
| }); | |||||||||||
| return lookups; | |||||||||||
| } | |||||||||||
| /** \} */ | |||||||||||
| /* -------------------------------------------------------------------- */ | |||||||||||
| /** \name Transfer Curve Domain | |||||||||||
| * \{ */ | |||||||||||
| /** | |||||||||||
| * Determine curve type(s) for the copied curves given the supported set of types and knot modes. | |||||||||||
| * If a curve type is not supported the default type is set. | |||||||||||
| */ | |||||||||||
| static void determine_copyable_curve_types( | |||||||||||
| const bke::CurvesGeometry &src_curves, | |||||||||||
| bke::CurvesGeometry &dst_curves, | |||||||||||
| const IndexMask selection, | |||||||||||
| const IndexMask selection_inverse, | |||||||||||
| const CurveTypeMask supported_curve_type_mask, | |||||||||||
| const int8_t default_curve_type = int8_t(CURVE_TYPE_POLY)) | |||||||||||
| { | |||||||||||
| const VArray<int8_t> src_curve_types = src_curves.curve_types(); | |||||||||||
| const VArray<int8_t> src_knot_modes = src_curves.nurbs_knots_modes(); | |||||||||||
| MutableSpan<int8_t> dst_curve_types = dst_curves.curve_types_for_write(); | |||||||||||
| threading::parallel_for(selection.index_range(), 4096, [&](const IndexRange selection_range) { | static bke::curves::CurvePoint lookup_curve_point(const bke::CurvesGeometry &src_curves, | ||||||||||
| for (const int64_t curve_i : selection.slice(selection_range)) { | const CurveType curve_type, | ||||||||||
| if (supported_curve_type_mask & CURVE_TYPE_AS_MASK(src_curve_types[curve_i])) { | const int64_t curve_index, | ||||||||||
| dst_curve_types[curve_i] = src_curve_types[curve_i]; | const Span<float> accumulated_lengths, | ||||||||||
| } | const float sample_length, | ||||||||||
| else { | const bool cyclic, | ||||||||||
| dst_curve_types[curve_i] = default_curve_type; | const int resolution, | ||||||||||
| } | const int num_curve_points) | ||||||||||
| } | |||||||||||
| }); | |||||||||||
| array_utils::copy(src_curve_types, selection_inverse, dst_curve_types); | |||||||||||
| } | |||||||||||
| /** | |||||||||||
| * Determine if a curve is treated as an evaluated curve. Curves which inherently do not support | |||||||||||
| * trimming are discretized (e.g. NURBS). | |||||||||||
| */ | |||||||||||
| static bool copy_as_evaluated_curve(const int8_t src_type, const int8_t dst_type) | |||||||||||
| { | { | ||||||||||
| return src_type != CURVE_TYPE_POLY && dst_type == CURVE_TYPE_POLY; | if (num_curve_points == 1) { | ||||||||||
| return {{0, 0}, 0.0f}; | |||||||||||
| } | } | ||||||||||
| /** \} */ | if (curve_type == CURVE_TYPE_CATMULL_ROM) { | ||||||||||
| return lookup_point_uniform_spacing( | |||||||||||
| /* -------------------------------------------------------------------- */ | accumulated_lengths, sample_length, cyclic, resolution, num_curve_points); | ||||||||||
| /** \name Specialized Curve Constructors | |||||||||||
| * \{ */ | |||||||||||
| static void compute_trim_result_offsets(const bke::CurvesGeometry &src_curves, | |||||||||||
| const IndexMask selection, | |||||||||||
| const IndexMask inverse_selection, | |||||||||||
| const Span<bke::curves::CurvePoint> start_points, | |||||||||||
| const Span<bke::curves::CurvePoint> end_points, | |||||||||||
| const VArray<int8_t> dst_curve_types, | |||||||||||
| MutableSpan<int> dst_curve_offsets, | |||||||||||
| Vector<int64_t> &r_curve_indices, | |||||||||||
| Vector<int64_t> &r_point_curve_indices) | |||||||||||
| { | |||||||||||
| BLI_assert(r_curve_indices.size() == 0); | |||||||||||
| BLI_assert(r_point_curve_indices.size() == 0); | |||||||||||
| const VArray<bool> cyclic = src_curves.cyclic(); | |||||||||||
| const VArray<int8_t> curve_types = src_curves.curve_types(); | |||||||||||
| r_curve_indices.reserve(selection.size()); | |||||||||||
| for (const int64_t curve_i : selection) { | |||||||||||
| int64_t src_point_count; | |||||||||||
| if (copy_as_evaluated_curve(curve_types[curve_i], dst_curve_types[curve_i])) { | |||||||||||
| src_point_count = src_curves.evaluated_points_for_curve(curve_i).size(); | |||||||||||
| } | } | ||||||||||
| else { | else if (curve_type == CURVE_TYPE_BEZIER) { | ||||||||||
| src_point_count = int64_t(src_curves.points_num_for_curve(curve_i)); | return lookup_point_bezier(src_curves, | ||||||||||
| curve_index, | |||||||||||
| accumulated_lengths, | |||||||||||
| sample_length, | |||||||||||
| cyclic, | |||||||||||
| resolution, | |||||||||||
| num_curve_points); | |||||||||||
| } | } | ||||||||||
| BLI_assert(src_point_count > 0); | else if (curve_type == CURVE_TYPE_POLY) { | ||||||||||
| return lookup_point_polygonal(accumulated_lengths, sample_length, cyclic, num_curve_points); | |||||||||||
| if (start_points[curve_i] == end_points[curve_i]) { | |||||||||||
| dst_curve_offsets[curve_i] = 1; | |||||||||||
| r_point_curve_indices.append(curve_i); | |||||||||||
| } | } | ||||||||||
| else { | else { | ||||||||||
| const bke::curves::IndexRangeCyclic point_range = get_range_between_endpoints( | /* Handle evaluated curve. */ | ||||||||||
| start_points[curve_i], end_points[curve_i], {0, src_point_count}); | BLI_assert(resolution > 0); | ||||||||||
| const int count = point_range.size() + !start_points[curve_i].is_controlpoint() + | return lookup_point_polygonal(accumulated_lengths, | ||||||||||
| !end_points[curve_i].is_controlpoint(); | sample_length, | ||||||||||
| dst_curve_offsets[curve_i] = count; | cyclic, | ||||||||||
| r_curve_indices.append(curve_i); | src_curves.evaluated_points_for_curve(curve_index).size()); | ||||||||||
| } | |||||||||||
| BLI_assert(dst_curve_offsets[curve_i] > 0); | |||||||||||
| } | |||||||||||
| threading::parallel_for( | |||||||||||
| inverse_selection.index_range(), 4096, [&](const IndexRange selection_range) { | |||||||||||
| for (const int64_t curve_i : inverse_selection.slice(selection_range)) { | |||||||||||
| dst_curve_offsets[curve_i] = src_curves.points_num_for_curve(curve_i); | |||||||||||
| } | } | ||||||||||
| }); | |||||||||||
| bke::curves::accumulate_counts_to_offsets(dst_curve_offsets); | |||||||||||
| } | } | ||||||||||
| /** \} */ | /** \} */ | ||||||||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||||||||
| /** \name Utility Functions | /** \name Utility Functions | ||||||||||
| * \{ */ | * \{ */ | ||||||||||
| static void fill_bezier_data(bke::CurvesGeometry &dst_curves, const IndexMask selection) | static void fill_bezier_data(bke::CurvesGeometry &dst_curves, const IndexMask selection) | ||||||||||
| { | { | ||||||||||
| if (dst_curves.has_curve_with_type(CURVE_TYPE_BEZIER)) { | if (!dst_curves.has_curve_with_type(CURVE_TYPE_BEZIER)) { | ||||||||||
| return; | |||||||||||
| } | |||||||||||
| MutableSpan<float3> handle_positions_left = dst_curves.handle_positions_left_for_write(); | MutableSpan<float3> handle_positions_left = dst_curves.handle_positions_left_for_write(); | ||||||||||
| MutableSpan<float3> handle_positions_right = dst_curves.handle_positions_right_for_write(); | MutableSpan<float3> handle_positions_right = dst_curves.handle_positions_right_for_write(); | ||||||||||
| MutableSpan<int8_t> handle_types_left = dst_curves.handle_types_left_for_write(); | MutableSpan<int8_t> handle_types_left = dst_curves.handle_types_left_for_write(); | ||||||||||
| MutableSpan<int8_t> handle_types_right = dst_curves.handle_types_right_for_write(); | MutableSpan<int8_t> handle_types_right = dst_curves.handle_types_right_for_write(); | ||||||||||
| threading::parallel_for(selection.index_range(), 4096, [&](const IndexRange range) { | threading::parallel_for(selection.index_range(), 4096, [&](const IndexRange range) { | ||||||||||
| for (const int64_t curve_i : selection.slice(range)) { | for (const int64_t curve_i : selection.slice(range)) { | ||||||||||
| const IndexRange points = dst_curves.points_for_curve(curve_i); | const IndexRange points = dst_curves.points_for_curve(curve_i); | ||||||||||
| handle_types_right.slice(points).fill(int8_t(BEZIER_HANDLE_FREE)); | handle_types_right.slice(points).fill(int8_t(BEZIER_HANDLE_FREE)); | ||||||||||
| handle_types_left.slice(points).fill(int8_t(BEZIER_HANDLE_FREE)); | handle_types_left.slice(points).fill(int8_t(BEZIER_HANDLE_FREE)); | ||||||||||
| handle_positions_left.slice(points).fill({0.0f, 0.0f, 0.0f}); | handle_positions_left.slice(points).fill({0.0f, 0.0f, 0.0f}); | ||||||||||
| handle_positions_right.slice(points).fill({0.0f, 0.0f, 0.0f}); | handle_positions_right.slice(points).fill({0.0f, 0.0f, 0.0f}); | ||||||||||
| } | } | ||||||||||
| }); | }); | ||||||||||
| } | } | ||||||||||
| } | |||||||||||
| static void fill_nurbs_data(bke::CurvesGeometry &dst_curves, const IndexMask selection) | static void fill_nurbs_data(bke::CurvesGeometry &dst_curves, const IndexMask selection) | ||||||||||
| { | { | ||||||||||
| if (dst_curves.has_curve_with_type(CURVE_TYPE_NURBS)) { | if (!dst_curves.has_curve_with_type(CURVE_TYPE_NURBS)) { | ||||||||||
| bke::curves::fill_points(dst_curves, selection, 0.0f, dst_curves.nurbs_weights_for_write()); | return; | ||||||||||
| } | } | ||||||||||
| bke::curves::fill_points(dst_curves, selection, 0.0f, dst_curves.nurbs_weights_for_write()); | |||||||||||
| } | } | ||||||||||
| template<typename T> | template<typename T> | ||||||||||
| static int64_t copy_point_data_between_endpoints(const Span<T> src_data, | static int64_t copy_point_data_between_endpoints(const Span<T> src_data, | ||||||||||
| MutableSpan<T> dst_data, | MutableSpan<T> dst_data, | ||||||||||
| const bke::curves::IndexRangeCyclic src_range, | const bke::curves::IndexRangeCyclic src_range, | ||||||||||
| const int64_t src_index, | |||||||||||
| int64_t dst_index) | int64_t dst_index) | ||||||||||
| { | { | ||||||||||
| int64_t increment; | int64_t increment; | ||||||||||
| if (src_range.cycles()) { | if (src_range.cycles()) { | ||||||||||
| increment = src_range.size_before_loop(); | increment = src_range.size_before_loop(); | ||||||||||
| dst_data.slice(dst_index, increment).copy_from(src_data.slice(src_index, increment)); | dst_data.slice(dst_index, increment).copy_from(src_data.slice(src_range.first(), increment)); | ||||||||||
| dst_index += increment; | dst_index += increment; | ||||||||||
| increment = src_range.size_after_loop(); | increment = src_range.size_after_loop(); | ||||||||||
| dst_data.slice(dst_index, increment) | dst_data.slice(dst_index, increment) | ||||||||||
| .copy_from(src_data.slice(src_range.curve_range().first(), increment)); | .copy_from(src_data.slice(src_range.curve_range().first(), increment)); | ||||||||||
| dst_index += increment; | dst_index += increment; | ||||||||||
| } | } | ||||||||||
| else { | else { | ||||||||||
| increment = src_range.one_after_last() - src_range.first(); | increment = src_range.one_after_last() - src_range.first(); | ||||||||||
| dst_data.slice(dst_index, increment).copy_from(src_data.slice(src_index, increment)); | dst_data.slice(dst_index, increment).copy_from(src_data.slice(src_range.first(), increment)); | ||||||||||
| dst_index += increment; | dst_index += increment; | ||||||||||
| } | } | ||||||||||
| return dst_index; | return dst_index; | ||||||||||
| } | } | ||||||||||
| /** \} */ | /** \} */ | ||||||||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||||||||
| Show All 38 Lines | return bke::curves::bezier::insert(positions[insertion_point.index], | ||||||||||
| handles_left[insertion_point.next_index], | handles_left[insertion_point.next_index], | ||||||||||
| positions[insertion_point.next_index], | positions[insertion_point.next_index], | ||||||||||
| insertion_point.parameter); | insertion_point.parameter); | ||||||||||
| } | } | ||||||||||
| /** \} */ | /** \} */ | ||||||||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||||||||
| /** \name Sample Single Point | |||||||||||
| * \{ */ | |||||||||||
| template<typename T> | |||||||||||
| static void sample_linear(const Span<T> src_data, | |||||||||||
| MutableSpan<T> dst_data, | |||||||||||
| const IndexRange dst_range, | |||||||||||
| const bke::curves::CurvePoint sample_point) | |||||||||||
| { | |||||||||||
| BLI_assert(dst_range.size() == 1); | |||||||||||
| if (sample_point.is_controlpoint()) { | |||||||||||
| /* Resolves cases where the source curve consist of a single control point. */ | |||||||||||
| const int index = sample_point.parameter == 1.0 ? sample_point.next_index : sample_point.index; | |||||||||||
| dst_data[dst_range.first()] = src_data[index]; | |||||||||||
| } | |||||||||||
| else { | |||||||||||
| dst_data[dst_range.first()] = attribute_math::mix2( | |||||||||||
| sample_point.parameter, src_data[sample_point.index], src_data[sample_point.next_index]); | |||||||||||
| } | |||||||||||
| } | |||||||||||
| template<typename T> | |||||||||||
| static void sample_catmull_rom(const Span<T> src_data, | |||||||||||
| MutableSpan<T> dst_data, | |||||||||||
| const IndexRange dst_range, | |||||||||||
| const bke::curves::CurvePoint sample_point, | |||||||||||
| const bool src_cyclic) | |||||||||||
| { | |||||||||||
| BLI_assert(dst_range.size() == 1); | |||||||||||
| if (sample_point.is_controlpoint()) { | |||||||||||
| /* Resolves cases where the source curve consist of a single control point. */ | |||||||||||
| const int index = sample_point.parameter == 1.0 ? sample_point.next_index : sample_point.index; | |||||||||||
| dst_data[dst_range.first()] = src_data[index]; | |||||||||||
| } | |||||||||||
| else { | |||||||||||
| dst_data[dst_range.first()] = interpolate_catmull_rom(src_data, sample_point, src_cyclic); | |||||||||||
| } | |||||||||||
| } | |||||||||||
| static void sample_bezier(const Span<float3> src_positions, | |||||||||||
| const Span<float3> src_handles_l, | |||||||||||
| const Span<float3> src_handles_r, | |||||||||||
| const Span<int8_t> src_types_l, | |||||||||||
| const Span<int8_t> src_types_r, | |||||||||||
| MutableSpan<float3> dst_positions, | |||||||||||
| MutableSpan<float3> dst_handles_l, | |||||||||||
| MutableSpan<float3> dst_handles_r, | |||||||||||
| MutableSpan<int8_t> dst_types_l, | |||||||||||
| MutableSpan<int8_t> dst_types_r, | |||||||||||
| const IndexRange dst_range, | |||||||||||
| const bke::curves::CurvePoint sample_point) | |||||||||||
| { | |||||||||||
| BLI_assert(dst_range.size() == 1); | |||||||||||
| if (sample_point.is_controlpoint()) { | |||||||||||
| /* Resolves cases where the source curve consist of a single control point. */ | |||||||||||
| const int index = sample_point.parameter == 1.0 ? sample_point.next_index : sample_point.index; | |||||||||||
| dst_positions[dst_range.first()] = src_positions[index]; | |||||||||||
| dst_handles_l[dst_range.first()] = src_handles_l[index]; | |||||||||||
| dst_handles_r[dst_range.first()] = src_handles_r[index]; | |||||||||||
| dst_types_l[dst_range.first()] = src_types_l[index]; | |||||||||||
| dst_types_r[dst_range.first()] = src_types_r[index]; | |||||||||||
| } | |||||||||||
| else { | |||||||||||
| bke::curves::bezier::Insertion insertion_point = knot_insert_bezier( | |||||||||||
| src_positions, src_handles_l, src_handles_r, sample_point); | |||||||||||
| dst_positions[dst_range.first()] = insertion_point.position; | |||||||||||
| dst_handles_l[dst_range.first()] = insertion_point.left_handle; | |||||||||||
| dst_handles_r[dst_range.first()] = insertion_point.right_handle; | |||||||||||
| dst_types_l[dst_range.first()] = BEZIER_HANDLE_FREE; | |||||||||||
| dst_types_r[dst_range.first()] = BEZIER_HANDLE_FREE; | |||||||||||
| } | |||||||||||
| } | |||||||||||
| /** \} */ | |||||||||||
| /* -------------------------------------------------------------------- */ | |||||||||||
| /** \name Sample Curve Interval (Trim) | /** \name Sample Curve Interval (Trim) | ||||||||||
| * \{ */ | * \{ */ | ||||||||||
| /** | /** | ||||||||||
| * Sample source curve data in the interval defined by the points [start_point, end_point]. | * Sample source curve data in the interval defined by the points [start_point, end_point]. | ||||||||||
| * Uses linear interpolation to compute the endpoints. | * Uses linear interpolation to compute the endpoints. | ||||||||||
| * | * | ||||||||||
| * \tparam include_start_point If False, the 'start_point' point sample will not be copied | * \tparam include_start_point If False, the 'start_point' point sample will not be copied | ||||||||||
| * and not accounted for in the destination range. | * and not accounted for in the destination range. | ||||||||||
| * \param src_data: Source to sample from. | * \param src_data: Source to sample from. | ||||||||||
| * \param dst_data: Destination to write samples to. | * \param dst_data: Destination to write samples to. | ||||||||||
| * \param src_range: Interval within [start_point, end_point] to copy from the source point domain. | * \param src_range: Interval within [start_point, end_point] to copy from the source point domain. | ||||||||||
| * \param dst_range: Interval to copy point data to in the destination buffer. | * \param dst_range: Interval to copy point data to in the destination buffer. | ||||||||||
| * \param start_point: Point on the source curve to start sampling from. | * \param start_point: Point on the source curve to start sampling from. | ||||||||||
| * \param end_point: Last point to sample in the source curve. | * \param end_point: Last point to sample in the source curve. | ||||||||||
| */ | */ | ||||||||||
| template<typename T, bool include_start_point = true> | template<typename T, bool include_start_point = true> | ||||||||||
| static void sample_interval_linear(const Span<T> src_data, | static void sample_interval_linear(const Span<T> src_data, | ||||||||||
| MutableSpan<T> dst_data, | MutableSpan<T> dst_data, | ||||||||||
| const bke::curves::IndexRangeCyclic src_range, | bke::curves::IndexRangeCyclic src_range, | ||||||||||
| const IndexRange dst_range, | const IndexRange dst_range, | ||||||||||
| const bke::curves::CurvePoint start_point, | const bke::curves::CurvePoint start_point, | ||||||||||
| const bke::curves::CurvePoint end_point) | const bke::curves::CurvePoint end_point) | ||||||||||
| { | { | ||||||||||
| int64_t src_index = src_range.first(); | |||||||||||
| int64_t dst_index = dst_range.first(); | int64_t dst_index = dst_range.first(); | ||||||||||
| if (start_point.is_controlpoint()) { | if (start_point.is_controlpoint()) { | ||||||||||
| /* 'start_point' is included in the copy iteration. */ | /* 'start_point' is included in the copy iteration. */ | ||||||||||
| if constexpr (!include_start_point) { | if constexpr (!include_start_point) { | ||||||||||
| /* Skip first. */ | /* Skip first. */ | ||||||||||
| ++src_index; | src_range = src_range.drop_front(); | ||||||||||
| } | } | ||||||||||
| } | } | ||||||||||
| else if constexpr (!include_start_point) { | else if constexpr (!include_start_point) { | ||||||||||
| /* Do nothing (excluded). */ | /* Do nothing (excluded). */ | ||||||||||
| } | } | ||||||||||
| else { | else { | ||||||||||
| /* General case, sample 'start_point' */ | /* General case, sample 'start_point' */ | ||||||||||
| dst_data[dst_index] = attribute_math::mix2( | dst_data[dst_index] = attribute_math::mix2( | ||||||||||
| start_point.parameter, src_data[start_point.index], src_data[start_point.next_index]); | start_point.parameter, src_data[start_point.index], src_data[start_point.next_index]); | ||||||||||
| ++dst_index; | ++dst_index; | ||||||||||
| } | } | ||||||||||
| dst_index = copy_point_data_between_endpoints( | dst_index = copy_point_data_between_endpoints(src_data, dst_data, src_range, dst_index); | ||||||||||
| src_data, dst_data, src_range, src_index, dst_index); | if (dst_range.size() == 1) { | ||||||||||
| BLI_assert(dst_index == dst_range.one_after_last()); | |||||||||||
| return; | |||||||||||
| } | |||||||||||
| /* Handle last case */ | /* Handle last case */ | ||||||||||
| if (end_point.is_controlpoint()) { | if (end_point.is_controlpoint()) { | ||||||||||
| /* 'end_point' is included in the copy iteration. */ | /* 'end_point' is included in the copy iteration. */ | ||||||||||
| } | } | ||||||||||
| else { | else { | ||||||||||
| dst_data[dst_index] = attribute_math::mix2( | dst_data[dst_index] = attribute_math::mix2( | ||||||||||
| end_point.parameter, src_data[end_point.index], src_data[end_point.next_index]); | end_point.parameter, src_data[end_point.index], src_data[end_point.next_index]); | ||||||||||
| #ifdef DEBUG | #ifdef DEBUG | ||||||||||
| ++dst_index; | ++dst_index; | ||||||||||
| #endif | #endif | ||||||||||
| } | } | ||||||||||
| BLI_assert(dst_index == dst_range.one_after_last()); | BLI_assert(dst_index == dst_range.one_after_last()); | ||||||||||
| } | } | ||||||||||
| template<typename T, bool include_start_point = true> | template<typename T> | ||||||||||
| static void sample_interval_catmull_rom(const Span<T> src_data, | static void sample_interval_catmull_rom(const Span<T> src_data, | ||||||||||
| MutableSpan<T> dst_data, | MutableSpan<T> dst_data, | ||||||||||
| const bke::curves::IndexRangeCyclic src_range, | bke::curves::IndexRangeCyclic src_range, | ||||||||||
| const IndexRange dst_range, | const IndexRange dst_range, | ||||||||||
| const bke::curves::CurvePoint start_point, | const bke::curves::CurvePoint start_point, | ||||||||||
| const bke::curves::CurvePoint end_point, | const bke::curves::CurvePoint end_point, | ||||||||||
| const bool src_cyclic) | const bool src_cyclic) | ||||||||||
| { | { | ||||||||||
| int64_t src_index = src_range.first(); | |||||||||||
| int64_t dst_index = dst_range.first(); | int64_t dst_index = dst_range.first(); | ||||||||||
| if (start_point.is_controlpoint()) { | if (start_point.is_controlpoint()) { | ||||||||||
| /* 'start_point' is included in the copy iteration. */ | |||||||||||
| if constexpr (!include_start_point) { | |||||||||||
| /* Skip first. */ | |||||||||||
| ++src_index; | |||||||||||
| } | |||||||||||
| } | |||||||||||
| else if constexpr (!include_start_point) { | |||||||||||
| /* Do nothing (excluded). */ | |||||||||||
| } | } | ||||||||||
| else { | else { | ||||||||||
| /* General case, sample 'start_point' */ | /* General case, sample 'start_point' */ | ||||||||||
| dst_data[dst_index] = interpolate_catmull_rom(src_data, start_point, src_cyclic); | dst_data[dst_index] = interpolate_catmull_rom(src_data, start_point, src_cyclic); | ||||||||||
| ++dst_index; | ++dst_index; | ||||||||||
| } | } | ||||||||||
| dst_index = copy_point_data_between_endpoints( | dst_index = copy_point_data_between_endpoints(src_data, dst_data, src_range, dst_index); | ||||||||||
| src_data, dst_data, src_range, src_index, dst_index); | if (dst_range.size() == 1) { | ||||||||||
| BLI_assert(dst_index == dst_range.one_after_last()); | |||||||||||
| return; | |||||||||||
| } | |||||||||||
| /* Handle last case */ | /* Handle last case */ | ||||||||||
| if (end_point.is_controlpoint()) { | if (end_point.is_controlpoint()) { | ||||||||||
| /* 'end_point' is included in the copy iteration. */ | /* 'end_point' is included in the copy iteration. */ | ||||||||||
| } | } | ||||||||||
| else { | else { | ||||||||||
| dst_data[dst_index] = interpolate_catmull_rom(src_data, end_point, src_cyclic); | dst_data[dst_index] = interpolate_catmull_rom(src_data, end_point, src_cyclic); | ||||||||||
| #ifdef DEBUG | #ifdef DEBUG | ||||||||||
| Show All 9 Lines | static void sample_interval_bezier(const Span<float3> src_positions, | ||||||||||
| const Span<float3> src_handles_r, | const Span<float3> src_handles_r, | ||||||||||
| const Span<int8_t> src_types_l, | const Span<int8_t> src_types_l, | ||||||||||
| const Span<int8_t> src_types_r, | const Span<int8_t> src_types_r, | ||||||||||
| MutableSpan<float3> dst_positions, | MutableSpan<float3> dst_positions, | ||||||||||
| MutableSpan<float3> dst_handles_l, | MutableSpan<float3> dst_handles_l, | ||||||||||
| MutableSpan<float3> dst_handles_r, | MutableSpan<float3> dst_handles_r, | ||||||||||
| MutableSpan<int8_t> dst_types_l, | MutableSpan<int8_t> dst_types_l, | ||||||||||
| MutableSpan<int8_t> dst_types_r, | MutableSpan<int8_t> dst_types_r, | ||||||||||
| const bke::curves::IndexRangeCyclic src_range, | bke::curves::IndexRangeCyclic src_range, | ||||||||||
| const IndexRange dst_range, | const IndexRange dst_range, | ||||||||||
| const bke::curves::CurvePoint start_point, | const bke::curves::CurvePoint start_point, | ||||||||||
| const bke::curves::CurvePoint end_point) | const bke::curves::CurvePoint end_point) | ||||||||||
| { | { | ||||||||||
| bke::curves::bezier::Insertion start_point_insert; | bke::curves::bezier::Insertion start_point_insert; | ||||||||||
| int64_t src_index = src_range.first(); | |||||||||||
| int64_t dst_index = dst_range.first(); | int64_t dst_index = dst_range.first(); | ||||||||||
| bool start_point_trimmed = false; | bool start_point_trimmed = false; | ||||||||||
| if (start_point.is_controlpoint()) { | if (start_point.is_controlpoint()) { | ||||||||||
| /* The 'start_point' control point is included in the copy iteration. */ | /* The 'start_point' control point is included in the copy iteration. */ | ||||||||||
| if constexpr (!include_start_point) { | if constexpr (!include_start_point) { | ||||||||||
| ++src_index; /* Skip first! */ | src_range = src_range.drop_front(); | ||||||||||
| } | } | ||||||||||
| } | } | ||||||||||
| else if constexpr (!include_start_point) { | else if constexpr (!include_start_point) { | ||||||||||
| /* Do nothing, 'start_point' is excluded. */ | /* Do nothing, 'start_point' is excluded. */ | ||||||||||
| } | } | ||||||||||
| else { | else { | ||||||||||
| /* General case, sample 'start_point'. */ | /* General case, sample 'start_point'. */ | ||||||||||
| start_point_insert = knot_insert_bezier( | start_point_insert = knot_insert_bezier( | ||||||||||
| src_positions, src_handles_l, src_handles_r, start_point); | src_positions, src_handles_l, src_handles_r, start_point); | ||||||||||
| dst_positions[dst_range.first()] = start_point_insert.position; | dst_positions[dst_range.first()] = start_point_insert.position; | ||||||||||
| dst_handles_l[dst_range.first()] = start_point_insert.left_handle; | dst_handles_l[dst_range.first()] = start_point_insert.left_handle; | ||||||||||
| dst_handles_r[dst_range.first()] = start_point_insert.right_handle; | dst_handles_r[dst_range.first()] = start_point_insert.right_handle; | ||||||||||
| dst_types_l[dst_range.first()] = src_types_l[start_point.index]; | dst_types_l[dst_range.first()] = src_types_l[start_point.index]; | ||||||||||
| dst_types_r[dst_range.first()] = src_types_r[start_point.index]; | dst_types_r[dst_range.first()] = src_types_r[start_point.index]; | ||||||||||
| start_point_trimmed = true; | start_point_trimmed = true; | ||||||||||
| ++dst_index; | ++dst_index; | ||||||||||
| } | } | ||||||||||
| /* Copy point data between the 'start_point' and 'end_point'. */ | /* Copy point data between the 'start_point' and 'end_point'. */ | ||||||||||
| int64_t increment = src_range.cycles() ? src_range.size_before_loop() : | int64_t increment = src_range.cycles() ? src_range.size_before_loop() : | ||||||||||
| src_range.one_after_last() - src_range.first(); | src_range.one_after_last() - src_range.first(); | ||||||||||
| const IndexRange dst_range_to_end(dst_index, increment); | const IndexRange dst_range_to_end(dst_index, increment); | ||||||||||
| const IndexRange src_range_to_end(src_index, increment); | const IndexRange src_range_to_end(src_range.first(), increment); | ||||||||||
| dst_positions.slice(dst_range_to_end).copy_from(src_positions.slice(src_range_to_end)); | dst_positions.slice(dst_range_to_end).copy_from(src_positions.slice(src_range_to_end)); | ||||||||||
| dst_handles_l.slice(dst_range_to_end).copy_from(src_handles_l.slice(src_range_to_end)); | dst_handles_l.slice(dst_range_to_end).copy_from(src_handles_l.slice(src_range_to_end)); | ||||||||||
| dst_handles_r.slice(dst_range_to_end).copy_from(src_handles_r.slice(src_range_to_end)); | dst_handles_r.slice(dst_range_to_end).copy_from(src_handles_r.slice(src_range_to_end)); | ||||||||||
| dst_types_l.slice(dst_range_to_end).copy_from(src_types_l.slice(src_range_to_end)); | dst_types_l.slice(dst_range_to_end).copy_from(src_types_l.slice(src_range_to_end)); | ||||||||||
| dst_types_r.slice(dst_range_to_end).copy_from(src_types_r.slice(src_range_to_end)); | dst_types_r.slice(dst_range_to_end).copy_from(src_types_r.slice(src_range_to_end)); | ||||||||||
| dst_index += increment; | dst_index += increment; | ||||||||||
| if (dst_range.size() == 1) { | |||||||||||
| BLI_assert(dst_index == dst_range.one_after_last()); | |||||||||||
| return; | |||||||||||
| } | |||||||||||
| increment = src_range.size_after_loop(); | increment = src_range.size_after_loop(); | ||||||||||
| if (src_range.cycles() && increment > 0) { | if (src_range.cycles() && increment > 0) { | ||||||||||
| const IndexRange dst_range_looped(dst_index, increment); | const IndexRange dst_range_looped(dst_index, increment); | ||||||||||
| const IndexRange src_range_looped(src_range.curve_range().first(), increment); | const IndexRange src_range_looped(src_range.curve_range().first(), increment); | ||||||||||
| dst_positions.slice(dst_range_looped).copy_from(src_positions.slice(src_range_looped)); | dst_positions.slice(dst_range_looped).copy_from(src_positions.slice(src_range_looped)); | ||||||||||
| dst_handles_l.slice(dst_range_looped).copy_from(src_handles_l.slice(src_range_looped)); | dst_handles_l.slice(dst_range_looped).copy_from(src_handles_l.slice(src_range_looped)); | ||||||||||
| dst_handles_r.slice(dst_range_looped).copy_from(src_handles_r.slice(src_range_looped)); | dst_handles_r.slice(dst_range_looped).copy_from(src_handles_r.slice(src_range_looped)); | ||||||||||
| dst_types_l.slice(dst_range_looped).copy_from(src_types_l.slice(src_range_looped)); | dst_types_l.slice(dst_range_looped).copy_from(src_types_l.slice(src_range_looped)); | ||||||||||
| ▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | |||||||||||
| #endif // DEBUG | #endif // DEBUG | ||||||||||
| } | } | ||||||||||
| BLI_assert(dst_index == dst_range.one_after_last()); | BLI_assert(dst_index == dst_range.one_after_last()); | ||||||||||
| } | } | ||||||||||
| /** \} */ | /** \} */ | ||||||||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||||||||
| /** \name Convert to Point Curves | |||||||||||
| * \{ */ | |||||||||||
| static void convert_point_polygonal_curves( | |||||||||||
| const bke::CurvesGeometry &src_curves, | |||||||||||
| bke::CurvesGeometry &dst_curves, | |||||||||||
| const IndexMask selection, | |||||||||||
| const Span<bke::curves::CurvePoint> sample_points, | |||||||||||
| MutableSpan<bke::AttributeTransferData> transfer_attributes) | |||||||||||
| { | |||||||||||
| const Span<float3> src_positions = src_curves.positions(); | |||||||||||
| MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); | |||||||||||
| threading::parallel_for(selection.index_range(), 4096, [&](const IndexRange range) { | |||||||||||
| for (const int64_t curve_i : selection.slice(range)) { | |||||||||||
| const IndexRange src_points = src_curves.points_for_curve(curve_i); | |||||||||||
| const IndexRange dst_points = dst_curves.points_for_curve(curve_i); | |||||||||||
| sample_linear<float3>( | |||||||||||
| src_positions.slice(src_points), dst_positions, dst_points, sample_points[curve_i]); | |||||||||||
| for (bke::AttributeTransferData &attribute : transfer_attributes) { | |||||||||||
| attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) { | |||||||||||
| using T = decltype(dummy); | |||||||||||
| sample_linear<T>(attribute.src.template typed<T>().slice(src_points), | |||||||||||
| attribute.dst.span.typed<T>(), | |||||||||||
| dst_curves.points_for_curve(curve_i), | |||||||||||
| sample_points[curve_i]); | |||||||||||
| }); | |||||||||||
| } | |||||||||||
| } | |||||||||||
| }); | |||||||||||
| fill_bezier_data(dst_curves, selection); | |||||||||||
| fill_nurbs_data(dst_curves, selection); | |||||||||||
| } | |||||||||||
| static void convert_point_catmull_curves( | |||||||||||
| const bke::CurvesGeometry &src_curves, | |||||||||||
| bke::CurvesGeometry &dst_curves, | |||||||||||
| const IndexMask selection, | |||||||||||
| const Span<bke::curves::CurvePoint> sample_points, | |||||||||||
| MutableSpan<bke::AttributeTransferData> transfer_attributes) | |||||||||||
| { | |||||||||||
| const Span<float3> src_positions = src_curves.positions(); | |||||||||||
| const VArray<bool> src_cyclic = src_curves.cyclic(); | |||||||||||
| MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); | |||||||||||
| threading::parallel_for(selection.index_range(), 4096, [&](const IndexRange range) { | |||||||||||
| for (const int64_t curve_i : selection.slice(range)) { | |||||||||||
| const IndexRange src_points = src_curves.points_for_curve(curve_i); | |||||||||||
| const IndexRange dst_points = dst_curves.points_for_curve(curve_i); | |||||||||||
| sample_catmull_rom<float3>(src_positions.slice(src_points), | |||||||||||
| dst_positions, | |||||||||||
| dst_points, | |||||||||||
| sample_points[curve_i], | |||||||||||
| src_cyclic[curve_i]); | |||||||||||
| for (bke::AttributeTransferData &attribute : transfer_attributes) { | |||||||||||
| attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) { | |||||||||||
| using T = decltype(dummy); | |||||||||||
| sample_catmull_rom<T>(attribute.src.template typed<T>().slice(src_points), | |||||||||||
| attribute.dst.span.typed<T>(), | |||||||||||
| dst_points, | |||||||||||
| sample_points[curve_i], | |||||||||||
| src_cyclic[curve_i]); | |||||||||||
| }); | |||||||||||
| } | |||||||||||
| } | |||||||||||
| }); | |||||||||||
| fill_bezier_data(dst_curves, selection); | |||||||||||
| fill_nurbs_data(dst_curves, selection); | |||||||||||
| } | |||||||||||
| static void convert_point_bezier_curves( | |||||||||||
| const bke::CurvesGeometry &src_curves, | |||||||||||
| bke::CurvesGeometry &dst_curves, | |||||||||||
| const IndexMask selection, | |||||||||||
| const Span<bke::curves::CurvePoint> sample_points, | |||||||||||
| MutableSpan<bke::AttributeTransferData> transfer_attributes) | |||||||||||
| { | |||||||||||
| const Span<float3> src_positions = src_curves.positions(); | |||||||||||
| const VArraySpan<int8_t> src_types_l{src_curves.handle_types_left()}; | |||||||||||
| const VArraySpan<int8_t> src_types_r{src_curves.handle_types_right()}; | |||||||||||
| const Span<float3> src_handles_l = src_curves.handle_positions_left(); | |||||||||||
| const Span<float3> src_handles_r = src_curves.handle_positions_right(); | |||||||||||
| MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); | |||||||||||
| MutableSpan<int8_t> dst_types_l = dst_curves.handle_types_left_for_write(); | |||||||||||
| MutableSpan<int8_t> dst_types_r = dst_curves.handle_types_right_for_write(); | |||||||||||
| MutableSpan<float3> dst_handles_l = dst_curves.handle_positions_left_for_write(); | |||||||||||
| MutableSpan<float3> dst_handles_r = dst_curves.handle_positions_right_for_write(); | |||||||||||
| threading::parallel_for(selection.index_range(), 4096, [&](const IndexRange range) { | |||||||||||
| for (const int64_t curve_i : selection.slice(range)) { | |||||||||||
| const IndexRange src_points = src_curves.points_for_curve(curve_i); | |||||||||||
| const IndexRange dst_points = dst_curves.points_for_curve(curve_i); | |||||||||||
| sample_bezier(src_positions.slice(src_points), | |||||||||||
| src_handles_l.slice(src_points), | |||||||||||
| src_handles_r.slice(src_points), | |||||||||||
| src_types_l.slice(src_points), | |||||||||||
| src_types_r.slice(src_points), | |||||||||||
| dst_positions, | |||||||||||
| dst_handles_l, | |||||||||||
| dst_handles_r, | |||||||||||
| dst_types_l, | |||||||||||
| dst_types_r, | |||||||||||
| dst_points, | |||||||||||
| sample_points[curve_i]); | |||||||||||
| for (bke::AttributeTransferData &attribute : transfer_attributes) { | |||||||||||
| attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) { | |||||||||||
| using T = decltype(dummy); | |||||||||||
| sample_linear<T>(attribute.src.template typed<T>().slice(src_points), | |||||||||||
| attribute.dst.span.typed<T>(), | |||||||||||
| dst_points, | |||||||||||
| sample_points[curve_i]); | |||||||||||
| }); | |||||||||||
| } | |||||||||||
| } | |||||||||||
| }); | |||||||||||
| fill_nurbs_data(dst_curves, selection); | |||||||||||
| } | |||||||||||
| static void convert_point_evaluated_curves( | |||||||||||
| const bke::CurvesGeometry &src_curves, | |||||||||||
| bke::CurvesGeometry &dst_curves, | |||||||||||
| const IndexMask selection, | |||||||||||
| const Span<bke::curves::CurvePoint> evaluated_sample_points, | |||||||||||
| MutableSpan<bke::AttributeTransferData> transfer_attributes) | |||||||||||
| { | |||||||||||
| const Span<float3> src_eval_positions = src_curves.evaluated_positions(); | |||||||||||
| MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); | |||||||||||
| threading::parallel_for(selection.index_range(), 4096, [&](const IndexRange range) { | |||||||||||
| for (const int64_t curve_i : selection.slice(range)) { | |||||||||||
| const IndexRange dst_points = dst_curves.points_for_curve(curve_i); | |||||||||||
| const IndexRange src_evaluated_points = src_curves.evaluated_points_for_curve(curve_i); | |||||||||||
| sample_linear<float3>(src_eval_positions.slice(src_evaluated_points), | |||||||||||
| dst_positions, | |||||||||||
| dst_points, | |||||||||||
| evaluated_sample_points[curve_i]); | |||||||||||
| for (bke::AttributeTransferData &attribute : transfer_attributes) { | |||||||||||
| attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) { | |||||||||||
| using T = decltype(dummy); | |||||||||||
| GArray evaluated_data(CPPType::get<T>(), src_evaluated_points.size()); | |||||||||||
| GMutableSpan evaluated_span = evaluated_data.as_mutable_span(); | |||||||||||
| src_curves.interpolate_to_evaluated( | |||||||||||
| curve_i, attribute.src.slice(src_curves.points_for_curve(curve_i)), evaluated_span); | |||||||||||
| sample_linear<T>(evaluated_span.typed<T>(), | |||||||||||
| attribute.dst.span.typed<T>(), | |||||||||||
| dst_points, | |||||||||||
| evaluated_sample_points[curve_i]); | |||||||||||
| }); | |||||||||||
| } | |||||||||||
| } | |||||||||||
| }); | |||||||||||
| fill_bezier_data(dst_curves, selection); | |||||||||||
| fill_nurbs_data(dst_curves, selection); | |||||||||||
| } | |||||||||||
| /** \} */ | |||||||||||
| /* -------------------------------------------------------------------- */ | |||||||||||
| /** \name Trim Curves | /** \name Trim Curves | ||||||||||
| * \{ */ | * \{ */ | ||||||||||
| static void trim_attribute_linear(const bke::CurvesGeometry &src_curves, | static void trim_attribute_linear(const bke::CurvesGeometry &src_curves, | ||||||||||
| bke::CurvesGeometry &dst_curves, | bke::CurvesGeometry &dst_curves, | ||||||||||
| const IndexMask selection, | const IndexMask selection, | ||||||||||
| const Span<bke::curves::CurvePoint> start_points, | const Span<bke::curves::CurvePoint> start_points, | ||||||||||
| const Span<bke::curves::CurvePoint> end_points, | const Span<bke::curves::CurvePoint> end_points, | ||||||||||
| const Span<bke::curves::IndexRangeCyclic> src_ranges, | |||||||||||
| MutableSpan<bke::AttributeTransferData> transfer_attributes) | MutableSpan<bke::AttributeTransferData> transfer_attributes) | ||||||||||
| { | { | ||||||||||
| for (bke::AttributeTransferData &attribute : transfer_attributes) { | for (bke::AttributeTransferData &attribute : transfer_attributes) { | ||||||||||
| attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) { | attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) { | ||||||||||
| using T = decltype(dummy); | using T = decltype(dummy); | ||||||||||
| threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { | threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { | ||||||||||
| for (const int64_t curve_i : selection.slice(range)) { | for (const int64_t curve_i : selection.slice(range)) { | ||||||||||
| const IndexRange src_points = src_curves.points_for_curve(curve_i); | const IndexRange src_points = src_curves.points_for_curve(curve_i); | ||||||||||
| bke::curves::IndexRangeCyclic src_sample_range = get_range_between_endpoints( | |||||||||||
| start_points[curve_i], end_points[curve_i], {0, src_points.size()}); | |||||||||||
| sample_interval_linear<T>(attribute.src.template typed<T>().slice(src_points), | sample_interval_linear<T>(attribute.src.template typed<T>().slice(src_points), | ||||||||||
| attribute.dst.span.typed<T>(), | attribute.dst.span.typed<T>(), | ||||||||||
| src_sample_range, | src_ranges[curve_i], | ||||||||||
| dst_curves.points_for_curve(curve_i), | dst_curves.points_for_curve(curve_i), | ||||||||||
| start_points[curve_i], | start_points[curve_i], | ||||||||||
| end_points[curve_i]); | end_points[curve_i]); | ||||||||||
| } | } | ||||||||||
| }); | }); | ||||||||||
| }); | }); | ||||||||||
| } | } | ||||||||||
| } | } | ||||||||||
| static void trim_polygonal_curves(const bke::CurvesGeometry &src_curves, | static void trim_polygonal_curves(const bke::CurvesGeometry &src_curves, | ||||||||||
| bke::CurvesGeometry &dst_curves, | bke::CurvesGeometry &dst_curves, | ||||||||||
| const IndexMask selection, | const IndexMask selection, | ||||||||||
| const Span<bke::curves::CurvePoint> start_points, | const Span<bke::curves::CurvePoint> start_points, | ||||||||||
| const Span<bke::curves::CurvePoint> end_points, | const Span<bke::curves::CurvePoint> end_points, | ||||||||||
| const Span<bke::curves::IndexRangeCyclic> src_ranges, | |||||||||||
| MutableSpan<bke::AttributeTransferData> transfer_attributes) | MutableSpan<bke::AttributeTransferData> transfer_attributes) | ||||||||||
| { | { | ||||||||||
| const Span<float3> src_positions = src_curves.positions(); | const Span<float3> src_positions = src_curves.positions(); | ||||||||||
| MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); | MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); | ||||||||||
| threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { | threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { | ||||||||||
| for (const int64_t curve_i : selection.slice(range)) { | for (const int64_t curve_i : selection.slice(range)) { | ||||||||||
| const IndexRange src_points = src_curves.points_for_curve(curve_i); | const IndexRange src_points = src_curves.points_for_curve(curve_i); | ||||||||||
| const IndexRange dst_points = dst_curves.points_for_curve(curve_i); | const IndexRange dst_points = dst_curves.points_for_curve(curve_i); | ||||||||||
| bke::curves::IndexRangeCyclic src_sample_range = get_range_between_endpoints( | |||||||||||
| start_points[curve_i], end_points[curve_i], {0, src_points.size()}); | |||||||||||
| sample_interval_linear<float3>(src_positions.slice(src_points), | sample_interval_linear<float3>(src_positions.slice(src_points), | ||||||||||
| dst_positions, | dst_positions, | ||||||||||
| src_sample_range, | src_ranges[curve_i], | ||||||||||
| dst_points, | dst_points, | ||||||||||
| start_points[curve_i], | start_points[curve_i], | ||||||||||
| end_points[curve_i]); | end_points[curve_i]); | ||||||||||
| } | } | ||||||||||
| }); | }); | ||||||||||
| fill_bezier_data(dst_curves, selection); | fill_bezier_data(dst_curves, selection); | ||||||||||
| fill_nurbs_data(dst_curves, selection); | fill_nurbs_data(dst_curves, selection); | ||||||||||
| trim_attribute_linear( | trim_attribute_linear(src_curves, | ||||||||||
| src_curves, dst_curves, selection, start_points, end_points, transfer_attributes); | dst_curves, | ||||||||||
| selection, | |||||||||||
| start_points, | |||||||||||
| end_points, | |||||||||||
| src_ranges, | |||||||||||
| transfer_attributes); | |||||||||||
| } | } | ||||||||||
| static void trim_catmull_rom_curves(const bke::CurvesGeometry &src_curves, | static void trim_catmull_rom_curves(const bke::CurvesGeometry &src_curves, | ||||||||||
| bke::CurvesGeometry &dst_curves, | bke::CurvesGeometry &dst_curves, | ||||||||||
| const IndexMask selection, | const IndexMask selection, | ||||||||||
| const Span<bke::curves::CurvePoint> start_points, | const Span<bke::curves::CurvePoint> start_points, | ||||||||||
| const Span<bke::curves::CurvePoint> end_points, | const Span<bke::curves::CurvePoint> end_points, | ||||||||||
| const Span<bke::curves::IndexRangeCyclic> src_ranges, | |||||||||||
| MutableSpan<bke::AttributeTransferData> transfer_attributes) | MutableSpan<bke::AttributeTransferData> transfer_attributes) | ||||||||||
| { | { | ||||||||||
| const Span<float3> src_positions = src_curves.positions(); | const Span<float3> src_positions = src_curves.positions(); | ||||||||||
| const VArray<bool> src_cyclic = src_curves.cyclic(); | const VArray<bool> src_cyclic = src_curves.cyclic(); | ||||||||||
| MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); | MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); | ||||||||||
| threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { | threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { | ||||||||||
| for (const int64_t curve_i : selection.slice(range)) { | for (const int64_t curve_i : selection.slice(range)) { | ||||||||||
| const IndexRange src_points = src_curves.points_for_curve(curve_i); | const IndexRange src_points = src_curves.points_for_curve(curve_i); | ||||||||||
| const IndexRange dst_points = dst_curves.points_for_curve(curve_i); | const IndexRange dst_points = dst_curves.points_for_curve(curve_i); | ||||||||||
| bke::curves::IndexRangeCyclic src_sample_range = get_range_between_endpoints( | |||||||||||
| start_points[curve_i], end_points[curve_i], {0, src_points.size()}); | |||||||||||
| sample_interval_catmull_rom<float3>(src_positions.slice(src_points), | sample_interval_catmull_rom<float3>(src_positions.slice(src_points), | ||||||||||
| dst_positions, | dst_positions, | ||||||||||
| src_sample_range, | src_ranges[curve_i], | ||||||||||
| dst_points, | dst_points, | ||||||||||
| start_points[curve_i], | start_points[curve_i], | ||||||||||
| end_points[curve_i], | end_points[curve_i], | ||||||||||
| src_cyclic[curve_i]); | src_cyclic[curve_i]); | ||||||||||
| } | } | ||||||||||
| }); | }); | ||||||||||
| fill_bezier_data(dst_curves, selection); | fill_bezier_data(dst_curves, selection); | ||||||||||
| fill_nurbs_data(dst_curves, selection); | fill_nurbs_data(dst_curves, selection); | ||||||||||
| for (bke::AttributeTransferData &attribute : transfer_attributes) { | for (bke::AttributeTransferData &attribute : transfer_attributes) { | ||||||||||
| attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) { | attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) { | ||||||||||
| using T = decltype(dummy); | using T = decltype(dummy); | ||||||||||
| threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { | threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { | ||||||||||
| for (const int64_t curve_i : selection.slice(range)) { | for (const int64_t curve_i : selection.slice(range)) { | ||||||||||
| const IndexRange src_points = src_curves.points_for_curve(curve_i); | const IndexRange src_points = src_curves.points_for_curve(curve_i); | ||||||||||
| const IndexRange dst_points = dst_curves.points_for_curve(curve_i); | const IndexRange dst_points = dst_curves.points_for_curve(curve_i); | ||||||||||
| bke::curves::IndexRangeCyclic src_sample_range = get_range_between_endpoints( | |||||||||||
| start_points[curve_i], end_points[curve_i], {0, src_points.size()}); | |||||||||||
| sample_interval_catmull_rom<T>(attribute.src.template typed<T>().slice(src_points), | sample_interval_catmull_rom<T>(attribute.src.template typed<T>().slice(src_points), | ||||||||||
| attribute.dst.span.typed<T>(), | attribute.dst.span.typed<T>(), | ||||||||||
| src_sample_range, | src_ranges[curve_i], | ||||||||||
| dst_points, | dst_points, | ||||||||||
| start_points[curve_i], | start_points[curve_i], | ||||||||||
| end_points[curve_i], | end_points[curve_i], | ||||||||||
| src_cyclic[curve_i]); | src_cyclic[curve_i]); | ||||||||||
| } | } | ||||||||||
| }); | }); | ||||||||||
| }); | }); | ||||||||||
| } | } | ||||||||||
| } | } | ||||||||||
| static void trim_bezier_curves(const bke::CurvesGeometry &src_curves, | static void trim_bezier_curves(const bke::CurvesGeometry &src_curves, | ||||||||||
| bke::CurvesGeometry &dst_curves, | bke::CurvesGeometry &dst_curves, | ||||||||||
| const IndexMask selection, | const IndexMask selection, | ||||||||||
| const Span<bke::curves::CurvePoint> start_points, | const Span<bke::curves::CurvePoint> start_points, | ||||||||||
| const Span<bke::curves::CurvePoint> end_points, | const Span<bke::curves::CurvePoint> end_points, | ||||||||||
| const Span<bke::curves::IndexRangeCyclic> src_ranges, | |||||||||||
| MutableSpan<bke::AttributeTransferData> transfer_attributes) | MutableSpan<bke::AttributeTransferData> transfer_attributes) | ||||||||||
| { | { | ||||||||||
| const Span<float3> src_positions = src_curves.positions(); | const Span<float3> src_positions = src_curves.positions(); | ||||||||||
| const VArraySpan<int8_t> src_types_l{src_curves.handle_types_left()}; | const VArraySpan<int8_t> src_types_l{src_curves.handle_types_left()}; | ||||||||||
| const VArraySpan<int8_t> src_types_r{src_curves.handle_types_right()}; | const VArraySpan<int8_t> src_types_r{src_curves.handle_types_right()}; | ||||||||||
| const Span<float3> src_handles_l = src_curves.handle_positions_left(); | const Span<float3> src_handles_l = src_curves.handle_positions_left(); | ||||||||||
| const Span<float3> src_handles_r = src_curves.handle_positions_right(); | const Span<float3> src_handles_r = src_curves.handle_positions_right(); | ||||||||||
| MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); | MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); | ||||||||||
| MutableSpan<int8_t> dst_types_l = dst_curves.handle_types_left_for_write(); | MutableSpan<int8_t> dst_types_l = dst_curves.handle_types_left_for_write(); | ||||||||||
| MutableSpan<int8_t> dst_types_r = dst_curves.handle_types_right_for_write(); | MutableSpan<int8_t> dst_types_r = dst_curves.handle_types_right_for_write(); | ||||||||||
| MutableSpan<float3> dst_handles_l = dst_curves.handle_positions_left_for_write(); | MutableSpan<float3> dst_handles_l = dst_curves.handle_positions_left_for_write(); | ||||||||||
| MutableSpan<float3> dst_handles_r = dst_curves.handle_positions_right_for_write(); | MutableSpan<float3> dst_handles_r = dst_curves.handle_positions_right_for_write(); | ||||||||||
| threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { | threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { | ||||||||||
| for (const int64_t curve_i : selection.slice(range)) { | for (const int64_t curve_i : selection.slice(range)) { | ||||||||||
| const IndexRange src_points = src_curves.points_for_curve(curve_i); | const IndexRange src_points = src_curves.points_for_curve(curve_i); | ||||||||||
| const IndexRange dst_points = dst_curves.points_for_curve(curve_i); | const IndexRange dst_points = dst_curves.points_for_curve(curve_i); | ||||||||||
| bke::curves::IndexRangeCyclic src_sample_range = get_range_between_endpoints( | |||||||||||
| start_points[curve_i], end_points[curve_i], {0, src_points.size()}); | |||||||||||
| sample_interval_bezier(src_positions.slice(src_points), | sample_interval_bezier(src_positions.slice(src_points), | ||||||||||
| src_handles_l.slice(src_points), | src_handles_l.slice(src_points), | ||||||||||
| src_handles_r.slice(src_points), | src_handles_r.slice(src_points), | ||||||||||
| src_types_l.slice(src_points), | src_types_l.slice(src_points), | ||||||||||
| src_types_r.slice(src_points), | src_types_r.slice(src_points), | ||||||||||
| dst_positions, | dst_positions, | ||||||||||
| dst_handles_l, | dst_handles_l, | ||||||||||
| dst_handles_r, | dst_handles_r, | ||||||||||
| dst_types_l, | dst_types_l, | ||||||||||
| dst_types_r, | dst_types_r, | ||||||||||
| src_sample_range, | src_ranges[curve_i], | ||||||||||
| dst_points, | dst_points, | ||||||||||
| start_points[curve_i], | start_points[curve_i], | ||||||||||
| end_points[curve_i]); | end_points[curve_i]); | ||||||||||
| } | } | ||||||||||
| }); | }); | ||||||||||
| fill_nurbs_data(dst_curves, selection); | fill_nurbs_data(dst_curves, selection); | ||||||||||
| trim_attribute_linear( | trim_attribute_linear(src_curves, | ||||||||||
| src_curves, dst_curves, selection, start_points, end_points, transfer_attributes); | dst_curves, | ||||||||||
| selection, | |||||||||||
| start_points, | |||||||||||
| end_points, | |||||||||||
| src_ranges, | |||||||||||
| transfer_attributes); | |||||||||||
| } | } | ||||||||||
| static void trim_evaluated_curves(const bke::CurvesGeometry &src_curves, | static void trim_evaluated_curves(const bke::CurvesGeometry &src_curves, | ||||||||||
| bke::CurvesGeometry &dst_curves, | bke::CurvesGeometry &dst_curves, | ||||||||||
| const IndexMask selection, | const IndexMask selection, | ||||||||||
| const Span<bke::curves::CurvePoint> start_points, | const Span<bke::curves::CurvePoint> start_points, | ||||||||||
| const Span<bke::curves::CurvePoint> end_points, | const Span<bke::curves::CurvePoint> end_points, | ||||||||||
| const Span<bke::curves::IndexRangeCyclic> src_ranges, | |||||||||||
| MutableSpan<bke::AttributeTransferData> transfer_attributes) | MutableSpan<bke::AttributeTransferData> transfer_attributes) | ||||||||||
| { | { | ||||||||||
| const Span<float3> src_eval_positions = src_curves.evaluated_positions(); | const Span<float3> src_eval_positions = src_curves.evaluated_positions(); | ||||||||||
| MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); | MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); | ||||||||||
| threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { | threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { | ||||||||||
| for (const int64_t curve_i : selection.slice(range)) { | for (const int64_t curve_i : selection.slice(range)) { | ||||||||||
| const IndexRange dst_points = dst_curves.points_for_curve(curve_i); | const IndexRange dst_points = dst_curves.points_for_curve(curve_i); | ||||||||||
| const IndexRange src_evaluated_points = src_curves.evaluated_points_for_curve(curve_i); | const IndexRange src_evaluated_points = src_curves.evaluated_points_for_curve(curve_i); | ||||||||||
| bke::curves::IndexRangeCyclic src_sample_range = get_range_between_endpoints( | |||||||||||
| start_points[curve_i], end_points[curve_i], {0, src_evaluated_points.size()}); | |||||||||||
| sample_interval_linear<float3>(src_eval_positions.slice(src_evaluated_points), | sample_interval_linear<float3>(src_eval_positions.slice(src_evaluated_points), | ||||||||||
| dst_positions, | dst_positions, | ||||||||||
| src_sample_range, | src_ranges[curve_i], | ||||||||||
| dst_points, | dst_points, | ||||||||||
| start_points[curve_i], | start_points[curve_i], | ||||||||||
| end_points[curve_i]); | end_points[curve_i]); | ||||||||||
| } | } | ||||||||||
| }); | }); | ||||||||||
| fill_bezier_data(dst_curves, selection); | fill_bezier_data(dst_curves, selection); | ||||||||||
| fill_nurbs_data(dst_curves, selection); | fill_nurbs_data(dst_curves, selection); | ||||||||||
| for (bke::AttributeTransferData &attribute : transfer_attributes) { | for (bke::AttributeTransferData &attribute : transfer_attributes) { | ||||||||||
| attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) { | attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) { | ||||||||||
| using T = decltype(dummy); | using T = decltype(dummy); | ||||||||||
| threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { | threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { | ||||||||||
| for (const int64_t curve_i : selection.slice(range)) { | for (const int64_t curve_i : selection.slice(range)) { | ||||||||||
| /* Interpolate onto the evaluated point domain and sample the evaluated domain. */ | /* Interpolate onto the evaluated point domain and sample the evaluated domain. */ | ||||||||||
| const IndexRange src_evaluated_points = src_curves.evaluated_points_for_curve(curve_i); | const IndexRange src_evaluated_points = src_curves.evaluated_points_for_curve(curve_i); | ||||||||||
| GArray evaluated_data(CPPType::get<T>(), src_evaluated_points.size()); | GArray evaluated_data(CPPType::get<T>(), src_evaluated_points.size()); | ||||||||||
| GMutableSpan evaluated_span = evaluated_data.as_mutable_span(); | GMutableSpan evaluated_span = evaluated_data.as_mutable_span(); | ||||||||||
| src_curves.interpolate_to_evaluated( | src_curves.interpolate_to_evaluated( | ||||||||||
| curve_i, attribute.src.slice(src_curves.points_for_curve(curve_i)), evaluated_span); | curve_i, attribute.src.slice(src_curves.points_for_curve(curve_i)), evaluated_span); | ||||||||||
| bke::curves::IndexRangeCyclic src_sample_range = get_range_between_endpoints( | |||||||||||
| start_points[curve_i], end_points[curve_i], {0, src_evaluated_points.size()}); | |||||||||||
| sample_interval_linear<T>(evaluated_span.typed<T>(), | sample_interval_linear<T>(evaluated_span.typed<T>(), | ||||||||||
| attribute.dst.span.typed<T>(), | attribute.dst.span.typed<T>(), | ||||||||||
| src_sample_range, | src_ranges[curve_i], | ||||||||||
| dst_curves.points_for_curve(curve_i), | dst_curves.points_for_curve(curve_i), | ||||||||||
| start_points[curve_i], | start_points[curve_i], | ||||||||||
| end_points[curve_i]); | end_points[curve_i]); | ||||||||||
| } | } | ||||||||||
| }); | }); | ||||||||||
| }); | }); | ||||||||||
| } | } | ||||||||||
| } | } | ||||||||||
| /* -------------------------------------------------------------------- */ | |||||||||||
| /** \name Compute trim parameters | |||||||||||
| * \{ */ | |||||||||||
| static float trim_sample_length(const Span<float> accumulated_lengths, | |||||||||||
| const float sample_length, | |||||||||||
| const GeometryNodeCurveSampleMode mode) | |||||||||||
| { | |||||||||||
| float length = mode == GEO_NODE_CURVE_SAMPLE_FACTOR ? | |||||||||||
| sample_length * accumulated_lengths.last() : | |||||||||||
| sample_length; | |||||||||||
| return std::clamp(length, 0.0f, accumulated_lengths.last()); | |||||||||||
HooglyBooglyUnsubmitted Done Inline Actions
HooglyBoogly: | |||||||||||
Done Inline ActionsSupposed C++ std lib should be preferred over the internal C functions Osares: Supposed C++ std lib should be preferred over the internal C functions | |||||||||||
| } | |||||||||||
| /** | |||||||||||
| * Compute the selection for the given curve type. Tracks indices for splitting the selection if | |||||||||||
| * there are curves reduced to a single point. | |||||||||||
| */ | |||||||||||
| void compute_curve_trim_parameters(const bke::CurvesGeometry &curves, | |||||||||||
| const IndexMask selection, | |||||||||||
| const VArray<float> &starts, | |||||||||||
| const VArray<float> &ends, | |||||||||||
| const GeometryNodeCurveSampleMode mode, | |||||||||||
| MutableSpan<int> dst_curve_size, | |||||||||||
| MutableSpan<int8_t> dst_curve_types, | |||||||||||
| MutableSpan<bke::curves::CurvePoint> start_points, | |||||||||||
| MutableSpan<bke::curves::CurvePoint> end_points, | |||||||||||
| MutableSpan<bke::curves::IndexRangeCyclic> src_ranges) | |||||||||||
| { | |||||||||||
| const VArray<bool> src_cyclic = curves.cyclic(); | |||||||||||
| const VArray<int> resolution = curves.resolution(); | |||||||||||
| const VArray<int8_t> curve_types = curves.curve_types(); | |||||||||||
| /* Compute. */ | |||||||||||
| threading::parallel_for( | |||||||||||
| selection.index_range(), 128, [&](const IndexRange selection_range) { | |||||||||||
| for (const int64_t curve_i : selection.slice(selection_range)) { | |||||||||||
| CurveType curve_type = CurveType(curve_types[curve_i]); | |||||||||||
| int point_count; | |||||||||||
| if (curve_type == CURVE_TYPE_NURBS) { | |||||||||||
| dst_curve_types[curve_i] = CURVE_TYPE_POLY; | |||||||||||
| point_count = curves.evaluated_points_for_curve(curve_i).size(); | |||||||||||
| } | |||||||||||
| else { | |||||||||||
| dst_curve_types[curve_i] = curve_type; | |||||||||||
| point_count = curves.points_num_for_curve(curve_i); | |||||||||||
| } | |||||||||||
| if (point_count == 1) { | |||||||||||
| /* Single point. */ | |||||||||||
| dst_curve_size[curve_i] = 1; | |||||||||||
| src_ranges[curve_i] = bke::curves::IndexRangeCyclic(0, 0, 1, 1); | |||||||||||
| start_points[curve_i] = {0, 0, 0.0f}; | |||||||||||
| end_points[curve_i] = {0, 0, 0.0f}; | |||||||||||
| continue; | |||||||||||
| } | |||||||||||
| const bool cyclic = src_cyclic[curve_i]; | |||||||||||
| const Span<float> lengths = curves.evaluated_lengths_for_curve(curve_i, cyclic); | |||||||||||
| BLI_assert(lengths.size() > 0); | |||||||||||
| const float start_length = trim_sample_length(lengths, starts[curve_i], mode); | |||||||||||
| float end_length; | |||||||||||
| bool equal_sample_point; | |||||||||||
| if (cyclic) { | |||||||||||
| end_length = trim_sample_length(lengths, ends[curve_i], mode); | |||||||||||
| const float cyclic_start = start_length == lengths.last() ? 0.0f : start_length; | |||||||||||
| const float cyclic_end = end_length == lengths.last() ? 0.0f : end_length; | |||||||||||
| equal_sample_point = cyclic_start == cyclic_end; | |||||||||||
| } | |||||||||||
| else { | |||||||||||
| end_length = ends[curve_i] <= starts[curve_i] ? | |||||||||||
| start_length : | |||||||||||
| trim_sample_length(lengths, ends[curve_i], mode); | |||||||||||
| equal_sample_point = start_length == end_length; | |||||||||||
| } | |||||||||||
| start_points[curve_i] = lookup_curve_point(curves, | |||||||||||
| curve_type, | |||||||||||
| curve_i, | |||||||||||
| lengths, | |||||||||||
| start_length, | |||||||||||
| cyclic, | |||||||||||
| resolution[curve_i], | |||||||||||
| point_count); | |||||||||||
| if (equal_sample_point) { | |||||||||||
| end_points[curve_i] = start_points[curve_i]; | |||||||||||
| if (end_length <= start_length) { | |||||||||||
| /* Single point. */ | |||||||||||
| dst_curve_size[curve_i] = 1; | |||||||||||
| src_ranges[curve_i] = bke::curves::IndexRangeCyclic::get_range_from_size( | |||||||||||
| start_points[curve_i].index, | |||||||||||
| start_points[curve_i].is_controlpoint(), /* Only iterate if control point. */ | |||||||||||
| point_count); | |||||||||||
| } | |||||||||||
| else { | |||||||||||
| /* Split. */ | |||||||||||
| src_ranges[curve_i] = bke::curves::IndexRangeCyclic::get_range_between_endpoints( | |||||||||||
| start_points[curve_i], end_points[curve_i], point_count) | |||||||||||
| .push_loop(); | |||||||||||
| const int count = 1 + !start_points[curve_i].is_controlpoint() + point_count; | |||||||||||
| BLI_assert(count > 1); | |||||||||||
| dst_curve_size[curve_i] = count; | |||||||||||
| } | |||||||||||
| } | |||||||||||
| else { | |||||||||||
| /* General case. */ | |||||||||||
| end_points[curve_i] = lookup_curve_point(curves, | |||||||||||
| curve_type, | |||||||||||
| curve_i, | |||||||||||
| lengths, | |||||||||||
| end_length, | |||||||||||
| cyclic, | |||||||||||
| resolution[curve_i], | |||||||||||
| point_count); | |||||||||||
| src_ranges[curve_i] = bke::curves::IndexRangeCyclic::get_range_between_endpoints( | |||||||||||
| start_points[curve_i], end_points[curve_i], point_count); | |||||||||||
| const int count = src_ranges[curve_i].size() + | |||||||||||
| !start_points[curve_i].is_controlpoint() + | |||||||||||
| !end_points[curve_i].is_controlpoint(); | |||||||||||
| BLI_assert(count > 1); | |||||||||||
| dst_curve_size[curve_i] = count; | |||||||||||
| } | |||||||||||
| BLI_assert(dst_curve_size[curve_i] > 0); | |||||||||||
| } | |||||||||||
| }); | |||||||||||
| } | |||||||||||
| /** \} */ | |||||||||||
| bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves, | bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves, | ||||||||||
| const IndexMask selection, | const IndexMask selection, | ||||||||||
| const Span<bke::curves::CurvePoint> start_points, | const VArray<float> &starts, | ||||||||||
| const Span<bke::curves::CurvePoint> end_points) | const VArray<float> &ends, | ||||||||||
| const GeometryNodeCurveSampleMode mode) | |||||||||||
| { | { | ||||||||||
| BLI_assert(selection.size() > 0); | BLI_assert(selection.size() > 0); | ||||||||||
| BLI_assert(selection.last() <= start_points.size()); | BLI_assert(selection.last() <= src_curves.curves_num()); | ||||||||||
| BLI_assert(start_points.size() == end_points.size()); | BLI_assert(starts.size() == src_curves.curves_num()); | ||||||||||
| BLI_assert(starts.size() == ends.size()); | |||||||||||
| src_curves.ensure_evaluated_lengths(); | |||||||||||
| src_curves.ensure_evaluated_offsets(); | |||||||||||
| Vector<int64_t> inverse_selection_indices; | Vector<int64_t> inverse_selection_indices; | ||||||||||
| const IndexMask inverse_selection = selection.invert(src_curves.curves_range(), | const IndexMask inverse_selection = selection.invert(src_curves.curves_range(), | ||||||||||
| inverse_selection_indices); | inverse_selection_indices); | ||||||||||
| /* Create trim curves. */ | /* Create destination curves. */ | ||||||||||
| bke::CurvesGeometry dst_curves(0, src_curves.curves_num()); | bke::CurvesGeometry dst_curves(0, src_curves.curves_num()); | ||||||||||
| determine_copyable_curve_types(src_curves, | MutableSpan<int> dst_curve_offsets = dst_curves.offsets_for_write(); | ||||||||||
| dst_curves, | MutableSpan<int8_t> dst_curve_types = dst_curves.curve_types_for_write(); | ||||||||||
| selection, | Array<bke::curves::CurvePoint, 12> start_points(src_curves.curves_num()); | ||||||||||
| inverse_selection, | Array<bke::curves::CurvePoint, 12> end_points(src_curves.curves_num()); | ||||||||||
| (CurveTypeMask)(CURVE_TYPE_MASK_CATMULL_ROM | | Array<bke::curves::IndexRangeCyclic, 12> src_ranges(src_curves.curves_num()); | ||||||||||
| CURVE_TYPE_MASK_POLY | CURVE_TYPE_MASK_BEZIER)); | |||||||||||
| if (src_curves.has_curve_with_type({CURVE_TYPE_BEZIER, CURVE_TYPE_NURBS})) { | |||||||||||
| Vector<int64_t> curve_indices; | src_curves.ensure_evaluated_offsets(); | ||||||||||
| Vector<int64_t> point_curve_indices; | if (src_curves.has_curve_with_type(CURVE_TYPE_NURBS)) { | ||||||||||
Done Inline ActionsIf there is only one curve type, wouldn't building the second mask per type have a performance impact? If we're able choose, I think it's better to compromise the multi-type performance that the single-type performance. HooglyBoogly: If there is only one curve type, wouldn't building the second mask per type have a performance… | |||||||||||
Done Inline ActionsNo the masks are only updated if there are actually any changes to the mask (i.e. a curve is trimmed to a single point). But I made it more explicit by following the idea of ignoring edge cases to benefit the general case by only tracking the single point curves and implementing IndexMask::invert_submask() instead. Osares: No the masks are only updated if there are actually any changes to the mask (i.e. a curve is… | |||||||||||
| compute_trim_result_offsets(src_curves, | src_curves.evaluated_positions(); | ||||||||||
| } | |||||||||||
Not Done Inline ActionsDo you really have to write out all the IndexMask default constructors? HooglyBoogly: Do you really have to write out all the `IndexMask` default constructors? | |||||||||||
Done Inline ActionsSeems not so. Always room for understanding C++ better (or re-remember)! Osares: Seems not so. Always room for understanding C++ better (or re-remember)! | |||||||||||
| } | |||||||||||
| /* Compute destiation curves. */ | |||||||||||
| compute_curve_trim_parameters(src_curves, | |||||||||||
| selection, | selection, | ||||||||||
| inverse_selection, | starts, | ||||||||||
| ends, | |||||||||||
Done Inline ActionsIs single_curve_count redundant with updated_curve_point_indices.size()? HooglyBoogly: Is `single_curve_count` redundant with `updated_curve_point_indices.size()`? | |||||||||||
Done Inline ActionsIt wasn't but exposed is_empty() in threading::EnumerableThreadSpecific which achieves the same result. Osares: It wasn't but exposed is_empty() in threading::EnumerableThreadSpecific which achieves the same… | |||||||||||
| mode, | |||||||||||
| dst_curve_offsets, | |||||||||||
| dst_curve_types, | |||||||||||
| start_points, | start_points, | ||||||||||
| end_points, | end_points, | ||||||||||
| dst_curves.curve_types(), | src_ranges); | ||||||||||
| dst_curves.offsets_for_write(), | |||||||||||
| curve_indices, | /* Transfer copied curves parameters. */ | ||||||||||
| point_curve_indices); | const VArray<int8_t> src_curve_types = src_curves.curve_types(); | ||||||||||
| /* Finalize by updating the geometry container. */ | threading::parallel_for( | ||||||||||
| inverse_selection.index_range(), 4096, [&](const IndexRange selection_range) { | |||||||||||
| for (const int64_t curve_i : inverse_selection.slice(selection_range)) { | |||||||||||
| dst_curve_offsets[curve_i] = src_curves.points_num_for_curve(curve_i); | |||||||||||
| dst_curve_types[curve_i] = src_curve_types[curve_i]; | |||||||||||
| } | |||||||||||
| }); | |||||||||||
| /* Finalize and update the geometry container. */ | |||||||||||
| bke::curves::accumulate_counts_to_offsets(dst_curve_offsets); | |||||||||||
| dst_curves.resize(dst_curves.offsets().last(), dst_curves.curves_num()); | dst_curves.resize(dst_curves.offsets().last(), dst_curves.curves_num()); | ||||||||||
| dst_curves.update_curve_types(); | dst_curves.update_curve_types(); | ||||||||||
| /* Populate curve domain. */ | /* Populate curve domain. */ | ||||||||||
| const bke::AttributeAccessor src_attributes = src_curves.attributes(); | const bke::AttributeAccessor src_attributes = src_curves.attributes(); | ||||||||||
| bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); | bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); | ||||||||||
| bke::copy_attribute_domain(src_attributes, | Set<std::string> transfer_curve_skip = {"cyclic", "curve_type", "nurbs_order", "knots_mode"}; | ||||||||||
| dst_attributes, | if (dst_curves.has_curve_with_type(CURVE_TYPE_NURBS)) { | ||||||||||
| selection, | /* If a NURBS curve is copied keep */ | ||||||||||
| ATTR_DOMAIN_CURVE, | transfer_curve_skip.remove("nurbs_order"); | ||||||||||
| {"cyclic", "curve_type", "nurbs_order", "knots_mode"}); | transfer_curve_skip.remove("knots_mode"); | ||||||||||
| } | |||||||||||
| bke::copy_attribute_domain( | |||||||||||
| src_attributes, dst_attributes, selection, ATTR_DOMAIN_CURVE, transfer_curve_skip); | |||||||||||
| /* Fetch custom point domain attributes for transfer (copy). */ | /* Fetch custom point domain attributes for transfer (copy). */ | ||||||||||
| Vector<bke::AttributeTransferData> transfer_attributes = bke::retrieve_attributes_for_transfer( | Vector<bke::AttributeTransferData> transfer_attributes = bke::retrieve_attributes_for_transfer( | ||||||||||
| src_attributes, | src_attributes, | ||||||||||
| dst_attributes, | dst_attributes, | ||||||||||
| ATTR_DOMAIN_MASK_POINT, | ATTR_DOMAIN_MASK_POINT, | ||||||||||
| {"position", | {"position", | ||||||||||
| "handle_left", | "handle_left", | ||||||||||
| "handle_right", | "handle_right", | ||||||||||
| "handle_type_left", | "handle_type_left", | ||||||||||
| "handle_type_right", | "handle_type_right", | ||||||||||
| "nurbs_weight"}); | "nurbs_weight"}); | ||||||||||
| auto trim_catmull = [&](IndexMask selection) { | auto trim_catmull = [&](const IndexMask selection) { | ||||||||||
| trim_catmull_rom_curves( | trim_catmull_rom_curves(src_curves, | ||||||||||
| src_curves, dst_curves, selection, start_points, end_points, transfer_attributes); | dst_curves, | ||||||||||
| selection, | |||||||||||
| start_points, | |||||||||||
| end_points, | |||||||||||
| src_ranges, | |||||||||||
| transfer_attributes); | |||||||||||
| }; | }; | ||||||||||
| auto trim_poly = [&](IndexMask selection) { | auto trim_poly = [&](const IndexMask selection) { | ||||||||||
| trim_polygonal_curves( | trim_polygonal_curves(src_curves, | ||||||||||
Done Inline ActionsI think it's not changing in this patch, but in theory we could avoid creating/writing to curve types completely in some cases (when all the curves don't change type). That would allow making use of future CoW improvements so that the attribute data is shared. Maybe I'm prematurely optimizing, but I think it's good to keep things in mind. HooglyBoogly: I think it's not changing in this patch, but in theory we could avoid creating/writing to curve… | |||||||||||
Done Inline ActionsIt would be interesting if CustomData could reference data in other CustomData buffers rather then copying everything. Ensuring all attributes are set properly is not simple even now though (it gets a bit tricky ensuring all curve specific attributes are set/removed properly). So hopefully such a system would not introduce more edge cases. I guess edge cases would be reduced if one avoids 'evaluated' copies. Osares: It would be interesting if CustomData could reference data in other CustomData buffers rather… | |||||||||||
| src_curves, dst_curves, selection, start_points, end_points, transfer_attributes); | dst_curves, | ||||||||||
| selection, | |||||||||||
| start_points, | |||||||||||
| end_points, | |||||||||||
| src_ranges, | |||||||||||
| transfer_attributes); | |||||||||||
| }; | }; | ||||||||||
| auto trim_bezier = [&](IndexMask selection) { | auto trim_bezier = [&](const IndexMask selection) { | ||||||||||
| trim_bezier_curves( | trim_bezier_curves(src_curves, | ||||||||||
| src_curves, dst_curves, selection, start_points, end_points, transfer_attributes); | dst_curves, | ||||||||||
| selection, | |||||||||||
| start_points, | |||||||||||
| end_points, | |||||||||||
| src_ranges, | |||||||||||
| transfer_attributes); | |||||||||||
| }; | }; | ||||||||||
| auto trim_evaluated = [&](IndexMask selection) { | auto trim_evaluated = [&](const IndexMask selection) { | ||||||||||
| /* Ensure evaluated positions are available. */ | /* Ensure evaluated positions are available. */ | ||||||||||
| src_curves.ensure_evaluated_offsets(); | src_curves.ensure_evaluated_offsets(); | ||||||||||
| src_curves.evaluated_positions(); | src_curves.evaluated_positions(); | ||||||||||
| trim_evaluated_curves( | trim_evaluated_curves(src_curves, | ||||||||||
| src_curves, dst_curves, selection, start_points, end_points, transfer_attributes); | dst_curves, | ||||||||||
| }; | selection, | ||||||||||
| start_points, | |||||||||||
| auto single_point_catmull = [&](IndexMask selection) { | end_points, | ||||||||||
| convert_point_catmull_curves( | src_ranges, | ||||||||||
| src_curves, dst_curves, selection, start_points, transfer_attributes); | transfer_attributes); | ||||||||||
| }; | |||||||||||
| auto single_point_poly = [&](IndexMask selection) { | |||||||||||
| convert_point_polygonal_curves( | |||||||||||
| src_curves, dst_curves, selection, start_points, transfer_attributes); | |||||||||||
| }; | |||||||||||
| auto single_point_bezier = [&](IndexMask selection) { | |||||||||||
| convert_point_bezier_curves( | |||||||||||
| src_curves, dst_curves, selection, start_points, transfer_attributes); | |||||||||||
| }; | |||||||||||
| auto single_point_evaluated = [&](IndexMask selection) { | |||||||||||
| convert_point_evaluated_curves( | |||||||||||
| src_curves, dst_curves, selection, start_points, transfer_attributes); | |||||||||||
| }; | }; | ||||||||||
| /* Populate point domain. */ | /* Populate point domain. */ | ||||||||||
| bke::curves::foreach_curve_by_type(src_curves.curve_types(), | bke::curves::foreach_curve_by_type(src_curves.curve_types(), | ||||||||||
| src_curves.curve_type_counts(), | src_curves.curve_type_counts(), | ||||||||||
| curve_indices.as_span(), | selection, | ||||||||||
| trim_catmull, | trim_catmull, | ||||||||||
| trim_poly, | trim_poly, | ||||||||||
| trim_bezier, | trim_bezier, | ||||||||||
| trim_evaluated); | trim_evaluated); | ||||||||||
| if (point_curve_indices.size()) { | |||||||||||
| bke::curves::foreach_curve_by_type(src_curves.curve_types(), | |||||||||||
| src_curves.curve_type_counts(), | |||||||||||
| point_curve_indices.as_span(), | |||||||||||
| single_point_catmull, | |||||||||||
| single_point_poly, | |||||||||||
| single_point_bezier, | |||||||||||
| single_point_evaluated); | |||||||||||
| } | |||||||||||
| /* Cleanup/close context */ | /* Cleanup/close context */ | ||||||||||
| for (bke::AttributeTransferData &attribute : transfer_attributes) { | for (bke::AttributeTransferData &attribute : transfer_attributes) { | ||||||||||
| attribute.dst.finish(); | attribute.dst.finish(); | ||||||||||
| } | } | ||||||||||
| /* Copy unselected */ | /* Copy unselected */ | ||||||||||
| if (!inverse_selection.is_empty()) { | if (!inverse_selection.is_empty()) { | ||||||||||
| transfer_curve_skip.remove("cyclic"); | |||||||||||
| bke::copy_attribute_domain( | bke::copy_attribute_domain( | ||||||||||
| src_attributes, dst_attributes, inverse_selection, ATTR_DOMAIN_CURVE); | src_attributes, dst_attributes, inverse_selection, ATTR_DOMAIN_CURVE, transfer_curve_skip); | ||||||||||
| /* Trim curves are no longer cyclic. If all curves are trimmed, this will be set implicitly. */ | /* Trim curves are no longer cyclic. If all curves are trimmed, this will be set implicitly. */ | ||||||||||
| dst_curves.cyclic_for_write().fill_indices(selection, false); | dst_curves.cyclic_for_write().fill_indices(selection, false); | ||||||||||
| Set<std::string> copy_point_skip; | |||||||||||
| if (!dst_curves.has_curve_with_type(CURVE_TYPE_NURBS) && | |||||||||||
| src_curves.has_curve_with_type(CURVE_TYPE_NURBS)) { | |||||||||||
| copy_point_skip.add("nurbs_weight"); | |||||||||||
| } | |||||||||||
| /* Copy point domain. */ | /* Copy point domain. */ | ||||||||||
| for (auto &attribute : bke::retrieve_attributes_for_transfer( | for (auto &attribute : bke::retrieve_attributes_for_transfer( | ||||||||||
| src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) { | src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, copy_point_skip)) { | ||||||||||
| bke::curves::copy_point_data( | bke::curves::copy_point_data( | ||||||||||
| src_curves, dst_curves, inverse_selection, attribute.src, attribute.dst.span); | src_curves, dst_curves, inverse_selection, attribute.src, attribute.dst.span); | ||||||||||
| attribute.dst.finish(); | attribute.dst.finish(); | ||||||||||
| } | } | ||||||||||
| } | } | ||||||||||
| dst_curves.tag_topology_changed(); | dst_curves.tag_topology_changed(); | ||||||||||
| return dst_curves; | return dst_curves; | ||||||||||
| } | } | ||||||||||
| /** \} */ | /** \} */ | ||||||||||
| } // namespace blender::geometry | } // namespace blender::geometry | ||||||||||