Blender V5.0
editors/mesh/mesh_mirror.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
10
11#include "MEM_guardedalloc.h"
12
13#include "DNA_object_types.h"
14
15#include "BKE_editmesh.hh"
16#include "BKE_mesh.hh"
17#include "BKE_mesh_types.hh"
18
19#include "BLI_kdtree.h"
20
21#include "ED_mesh.hh"
22
23/* -------------------------------------------------------------------- */
26
27#define KD_THRESH 0.00002f
28
29static struct {
30 KDTree_3d *tree;
31} MirrKdStore = {nullptr};
32
34{
35 Mesh *mesh = static_cast<Mesh *>(ob->data);
36 const bool use_em = (!mesh_eval && em && mesh->runtime->edit_mesh.get() == em);
37 const int totvert = use_em ? em->bm->totvert :
38 mesh_eval ? mesh_eval->verts_num :
39 mesh->verts_num;
40
41 if (MirrKdStore.tree) { /* happens when entering this call without ending it */
43 }
44
45 MirrKdStore.tree = BLI_kdtree_3d_new(totvert);
46
47 if (use_em) {
48 BMVert *eve;
49 BMIter iter;
50 int i;
51
52 /* this needs to be valid for index lookups later (callers need) */
54
55 BM_ITER_MESH_INDEX (eve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
56 BLI_kdtree_3d_insert(MirrKdStore.tree, i, eve->co);
57 }
58 }
59 else {
60 const blender::Span<blender::float3> positions = mesh_eval ? mesh_eval->vert_positions() :
61 mesh->vert_positions();
62 for (int i = 0; i < totvert; i++) {
63 BLI_kdtree_3d_insert(MirrKdStore.tree, i, positions[i]);
64 }
65 }
66
67 BLI_kdtree_3d_balance(MirrKdStore.tree);
68}
69
71 BMEditMesh *em,
72 Mesh *mesh_eval,
73 const float co[3])
74{
75 if (MirrKdStore.tree == nullptr) {
76 ED_mesh_mirror_spatial_table_begin(ob, em, mesh_eval);
77 }
78
79 if (MirrKdStore.tree) {
80 KDTreeNearest_3d nearest;
81 const int i = BLI_kdtree_3d_find_nearest(MirrKdStore.tree, co, &nearest);
82
83 if (i != -1) {
84 if (nearest.dist < KD_THRESH) {
85 return i;
86 }
87 }
88 }
89 return -1;
90}
91
93{
94 /* TODO: store this in object/object-data (keep unused argument for now). */
95 if (MirrKdStore.tree) {
96 BLI_kdtree_3d_free(MirrKdStore.tree);
97 MirrKdStore.tree = nullptr;
98 }
99}
100
102
103/* -------------------------------------------------------------------- */
106
108
113
114static int mirrtopo_hash_sort(const void *l1, const void *l2)
115{
116 if (MirrTopoHash_t(intptr_t(l1)) > MirrTopoHash_t(intptr_t(l2))) {
117 return 1;
118 }
119 if (MirrTopoHash_t(intptr_t(l1)) < MirrTopoHash_t(intptr_t(l2))) {
120 return -1;
121 }
122 return 0;
123}
124
125static int mirrtopo_vert_sort(const void *v1, const void *v2)
126{
127 if (((MirrTopoVert_t *)v1)->hash > ((MirrTopoVert_t *)v2)->hash) {
128 return 1;
129 }
130 if (((MirrTopoVert_t *)v1)->hash < ((MirrTopoVert_t *)v2)->hash) {
131 return -1;
132 }
133 return 0;
134}
135
137{
138 const bool is_editmode = em != nullptr;
139 int totvert;
140 int totedge;
141
142 if (em) {
143 totvert = em->bm->totvert;
144 totedge = em->bm->totedge;
145 }
146 else {
147 totvert = mesh->verts_num;
148 totedge = mesh->edges_num;
149 }
150
151 if ((mesh_topo_store->index_lookup == nullptr) ||
152 (mesh_topo_store->prev_is_editmode != is_editmode) ||
153 (totvert != mesh_topo_store->prev_vert_tot) || (totedge != mesh_topo_store->prev_edge_tot))
154 {
155 return true;
156 }
157 return false;
158}
159
161 Mesh *mesh,
163 const bool skip_em_vert_array_init)
164{
165 if (em) {
166 BLI_assert(mesh == nullptr);
167 }
168 const bool is_editmode = (em != nullptr);
169
170 /* Edit-mode variables. */
171 BMEdge *eed;
172 BMIter iter;
173
174 int a, last;
175 int totvert, totedge;
176 int tot_unique = -1, tot_unique_prev = -1;
177 int tot_unique_edges = 0, tot_unique_edges_prev;
178
179 MirrTopoHash_t topo_pass = 1;
180
181 /* reallocate if needed */
183
184 mesh_topo_store->prev_is_editmode = is_editmode;
185
186 if (em) {
188
189 totvert = em->bm->totvert;
190 }
191 else {
192 totvert = mesh->verts_num;
193 }
194
195 MirrTopoHash_t *topo_hash = MEM_calloc_arrayN<MirrTopoHash_t>(totvert, __func__);
196
197 /* Initialize the vert-edge-user counts used to detect unique topology */
198 if (em) {
199 totedge = em->bm->totedge;
200
201 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
202 const int i1 = BM_elem_index_get(eed->v1), i2 = BM_elem_index_get(eed->v2);
203 topo_hash[i1]++;
204 topo_hash[i2]++;
205 }
206 }
207 else {
208 totedge = mesh->edges_num;
209 for (const blender::int2 &edge : mesh->edges()) {
210 topo_hash[edge[0]]++;
211 topo_hash[edge[1]]++;
212 }
213 }
214
215 MirrTopoHash_t *topo_hash_prev = static_cast<MirrTopoHash_t *>(MEM_dupallocN(topo_hash));
216
217 tot_unique_prev = -1;
218 tot_unique_edges_prev = -1;
219 while (true) {
220 /* use the number of edges per vert to give verts unique topology IDs */
221
222 tot_unique_edges = 0;
223
224 /* This can make really big numbers, wrapping around here is fine */
225 if (em) {
226 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
227 const int i1 = BM_elem_index_get(eed->v1), i2 = BM_elem_index_get(eed->v2);
228 topo_hash[i1] += topo_hash_prev[i2] * topo_pass;
229 topo_hash[i2] += topo_hash_prev[i1] * topo_pass;
230 tot_unique_edges += (topo_hash[i1] != topo_hash[i2]);
231 }
232 }
233 else {
234 for (const blender::int2 &edge : mesh->edges()) {
235 const int i1 = edge[0], i2 = edge[1];
236 topo_hash[i1] += topo_hash_prev[i2] * topo_pass;
237 topo_hash[i2] += topo_hash_prev[i1] * topo_pass;
238 tot_unique_edges += (topo_hash[i1] != topo_hash[i2]);
239 }
240 }
241 memcpy(topo_hash_prev, topo_hash, sizeof(MirrTopoHash_t) * totvert);
242
243 /* sort so we can count unique values */
244 qsort(topo_hash_prev, totvert, sizeof(MirrTopoHash_t), mirrtopo_hash_sort);
245
246 tot_unique = 1; /* account for skipping the first value */
247 for (a = 1; a < totvert; a++) {
248 if (topo_hash_prev[a - 1] != topo_hash_prev[a]) {
249 tot_unique++;
250 }
251 }
252
253 if ((tot_unique <= tot_unique_prev) && (tot_unique_edges <= tot_unique_edges_prev)) {
254 /* Finish searching for unique values when 1 loop doesn't give a
255 * higher number of unique values compared to the previous loop. */
256 break;
257 }
258 tot_unique_prev = tot_unique;
259 tot_unique_edges_prev = tot_unique_edges;
260 /* Copy the hash calculated this iteration, so we can use them next time */
261 memcpy(topo_hash_prev, topo_hash, sizeof(MirrTopoHash_t) * totvert);
262
263 topo_pass++;
264 }
265
266 /* Hash/Index pairs are needed for sorting to find index pairs */
267 MirrTopoVert_t *topo_pairs = MEM_calloc_arrayN<MirrTopoVert_t>(totvert, "MirrTopoPairs");
268
269 /* since we are looping through verts, initialize these values here too */
270 intptr_t *index_lookup = static_cast<intptr_t *>(
271 MEM_mallocN(totvert * sizeof(*index_lookup), "mesh_topo_lookup"));
272
273 if (em) {
274 if (skip_em_vert_array_init == false) {
276 }
277 }
278
279 for (a = 0; a < totvert; a++) {
280 topo_pairs[a].hash = topo_hash[a];
281 topo_pairs[a].v_index = a;
282
283 /* initialize lookup */
284 index_lookup[a] = -1;
285 }
286
287 qsort(topo_pairs, totvert, sizeof(MirrTopoVert_t), mirrtopo_vert_sort);
288
289 last = 0;
290
291 /* Get the pairs out of the sorted hashes.
292 * NOTE: `totvert + 1` means we can use the previous 2,
293 * but you can't ever access the last 'a' index of #MirrTopoPairs. */
294 if (em) {
295 BMVert **vtable = em->bm->vtable;
296 for (a = 1; a <= totvert; a++) {
297 // printf("I %d %ld %d\n",
298 // (a - last), MirrTopoPairs[a].hash, MirrTopoPairs[a].v_index);
299 if ((a == totvert) || (topo_pairs[a - 1].hash != topo_pairs[a].hash)) {
300 const int match_count = a - last;
301 if (match_count == 2) {
302 const int j = topo_pairs[a - 1].v_index, k = topo_pairs[a - 2].v_index;
303 index_lookup[j] = intptr_t(vtable[k]);
304 index_lookup[k] = intptr_t(vtable[j]);
305 }
306 else if (match_count == 1) {
307 /* Center vertex. */
308 const int j = topo_pairs[a - 1].v_index;
309 index_lookup[j] = intptr_t(vtable[j]);
310 }
311 last = a;
312 }
313 }
314 }
315 else {
316 /* same as above, for mesh */
317 for (a = 1; a <= totvert; a++) {
318 if ((a == totvert) || (topo_pairs[a - 1].hash != topo_pairs[a].hash)) {
319 const int match_count = a - last;
320 if (match_count == 2) {
321 const int j = topo_pairs[a - 1].v_index, k = topo_pairs[a - 2].v_index;
322 index_lookup[j] = k;
323 index_lookup[k] = j;
324 }
325 else if (match_count == 1) {
326 /* Center vertex. */
327 const int j = topo_pairs[a - 1].v_index;
328 index_lookup[j] = j;
329 }
330 last = a;
331 }
332 }
333 }
334
335 MEM_freeN(topo_pairs);
336 topo_pairs = nullptr;
337
338 MEM_freeN(topo_hash);
339 MEM_freeN(topo_hash_prev);
340
341 mesh_topo_store->index_lookup = index_lookup;
342 mesh_topo_store->prev_vert_tot = totvert;
343 mesh_topo_store->prev_edge_tot = totedge;
344}
345
347{
348 MEM_SAFE_FREE(mesh_topo_store->index_lookup);
349 mesh_topo_store->prev_vert_tot = -1;
350 mesh_topo_store->prev_edge_tot = -1;
351}
352
354
355/* -------------------------------------------------------------------- */
358
359std::optional<EditMeshSymmetryHelper> EditMeshSymmetryHelper::create_if_needed(Object *ob,
360 uchar htype)
361{
362 BLI_assert(htype != 0);
363 BLI_assert((htype & ~(BM_VERT | BM_EDGE | BM_FACE)) == 0);
364
365 if (!ob || !ob->data) {
366 return std::nullopt;
367 }
368 Mesh *mesh = static_cast<Mesh *>(ob->data);
370
371 if (!em || !em->bm || mesh->symmetry == 0) {
372 return std::nullopt;
373 }
374 return EditMeshSymmetryHelper(ob, htype);
375}
376
377EditMeshSymmetryHelper::EditMeshSymmetryHelper(Object *ob, uchar htype)
378 : em_(BKE_editmesh_from_object(ob)), mesh_(static_cast<Mesh *>(ob->data)), htype_(htype)
379{
380 BMesh *bmesh = em_->bm;
381 use_topology_mirror_ = (mesh_->editflag & ME_EDIT_MIRROR_TOPO) != 0;
382
383 blender::Set<BMVert *> processed_verts;
384 blender::Set<BMEdge *> processed_edges;
385 blender::Set<BMFace *> processed_faces;
386
387 BMIter iter;
388
389 for (int axis = 0; axis < 3; axis++) {
390 if (mesh_->symmetry & (ME_SYMMETRY_X << axis)) {
392 axis,
393 (htype_ & BM_VERT) != 0,
394 (htype_ & BM_EDGE) != 0,
395 (htype_ & BM_FACE) != 0,
396 use_topology_mirror_);
397
398 if (htype_ & BM_VERT) {
399 BMVert *v_curr;
400 BM_ITER_MESH (v_curr, &iter, bmesh, BM_VERTS_OF_MESH) {
401 if (processed_verts.contains(v_curr)) {
402 continue;
403 }
404 BMVert *v_mirr = EDBM_verts_mirror_get(em_, v_curr);
405 if (v_mirr && v_mirr != v_curr) {
406 BMVert *v_mirr_check = EDBM_verts_mirror_get(em_, v_mirr);
407 if (v_mirr_check == v_curr) {
408 vert_to_mirror_map_.lookup_or_add(v_curr, {}).append(v_mirr);
409 vert_to_mirror_map_.lookup_or_add(v_mirr, {}).append(v_curr);
410 processed_verts.add(v_curr);
411 processed_verts.add(v_mirr);
412 }
413 }
414 }
415 }
416
417 if (htype_ & BM_EDGE) {
418 BMEdge *e_curr;
419 BM_ITER_MESH (e_curr, &iter, bmesh, BM_EDGES_OF_MESH) {
420 if (processed_edges.contains(e_curr)) {
421 continue;
422 }
423 BMEdge *e_mirr = EDBM_verts_mirror_get_edge(em_, e_curr);
424 if (e_mirr && e_mirr != e_curr) {
425 BMEdge *e_mirr_check = EDBM_verts_mirror_get_edge(em_, e_mirr);
426 if (e_mirr_check == e_curr) {
427 edge_to_mirror_map_.lookup_or_add(e_curr, {}).append(e_mirr);
428 edge_to_mirror_map_.lookup_or_add(e_mirr, {}).append(e_curr);
429 processed_edges.add(e_curr);
430 processed_edges.add(e_mirr);
431 }
432 }
433 }
434 }
435
436 if (htype_ & BM_FACE) {
437 BMFace *f_curr;
438 BM_ITER_MESH (f_curr, &iter, bmesh, BM_FACES_OF_MESH) {
439 if (processed_faces.contains(f_curr)) {
440 continue;
441 }
442 BMFace *f_mirr = EDBM_verts_mirror_get_face(em_, f_curr);
443 if (f_mirr && f_mirr != f_curr) {
444 BMFace *f_mirr_check = EDBM_verts_mirror_get_face(em_, f_mirr);
445 if (f_mirr_check == f_curr) {
446 face_to_mirror_map_.lookup_or_add(f_curr, {}).append(f_mirr);
447 face_to_mirror_map_.lookup_or_add(f_mirr, {}).append(f_curr);
448 processed_faces.add(f_curr);
449 processed_faces.add(f_mirr);
450 }
451 }
452 }
453 }
454
456 }
457 }
458}
460 blender::FunctionRef<void(BMVert *)> op) const
461{
462 BLI_assert((this->htype_ & BM_VERT) != 0);
463 const blender::Vector<BMVert *> *mirrors = this->vert_to_mirror_map_.lookup_ptr(v);
464 if (mirrors) {
465 for (BMVert *v_mirr : *mirrors) {
466 op(v_mirr);
467 }
468 }
469}
470
472 blender::FunctionRef<void(BMEdge *)> op) const
473{
474 BLI_assert((this->htype_ & BM_EDGE) != 0);
475 const blender::Vector<BMEdge *> *mirrors = this->edge_to_mirror_map_.lookup_ptr(e);
476 if (mirrors) {
477 for (BMEdge *e_mirr : *mirrors) {
478 op(e_mirr);
479 }
480 }
481}
482
484 blender::FunctionRef<void(BMFace *)> op) const
485{
486 BLI_assert((this->htype_ & BM_FACE) != 0);
487 const blender::Vector<BMFace *> *mirrors = this->face_to_mirror_map_.lookup_ptr(f);
488 if (mirrors) {
489 for (BMFace *f_mirr : *mirrors) {
490 op(f_mirr);
491 }
492 }
493}
494
496{
497 BLI_assert((this->htype_ & BM_VERT) != 0);
498 const blender::Vector<BMVert *> *mirrors = this->vert_to_mirror_map_.lookup_ptr(v);
499 if (mirrors) {
500 for (BMVert *v_mirr : *mirrors) {
501 if (BM_elem_flag_test(v_mirr, hflag) && !BM_elem_flag_test(v_mirr, BM_ELEM_HIDDEN)) {
502 return true;
503 }
504 }
505 }
506 return false;
507}
508
510{
511 BLI_assert((this->htype_ & BM_EDGE) != 0);
512 const blender::Vector<BMEdge *> *mirrors = this->edge_to_mirror_map_.lookup_ptr(e);
513 if (mirrors) {
514 for (BMEdge *e_mirr : *mirrors) {
515 if (BM_elem_flag_test(e_mirr, hflag) && !BM_elem_flag_test(e_mirr, BM_ELEM_HIDDEN)) {
516 return true;
517 }
518 }
519 }
520 return false;
521}
522
524{
525 BLI_assert((this->htype_ & BM_FACE) != 0);
526 const blender::Vector<BMFace *> *mirrors = this->face_to_mirror_map_.lookup_ptr(f);
527 if (mirrors) {
528 for (BMFace *f_mirr : *mirrors) {
529 if (BM_elem_flag_test(f_mirr, hflag) && !BM_elem_flag_test(f_mirr, BM_ELEM_HIDDEN)) {
530 return true;
531 }
532 }
533 }
534 return false;
535}
536
538 const char hflag,
539 const bool value) const
540{
541 apply_on_mirror_verts(v, [this, hflag, value](BMVert *v_mirr) {
542 if (hflag & BM_ELEM_SELECT) {
543 BM_vert_select_set(this->em_->bm, v_mirr, value);
544 }
545 const char hflag_test = char(hflag & ~BM_ELEM_SELECT);
546 if (hflag_test) {
547 BM_elem_flag_set(v_mirr, hflag_test, value);
548 }
549 });
550}
551
553 char hflag,
554 const bool value) const
555{
556 apply_on_mirror_edges(e, [this, hflag, value](BMEdge *e_mirr) {
557 if (hflag & BM_ELEM_SELECT) {
558 BM_edge_select_set(this->em_->bm, e_mirr, value);
559 }
560 char hflag_test = char(hflag & ~BM_ELEM_SELECT);
561 if (hflag_test) {
562 BM_elem_flag_set(e_mirr, hflag_test, value);
563 }
564 });
565}
566
568 const char hflag,
569 const bool value) const
570{
571 apply_on_mirror_faces(f, [this, hflag, value](BMFace *f_mirr) {
572 if (hflag & BM_ELEM_SELECT) {
573 BM_face_select_set(this->em_->bm, f_mirr, value);
574 }
575 char hflag_test = char(hflag & ~BM_ELEM_SELECT);
576 if (hflag_test) {
577 BM_elem_flag_set(f_mirr, hflag_test, value);
578 }
579 });
580}
581
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:61
#define BLI_assert(a)
Definition BLI_assert.h:46
A KD-tree for nearest neighbor search.
unsigned char uchar
unsigned int uint
@ ME_EDIT_MIRROR_TOPO
@ ME_SYMMETRY_X
Object is a sort of wrapper for general info.
BMVert * EDBM_verts_mirror_get(BMEditMesh *em, BMVert *v)
void EDBM_verts_mirror_cache_begin(BMEditMesh *em, int axis, bool use_self, bool use_select, bool respecthide, bool use_topology)
void EDBM_verts_mirror_cache_end(BMEditMesh *em)
BMEdge * EDBM_verts_mirror_get_edge(BMEditMesh *em, BMEdge *e)
BMFace * EDBM_verts_mirror_get_face(BMEditMesh *em, BMFace *f)
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
@ BM_ELEM_HIDDEN
@ BM_ELEM_SELECT
#define BM_elem_index_get(ele)
#define BM_elem_flag_set(ele, hflag, val)
#define BM_elem_flag_test(ele, hflag)
#define BM_ITER_MESH(ele, iter, bm, itype)
#define BM_ITER_MESH_INDEX(ele, iter, bm, itype, indexvar)
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_FACES_OF_MESH
BMesh const char void * data
void BM_face_select_set(BMesh *bm, BMFace *f, const bool select)
Select Face.
void BM_vert_select_set(BMesh *bm, BMVert *v, const bool select)
Select Vert.
void BM_edge_select_set(BMesh *bm, BMEdge *e, const bool select)
Select Edge.
void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
static std::optional< EditMeshSymmetryHelper > create_if_needed(Object *ob, uchar htype)
void set_hflag_on_mirror_edges(BMEdge *e, char hflag, bool value) const
void set_hflag_on_mirror_verts(BMVert *v, char hflag, bool value) const
void set_hflag_on_mirror_faces(BMFace *f, char hflag, bool value) const
bool any_mirror_edge_selected(BMEdge *e, char hflag) const
bool any_mirror_face_selected(BMFace *f, char hflag) const
void apply_on_mirror_faces(BMFace *f, blender::FunctionRef< void(BMFace *)> op) const
bool any_mirror_vert_selected(BMVert *v, char hflag) const
void apply_on_mirror_verts(BMVert *v, blender::FunctionRef< void(BMVert *)> op) const
void apply_on_mirror_edges(BMEdge *e, blender::FunctionRef< void(BMEdge *)> op) const
bool contains(const Key &key) const
Definition BLI_set.hh:310
bool add(const Key &key)
Definition BLI_set.hh:248
void ED_mesh_mirror_spatial_table_end(Object *)
int ED_mesh_mirror_spatial_table_lookup(Object *ob, BMEditMesh *em, Mesh *mesh_eval, const float co[3])
static int mirrtopo_vert_sort(const void *v1, const void *v2)
static int mirrtopo_hash_sort(const void *l1, const void *l2)
#define KD_THRESH
void ED_mesh_mirror_spatial_table_begin(Object *ob, BMEditMesh *em, Mesh *mesh_eval)
void ED_mesh_mirrtopo_init(BMEditMesh *em, Mesh *mesh, MirrTopoStore_t *mesh_topo_store, const bool skip_em_vert_array_init)
void ED_mesh_mirrtopo_free(MirrTopoStore_t *mesh_topo_store)
static struct @373065230156164077010030262226042245223102312262 MirrKdStore
uint MirrTopoHash_t
KDTree_3d * tree
bool ED_mesh_mirrtopo_recalc_check(BMEditMesh *em, Mesh *mesh, MirrTopoStore_t *mesh_topo_store)
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static MirrTopoStore_t mesh_topo_store
Definition meshtools.cc:224
VecBase< int32_t, 2 > int2
#define hash
Definition noise_c.cc:154
BMVert * v1
BMVert * v2
float co[3]
int totvert
int totedge
BMVert ** vtable
char symmetry
int edges_num
MeshRuntimeHandle * runtime
char editflag
int verts_num
i
Definition text_draw.cc:230