Blender V4.3
mesh_split_edges.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
5#include "BLI_array_utils.hh"
6#include "BLI_index_mask.hh"
7#include "BLI_ordered_edge.hh"
8
9#include "BKE_attribute.hh"
10#include "BKE_attribute_math.hh"
11#include "BKE_customdata.hh"
12#include "BKE_mesh.hh"
13#include "BKE_mesh_mapping.hh"
14
16#include "GEO_randomize.hh"
17
18namespace blender::geometry {
19
20static void propagate_vert_attributes(Mesh &mesh, const Span<int> new_to_old_verts_map)
21{
22 /* These types aren't supported for interpolation below. */
23 CustomData_free_layers(&mesh.vert_data, CD_SHAPEKEY, mesh.verts_num);
24 CustomData_free_layers(&mesh.vert_data, CD_CLOTH_ORCO, mesh.verts_num);
25 CustomData_free_layers(&mesh.vert_data, CD_MVERT_SKIN, mesh.verts_num);
27 &mesh.vert_data, mesh.verts_num, mesh.verts_num + new_to_old_verts_map.size());
28 mesh.verts_num += new_to_old_verts_map.size();
29
30 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
31 for (const StringRef id : attributes.all_ids()) {
32 if (attributes.lookup_meta_data(id)->domain != bke::AttrDomain::Point) {
33 continue;
34 }
35 bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id);
36 if (!attribute) {
37 continue;
38 }
39
40 bke::attribute_math::gather(attribute.span,
41 new_to_old_verts_map,
42 attribute.span.take_back(new_to_old_verts_map.size()));
43
44 attribute.finish();
45 }
46 if (float3 *orco = static_cast<float3 *>(
47 CustomData_get_layer_for_write(&mesh.vert_data, CD_ORCO, mesh.verts_num)))
48 {
49 array_utils::gather(Span(orco, mesh.verts_num),
50 new_to_old_verts_map,
51 MutableSpan(orco, mesh.verts_num).take_back(new_to_old_verts_map.size()));
52 }
53 if (int *orig_indices = static_cast<int *>(
54 CustomData_get_layer_for_write(&mesh.vert_data, CD_ORIGINDEX, mesh.verts_num)))
55 {
57 Span(orig_indices, mesh.verts_num),
58 new_to_old_verts_map,
59 MutableSpan(orig_indices, mesh.verts_num).take_back(new_to_old_verts_map.size()));
60 }
61}
62
63static void propagate_edge_attributes(Mesh &mesh, const Span<int> new_to_old_edge_map)
64{
65 CustomData_free_layers(&mesh.edge_data, CD_FREESTYLE_EDGE, mesh.edges_num);
66 CustomData_realloc(&mesh.edge_data, mesh.edges_num, mesh.edges_num + new_to_old_edge_map.size());
67 mesh.edges_num += new_to_old_edge_map.size();
68
69 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
70 for (const StringRef id : attributes.all_ids()) {
71 if (attributes.lookup_meta_data(id)->domain != bke::AttrDomain::Edge) {
72 continue;
73 }
74 if (id == ".edge_verts") {
75 /* Edge vertices are updated and combined with new edges separately. */
76 continue;
77 }
78 bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id);
79 if (!attribute) {
80 continue;
81 }
83 attribute.span, new_to_old_edge_map, attribute.span.take_back(new_to_old_edge_map.size()));
84 attribute.finish();
85 }
86
87 if (int *orig_indices = static_cast<int *>(
88 CustomData_get_layer_for_write(&mesh.edge_data, CD_ORIGINDEX, mesh.edges_num)))
89 {
91 Span(orig_indices, mesh.edges_num),
92 new_to_old_edge_map,
93 MutableSpan(orig_indices, mesh.edges_num).take_back(new_to_old_edge_map.size()));
94 }
95}
96
99 const IndexMask &selected_edges,
100 const int verts_num,
101 IndexMaskMemory &memory)
102{
103 Array<bool> array(verts_num, false);
104 selected_edges.foreach_index_optimized<int>(GrainSize(4096), [&](const int i) {
105 array[edges[i][0]] = true;
106 array[edges[i][1]] = true;
107 });
108 return IndexMask::from_bools(array, memory);
109}
110
111static BitVector<> selection_to_bit_vector(const IndexMask &selection, const int total_size)
112{
113 BitVector<> bits(total_size);
114 selection.to_bits(bits);
115 return bits;
116}
117
125static int corner_on_edge_connected_to_vert(const Span<int> corner_verts,
126 const int corner,
127 const IndexRange face,
128 const int vert)
129{
130 if (corner_verts[corner] == vert) {
131 return corner;
132 }
133 const int other = bke::mesh::face_corner_next(face, corner);
134 BLI_assert(corner_verts[other] == vert);
135 return other;
136}
137
139
149 const Span<int> corner_verts,
150 const Span<int> corner_edges,
151 const GroupedSpan<int> edge_to_corner_map,
152 const Span<int> corner_to_face_map,
153 const BitSpan split_edges,
154 const Span<int> connected_corners,
155 const int vert)
156{
157 Vector<CornerGroup> groups;
158 /* Each corner should only be added to a single group. */
159 BitVector<> used_corners(connected_corners.size());
160 for (const int start_corner : connected_corners) {
161 CornerGroup group;
162 Vector<int> corner_stack({start_corner});
163 while (!corner_stack.is_empty()) {
164 const int corner = corner_stack.pop_last();
165 const int i = connected_corners.first_index(corner);
166 if (used_corners[i]) {
167 continue;
168 }
169 used_corners[i].set();
170 group.append(corner);
171 const int face = corner_to_face_map[corner];
172 const int prev_corner = bke::mesh::face_corner_prev(faces[face], corner);
173 /* Travel across the two edges neighboring this vertex, if they aren't split. */
174 for (const int edge : {corner_edges[corner], corner_edges[prev_corner]}) {
175 if (split_edges[edge]) {
176 continue;
177 }
178 for (const int other_corner : edge_to_corner_map[edge]) {
179 const int other_face = corner_to_face_map[other_corner];
180 if (other_face == face) {
181 /* Avoid continuing back to the same face. */
182 continue;
183 }
184 const int neighbor_corner = corner_on_edge_connected_to_vert(
185 corner_verts, other_corner, faces[other_face], vert);
186 corner_stack.append(neighbor_corner);
187 }
188 }
189 }
190 if (!group.is_empty()) {
191 groups.append(std::move(group));
192 }
193 }
194
195 return groups;
196}
197
198/* Calculate groups of corners that are contiguously connected to each input vertex.
199 * BLI_NOINLINE because MSVC 17.7 has a codegen bug here, given there is only a single call to this
200 * function, not inlining it for all platforms won't affect performance. See
201 * https://developercommunity.visualstudio.com/t/10448291 for details. */
203 const OffsetIndices<int> faces,
204 const Span<int> corner_verts,
205 const Span<int> corner_edges,
206 const GroupedSpan<int> vert_to_corner_map,
207 const GroupedSpan<int> edge_to_corner_map,
208 const Span<int> corner_to_face_map,
209 const BitSpan split_edges,
210 const IndexMask &affected_verts)
211{
212 Array<Vector<CornerGroup>> corner_groups(affected_verts.size(), NoInitialization());
213 affected_verts.foreach_index(GrainSize(512), [&](const int vert, const int mask) {
214 new (&corner_groups[mask])
216 corner_verts,
217 corner_edges,
218 edge_to_corner_map,
219 corner_to_face_map,
221 vert_to_corner_map[vert],
222 vert));
223 });
224 return corner_groups;
225}
226
232
235 const BitSpan loose_edges,
236 const BitSpan split_edges,
237 const int vert)
238{
239 VertLooseEdges info;
240 for (const int edge : vert_to_edge_map[vert]) {
241 if (loose_edges[edge]) {
242 if (split_edges[edge]) {
243 info.selected.append(edge);
244 }
245 else {
246 info.unselected.append(edge);
247 }
248 }
249 }
250 return info;
251}
252
265 const IndexMask &affected_verts,
266 const Span<Vector<CornerGroup>> corner_groups,
267 const GroupedSpan<int> vert_to_edge_map,
268 const BitSpan loose_edges,
269 const BitSpan split_edges,
270 Array<int> &offset_data)
271{
272 offset_data.reinitialize(affected_verts.size() + 1);
273 MutableSpan<int> new_verts_nums = offset_data;
274 threading::parallel_for(affected_verts.index_range(), 2048, [&](const IndexRange range) {
275 /* Start with -1 for the reused vertex. None of the final sizes should be negative. */
276 new_verts_nums.slice(range).fill(-1);
277 for (const int i : range) {
278 new_verts_nums[i] += corner_groups[i].size();
279 }
280 });
281 if (!loose_edges.is_empty()) {
282 affected_verts.foreach_index(GrainSize(512), [&](const int vert, const int mask) {
283 const VertLooseEdges info = calc_vert_loose_edges(
284 vert_to_edge_map, loose_edges, split_edges, vert);
285 new_verts_nums[mask] += info.selected.size();
286 if (corner_groups[mask].is_empty()) {
287 /* Loose edges share their vertex with a corner group if possible. */
288 new_verts_nums[mask] += info.unselected.size() > 0;
289 }
290 });
291 }
293}
294
300static void update_corner_verts(const int orig_verts_num,
301 const Span<Vector<CornerGroup>> corner_groups,
302 const OffsetIndices<int> new_verts_by_affected_vert,
303 MutableSpan<int> new_corner_verts)
304{
305 threading::parallel_for(corner_groups.index_range(), 512, [&](const IndexRange range) {
306 for (const int new_vert : range) {
307 const Span<CornerGroup> groups = corner_groups[new_vert];
308 const IndexRange new_verts = new_verts_by_affected_vert[new_vert];
309 for (const int group : groups.index_range().drop_back(1)) {
310 const int new_vert = orig_verts_num + new_verts[group];
311 new_corner_verts.fill_indices(groups[group].as_span(), new_vert);
312 }
313 }
314 });
315}
316
318 const Span<int> corner_verts,
319 const Span<int> corner_to_face_map,
320 const int corner)
321{
322 const int face = corner_to_face_map[corner];
323 const int corner_next = bke::mesh::face_corner_next(faces[face], corner);
324 return OrderedEdge(corner_verts[corner], corner_verts[corner_next]);
325}
326
335 const Span<int> corner_verts,
336 const GroupedSpan<int> edge_to_corner_map,
337 const Span<int> corner_to_face_map,
338 const IndexMask &selected_edges,
339 MutableSpan<int2> edges,
340 MutableSpan<int> corner_edges,
341 MutableSpan<int> r_new_edge_offsets)
342{
343 /* Calculate the offset of new edges assuming no new edges are identical and are merged. */
344 selected_edges.foreach_index_optimized<int>(
345 GrainSize(4096), [&](const int edge, const int mask) {
346 r_new_edge_offsets[mask] = std::max<int>(edge_to_corner_map[edge].size() - 1, 0);
347 });
348 const OffsetIndices offsets = offset_indices::accumulate_counts_to_offsets(r_new_edge_offsets);
349
350 Array<int2> new_edges(offsets.total_size());
351
352 /* Count the number of final new edges per edge, to use as offsets if there are duplicates. */
353 Array<int> num_edges_per_edge_merged(r_new_edge_offsets.size());
354 std::atomic<bool> found_duplicate = false;
355
356 /* The first new edge for each selected edge is reused-- we modify the existing edge in
357 * place. Simply reusing the first new edge isn't enough because deduplication might make
358 * multiple new edges reuse the original. */
359 Array<bool> is_reused(corner_verts.size(), false);
360
361 /* Calculate per-original split edge deduplication of new edges, which are stored by the
362 * corner vertices of connected faces. Update corner verts to store the updated indices. */
363 selected_edges.foreach_index(GrainSize(1024), [&](const int edge, const int mask) {
364 if (edge_to_corner_map[edge].is_empty()) {
365 /* Handle loose edges. */
366 num_edges_per_edge_merged[mask] = 0;
367 return;
368 }
369
370 const int new_edges_start = offsets[mask].start();
371 Vector<OrderedEdge> deduplication;
372 for (const int corner : edge_to_corner_map[edge]) {
373 const OrderedEdge edge = edge_from_corner(faces, corner_verts, corner_to_face_map, corner);
374 int index = deduplication.first_index_of_try(edge);
375 if (UNLIKELY(index != -1)) {
376 found_duplicate.store(true, std::memory_order_relaxed);
377 }
378 else {
379 index = deduplication.append_and_get_index(edge);
380 }
381
382 if (index == 0) {
383 is_reused[corner] = true;
384 }
385 else {
386 corner_edges[corner] = edges.size() + new_edges_start + index - 1;
387 }
388 }
389
390 const int new_edges_num = deduplication.size() - 1;
391
392 edges[edge] = int2(deduplication.first().v_low, deduplication.first().v_high);
393 new_edges.as_mutable_span()
394 .slice(new_edges_start, new_edges_num)
395 .copy_from(deduplication.as_span().drop_front(1).cast<int2>());
396
397 num_edges_per_edge_merged[mask] = new_edges_num;
398 });
399
400 if (!found_duplicate) {
401 /* No edges were merged, we can use the existing output array and offsets. */
402 return new_edges;
403 }
404
405 /* Update corner edges to remove the "holes" left by merged new edges. */
406 const OffsetIndices offsets_merged = offset_indices::accumulate_counts_to_offsets(
407 num_edges_per_edge_merged);
408 selected_edges.foreach_index(GrainSize(2048), [&](const int edge, const int mask) {
409 const int difference = offsets[mask].start() - offsets_merged[mask].start();
410 for (const int corner : edge_to_corner_map[edge]) {
411 if (!is_reused[corner]) {
412 corner_edges[corner] -= difference;
413 }
414 }
415 });
416
417 /* Create new edges without the empty slots for the duplicates */
418 Array<int2> new_edges_merged(offsets_merged.total_size());
419 threading::parallel_for(offsets_merged.index_range(), 1024, [&](const IndexRange range) {
420 for (const int i : range) {
421 new_edges_merged.as_mutable_span()
422 .slice(offsets_merged[i])
423 .copy_from(new_edges.as_span().slice(offsets[i].start(), offsets_merged[i].size()));
424 }
425 });
426
427 r_new_edge_offsets.copy_from(num_edges_per_edge_merged);
428 return new_edges_merged;
429}
430
432 const Span<int> corner_verts,
433 const GroupedSpan<int> edge_to_corner_map,
434 const Span<int> corner_to_face_map,
435 const IndexMask &unselected_edges,
436 MutableSpan<int2> edges)
437{
438 unselected_edges.foreach_index(GrainSize(1024), [&](const int edge) {
439 const Span<int> edge_corners = edge_to_corner_map[edge];
440 if (edge_corners.is_empty()) {
441 return;
442 }
443 const int corner = edge_corners.first();
444 const OrderedEdge new_edge = edge_from_corner(faces, corner_verts, corner_to_face_map, corner);
445 edges[edge] = int2(new_edge.v_low, new_edge.v_high);
446 });
447}
448
449static void swap_edge_vert(int2 &edge, const int old_vert, const int new_vert)
450{
451 if (edge[0] == old_vert) {
452 edge[0] = new_vert;
453 }
454 else if (edge[1] == old_vert) {
455 edge[1] = new_vert;
456 }
457}
458
464static void reassign_loose_edge_verts(const int orig_verts_num,
465 const IndexMask &affected_verts,
466 const GroupedSpan<int> vert_to_edge_map,
467 const BitSpan loose_edges,
468 const BitSpan split_edges,
469 const Span<Vector<CornerGroup>> corner_groups,
470 const OffsetIndices<int> new_verts_by_affected_vert,
471 MutableSpan<int2> edges)
472{
473 affected_verts.foreach_index(GrainSize(1024), [&](const int vert, const int mask) {
474 const IndexRange new_verts = new_verts_by_affected_vert[mask];
475 /* Account for the reuse of the original vertex by non-loose corner groups. In practice this
476 * means using the new vertices for each split loose edge until we run out of new vertices.
477 * We then expect the count to match up with the number of new vertices reserved by
478 * #calc_vert_ranges_per_old_vert. */
479 int new_vert_i = std::max<int>(corner_groups[mask].size() - 1, 0);
480 if (new_vert_i == new_verts.size()) {
481 return;
482 }
483 const VertLooseEdges vert_info = calc_vert_loose_edges(
484 vert_to_edge_map, loose_edges, split_edges, vert);
485 for (const int edge : vert_info.selected) {
486 const int new_vert = orig_verts_num + new_verts[new_vert_i];
487 swap_edge_vert(edges[edge], vert, new_vert);
488 new_vert_i++;
489 if (new_vert_i == new_verts.size()) {
490 return;
491 }
492 }
493 const int new_vert = orig_verts_num + new_verts[new_vert_i];
494 for (const int orig_edge : vert_info.unselected) {
495 swap_edge_vert(edges[orig_edge], vert, new_vert);
496 }
497 });
498}
499
504static Array<int> offsets_to_map(const IndexMask &mask, const OffsetIndices<int> offsets)
505{
506 Array<int> map(offsets.total_size());
507 mask.foreach_index(GrainSize(1024), [&](const int i, const int mask) {
508 map.as_mutable_span().slice(offsets[mask]).fill(i);
509 });
510 return map;
511}
512
513void split_edges(Mesh &mesh,
514 const IndexMask &selected_edges,
515 const bke::AttributeFilter & /*attribute_filter*/)
516{
517 const int orig_verts_num = mesh.verts_num;
518 const Span<int2> orig_edges = mesh.edges();
519 const OffsetIndices faces = mesh.faces();
520
521 IndexMaskMemory memory;
522 const IndexMask affected_verts = vert_selection_from_edge(
523 orig_edges, selected_edges, orig_verts_num, memory);
524 const BitVector<> selection_bits = selection_to_bit_vector(selected_edges, orig_edges.size());
525 const bke::LooseEdgeCache &loose_edges = mesh.loose_edges();
526
527 const GroupedSpan<int> vert_to_corner_map = mesh.vert_to_corner_map();
528
529 Array<int> edge_to_corner_offsets;
530 Array<int> edge_to_corner_indices;
531 const GroupedSpan<int> edge_to_corner_map = bke::mesh::build_edge_to_corner_map(
532 mesh.corner_edges(), orig_edges.size(), edge_to_corner_offsets, edge_to_corner_indices);
533
534 Array<int> vert_to_edge_offsets;
535 Array<int> vert_to_edge_indices;
536 GroupedSpan<int> vert_to_edge_map;
537 if (loose_edges.count > 0) {
538 vert_to_edge_map = bke::mesh::build_vert_to_edge_map(
539 orig_edges, orig_verts_num, vert_to_edge_offsets, vert_to_edge_indices);
540 }
541
542 const Array<int> corner_to_face_map = mesh.corner_to_face_map();
543
544 const Array<Vector<CornerGroup>> corner_groups = calc_all_corner_groups(faces,
545 mesh.corner_verts(),
546 mesh.corner_edges(),
547 vert_to_corner_map,
548 edge_to_corner_map,
549 corner_to_face_map,
550 selection_bits,
551 affected_verts);
552
553 Array<int> vert_new_vert_offset_data;
554 const OffsetIndices new_verts_by_affected_vert = calc_vert_ranges_per_old_vert(
555 affected_verts,
556 corner_groups,
557 vert_to_edge_map,
558 loose_edges.is_loose_bits,
559 selection_bits,
560 vert_new_vert_offset_data);
561
562 MutableSpan<int> corner_verts = mesh.corner_verts_for_write();
563 update_corner_verts(orig_verts_num, corner_groups, new_verts_by_affected_vert, corner_verts);
564
565 Array<int> new_edge_offsets(selected_edges.size() + 1);
566 Array<int2> new_edges = calc_new_edges(faces,
567 corner_verts,
568 edge_to_corner_map,
569 corner_to_face_map,
570 selected_edges,
571 mesh.edges_for_write(),
572 mesh.corner_edges_for_write(),
573 new_edge_offsets);
574 const IndexMask unselected_edges = selected_edges.complement(orig_edges.index_range(), memory);
576 corner_verts,
577 edge_to_corner_map,
578 corner_to_face_map,
579 unselected_edges,
580 mesh.edges_for_write());
581
582 if (loose_edges.count > 0) {
583 reassign_loose_edge_verts(orig_verts_num,
584 affected_verts,
585 vert_to_edge_map,
586 loose_edges.is_loose_bits,
587 selection_bits,
588 corner_groups,
589 new_verts_by_affected_vert,
590 mesh.edges_for_write());
591 }
592
593 const Array<int> edge_map = offsets_to_map(selected_edges, new_edge_offsets.as_span());
594 propagate_edge_attributes(mesh, edge_map);
595 mesh.edges_for_write().take_back(new_edges.size()).copy_from(new_edges);
596
597 const Array<int> vert_map = offsets_to_map(affected_verts, new_verts_by_affected_vert);
598 propagate_vert_attributes(mesh, vert_map);
599
600 mesh.tag_edges_split();
601
604}
605
606} // namespace blender::geometry
CustomData interface, see also DNA_customdata_types.h.
void CustomData_realloc(CustomData *data, int old_size, int new_size, eCDAllocType alloctype=CD_CONSTRUCT)
void CustomData_free_layers(CustomData *data, eCustomDataType type, int totelem)
void * CustomData_get_layer_for_write(CustomData *data, eCustomDataType type, int totelem)
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_NOINLINE
#define UNLIKELY(x)
@ CD_MVERT_SKIN
@ CD_FREESTYLE_EDGE
@ CD_CLOTH_ORCO
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
int64_t size() const
Definition BLI_array.hh:245
Span< T > as_span() const
Definition BLI_array.hh:232
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:237
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:388
constexpr int64_t size() const
constexpr int64_t size() const
Definition BLI_span.hh:494
constexpr MutableSpan take_back(const int64_t n) const
Definition BLI_span.hh:641
constexpr Span take_back(int64_t n) const
Definition BLI_span.hh:205
constexpr const T & first() const
Definition BLI_span.hh:316
constexpr int64_t first_index(const T &search_value) const
Definition BLI_span.hh:378
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr bool is_empty() const
Definition BLI_span.hh:261
int64_t size() const
int64_t append_and_get_index(const T &value)
void append(const T &value)
Span< T > as_span() const
const T & first() const
int64_t first_index_of_try(const T &value) const
void append(const bool value)
void foreach_index_optimized(Fn &&fn) const
IndexMask complement(const IndexMask &universe, IndexMaskMemory &memory) const
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
void foreach_index(Fn &&fn) const
ccl_device_inline float4 mask(const int4 mask, const float4 a)
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
void gather(GSpan src, Span< int > map, GMutableSpan dst)
int face_corner_prev(const IndexRange face, const int corner)
Definition BKE_mesh.hh:243
int face_corner_next(const IndexRange face, const int corner)
Definition BKE_mesh.hh:252
void debug_randomize_edge_order(Mesh *mesh)
Definition randomize.cc:108
static Array< int > offsets_to_map(const IndexMask &mask, const OffsetIndices< int > offsets)
static BitVector selection_to_bit_vector(const IndexMask &selection, const int total_size)
void split_edges(Mesh &mesh, const IndexMask &mask, const bke::AttributeFilter &attribute_filter={})
static Vector< CornerGroup > calc_corner_groups_for_vertex(const OffsetIndices< int > faces, const Span< int > corner_verts, const Span< int > corner_edges, const GroupedSpan< int > edge_to_corner_map, const Span< int > corner_to_face_map, const BitSpan split_edges, const Span< int > connected_corners, const int vert)
static void reassign_loose_edge_verts(const int orig_verts_num, const IndexMask &affected_verts, const GroupedSpan< int > vert_to_edge_map, const BitSpan loose_edges, const BitSpan split_edges, const Span< Vector< CornerGroup > > corner_groups, const OffsetIndices< int > new_verts_by_affected_vert, MutableSpan< int2 > edges)
static Array< int2 > calc_new_edges(const OffsetIndices< int > faces, const Span< int > corner_verts, const GroupedSpan< int > edge_to_corner_map, const Span< int > corner_to_face_map, const IndexMask &selected_edges, MutableSpan< int2 > edges, MutableSpan< int > corner_edges, MutableSpan< int > r_new_edge_offsets)
static void propagate_vert_attributes(Mesh &mesh, const Span< int > new_to_old_verts_map)
static OrderedEdge edge_from_corner(const OffsetIndices< int > faces, const Span< int > corner_verts, const Span< int > corner_to_face_map, const int corner)
static BLI_NOINLINE Array< Vector< CornerGroup > > calc_all_corner_groups(const OffsetIndices< int > faces, const Span< int > corner_verts, const Span< int > corner_edges, const GroupedSpan< int > vert_to_corner_map, const GroupedSpan< int > edge_to_corner_map, const Span< int > corner_to_face_map, const BitSpan split_edges, const IndexMask &affected_verts)
static OffsetIndices< int > calc_vert_ranges_per_old_vert(const IndexMask &affected_verts, const Span< Vector< CornerGroup > > corner_groups, const GroupedSpan< int > vert_to_edge_map, const BitSpan loose_edges, const BitSpan split_edges, Array< int > &offset_data)
void debug_randomize_vert_order(Mesh *mesh)
Definition randomize.cc:87
static VertLooseEdges calc_vert_loose_edges(const GroupedSpan< int > vert_to_edge_map, const BitSpan loose_edges, const BitSpan split_edges, const int vert)
static void swap_edge_vert(int2 &edge, const int old_vert, const int new_vert)
static int corner_on_edge_connected_to_vert(const Span< int > corner_verts, const int corner, const IndexRange face, const int vert)
static void update_unselected_edges(const OffsetIndices< int > faces, const Span< int > corner_verts, const GroupedSpan< int > edge_to_corner_map, const Span< int > corner_to_face_map, const IndexMask &unselected_edges, MutableSpan< int2 > edges)
static void propagate_edge_attributes(Mesh &mesh, const Span< int > new_to_old_edge_map)
IndexMask vert_selection_from_edge(Span< int2 > edges, const IndexMask &edge_mask, int verts_num, IndexMaskMemory &memory)
static void update_corner_verts(const int orig_verts_num, const Span< Vector< CornerGroup > > corner_groups, const OffsetIndices< int > new_verts_by_affected_vert, MutableSpan< int > new_corner_verts)
OffsetIndices< int > accumulate_counts_to_offsets(MutableSpan< int > counts_to_offsets, int start_offset=0)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
blender::BitVector is_loose_bits