Blender V5.0
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
12#include "BKE_attribute.hh"
13#include "BKE_curves.hh"
14#include "BKE_curves_utils.hh"
15#include "BKE_deform.hh"
16
17#include "GEO_reorder.hh"
18
19#include "ED_curves.hh"
20
21namespace blender::ed::curves {
22
24{
25 const bke::AttributeAccessor attributes = curves.attributes();
26 const VArray<bool> selection = *attributes.lookup_or_default<bool>(
27 ".selection", selection_domain, true);
28 const int domain_size_orig = attributes.domain_size(selection_domain);
29 IndexMaskMemory memory;
30 const IndexMask mask = IndexMask::from_bools(selection, memory);
31 switch (selection_domain) {
33 curves.remove_points(mask, {});
34 break;
36 curves.remove_curves(mask, {});
37 break;
38 default:
40 }
41
42 return attributes.domain_size(selection_domain) != domain_size_orig;
43}
44
45static void curve_offsets_from_selection(const Span<IndexRange> selected_points,
46 const IndexRange points,
47 const int curve,
48 const bool cyclic,
49 Vector<int> &r_new_curve_offsets,
50 Vector<bool> &r_new_cyclic,
51 Vector<IndexRange> &r_src_ranges,
52 Vector<int> &r_dst_offsets,
53 Vector<int> &r_dst_to_src_curve)
54{
55 if (selected_points.is_empty()) {
56 return;
57 }
58 const bool merge_loop = cyclic && selected_points.first().size() < points.size() &&
59 selected_points.first().first() == points.first() &&
60 selected_points.last().last() == points.last();
61
62 int last_dst_offset = r_dst_offsets.last();
63 int last_curve_offset = r_new_curve_offsets.last();
64 for (const IndexRange range : selected_points.drop_front(merge_loop)) {
65 r_src_ranges.append(range);
66 last_dst_offset += range.size();
67 r_dst_offsets.append(last_dst_offset);
68 last_curve_offset += range.size();
69 r_new_curve_offsets.append(last_curve_offset);
70 };
71 if (merge_loop) {
72 const IndexRange merge_to_end = selected_points.first();
73 r_src_ranges.append(merge_to_end);
74 r_dst_offsets.append(last_dst_offset + merge_to_end.size());
75 r_new_curve_offsets.last() += merge_to_end.size();
76 }
77 const int curves_added = selected_points.size() - merge_loop;
78 r_dst_to_src_curve.append_n_times(curve, curves_added);
79 r_new_cyclic.append_n_times(cyclic && selected_points.first().size() == points.size(),
80 curves_added);
81}
82
83static void append_point_knots(const Span<IndexRange> src_ranges,
84 const OffsetIndices<int> dst_offsets,
85 const Span<int> dst_to_src_curve,
86 const bke::CurvesGeometry &src_curves,
88{
89 curves.nurbs_custom_knots_update_size();
90
91 const Span<int> src_points_by_curve = src_curves.points_by_curve().data();
92 const Span<int> src_knots_by_curve = src_curves.nurbs_custom_knots_by_curve().data();
93 const VArray<int8_t> src_orders = src_curves.nurbs_orders();
94 const VArray<int8_t> knot_modes = curves.nurbs_knots_modes();
95 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
96 const OffsetIndices<int> knots_by_curve = curves.nurbs_custom_knots_by_curve();
97 MutableSpan<float> dst_knots = curves.nurbs_custom_knots_for_write();
98 /* Source knots must be defined after destination knots, because when `src_curves` == `curves`
99 * call to `nurbs_custom_knots_for_write()` might invalidate the result of previously called
100 * `nurbs_custom_knots()`. */
101 const Span<float> src_knots = src_curves.nurbs_custom_knots();
102
103 const int old_curves_num = curves.curves_num() - dst_to_src_curve.size();
104
105 int range = 0;
106 for (const int appended_curve : dst_to_src_curve.index_range()) {
107 const int dst_curve = appended_curve + old_curves_num;
108 if (knot_modes[dst_curve] != NURBS_KNOT_MODE_CUSTOM) {
109 range++;
110 continue;
111 }
112 const int src_curve = dst_to_src_curve[appended_curve];
113 const int order = src_orders[src_curve];
114 const int first_curve_point = src_points_by_curve[src_curve];
115 const int first_curve_knot = src_knots_by_curve[src_curve];
116 const int point_to_knot = -first_curve_point + first_curve_knot;
117 const IndexRange src_range = src_ranges[range];
118 const IndexRange src_knot_range = IndexRange::from_begin_size(
119 src_range.first() + point_to_knot, src_range.size() + order);
120 const IndexRange dst_knot_range = knots_by_curve[dst_curve];
121 dst_knots.slice(dst_knot_range.take_front(src_knot_range.size()))
122 .copy_from(src_knots.slice(src_knot_range));
123 if (dst_offsets[range].size() != points_by_curve[dst_curve].size()) {
124 range++;
125 const IndexRange merged_tail = src_ranges[range];
126 const IndexRange src_tail_knots = merged_tail.shift(point_to_knot + order);
127 const IndexRange dst_tail_knots = dst_knot_range.take_back(src_tail_knots.size());
128 const float knot_shift = dst_knots[dst_tail_knots.one_before_start()] -
129 src_knots[src_tail_knots.one_before_start()];
130 for (const int i : src_tail_knots.index_range()) {
131 dst_knots[dst_tail_knots[i]] = src_knots[src_tail_knots[i]] + knot_shift;
132 }
133 }
134 range++;
135 }
136}
137
139{
140 if (curves.is_empty()) {
141 return;
142 }
143 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
144 const VArray<bool> src_cyclic = curves.cyclic();
145
146 Vector<int> dst_to_src_curve;
147 Vector<int> new_curve_offsets({points_by_curve.data().last()});
148 Vector<IndexRange> src_ranges;
149 Vector<int> dst_offsets({0});
150 Vector<bool> dst_cyclic;
151 dst_to_src_curve.reserve(curves.curves_num());
152 new_curve_offsets.reserve(curves.curves_num() + 1);
153 src_ranges.reserve(curves.curves_num());
154 dst_offsets.reserve(curves.curves_num() + 1);
155 dst_cyclic.reserve(curves.curves_num());
156
157 /* Add the duplicated curves and points. */
159 mask,
160 points_by_curve,
161 [&](const int curve, const IndexRange points, Span<IndexRange> ranges_to_duplicate) {
162 curve_offsets_from_selection(ranges_to_duplicate,
163 points,
164 curve,
165 src_cyclic[curve],
166 new_curve_offsets,
167 dst_cyclic,
168 src_ranges,
169 dst_offsets,
170 dst_to_src_curve);
171 });
172
173 const int old_curves_num = curves.curves_num();
174 const int old_points_num = curves.points_num();
175 const int num_curves_to_add = dst_to_src_curve.size();
176 const int num_points_to_add = mask.size();
177
178 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
179
180 /* Delete selection attribute so that it will not have to be resized. */
181 remove_selection_attributes(attributes);
182
183 curves.resize(old_points_num + num_points_to_add, old_curves_num + num_curves_to_add);
184
185 array_utils::copy(new_curve_offsets.as_span(),
186 curves.offsets_for_write().drop_front(old_curves_num));
187
188 /* Transfer curve and point attributes. */
189 attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
190 bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(iter.name);
191 if (!attribute) {
192 return;
193 }
194
195 switch (iter.domain) {
197 if (iter.name == "cyclic") {
198 attribute.finish();
199 return;
200 }
202 attribute.span,
203 dst_to_src_curve,
204 attribute.span.slice(IndexRange(old_curves_num, num_curves_to_add)));
205 break;
206 }
209 src_ranges.as_span(),
210 dst_offsets.as_span(),
211 attribute.span,
212 attribute.span.slice(IndexRange(old_points_num, num_points_to_add)));
213 break;
214 }
215 default: {
216 attribute.finish();
218 return;
219 }
220 }
221
222 attribute.finish();
223 });
224
225 if (!(src_cyclic.is_single() && !src_cyclic.get_internal_single())) {
226 array_utils::copy(dst_cyclic.as_span(), curves.cyclic_for_write().drop_front(old_curves_num));
227 }
228
229 curves.update_curve_types();
230 curves.tag_topology_changed();
231
232 if (curves.nurbs_has_custom_knots()) {
233 append_point_knots(src_ranges, dst_offsets.as_span(), dst_to_src_curve, curves, curves);
234 }
235
236 for (const StringRef selection_name : get_curves_selection_attribute_names(curves)) {
238 selection_name, bke::AttrDomain::Point);
239 selection.span.take_back(num_points_to_add).fill(true);
240 selection.finish();
241 }
242}
243
245{
246 curves.nurbs_custom_knots_update_size();
247 const int old_curves_num = curves.curves_num() - mask.size();
249}
250
252{
253 const int orig_points_num = curves.points_num();
254 const int orig_curves_num = curves.curves_num();
255 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
256
257 /* Delete selection attribute so that it will not have to be resized. */
258 remove_selection_attributes(attributes);
259
260 /* Resize the curves and copy the offsets of duplicated curves into the new offsets. */
261 curves.resize(curves.points_num(), orig_curves_num + mask.size());
262 const IndexRange orig_curves_range = curves.curves_range().take_front(orig_curves_num);
263 const IndexRange new_curves_range = curves.curves_range().drop_front(orig_curves_num);
264
265 MutableSpan<int> offset_data = curves.offsets_for_write();
267 OffsetIndices<int>(offset_data.take_front(orig_curves_num + 1)),
268 mask,
269 orig_points_num,
270 offset_data.drop_front(orig_curves_num));
271 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
272
273 /* Resize the points array to match the new total point count. */
274 curves.resize(points_by_curve.total_size(), curves.curves_num());
275
276 attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
277 bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(iter.name);
278 switch (iter.domain) {
280 bke::attribute_math::gather_group_to_group(points_by_curve.slice(orig_curves_range),
281 points_by_curve.slice(new_curves_range),
282 mask,
283 attribute.span,
284 attribute.span);
285 break;
287 array_utils::gather(attribute.span, mask, attribute.span.take_back(mask.size()));
288 break;
289 default:
291 return;
292 }
293 attribute.finish();
294 });
295
296 curves.update_curve_types();
297 curves.tag_topology_changed();
298
299 if (curves.nurbs_has_custom_knots()) {
301 }
302
303 for (const StringRef selection_name : get_curves_selection_attribute_names(curves)) {
305 selection_name, bke::AttrDomain::Curve);
306 selection.span.take_back(mask.size()).fill(true);
307 selection.finish();
308 }
309}
310
311static void invert_ranges(const IndexRange universe,
312 const Span<IndexRange> ranges,
313 Array<IndexRange> &inverted)
314{
315 const bool contains_first = ranges.first().first() == universe.first();
316 const bool contains_last = ranges.last().last() == universe.last();
317 inverted.reinitialize(ranges.size() - 1 + !contains_first + !contains_last);
318
319 int64_t start = contains_first ? ranges.first().one_after_last() : universe.first();
320 int i = 0;
321 for (const IndexRange range : ranges.drop_front(contains_first)) {
322 inverted[i++] = IndexRange::from_begin_end(start, range.first());
323 start = range.one_after_last();
324 }
325 if (!contains_last) {
326 inverted.last() = IndexRange::from_begin_end(start, universe.one_after_last());
327 }
328}
329
330static IndexRange extend_range(const IndexRange range, const IndexRange universe)
331{
332 return IndexRange::from_begin_end_inclusive(math::max(range.start() - 1, universe.start()),
333 math::min(range.one_after_last(), universe.last()));
334}
335
341 const bool cyclic,
342 const Span<IndexRange> ranges,
343 Vector<IndexRange> &extended_ranges)
344{
345 extended_ranges.clear();
346 if (ranges.is_empty()) {
347 return;
348 }
349
350 const bool first_match = ranges.first().first() == universe.first();
351 const bool last_match = ranges.last().last() == universe.last();
352 const bool add_first = cyclic && last_match && !first_match;
353 const bool add_last = cyclic && first_match && !last_match;
354
355 IndexRange current = add_first ? IndexRange::from_single(universe.first()) :
356 extend_range(ranges.first(), universe);
357 for (const IndexRange range : ranges.drop_front(!add_first)) {
358 const IndexRange extended = extend_range(range, universe);
359 if (extended.first() <= current.last()) {
360 current = IndexRange::from_begin_end_inclusive(current.start(), extended.last());
361 }
362 else {
363 extended_ranges.append(current);
364 current = extended;
365 }
366 }
367 extended_ranges.append(current);
368 if (add_last) {
369 extended_ranges.append(IndexRange::from_single(universe.last()));
370 }
371}
372
374 const Span<int> dst_to_src_curve,
375 const Span<int> offsets,
376 const Span<bool> cyclic,
377 const Span<IndexRange> src_ranges,
378 const OffsetIndices<int> dst_offsets)
379{
380 bke::CurvesGeometry dst_curves(offsets.last(), dst_to_src_curve.size());
382
383 if (!dst_curves.is_empty()) {
384 array_utils::copy(offsets, dst_curves.offsets_for_write());
385 }
386 dst_curves.cyclic_for_write().copy_from(cyclic);
387
388 const bke::AttributeAccessor src_attributes = src_curves.attributes();
389 bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
390
391 bke::gather_attributes(src_attributes,
395 dst_to_src_curve,
396 dst_attributes);
397
398 for (auto &attribute : bke::retrieve_attributes_for_transfer(
399 src_attributes,
400 dst_attributes,
404 {
406 src_ranges, dst_offsets, attribute.src, attribute.dst.span);
407 attribute.dst.finish();
408 };
409
410 dst_curves.update_curve_types();
411
412 if (src_curves.nurbs_has_custom_knots()) {
413 append_point_knots(src_ranges, dst_offsets, dst_to_src_curve, src_curves, dst_curves);
414 }
415 return dst_curves;
416}
417
419 const IndexMask &points_to_split)
420{
421 const OffsetIndices points_by_curve = curves.points_by_curve();
422 const VArray<bool> cyclic = curves.cyclic();
423
424 Vector<int> curve_map;
425 Vector<int> new_offsets({0});
426
427 Vector<IndexRange> src_ranges;
428 Vector<int> dst_offsets({0});
429 Vector<bool> new_cyclic;
430
431 Vector<IndexRange> deselect;
432
433 Array<IndexRange> unselected_curve_points;
434 Vector<IndexRange> curve_points_to_preserve;
435
437 points_to_split,
438 points_by_curve,
439 [&](const int curve, const IndexRange points, const Span<IndexRange> selected_curve_points) {
440 curve_offsets_from_selection(selected_curve_points,
441 points,
442 curve,
443 cyclic[curve],
444 new_offsets,
445 new_cyclic,
446 src_ranges,
447 dst_offsets,
448 curve_map);
449 /* Invert ranges to get non selected points. */
450 invert_ranges(points, selected_curve_points, unselected_curve_points);
451 /* Extended every range to left and right by one point. Any resulting intersection is
452 * merged. */
454 points, cyclic[curve], unselected_curve_points, curve_points_to_preserve);
455 const int size_before = curve_map.size();
456 /* Unselected part can contain all points from original curve, but have cuts. This happens
457 * when pairs of adjacent points are selected. To prevent loop merge and result curve from
458 * cyclic additional condition is checked. */
459 const bool can_merge_loop = !unselected_curve_points.is_empty() &&
460 (unselected_curve_points.first().first() == points.first() ||
461 unselected_curve_points.last().last() == points.last());
462 curve_offsets_from_selection(curve_points_to_preserve,
463 points,
464 curve,
465 cyclic[curve] && can_merge_loop,
466 new_offsets,
467 new_cyclic,
468 src_ranges,
469 dst_offsets,
470 curve_map);
471 deselect.append(IndexRange::from_begin_end(size_before, curve_map.size()));
472 },
473 [&](const IndexRange curves, const IndexRange /*unselected_points*/) {
474 deselect.append(IndexRange::from_begin_size(curve_map.size(), curves.size()));
475 int last_offset = new_offsets.last();
476 int last_dst_offset = dst_offsets.last();
477 for (const int curve : curves) {
478 /* Point ranges to `src_ranges` and `dst_offsets` have to be appended curve by curve to
479 * ease custom knots copying. It gives better mapping between `src_ranges` and
480 * `curve_map`. */
481 const IndexRange points = points_by_curve[curve];
482 src_ranges.append(points);
483 last_dst_offset += points.size();
484 dst_offsets.append(last_dst_offset);
485
486 last_offset += points.size();
487 new_offsets.append(last_offset);
488 curve_map.append(curve);
489 new_cyclic.append(cyclic[curve]);
490 }
491 });
492
494 curves, curve_map, new_offsets, new_cyclic, src_ranges, dst_offsets.as_span());
495
496 const OffsetIndices<int> new_points_by_curve = new_curves.points_by_curve();
498 new_curves, bke::AttrDomain::Point, [&](bke::GSpanAttributeWriter &selection) {
499 for (const IndexRange curves : deselect) {
500 for (const int curve : curves) {
501 fill_selection_false(selection.span.slice(new_points_by_curve[curve]));
502 }
503 }
504 });
505
506 return new_curves;
507}
508
510 const IndexMask &points_to_separate,
511 bke::CurvesGeometry &separated,
512 bke::CurvesGeometry &retained)
513{
514 const OffsetIndices points_by_curve = curves.points_by_curve();
515 const VArray<bool> cyclic = curves.cyclic();
516
517 Vector<int> separated_curve_map;
518 Vector<int> separated_offsets({0});
519 Vector<IndexRange> separated_src_ranges;
520 Vector<int> separated_dst_offsets({0});
521 Vector<bool> separated_cyclic;
522
523 Vector<int> retained_curve_map;
524 Vector<int> retained_offsets({0});
525 Vector<IndexRange> retained_src_ranges;
526 Vector<int> retained_dst_offsets({0});
527 Vector<bool> retained_cyclic;
528
529 Array<IndexRange> unselected_curve_points;
530 Vector<IndexRange> curve_points_to_retain;
531
533 points_to_separate,
534 points_by_curve,
535 [&](const int curve, const IndexRange points, const Span<IndexRange> selected_curve_points) {
536 curve_offsets_from_selection(selected_curve_points,
537 points,
538 curve,
539 cyclic[curve],
540 separated_offsets,
541 separated_cyclic,
542 separated_src_ranges,
543 separated_dst_offsets,
544 separated_curve_map);
545 /* Invert ranges to get non selected points. */
546 invert_ranges(points, selected_curve_points, unselected_curve_points);
547 /* Extended every range to left and right by one point. Any resulting intersection is
548 * merged. */
550 points, cyclic[curve], unselected_curve_points, curve_points_to_retain);
551 /* Unselected part can contain all points from original curve, but have cuts. This happens
552 * when pairs of adjacent points are selected. To prevent loop merge and result curve from
553 * cyclic additional condition is checked. */
554 const bool can_merge_loop = !unselected_curve_points.is_empty() &&
555 (unselected_curve_points.first().first() == points.first() ||
556 unselected_curve_points.last().last() == points.last());
557 curve_offsets_from_selection(curve_points_to_retain,
558 points,
559 curve,
560 cyclic[curve] && can_merge_loop,
561 retained_offsets,
562 retained_cyclic,
563 retained_src_ranges,
564 retained_dst_offsets,
565 retained_curve_map);
566 },
567 [&](const IndexRange curves, const IndexRange /*unselected_points*/) {
568 int last_offset = retained_offsets.last();
569 int last_dst_offset = retained_dst_offsets.last();
570 for (const int curve : curves) {
571 /* Point ranges to `retained_src_ranges` and `retained_dst_offsets` have to be appended
572 * curve by curve to ease custom knots copying. It gives better mapping between
573 * `retained_src_ranges` and `retained_curve_map`. */
574 const IndexRange points = points_by_curve[curve];
575 retained_src_ranges.append(points);
576 last_dst_offset += points.size();
577 retained_dst_offsets.append(last_dst_offset);
578
579 last_offset += points.size();
580 retained_offsets.append(last_offset);
581 retained_curve_map.append(curve);
582 retained_cyclic.append(cyclic[curve]);
583 }
584 });
585 {
587 remove_selection_attributes(attributes);
588
589 separated = copy_data_to_geometry(curves,
590 separated_curve_map,
591 separated_offsets,
592 separated_cyclic,
593 separated_src_ranges,
594 separated_dst_offsets.as_span());
595 }
596 {
598 remove_selection_attributes(attributes);
599
600 retained = copy_data_to_geometry(curves,
601 retained_curve_map,
602 retained_offsets,
603 retained_cyclic,
604 retained_src_ranges,
605 retained_dst_offsets.as_span());
606 }
607
609 retained, bke::AttrDomain::Point, [&](bke::GSpanAttributeWriter &selection) {
610 fill_selection_false(selection.span);
611 });
612}
613
615{
616 const int orig_points_num = curves.points_num();
617 const int orig_curves_num = curves.curves_num();
618 curves.resize(orig_points_num, orig_curves_num + new_sizes.size());
619
620 /* Find the final number of points by accumulating the new */
621 MutableSpan<int> new_offsets = curves.offsets_for_write().drop_front(orig_curves_num);
622 new_offsets.drop_back(1).copy_from(new_sizes);
623 offset_indices::accumulate_counts_to_offsets(new_offsets, orig_points_num);
624 /* First, resize the curve domain. */
625 curves.resize(curves.offsets().last(), curves.curves_num());
626
627 /* Initialize new attribute values, since #CurvesGeometry::resize() doesn't do that. */
628 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
630 attributes, bke::AttrDomain::Point, {}, curves.points_range().drop_front(orig_points_num));
632 attributes, bke::AttrDomain::Curve, {}, curves.curves_range().drop_front(orig_curves_num));
633
634 curves.update_curve_types();
635}
636
638 const IndexMask &curves_to_resize,
639 const Span<int> new_sizes)
640{
641 if (curves_to_resize.is_empty()) {
642 return;
643 }
644 BLI_assert(curves_to_resize.size() == new_sizes.size());
646
647 IndexMaskMemory memory;
648 IndexMask curves_to_copy;
649 std::optional<IndexRange> range = curves_to_resize.to_range();
650 /* Check if we need to copy some curves over. Write the new sizes into the offsets. */
651 if (range && curves.curves_range() == *range) {
652 curves_to_copy = {};
653 dst_curves.offsets_for_write().drop_back(1).copy_from(new_sizes);
654 }
655 else {
656 curves_to_copy = curves_to_resize.complement(curves.curves_range(), memory);
658 curves.offsets(), curves_to_copy, dst_curves.offsets_for_write());
659 array_utils::scatter(new_sizes, curves_to_resize, dst_curves.offsets_for_write());
660 }
661 /* Accumulate the sizes written from `new_sizes` into offsets. */
663
664 /* Resize the points domain. */
665 dst_curves.resize(dst_curves.offsets().last(), dst_curves.curves_num());
666
667 /* Copy point attributes and default initialize newly added point ranges. */
669 const OffsetIndices<int> src_offsets = curves.points_by_curve();
670 const OffsetIndices<int> dst_offsets = dst_curves.points_by_curve();
671 const bke::AttributeAccessor src_attributes = curves.attributes();
672 bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
673 src_attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
674 if (iter.domain != domain || bke::attribute_name_is_anonymous(iter.name)) {
675 return;
676 }
677 const GVArraySpan src = *iter.get(domain);
678 const CPPType &type = src.type();
680 iter.name, domain, iter.data_type);
681 if (!dst) {
682 return;
683 }
684
685 curves_to_resize.foreach_index(GrainSize(512), [&](const int curve_i) {
686 const IndexRange src_points = src_offsets[curve_i];
687 const IndexRange dst_points = dst_offsets[curve_i];
688 if (dst_points.size() < src_points.size()) {
689 const int src_excees = src_points.size() - dst_points.size();
690 dst.span.slice(dst_points).copy_from(src.slice(src_points.drop_back(src_excees)));
691 }
692 else {
693 const int dst_excees = dst_points.size() - src_points.size();
694 dst.span.slice(dst_points.drop_back(dst_excees)).copy_from(src.slice(src_points));
695 GMutableSpan dst_end_slice = dst.span.slice(dst_points.take_back(dst_excees));
696 type.value_initialize_n(dst_end_slice.data(), dst_end_slice.size());
697 }
698 });
699 array_utils::copy_group_to_group(src_offsets, dst_offsets, curves_to_copy, src, dst.span);
700 dst.finish();
701 });
702
703 dst_curves.update_curve_types();
704 if (dst_curves.nurbs_has_custom_knots()) {
707 }
708
709 /* Move the result into `curves`. */
710 curves = std::move(dst_curves);
711 curves.tag_topology_changed();
712}
713
714void reorder_curves(bke::CurvesGeometry &curves, const Span<int> old_by_new_indices_map)
715{
716 curves = geometry::reorder_curves_geometry(curves, old_by_new_indices_map, {});
717}
718
719} // namespace blender::ed::curves
@ ATTR_DOMAIN_MASK_POINT
Low-level operations for curves.
Low-level operations for curves.
support for deformation groups and hooks.
void BKE_defgroup_copy_list(ListBase *outbase, const ListBase *inbase)
Definition deform.cc:73
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
@ NURBS_KNOT_MODE_NORMAL
@ NURBS_KNOT_MODE_CUSTOM
long long int int64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
constexpr MutableSpan drop_back(const int64_t n) const
Definition BLI_span.hh:618
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:739
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:325
const T & last(const int64_t n=0) const
Definition BLI_array.hh:296
const T & first() const
Definition BLI_array.hh:281
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:419
bool is_empty() const
Definition BLI_array.hh:264
void value_initialize_n(void *ptr, int64_t n) const
void copy_from(GSpan values)
GMutableSpan take_back(const int64_t n) const
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 one_before_start() const
constexpr int64_t first() const
constexpr int64_t one_after_last() const
constexpr IndexRange shift(int64_t n) const
constexpr IndexRange drop_back(int64_t n) const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
static constexpr IndexRange from_begin_end(const int64_t begin, const int64_t end)
static constexpr IndexRange from_begin_size(const int64_t begin, const int64_t size)
constexpr int64_t start() const
constexpr IndexRange take_back(int64_t n) const
static constexpr IndexRange from_single(const int64_t index)
constexpr IndexRange index_range() const
constexpr IndexRange take_front(int64_t n) const
static constexpr IndexRange from_begin_end_inclusive(const int64_t begin, const int64_t last)
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
constexpr MutableSpan drop_back(const int64_t n) const
Definition BLI_span.hh:618
constexpr MutableSpan drop_front(const int64_t n) const
Definition BLI_span.hh:607
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:739
constexpr MutableSpan take_front(const int64_t n) const
Definition BLI_span.hh:629
constexpr Span drop_front(int64_t n) const
Definition BLI_span.hh:171
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
constexpr const T & first() const
Definition BLI_span.hh:315
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:325
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
int64_t size() const
void append(const T &value)
const T & last(const int64_t n=0) const
void reserve(const int64_t min_capacity)
Span< T > as_span() const
void append_n_times(const T &value, const int64_t n)
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, AttrType data_type, const void *default_value=nullptr) const
void foreach_attribute(const FunctionRef< void(const AttributeIter &)> fn) const
int domain_size(const AttrDomain domain) const
GAttributeReader get() const
OffsetIndices< int > points_by_curve() const
IndexRange curves_range() const
MutableAttributeAccessor attributes_for_write()
Span< float > nurbs_custom_knots() const
void resize(int points_num, int curves_num)
AttributeAccessor attributes() const
MutableSpan< int > offsets_for_write()
bool nurbs_has_custom_knots() const
OffsetIndices< int > nurbs_custom_knots_by_curve() const
MutableSpan< bool > cyclic_for_write()
VArray< int8_t > nurbs_orders() const
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, AttrType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, AttrType 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
ccl_device_inline float2 mask(const MaskType mask, const float2 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)
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
void gather(GSpan src, Span< int > map, GMutableSpan dst)
void gather_ranges_to_groups(Span< IndexRange > src_ranges, OffsetIndices< int > dst_offsets, GSpan src, GMutableSpan dst)
void gather_group_to_group(OffsetIndices< int > src_offsets, OffsetIndices< int > dst_offsets, const IndexMask &selection, GSpan src, GMutableSpan dst)
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 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)
bool attribute_name_is_anonymous(const StringRef name)
Vector< AttributeTransferData > retrieve_attributes_for_transfer(const AttributeAccessor src_attributes, MutableAttributeAccessor dst_attributes, AttrDomainMask domain_mask, const AttributeFilter &attribute_filter={})
auto attribute_filter_from_skip_ref(const Span< StringRef > skip)
void fill_attribute_range_default(MutableAttributeAccessor dst_attributes, AttrDomain domain, const AttributeFilter &attribute_filter, IndexRange range)
void gather_attributes(AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, const IndexMask &selection, MutableAttributeAccessor dst_attributes)
bool remove_selection(bke::CurvesGeometry &curves, const bke::AttrDomain selection_domain)
static bke::CurvesGeometry copy_data_to_geometry(const bke::CurvesGeometry &src_curves, const Span< int > dst_to_src_curve, const Span< int > offsets, const Span< bool > cyclic, const Span< IndexRange > src_ranges, const OffsetIndices< int > dst_offsets)
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)
static void append_point_knots(const Span< IndexRange > src_ranges, const OffsetIndices< int > dst_offsets, const Span< int > dst_to_src_curve, const bke::CurvesGeometry &src_curves, bke::CurvesGeometry &curves)
void duplicate_curves(bke::CurvesGeometry &curves, const IndexMask &mask)
void duplicate_points(bke::CurvesGeometry &curves, const IndexMask &mask)
static IndexRange extend_range(const IndexRange range, const IndexRange universe)
static void curve_offsets_from_selection(const Span< IndexRange > selected_points, const IndexRange points, const int curve, const bool cyclic, Vector< int > &r_new_curve_offsets, Vector< bool > &r_new_cyclic, Vector< IndexRange > &r_src_ranges, Vector< int > &r_dst_offsets, Vector< int > &r_dst_to_src_curve)
void foreach_selection_attribute_writer(bke::CurvesGeometry &curves, bke::AttrDomain selection_domain, blender::FunctionRef< void(bke::GSpanAttributeWriter &selection)> fn)
void separate_points(const bke::CurvesGeometry &curves, const IndexMask &points_to_separate, bke::CurvesGeometry &separated, bke::CurvesGeometry &retained)
bke::CurvesGeometry split_points(const bke::CurvesGeometry &curves, const IndexMask &points_to_split)
void fill_selection_false(GMutableSpan selection)
void add_curves(bke::CurvesGeometry &curves, const Span< int > new_sizes)
static void invert_ranges(const IndexRange universe, const Span< IndexRange > ranges, Array< IndexRange > &inverted)
Span< StringRef > get_curves_selection_attribute_names(const bke::CurvesGeometry &curves)
static void append_curve_knots(const IndexMask &mask, bke::CurvesGeometry &curves)
static void extend_range_by_1_within_bounds(const IndexRange universe, const bool cyclic, const Span< IndexRange > ranges, Vector< IndexRange > &extended_ranges)
void reorder_curves(bke::CurvesGeometry &curves, const Span< int > old_by_new_indices_map)
bke::CurvesGeometry reorder_curves_geometry(const bke::CurvesGeometry &src_curves, Span< int > old_by_new_map, const bke::AttributeFilter &attribute_filter)
Definition reorder.cc:348
T min(const T &a, const T &b)
T max(const T &a, const T &b)
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)
ListBase vertex_group_names
i
Definition text_draw.cc:230