Blender V5.0
curves_utils.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BKE_curves_utils.hh"
10#include "BKE_customdata.hh"
11
12#include "BLI_array_utils.hh"
13
14namespace blender::bke::curves {
15
17 const IndexMask &curve_selection,
18 IndexMaskMemory &memory)
19{
20 Array<index_mask::IndexMask::Initializer> point_ranges(curve_selection.size());
21 curve_selection.foreach_index(GrainSize(2048), [&](const int curve, const int pos) {
22 point_ranges[pos] = points_by_curve[curve];
23 });
24 return IndexMask::from_initializers(point_ranges, memory);
25}
26
28 const CurveType curve_type,
29 IndexMaskMemory &memory)
30{
31 return curve_to_point_selection(curves.points_by_curve(),
32 indices_for_type(curves.curve_types(),
33 curves.curve_type_counts(),
34 curve_type,
35 curves.curves_range(),
36 memory),
37 memory);
38}
39
40void fill_points(const OffsetIndices<int> points_by_curve,
41 const IndexMask &curve_selection,
42 const GPointer value,
43 GMutableSpan dst)
44{
45 BLI_assert(*value.type() == dst.type());
46 const CPPType &type = dst.type();
47 curve_selection.foreach_index(GrainSize(512), [&](const int i) {
48 const IndexRange points = points_by_curve[i];
49 type.fill_assign_n(value.get(), dst.slice(points).data(), points.size());
50 });
51}
52
54{
55 CurvesGeometry dst_curves(0, src_curves.curves_num());
56 copy_attributes(src_curves.attributes(),
59 {},
60 dst_curves.attributes_for_write());
61 dst_curves.runtime->type_counts = src_curves.runtime->type_counts;
62 return dst_curves;
63}
64
66 const std::array<int, CURVE_TYPES_NUM> &type_counts,
67 const CurveType type,
68 const IndexMask &selection,
69 IndexMaskMemory &memory)
70{
71 if (type_counts[type] == types.size()) {
72 return selection;
73 }
74 if (types.is_single()) {
75 return types.get_internal_single() == type ? IndexMask(types.size()) : IndexMask(0);
76 }
77 Span<int8_t> types_span = types.get_internal_span();
78 return IndexMask::from_predicate(selection, GrainSize(4096), memory, [&](const int index) {
79 return types_span[index] == type;
80 });
81}
82
84 const std::array<int, CURVE_TYPES_NUM> &counts,
85 const IndexMask &selection,
86 FunctionRef<void(IndexMask)> catmull_rom_fn,
87 FunctionRef<void(IndexMask)> poly_fn,
88 FunctionRef<void(IndexMask)> bezier_fn,
89 FunctionRef<void(IndexMask)> nurbs_fn)
90{
91 auto call_if_not_empty = [&](const CurveType type, FunctionRef<void(IndexMask)> fn) {
92 IndexMaskMemory memory;
93 const IndexMask mask = indices_for_type(types, counts, type, selection, memory);
94 if (!mask.is_empty()) {
95 fn(mask);
96 }
97 };
98 call_if_not_empty(CURVE_TYPE_CATMULL_ROM, catmull_rom_fn);
99 call_if_not_empty(CURVE_TYPE_POLY, poly_fn);
100 call_if_not_empty(CURVE_TYPE_BEZIER, bezier_fn);
101 call_if_not_empty(CURVE_TYPE_NURBS, nurbs_fn);
102}
103
104static void if_has_data_call_callback(const Span<int> offset_data,
105 const int begin,
106 const int end,
107 UnselectedCallback callback)
108{
109 if (begin < end) {
111 const IndexRange points = IndexRange::from_begin_end(offset_data[begin], offset_data[end]);
112 callback(curves, points);
113 }
114};
115
116template<typename Fn>
118 const OffsetIndices<int> points_by_curve,
119 const SelectedCallback selected_fn,
120 const Fn unselected_fn)
121{
122 Vector<IndexRange> ranges;
123 Span<int> offset_data = points_by_curve.data();
124
125 int curve_i = mask.is_empty() ? -1 : 0;
126
127 int range_first = mask.is_empty() ? 0 : mask.first();
128 int range_last = range_first - 1;
129
130 mask.foreach_index([&](const int64_t index) {
131 if (offset_data[curve_i + 1] <= index) {
132 int first_unselected_curve = curve_i;
133 if (range_last >= range_first) {
134 ranges.append(IndexRange::from_begin_end_inclusive(range_first, range_last));
135 selected_fn(curve_i, points_by_curve[curve_i], ranges);
136 ranges.clear();
137 first_unselected_curve++;
138 }
139 do {
140 ++curve_i;
141 } while (offset_data[curve_i + 1] <= index);
142 if constexpr (std::is_invocable_r_v<void, Fn, IndexRange, IndexRange>) {
143 if_has_data_call_callback(offset_data, first_unselected_curve, curve_i, unselected_fn);
144 }
145 range_first = index;
146 }
147 else if (range_last + 1 != index) {
148 ranges.append(IndexRange::from_begin_end_inclusive(range_first, range_last));
149 range_first = index;
150 }
151 range_last = index;
152 });
153
154 if (range_last - range_first >= 0) {
155 ranges.append(IndexRange::from_begin_end_inclusive(range_first, range_last));
156 selected_fn(curve_i, points_by_curve[curve_i], ranges);
157 }
158 if constexpr (std::is_invocable_r_v<void, Fn, IndexRange, IndexRange>) {
159 if_has_data_call_callback(offset_data, curve_i + 1, points_by_curve.size(), unselected_fn);
160 }
161}
162
164 const OffsetIndices<int> points_by_curve,
165 const SelectedCallback selected_fn)
166{
167 foreach_selected_point_ranges_per_curve_<void()>(mask, points_by_curve, selected_fn, nullptr);
168}
169
171 const OffsetIndices<int> points_by_curve,
172 const SelectedCallback selected_fn,
173 const UnselectedCallback unselected_fn)
174{
176 mask, points_by_curve, selected_fn, unselected_fn);
177}
178
179namespace bezier {
180
182 const IndexMask &curves_selection)
183{
184 if (curves.is_empty() || !curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
185 return {};
186 }
187 const OffsetIndices points_by_curve = curves.points_by_curve();
188 const Span<float3> positions = curves.positions();
189 const std::optional<Span<float3>> handle_positions_left = curves.handle_positions_left();
190 const std::optional<Span<float3>> handle_positions_right = curves.handle_positions_right();
191 if (!handle_positions_left || !handle_positions_right) {
192 return {};
193 }
194
195 Array<float3> all_positions(positions.size() * 3);
196 curves_selection.foreach_index(GrainSize(1024), [&](const int curve) {
197 const IndexRange points = points_by_curve[curve];
198 for (const int point : points) {
199 const int index = point * 3;
200 all_positions[index] = (*handle_positions_left)[point];
201 all_positions[index + 1] = positions[point];
202 all_positions[index + 2] = (*handle_positions_right)[point];
203 }
204 });
205
206 return all_positions;
207}
208
210 const IndexMask &curves_selection,
211 const Span<float3> all_positions)
212{
213 if (curves_selection.is_empty() || curves.is_empty() ||
214 !curves.has_curve_with_type(CURVE_TYPE_BEZIER))
215 {
216 return;
217 }
218
219 const OffsetIndices points_by_curve = curves.points_by_curve();
220 MutableSpan<float3> positions = curves.positions_for_write();
221 MutableSpan<float3> handle_positions_left = curves.handle_positions_left_for_write();
222 MutableSpan<float3> handle_positions_right = curves.handle_positions_right_for_write();
223
224 curves_selection.foreach_index(GrainSize(1024), [&](const int curve) {
225 const IndexRange points = points_by_curve[curve];
226 for (const int point : points) {
227 const int index = point * 3;
228 handle_positions_left[point] = all_positions[index];
229 positions[point] = all_positions[index + 1];
230 handle_positions_right[point] = all_positions[index + 2];
231 }
232 });
233}
234
235} // namespace bezier
236
237namespace nurbs {
238
240 const IndexMask &src_curves,
241 const int dst_curve_offset,
243{
244 const OffsetIndices<int> src_knots_by_curve = src.nurbs_custom_knots_by_curve();
245 const int start_offset = dst.nurbs_custom_knots_by_curve()[dst_curve_offset].start();
246 Array<int> dst_offsets(src_curves.size() + 1);
247
249 src_knots_by_curve, src_curves, start_offset, dst_offsets);
250
251 array_utils::gather_group_to_group(src_knots_by_curve,
252 dst_offsets.as_span(),
253 src_curves,
254 src.nurbs_custom_knots(),
256}
257
259 const KnotsMode mode_for_regular,
260 const KnotsMode mode_for_cyclic,
262{
263 const VArray<bool> cyclic = curves.cyclic();
264 MutableSpan<int8_t> knot_modes = curves.nurbs_knots_modes_for_write();
265 mask.foreach_index(GrainSize(512), [&](const int64_t curve) {
266 int8_t &knot_mode = knot_modes[curve];
267 if (knot_mode == NURBS_KNOT_MODE_CUSTOM) {
268 knot_mode = cyclic[curve] ? mode_for_cyclic : mode_for_regular;
269 }
270 });
271 curves.nurbs_custom_knots_update_size();
272}
273
275 const IndexMask &exclude_curves,
276 bke::CurvesGeometry &dst_curves)
277{
278 BLI_assert(src_curves.curves_num() == dst_curves.curves_num());
279
280 if (src_curves.nurbs_has_custom_knots()) {
281 /* Ensure excluded curves don't have NURBS_KNOT_MODE_CUSTOM set. */
283 exclude_curves, NURBS_KNOT_MODE_NORMAL, NURBS_KNOT_MODE_NORMAL, dst_curves);
284 IndexMaskMemory memory;
286 src_curves,
288 src_curves.nurbs_custom_knot_curves(memory), exclude_curves, memory),
289 0,
290 dst_curves);
291 }
292}
293
294} // namespace nurbs
295
296} // namespace blender::bke::curves
Low-level operations for curves.
CustomData interface, see also DNA_customdata_types.h.
#define BLI_assert(a)
Definition BLI_assert.h:46
KnotsMode
@ NURBS_KNOT_MODE_NORMAL
@ NURBS_KNOT_MODE_CUSTOM
iter begin(iter)
long long int int64_t
Span< T > as_span() const
Definition BLI_array.hh:243
void fill_assign_n(const void *value, void *dst, int64_t n) const
GMutableSpan slice(const int64_t start, int64_t size) const
const CPPType & type() const
const CPPType * type() const
const void * get() const
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
static IndexMask from_initializers(const Span< Initializer > initializers, IndexMaskMemory &memory)
static IndexMask from_difference(const IndexMask &mask_a, const IndexMask &mask_b, IndexMaskMemory &memory)
constexpr int64_t size() const
static constexpr IndexRange from_begin_end(const int64_t begin, const int64_t end)
static constexpr IndexRange from_begin_end_inclusive(const int64_t begin, const int64_t last)
constexpr int64_t size() const
Definition BLI_span.hh:252
void append(const T &value)
MutableSpan< float > nurbs_custom_knots_for_write()
MutableAttributeAccessor attributes_for_write()
Span< float > nurbs_custom_knots() const
AttributeAccessor attributes() const
IndexMask nurbs_custom_knot_curves(IndexMaskMemory &memory) const
bool nurbs_has_custom_knots() const
OffsetIndices< int > nurbs_custom_knots_by_curve() const
void foreach_index(Fn &&fn) const
uint pos
static char ** types
Definition makesdna.cc:71
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
void gather_group_to_group(const OffsetIndices< int > src_offsets, const OffsetIndices< int > dst_offsets, const IndexMask &selection, const Span< T > src, MutableSpan< T > dst)
Array< float3 > retrieve_all_positions(const bke::CurvesGeometry &curves, const IndexMask &curves_selection)
void write_all_positions(bke::CurvesGeometry &curves, const IndexMask &curves_selection, Span< float3 > all_positions)
void gather_custom_knots(const bke::CurvesGeometry &src, const IndexMask &src_curves, int dst_curve_offset, bke::CurvesGeometry &dst)
void update_custom_knot_modes(const IndexMask &mask, const KnotsMode mode_for_regular, const KnotsMode mode_for_cyclic, bke::CurvesGeometry &curves)
void copy_custom_knots(const bke::CurvesGeometry &src_curves, const IndexMask &exclude_curves, bke::CurvesGeometry &dst_curves)
void foreach_selected_point_ranges_per_curve(const IndexMask &mask, OffsetIndices< int > points_by_curve, SelectedCallback selected_fn)
bke::CurvesGeometry copy_only_curve_domain(const bke::CurvesGeometry &src_curves)
FunctionRef< void( int curve_i, IndexRange curve_points, Span< IndexRange > selected_point_ranges)> SelectedCallback
IndexMask indices_for_type(const VArray< int8_t > &types, const std::array< int, CURVE_TYPES_NUM > &type_counts, const CurveType type, const IndexMask &selection, IndexMaskMemory &memory)
static void foreach_selected_point_ranges_per_curve_(const IndexMask &mask, const OffsetIndices< int > points_by_curve, const SelectedCallback selected_fn, const Fn unselected_fn)
IndexMask curve_type_point_selection(const bke::CurvesGeometry &curves, CurveType curve_type, IndexMaskMemory &memory)
static void if_has_data_call_callback(const Span< int > offset_data, const int begin, const int end, UnselectedCallback callback)
IndexMask curve_to_point_selection(OffsetIndices< int > points_by_curve, const IndexMask &curve_selection, IndexMaskMemory &memory)
FunctionRef< void(IndexRange curves, IndexRange unselected_points)> UnselectedCallback
void fill_points(OffsetIndices< int > points_by_curve, const IndexMask &curve_selection, GPointer value, GMutableSpan dst)
void foreach_curve_by_type(const VArray< int8_t > &types, const std::array< int, CURVE_TYPES_NUM > &type_counts, const IndexMask &selection, FunctionRef< void(IndexMask)> catmull_rom_fn, FunctionRef< void(IndexMask)> poly_fn, FunctionRef< void(IndexMask)> bezier_fn, FunctionRef< void(IndexMask)> nurbs_fn)
void copy_attributes(const AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, MutableAttributeAccessor dst_attributes)
OffsetIndices< int > gather_selected_offsets(OffsetIndices< int > src_offsets, const IndexMask &selection, int start_offset, MutableSpan< int > dst_offsets)
CurvesGeometryRuntimeHandle * runtime
i
Definition text_draw.cc:230