/* SPDX-FileCopyrightText: 2023 Blender Authors * * SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup bke */ #include "BKE_curves_utils.hh" #include "BKE_customdata.hh" namespace blender::bke::curves { IndexMask curve_to_point_selection(OffsetIndices points_by_curve, const IndexMask &curve_selection, IndexMaskMemory &memory) { Array point_ranges(curve_selection.size()); curve_selection.foreach_index(GrainSize(2048), [&](const int curve, const int pos) { point_ranges[pos] = points_by_curve[curve]; }); return IndexMask::from_initializers(point_ranges, memory); } void fill_points(const OffsetIndices points_by_curve, const IndexMask &curve_selection, const GPointer value, GMutableSpan dst) { BLI_assert(*value.type() == dst.type()); const CPPType &type = dst.type(); curve_selection.foreach_index(GrainSize(512), [&](const int i) { const IndexRange points = points_by_curve[i]; type.fill_assign_n(value.get(), dst.slice(points).data(), points.size()); }); } CurvesGeometry copy_only_curve_domain(const CurvesGeometry &src_curves) { CurvesGeometry dst_curves(0, src_curves.curves_num()); CustomData_init_from( &src_curves.curve_data, &dst_curves.curve_data, CD_MASK_ALL, src_curves.curves_num()); dst_curves.runtime->type_counts = src_curves.runtime->type_counts; return dst_curves; } IndexMask indices_for_type(const VArray &types, const std::array &type_counts, const CurveType type, const IndexMask &selection, IndexMaskMemory &memory) { if (type_counts[type] == types.size()) { return selection; } if (types.is_single()) { return types.get_internal_single() == type ? IndexMask(types.size()) : IndexMask(0); } Span types_span = types.get_internal_span(); return IndexMask::from_predicate(selection, GrainSize(4096), memory, [&](const int index) { return types_span[index] == type; }); } void foreach_curve_by_type(const VArray &types, const std::array &counts, const IndexMask &selection, FunctionRef catmull_rom_fn, FunctionRef poly_fn, FunctionRef bezier_fn, FunctionRef nurbs_fn) { auto call_if_not_empty = [&](const CurveType type, FunctionRef fn) { IndexMaskMemory memory; const IndexMask mask = indices_for_type(types, counts, type, selection, memory); if (!mask.is_empty()) { fn(mask); } }; call_if_not_empty(CURVE_TYPE_CATMULL_ROM, catmull_rom_fn); call_if_not_empty(CURVE_TYPE_POLY, poly_fn); call_if_not_empty(CURVE_TYPE_BEZIER, bezier_fn); call_if_not_empty(CURVE_TYPE_NURBS, nurbs_fn); } static void if_has_data_call_callback(const Span offset_data, const int begin, const int end, UnselectedCallback callback) { if (begin < end) { const IndexRange curves = IndexRange::from_begin_end(begin, end); const IndexRange points = IndexRange::from_begin_end(offset_data[begin], offset_data[end]); callback(curves, points); } }; template static void foreach_selected_point_ranges_per_curve_(const IndexMask &mask, const OffsetIndices points_by_curve, SelectedCallback selected_fn, Fn unselected_fn) { Vector ranges; Span offset_data = points_by_curve.data(); int curve_i = mask.is_empty() ? -1 : 0; int range_first = mask.is_empty() ? 0 : mask.first(); int range_last = range_first - 1; mask.foreach_index([&](const int64_t index) { if (offset_data[curve_i + 1] <= index) { int first_unselected_curve = curve_i; if (range_last >= range_first) { ranges.append(IndexRange::from_begin_end_inclusive(range_first, range_last)); selected_fn(curve_i, points_by_curve[curve_i], ranges); ranges.clear(); first_unselected_curve++; } do { ++curve_i; } while (offset_data[curve_i + 1] <= index); if constexpr (std::is_invocable_r_v) { if_has_data_call_callback(offset_data, first_unselected_curve, curve_i, unselected_fn); } range_first = index; } else if (range_last + 1 != index) { ranges.append(IndexRange::from_begin_end_inclusive(range_first, range_last)); range_first = index; } range_last = index; }); if (range_last - range_first >= 0) { ranges.append(IndexRange::from_begin_end_inclusive(range_first, range_last)); selected_fn(curve_i, points_by_curve[curve_i], ranges); } if constexpr (std::is_invocable_r_v) { if_has_data_call_callback(offset_data, curve_i + 1, points_by_curve.size(), unselected_fn); } } void foreach_selected_point_ranges_per_curve(const IndexMask &mask, const OffsetIndices offset_indices, SelectedCallback selected_fn) { foreach_selected_point_ranges_per_curve_(mask, offset_indices, selected_fn, nullptr); } void foreach_selected_point_ranges_per_curve(const IndexMask &mask, const OffsetIndices offset_indices, SelectedCallback selected_fn, UnselectedCallback unselected_fn) { foreach_selected_point_ranges_per_curve_( mask, offset_indices, selected_fn, unselected_fn); } namespace bezier { Array retrieve_all_positions(const bke::CurvesGeometry &curves, const IndexMask &curves_selection) { if (curves.is_empty() || !curves.has_curve_with_type(CURVE_TYPE_BEZIER)) { return {}; } const OffsetIndices points_by_curve = curves.points_by_curve(); const Span positions = curves.positions(); const Span handle_positions_left = curves.handle_positions_left(); const Span handle_positions_right = curves.handle_positions_right(); Array all_positions(positions.size() * 3); curves_selection.foreach_index(GrainSize(1024), [&](const int curve) { const IndexRange points = points_by_curve[curve]; for (const int point : points) { const int index = point * 3; all_positions[index] = handle_positions_left[point]; all_positions[index + 1] = positions[point]; all_positions[index + 2] = handle_positions_right[point]; } }); return all_positions; } void write_all_positions(bke::CurvesGeometry &curves, const IndexMask &curves_selection, const Span all_positions) { if (curves_selection.is_empty() || curves.is_empty() || !curves.has_curve_with_type(CURVE_TYPE_BEZIER)) { return; } BLI_assert(curves_selection.size() * 3 == all_positions.size()); const OffsetIndices points_by_curve = curves.points_by_curve(); MutableSpan positions = curves.positions_for_write(); MutableSpan handle_positions_left = curves.handle_positions_left_for_write(); MutableSpan handle_positions_right = curves.handle_positions_right_for_write(); curves_selection.foreach_index(GrainSize(1024), [&](const int curve) { const IndexRange points = points_by_curve[curve]; for (const int point : points) { const int index = point * 3; handle_positions_left[point] = all_positions[index]; positions[point] = all_positions[index + 1]; handle_positions_right[point] = all_positions[index + 2]; } }); } } // namespace bezier } // namespace blender::bke::curves