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
8
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
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;
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) {
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)) {
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) {
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)) {
220 selection_name, bke::AttrDomain::Curve);
221 selection.span.take_back(mask.size()).fill(true);
222 selection.finish();
223 }
224}
225
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
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between and object coordinate space Combine Create a color from its and value channels Color Retrieve a color attribute
constexpr MutableSpan drop_back(const int64_t n) const
Definition BLI_span.hh:619
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:326
Span< T > as_span() const
Definition BLI_array.hh:232
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:237
void value_initialize_n(void *ptr, int64_t n) const
void copy_from(GSpan values)
GMutableSpan slice(const int64_t start, int64_t size) const
const CPPType & type() const
GSpan slice(const int64_t start, int64_t size) const
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
constexpr int64_t first() const
constexpr IndexRange drop_back(int64_t n) const
constexpr int64_t size() const
constexpr int64_t start() const
constexpr IndexRange take_back(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
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 lookup_or_default(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const void *default_value=nullptr) const
int domain_size(const AttrDomain domain) 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_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type)
GSpanAttributeWriter lookup_for_write_span(StringRef attribute_id)
std::optional< IndexRange > to_range() const
IndexMask complement(const IndexMask &universe, IndexMaskMemory &memory) const
void foreach_index(Fn &&fn) const
OffsetIndices slice(const IndexRange range) const
static bool is_cyclic(const Nurb *nu)
ccl_device_inline float4 mask(const int4 mask, const float4 a)
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)
GPU_SHADER_INTERFACE_INFO(overlay_edit_curve_handle_iface, "vert").flat(Type pos vertex_in(1, Type::UINT, "data") .vertex_out(overlay_edit_curve_handle_iface) .geometry_layout(PrimitiveIn Frequency::GEOMETRY storage_buf(1, Qualifier::READ, "uint", "data[]", Frequency::GEOMETRY) .push_constant(Type Frequency::GEOMETRY selection[]