Blender V4.3
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
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/* -------------------------------------------------------------------- */
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
103/* -------------------------------------------------------------------- */
108
113
114static int mirrtopo_hash_sort(const void *l1, const void *l2)
115{
117 return 1;
118 }
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 = static_cast<MirrTopoHash_t *>(
196 MEM_callocN(totvert * sizeof(MirrTopoHash_t), __func__));
197
198 /* Initialize the vert-edge-user counts used to detect unique topology */
199 if (em) {
200 totedge = em->bm->totedge;
201
202 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
203 const int i1 = BM_elem_index_get(eed->v1), i2 = BM_elem_index_get(eed->v2);
204 topo_hash[i1]++;
205 topo_hash[i2]++;
206 }
207 }
208 else {
209 totedge = mesh->edges_num;
210 for (const blender::int2 &edge : mesh->edges()) {
211 topo_hash[edge[0]]++;
212 topo_hash[edge[1]]++;
213 }
214 }
215
216 MirrTopoHash_t *topo_hash_prev = static_cast<MirrTopoHash_t *>(MEM_dupallocN(topo_hash));
217
218 tot_unique_prev = -1;
219 tot_unique_edges_prev = -1;
220 while (true) {
221 /* use the number of edges per vert to give verts unique topology IDs */
222
223 tot_unique_edges = 0;
224
225 /* This can make really big numbers, wrapping around here is fine */
226 if (em) {
227 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
228 const int i1 = BM_elem_index_get(eed->v1), i2 = BM_elem_index_get(eed->v2);
229 topo_hash[i1] += topo_hash_prev[i2] * topo_pass;
230 topo_hash[i2] += topo_hash_prev[i1] * topo_pass;
231 tot_unique_edges += (topo_hash[i1] != topo_hash[i2]);
232 }
233 }
234 else {
235 for (const blender::int2 &edge : mesh->edges()) {
236 const int i1 = edge[0], i2 = edge[1];
237 topo_hash[i1] += topo_hash_prev[i2] * topo_pass;
238 topo_hash[i2] += topo_hash_prev[i1] * topo_pass;
239 tot_unique_edges += (topo_hash[i1] != topo_hash[i2]);
240 }
241 }
242 memcpy(topo_hash_prev, topo_hash, sizeof(MirrTopoHash_t) * totvert);
243
244 /* sort so we can count unique values */
245 qsort(topo_hash_prev, totvert, sizeof(MirrTopoHash_t), mirrtopo_hash_sort);
246
247 tot_unique = 1; /* account for skipping the first value */
248 for (a = 1; a < totvert; a++) {
249 if (topo_hash_prev[a - 1] != topo_hash_prev[a]) {
250 tot_unique++;
251 }
252 }
253
254 if ((tot_unique <= tot_unique_prev) && (tot_unique_edges <= tot_unique_edges_prev)) {
255 /* Finish searching for unique values when 1 loop doesn't give a
256 * higher number of unique values compared to the previous loop. */
257 break;
258 }
259 tot_unique_prev = tot_unique;
260 tot_unique_edges_prev = tot_unique_edges;
261 /* Copy the hash calculated this iteration, so we can use them next time */
262 memcpy(topo_hash_prev, topo_hash, sizeof(MirrTopoHash_t) * totvert);
263
264 topo_pass++;
265 }
266
267 /* Hash/Index pairs are needed for sorting to find index pairs */
268 MirrTopoVert_t *topo_pairs = static_cast<MirrTopoVert_t *>(
269 MEM_callocN(sizeof(MirrTopoVert_t) * totvert, "MirrTopoPairs"));
270
271 /* since we are looping through verts, initialize these values here too */
272 intptr_t *index_lookup = static_cast<intptr_t *>(
273 MEM_mallocN(totvert * sizeof(*index_lookup), "mesh_topo_lookup"));
274
275 if (em) {
276 if (skip_em_vert_array_init == false) {
278 }
279 }
280
281 for (a = 0; a < totvert; a++) {
282 topo_pairs[a].hash = topo_hash[a];
283 topo_pairs[a].v_index = a;
284
285 /* initialize lookup */
286 index_lookup[a] = -1;
287 }
288
289 qsort(topo_pairs, totvert, sizeof(MirrTopoVert_t), mirrtopo_vert_sort);
290
291 last = 0;
292
293 /* Get the pairs out of the sorted hashes.
294 * NOTE: `totvert + 1` means we can use the previous 2,
295 * but you can't ever access the last 'a' index of #MirrTopoPairs. */
296 if (em) {
297 BMVert **vtable = em->bm->vtable;
298 for (a = 1; a <= totvert; a++) {
299 // printf("I %d %ld %d\n",
300 // (a - last), MirrTopoPairs[a].hash, MirrTopoPairs[a].v_index);
301 if ((a == totvert) || (topo_pairs[a - 1].hash != topo_pairs[a].hash)) {
302 const int match_count = a - last;
303 if (match_count == 2) {
304 const int j = topo_pairs[a - 1].v_index, k = topo_pairs[a - 2].v_index;
305 index_lookup[j] = intptr_t(vtable[k]);
306 index_lookup[k] = intptr_t(vtable[j]);
307 }
308 else if (match_count == 1) {
309 /* Center vertex. */
310 const int j = topo_pairs[a - 1].v_index;
311 index_lookup[j] = intptr_t(vtable[j]);
312 }
313 last = a;
314 }
315 }
316 }
317 else {
318 /* same as above, for mesh */
319 for (a = 1; a <= totvert; a++) {
320 if ((a == totvert) || (topo_pairs[a - 1].hash != topo_pairs[a].hash)) {
321 const int match_count = a - last;
322 if (match_count == 2) {
323 const int j = topo_pairs[a - 1].v_index, k = topo_pairs[a - 2].v_index;
324 index_lookup[j] = k;
325 index_lookup[k] = j;
326 }
327 else if (match_count == 1) {
328 /* Center vertex. */
329 const int j = topo_pairs[a - 1].v_index;
330 index_lookup[j] = j;
331 }
332 last = a;
333 }
334 }
335 }
336
337 MEM_freeN(topo_pairs);
338 topo_pairs = nullptr;
339
340 MEM_freeN(topo_hash);
341 MEM_freeN(topo_hash_prev);
342
343 mesh_topo_store->index_lookup = index_lookup;
346}
347
354
#define BLI_assert(a)
Definition BLI_assert.h:50
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 MEM_SAFE_FREE(v)
#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 struct @445 MirrKdStore
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)
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:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
void *(* MEM_dupallocN)(const void *vmemh)
Definition mallocn.cc:39
static MirrTopoStore_t mesh_topo_store
Definition meshtools.cc:823
#define hash
Definition noise.c:154
_W64 int intptr_t
Definition stdint.h:118
BMVert * v1
BMVert * v2
float co[3]
int totvert
int totedge
BMVert ** vtable
int verts_num
intptr_t * index_lookup
Definition ED_mesh.hh:431
bool prev_is_editmode
Definition ED_mesh.hh:434