Blender V4.3
curves_edit.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
9#include "BLI_array_utils.hh"
10
11#include "BKE_attribute.hh"
12#include "BKE_curves.hh"
13#include "BKE_curves_utils.hh"
14
15#include "ED_curves.hh"
16
17namespace blender::ed::curves {
18
19bool remove_selection(bke::CurvesGeometry &curves, const bke::AttrDomain selection_domain)
20{
21 const bke::AttributeAccessor attributes = curves.attributes();
22 const VArray<bool> selection = *attributes.lookup_or_default<bool>(
23 ".selection", selection_domain, true);
24 const int domain_size_orig = attributes.domain_size(selection_domain);
25 IndexMaskMemory memory;
26 const IndexMask mask = IndexMask::from_bools(selection, memory);
27 switch (selection_domain) {
29 curves.remove_points(mask, {});
30 break;
32 curves.remove_curves(mask, {});
33 break;
34 default:
36 }
37
38 return attributes.domain_size(selection_domain) != domain_size_orig;
39}
40
42{
43 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
44 const VArray<bool> src_cyclic = curves.cyclic();
45
46 Array<bool> points_to_duplicate(curves.points_num());
47 mask.to_bools(points_to_duplicate.as_mutable_span());
48 const int num_points_to_add = mask.size();
49
50 int curr_dst_point_start = 0;
51 Array<int> dst_to_src_point(num_points_to_add);
52 Vector<int> dst_curve_counts;
53 Vector<int> dst_to_src_curve;
54 Vector<bool> dst_cyclic;
55
56 /* Add the duplicated curves and points. */
57 for (const int curve_i : curves.curves_range()) {
58 const IndexRange points = points_by_curve[curve_i];
59 const Span<bool> curve_points_to_duplicate = points_to_duplicate.as_span().slice(points);
60 const bool curve_cyclic = src_cyclic[curve_i];
61
62 /* Note, these ranges start at zero and needed to be shifted by `points.first()` */
63 const Vector<IndexRange> ranges_to_duplicate = array_utils::find_all_ranges(
64 curve_points_to_duplicate, true);
65
66 if (ranges_to_duplicate.is_empty()) {
67 continue;
68 }
69
70 const bool is_last_segment_selected = curve_cyclic &&
71 ranges_to_duplicate.first().first() == 0 &&
72 ranges_to_duplicate.last().last() == points.size() - 1;
73 const bool is_curve_self_joined = is_last_segment_selected && ranges_to_duplicate.size() != 1;
74 const bool is_cyclic = ranges_to_duplicate.size() == 1 && is_last_segment_selected;
75
76 const IndexRange range_ids = ranges_to_duplicate.index_range();
77 /* Skip the first range because it is joined to the end of the last range. */
78 for (const int range_i : ranges_to_duplicate.index_range().drop_front(is_curve_self_joined)) {
79 const IndexRange range = ranges_to_duplicate[range_i];
80
82 dst_to_src_point.as_mutable_span().slice(curr_dst_point_start, range.size()),
83 range.start() + points.first());
84 curr_dst_point_start += range.size();
85
86 dst_curve_counts.append(range.size());
87 dst_to_src_curve.append(curve_i);
88 dst_cyclic.append(is_cyclic);
89 }
90
91 /* Join the first range to the end of the last range. */
92 if (is_curve_self_joined) {
93 const IndexRange first_range = ranges_to_duplicate[range_ids.first()];
95 dst_to_src_point.as_mutable_span().slice(curr_dst_point_start, first_range.size()),
96 first_range.start() + points.first());
97 curr_dst_point_start += first_range.size();
98 dst_curve_counts[dst_curve_counts.size() - 1] += first_range.size();
99 }
100 }
101
102 const int old_curves_num = curves.curves_num();
103 const int old_points_num = curves.points_num();
104 const int num_curves_to_add = dst_to_src_curve.size();
105
106 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
107
108 /* Delete selection attribute so that it will not have to be resized. */
109 remove_selection_attributes(attributes);
110
111 curves.resize(old_points_num + num_points_to_add, old_curves_num + num_curves_to_add);
112
113 MutableSpan<int> new_curve_offsets = curves.offsets_for_write();
114 array_utils::copy(dst_curve_counts.as_span(),
115 new_curve_offsets.drop_front(old_curves_num).drop_back(1));
116 offset_indices::accumulate_counts_to_offsets(new_curve_offsets.drop_front(old_curves_num),
117 old_points_num);
118
119 /* Transfer curve and point attributes. */
120 attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
121 bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(iter.name);
122 if (!attribute) {
123 return;
124 }
125
126 switch (iter.domain) {
128 if (iter.name == "cyclic") {
129 attribute.finish();
130 return;
131 }
133 attribute.span,
134 dst_to_src_curve,
135 attribute.span.slice(IndexRange(old_curves_num, num_curves_to_add)));
136 break;
137 }
140 attribute.span,
141 dst_to_src_point,
142 attribute.span.slice(IndexRange(old_points_num, num_points_to_add)));
143 break;
144 }
145 default: {
146 attribute.finish();
148 return;
149 }
150 }
151
152 attribute.finish();
153 });
154
155 if (!(src_cyclic.is_single() && !src_cyclic.get_internal_single())) {
156 array_utils::copy(dst_cyclic.as_span(), curves.cyclic_for_write().drop_front(old_curves_num));
157 }
158
159 curves.update_curve_types();
160 curves.tag_topology_changed();
161
162 for (const StringRef selection_name : get_curves_selection_attribute_names(curves)) {
163 bke::SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_span<bool>(
164 selection_name, bke::AttrDomain::Point);
165 selection.span.take_back(num_points_to_add).fill(true);
166 selection.finish();
167 }
168}
169
171{
172 const int orig_points_num = curves.points_num();
173 const int orig_curves_num = curves.curves_num();
174 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
175
176 /* Delete selection attribute so that it will not have to be resized. */
177 remove_selection_attributes(attributes);
178
179 /* Resize the curves and copy the offsets of duplicated curves into the new offsets. */
180 curves.resize(curves.points_num(), orig_curves_num + mask.size());
181 const IndexRange orig_curves_range = curves.curves_range().take_front(orig_curves_num);
182 const IndexRange new_curves_range = curves.curves_range().drop_front(orig_curves_num);
183
184 MutableSpan<int> offset_data = curves.offsets_for_write();
186 OffsetIndices<int>(offset_data.take_front(orig_curves_num + 1)),
187 mask,
188 orig_points_num,
189 offset_data.drop_front(orig_curves_num));
190 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
191
192 /* Resize the points array to match the new total point count. */
193 curves.resize(points_by_curve.total_size(), curves.curves_num());
194
195 attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
196 bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(iter.name);
197 switch (iter.domain) {
199 bke::attribute_math::gather_group_to_group(points_by_curve.slice(orig_curves_range),
200 points_by_curve.slice(new_curves_range),
201 mask,
202 attribute.span,
203 attribute.span);
204 break;
206 array_utils::gather(attribute.span, mask, attribute.span.take_back(mask.size()));
207 break;
208 default:
210 return;
211 }
212 attribute.finish();
213 });
214
215 curves.update_curve_types();
216 curves.tag_topology_changed();
217
218 for (const StringRef selection_name : get_curves_selection_attribute_names(curves)) {
219 bke::SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_span<bool>(
220 selection_name, bke::AttrDomain::Curve);
221 selection.span.take_back(mask.size()).fill(true);
222 selection.finish();
223 }
224}
225
226void add_curves(bke::CurvesGeometry &curves, const Span<int> new_sizes)
227{
228 const int orig_points_num = curves.points_num();
229 const int orig_curves_num = curves.curves_num();
230 curves.resize(orig_points_num, orig_curves_num + new_sizes.size());
231
232 /* Find the final number of points by accumulating the new */
233 MutableSpan<int> new_offsets = curves.offsets_for_write().drop_front(orig_curves_num);
234 new_offsets.drop_back(1).copy_from(new_sizes);
235 offset_indices::accumulate_counts_to_offsets(new_offsets, orig_points_num);
236 /* First, resize the curve domain. */
237 curves.resize(curves.offsets().last(), curves.curves_num());
238
239 /* Initialize new attribute values, since #CurvesGeometry::resize() doesn't do that. */
240 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
242 attributes, bke::AttrDomain::Point, {}, curves.points_range().drop_front(orig_points_num));
244 attributes, bke::AttrDomain::Curve, {}, curves.curves_range().drop_front(orig_curves_num));
245
246 curves.update_curve_types();
247}
248
250 const IndexMask &curves_to_resize,
251 const Span<int> new_sizes)
252{
253 if (curves_to_resize.is_empty()) {
254 return;
255 }
256 BLI_assert(curves_to_resize.size() == new_sizes.size());
258
259 IndexMaskMemory memory;
260 IndexMask curves_to_copy;
261 std::optional<IndexRange> range = curves_to_resize.to_range();
262 /* Check if we need to copy some curves over. Write the new sizes into the offsets. */
263 if (range && curves.curves_range() == *range) {
264 curves_to_copy = {};
265 dst_curves.offsets_for_write().drop_back(1).copy_from(new_sizes);
266 }
267 else {
268 curves_to_copy = curves_to_resize.complement(curves.curves_range(), memory);
270 curves.offsets(), curves_to_copy, dst_curves.offsets_for_write());
271 array_utils::scatter(new_sizes, curves_to_resize, dst_curves.offsets_for_write());
272 }
273 /* Accumulate the sizes written from `new_sizes` into offsets. */
275
276 /* Resize the points domain.*/
277 dst_curves.resize(dst_curves.offsets().last(), dst_curves.curves_num());
278
279 /* Copy point attributes and default initialize newly added point ranges. */
281 const OffsetIndices<int> src_offsets = curves.points_by_curve();
282 const OffsetIndices<int> dst_offsets = dst_curves.points_by_curve();
283 const bke::AttributeAccessor src_attributes = curves.attributes();
284 bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
285 src_attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
286 if (iter.domain != domain || bke::attribute_name_is_anonymous(iter.name)) {
287 return;
288 }
289 const GVArraySpan src = *iter.get(domain);
290 const CPPType &type = src.type();
292 iter.name, domain, iter.data_type);
293 if (!dst) {
294 return;
295 }
296
297 curves_to_resize.foreach_index(GrainSize(512), [&](const int curve_i) {
298 const IndexRange src_points = src_offsets[curve_i];
299 const IndexRange dst_points = dst_offsets[curve_i];
300 if (dst_points.size() < src_points.size()) {
301 const int src_excees = src_points.size() - dst_points.size();
302 dst.span.slice(dst_points).copy_from(src.slice(src_points.drop_back(src_excees)));
303 }
304 else {
305 const int dst_excees = dst_points.size() - src_points.size();
306 dst.span.slice(dst_points.drop_back(dst_excees)).copy_from(src.slice(src_points));
307 GMutableSpan dst_end_slice = dst.span.slice(dst_points.take_back(dst_excees));
308 type.value_initialize_n(dst_end_slice.data(), dst_end_slice.size());
309 }
310 });
311 array_utils::copy_group_to_group(src_offsets, dst_offsets, curves_to_copy, src, dst.span);
312 dst.finish();
313 });
314
315 dst_curves.update_curve_types();
316
317 /* Move the result into `curves`. */
318 curves = std::move(dst_curves);
319 curves.tag_topology_changed();
320}
321
322} // namespace blender::ed::curves
Low-level operations for curves.
Low-level operations for curves.
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
Span< T > as_span() const
Definition BLI_array.hh:232
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:237
constexpr int64_t first() const
constexpr int64_t size() const
constexpr int64_t start() const
constexpr IndexRange take_front(int64_t n) const
constexpr IndexRange drop_front(int64_t n) const
constexpr MutableSpan drop_back(const int64_t n) const
Definition BLI_span.hh:619
constexpr MutableSpan drop_front(const int64_t n) const
Definition BLI_span.hh:608
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:726
constexpr MutableSpan take_front(const int64_t n) const
Definition BLI_span.hh:630
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:326
int64_t size() const
void append(const T &value)
const T & last(const int64_t n=0) const
bool is_empty() const
IndexRange index_range() const
Span< T > as_span() const
const T & first() const
void foreach_attribute(const FunctionRef< void(const AttributeIter &)> fn) const
GAttributeReader get() const
OffsetIndices< int > points_by_curve() const
MutableAttributeAccessor attributes_for_write()
void resize(int points_num, int curves_num)
MutableSpan< int > offsets_for_write()
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type)
std::optional< IndexRange > to_range() const
IndexMask complement(const IndexMask &universe, IndexMaskMemory &memory) const
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
void foreach_index(Fn &&fn) const
OffsetIndices slice(const IndexRange range) const
static bool is_cyclic(const Nurb *nu)
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size=4096)
void scatter(const Span< T > src, const Span< IndexT > indices, MutableSpan< T > dst, const int64_t grain_size=4096)
void copy_group_to_group(OffsetIndices< int > src_offsets, OffsetIndices< int > dst_offsets, const IndexMask &selection, GSpan src, GMutableSpan dst)
Vector< IndexRange > find_all_ranges(const Span< T > span, const T &value)
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
void fill_index_range(MutableSpan< T > span, const T start=0)
void gather(GSpan src, Span< int > map, GMutableSpan dst)
void gather_group_to_group(OffsetIndices< int > src_offsets, OffsetIndices< int > dst_offsets, const IndexMask &selection, GSpan src, GMutableSpan dst)
bke::CurvesGeometry copy_only_curve_domain(const bke::CurvesGeometry &src_curves)
bool attribute_name_is_anonymous(const StringRef name)
void fill_attribute_range_default(MutableAttributeAccessor dst_attributes, AttrDomain domain, const AttributeFilter &attribute_filter, IndexRange range)
bool remove_selection(bke::CurvesGeometry &curves, const bke::AttrDomain selection_domain)
void resize_curves(bke::CurvesGeometry &curves, const IndexMask &curves_to_resize, const Span< int > new_sizes)
void remove_selection_attributes(bke::MutableAttributeAccessor &attributes, Span< StringRef > selection_attribute_names)
void duplicate_curves(bke::CurvesGeometry &curves, const IndexMask &mask)
void duplicate_points(bke::CurvesGeometry &curves, const IndexMask &mask)
void add_curves(bke::CurvesGeometry &curves, const Span< int > new_sizes)
Span< StringRef > get_curves_selection_attribute_names(const bke::CurvesGeometry &curves)
void copy_group_sizes(OffsetIndices< int > offsets, const IndexMask &mask, MutableSpan< int > sizes)
OffsetIndices< int > accumulate_counts_to_offsets(MutableSpan< int > counts_to_offsets, int start_offset=0)
OffsetIndices< int > gather_selected_offsets(OffsetIndices< int > src_offsets, const IndexMask &selection, int start_offset, MutableSpan< int > dst_offsets)