Blender V4.5
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
#define BLI_assert(a)
Definition BLI_assert.h:46
A KD-tree for nearest neighbor search.
unsigned int uint
Object is a sort of wrapper for general info.
Read Guarded memory(de)allocation.
#define BM_elem_index_get(ele)
#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
void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
#define BM_VERT
ATTR_WARN_UNUSED_RESULT const BMVert * v2
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)
static struct @104204156256073357273333046377213072373255030157 MirrKdStore
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)
uint MirrTopoHash_t
KDTree_3d * tree
bool ED_mesh_mirrtopo_recalc_check(BMEditMesh *em, Mesh *mesh, MirrTopoStore_t *mesh_topo_store)
#define MEM_SAFE_FREE(v)
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:833
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
int edges_num
MeshRuntimeHandle * runtime
int verts_num
i
Definition text_draw.cc:230