Drop platform support for Solaris & AIX
[blender.git] / source / blender / bmesh / intern / bmesh_mesh_validate.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2012 Blender Foundation.
19  * All rights reserved.
20  *
21  * Contributor(s): Campbell Barton
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/bmesh/intern/bmesh_mesh_validate.c
27  *  \ingroup bmesh
28  *
29  * BM mesh validation function.
30  */
31
32 /* debug builds only */
33 #ifdef DEBUG
34
35 #include "BLI_utildefines.h"
36 #include "BLI_edgehash.h"
37
38 #include "bmesh.h"
39
40 #include "bmesh_mesh_validate.h"
41
42
43 /* macro which inserts the function name */
44 #if defined __GNUC__
45 #  define ERRMSG(format, args...) { fprintf(stderr, "%s: " format ", " AT "\n", __func__, ##args); errtot++; } (void)0
46 #else
47 #  define ERRMSG(format, ...) { fprintf(stderr, "%s: " format ", " AT "\n", __func__, __VA_ARGS__); errtot++; } (void)0
48 #endif
49
50 /**
51  * Check of this BMesh is valid, this function can be slow since its intended to help with debugging.
52  *
53  * \return true when the mesh is valid.
54  */
55 bool BM_mesh_validate(BMesh *bm)
56 {
57         EdgeHash *edge_hash = BLI_edgehash_new_ex(__func__, bm->totedge);
58         int errtot;
59
60         BMIter iter;
61         BMVert *v;
62         BMEdge *e;
63         BMFace *f;
64
65         int i, j;
66
67         errtot = -1; /* 'ERRMSG' next line will set at zero */
68         fprintf(stderr, "\n");
69         ERRMSG("This is a debugging function and not intended for general use, running slow test!");
70
71         /* force recalc, even if tagged as valid, since this mesh is suspect! */
72         bm->elem_index_dirty |= BM_ALL;
73         BM_mesh_elem_index_ensure(bm, BM_ALL);
74
75         BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
76                 if (BM_elem_flag_test(v, BM_ELEM_SELECT | BM_ELEM_HIDDEN) == (BM_ELEM_SELECT | BM_ELEM_HIDDEN)) {
77                         ERRMSG("vert %d: is hidden and selected", i);
78                 }
79
80                 if (v->e) {
81                         if (!BM_vert_in_edge(v->e, v)) {
82                                 ERRMSG("vert %d: is not in its referenced edge: %d", i, BM_elem_index_get(v->e));
83                         }
84                 }
85         }
86
87         /* check edges */
88         BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) {
89                 void **val_p;
90
91                 if (e->v1 == e->v2) {
92                         ERRMSG("edge %d: duplicate index: %d", i, BM_elem_index_get(e->v1));
93                 }
94
95                 /* build edgehash at the same time */
96                 if (BLI_edgehash_ensure_p(edge_hash, BM_elem_index_get(e->v1), BM_elem_index_get(e->v2), &val_p)) {
97                         BMEdge *e_other = *val_p;
98                         ERRMSG("edge %d, %d: are duplicates", i, BM_elem_index_get(e_other));
99                 }
100                 else {
101                         *val_p = e;
102                 }
103         }
104
105         /* edge radial structure */
106         BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) {
107                 if (BM_elem_flag_test(e, BM_ELEM_SELECT | BM_ELEM_HIDDEN) == (BM_ELEM_SELECT | BM_ELEM_HIDDEN)) {
108                         ERRMSG("edge %d: is hidden and selected", i);
109                 }
110
111                 if (e->l) {
112                         BMLoop *l_iter;
113                         BMLoop *l_first;
114
115                         j = 0;
116
117                         l_iter = l_first = e->l;
118                         /* we could do more checks here, but save for face checks */
119                         do {
120                                 if (l_iter->e != e) {
121                                         ERRMSG("edge %d: has invalid loop, loop is of face %d", i, BM_elem_index_get(l_iter->f));
122                                 }
123                                 else if (BM_vert_in_edge(e, l_iter->v) == false) {
124                                         ERRMSG("edge %d: has invalid loop with vert not in edge, loop is of face %d",
125                                                i, BM_elem_index_get(l_iter->f));
126                                 }
127                                 else if (BM_vert_in_edge(e, l_iter->next->v) == false) {
128                                         ERRMSG("edge %d: has invalid loop with next vert not in edge, loop is of face %d",
129                                                i, BM_elem_index_get(l_iter->f));
130                                 }
131                         } while ((l_iter = l_iter->radial_next) != l_first);
132                 }
133         }
134
135         /* face structure */
136         BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
137                 BMLoop *l_iter;
138                 BMLoop *l_first;
139
140                 if (BM_elem_flag_test(f, BM_ELEM_SELECT | BM_ELEM_HIDDEN) == (BM_ELEM_SELECT | BM_ELEM_HIDDEN)) {
141                         ERRMSG("face %d: is hidden and selected", i);
142                 }
143
144                 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
145
146                 do {
147                         BM_elem_flag_disable(l_iter,    BM_ELEM_INTERNAL_TAG);
148                         BM_elem_flag_disable(l_iter->v, BM_ELEM_INTERNAL_TAG);
149                         BM_elem_flag_disable(l_iter->e, BM_ELEM_INTERNAL_TAG);
150                 } while ((l_iter = l_iter->next) != l_first);
151
152                 j = 0;
153
154                 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
155                 do {
156                         if (BM_elem_flag_test(l_iter, BM_ELEM_INTERNAL_TAG)) {
157                                 ERRMSG("face %d: has duplicate loop at corner: %d", i, j);
158                         }
159                         if (BM_elem_flag_test(l_iter->v, BM_ELEM_INTERNAL_TAG)) {
160                                 ERRMSG("face %d: has duplicate vert: %d, at corner: %d", i, BM_elem_index_get(l_iter->v), j);
161                         }
162                         if (BM_elem_flag_test(l_iter->e, BM_ELEM_INTERNAL_TAG)) {
163                                 ERRMSG("face %d: has duplicate edge: %d, at corner: %d", i,  BM_elem_index_get(l_iter->e), j);
164                         }
165
166                         /* adjacent data checks */
167                         if (l_iter->f != f) {
168                                 ERRMSG("face %d: has loop that points to face: %d at corner: %d", i, BM_elem_index_get(l_iter->f), j);
169                         }
170                         if (l_iter != l_iter->prev->next) {
171                                 ERRMSG("face %d: has invalid 'prev/next' at corner: %d", i, j);
172                         }
173                         if (l_iter != l_iter->next->prev) {
174                                 ERRMSG("face %d: has invalid 'next/prev' at corner: %d", i, j);
175                         }
176                         if (l_iter != l_iter->radial_prev->radial_next) {
177                                 ERRMSG("face %d: has invalid 'radial_prev/radial_next' at corner: %d", i, j);
178                         }
179                         if (l_iter != l_iter->radial_next->radial_prev) {
180                                 ERRMSG("face %d: has invalid 'radial_next/radial_prev' at corner: %d", i, j);
181                         }
182
183                         BM_elem_flag_enable(l_iter,    BM_ELEM_INTERNAL_TAG);
184                         BM_elem_flag_enable(l_iter->v, BM_ELEM_INTERNAL_TAG);
185                         BM_elem_flag_enable(l_iter->e, BM_ELEM_INTERNAL_TAG);
186                         j++;
187                 } while ((l_iter = l_iter->next) != l_first);
188
189                 if (j != f->len) {
190                         ERRMSG("face %d: has length of %d but should be %d", i, f->len, j);
191                 }
192
193                 /* leave elements un-tagged, not essential but nice to avoid unintended dirty tag use later. */
194                 do {
195                         BM_elem_flag_disable(l_iter,    BM_ELEM_INTERNAL_TAG);
196                         BM_elem_flag_disable(l_iter->v, BM_ELEM_INTERNAL_TAG);
197                         BM_elem_flag_disable(l_iter->e, BM_ELEM_INTERNAL_TAG);
198                 } while ((l_iter = l_iter->next) != l_first);
199         }
200
201         BLI_edgehash_free(edge_hash, NULL);
202
203         const bool is_valid = (errtot == 0);
204         ERRMSG("Finished - errors %d", errtot);
205         return is_valid;
206 }
207
208
209 #endif