Blender  V3.3
editmesh_intersect.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
7 #include "MEM_guardedalloc.h"
8 
9 #include "DNA_object_types.h"
10 
11 #include "BLI_buffer.h"
12 #include "BLI_linklist_stack.h"
13 #include "BLI_math.h"
14 #include "BLI_memarena.h"
15 #include "BLI_stack.h"
16 
17 #include "BKE_context.h"
18 #include "BKE_editmesh.h"
19 #include "BKE_editmesh_bvh.h"
20 #include "BKE_layer.h"
21 #include "BKE_report.h"
22 
23 #include "RNA_access.h"
24 #include "RNA_define.h"
25 
26 #include "WM_types.h"
27 
28 #include "UI_interface.h"
29 #include "UI_resources.h"
30 
31 #include "ED_mesh.h"
32 #include "ED_screen.h"
33 
34 #include "intern/bmesh_private.h"
35 
36 #include "mesh_intern.h" /* own include */
37 
38 #include "tools/bmesh_boolean.h"
39 #include "tools/bmesh_intersect.h"
40 #include "tools/bmesh_separate.h"
41 
42 /* detect isolated holes and fill them */
43 #define USE_NET_ISLAND_CONNECT
44 
48 static int bm_face_isect_self(BMFace *f, void *UNUSED(user_data))
49 {
51  return 0;
52  }
53  return -1;
54 }
55 
59 static int bm_face_isect_pair(BMFace *f, void *UNUSED(user_data))
60 {
62  return -1;
63  }
65  return 1;
66  }
67  return 0;
68 }
69 
75 {
77  return -1;
78  }
80  return 0;
81  }
82  return 1;
83 }
84 
88 static void edbm_intersect_select(BMEditMesh *em, struct Mesh *me, bool do_select)
89 {
90  if (do_select) {
92 
94  BMIter iter;
95  BMEdge *e;
96 
97  BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
99  BM_edge_select_set(em->bm, e, true);
100  }
101  }
103  }
104  }
105 
106  EDBM_update(me,
107  &(const struct EDBMUpdate_Params){
108  .calc_looptri = true,
109  .calc_normals = true,
110  .is_destructive = true,
111  });
112 }
113 
114 /* -------------------------------------------------------------------- */
120 enum {
123 };
124 
125 enum {
129 };
130 
131 enum {
134 };
135 
137 {
138  const int mode = RNA_enum_get(op->ptr, "mode");
139  int (*test_fn)(BMFace *, void *);
140  bool use_separate_all = false;
141  bool use_separate_cut = false;
142  const int separate_mode = RNA_enum_get(op->ptr, "separate_mode");
143  const float eps = RNA_float_get(op->ptr, "threshold");
144 #ifdef WITH_GMP
145  const bool exact = RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT;
146 #else
147  if (RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT) {
148  BKE_report(op->reports, RPT_WARNING, "Compiled without GMP, using fast solver");
149  }
150  const bool exact = false;
151 #endif
152  bool use_self;
153  bool has_isect;
154 
155  switch (mode) {
156  case ISECT_SEL:
157  test_fn = bm_face_isect_self;
158  use_self = true;
159  break;
160  default: /* ISECT_SEL_UNSEL */
161  test_fn = bm_face_isect_pair;
162  use_self = false;
163  break;
164  }
165 
166  switch (separate_mode) {
167  case ISECT_SEPARATE_ALL:
168  use_separate_all = true;
169  break;
170  case ISECT_SEPARATE_CUT:
171  if (use_self == false) {
172  use_separate_cut = true;
173  }
174  else {
175  /* we could support this but would require more advanced logic inside 'BM_mesh_intersect'
176  * for now just separate all */
177  use_separate_all = true;
178  }
179  break;
180  default: /* ISECT_SEPARATE_NONE */
181  break;
182  }
183  ViewLayer *view_layer = CTX_data_view_layer(C);
184  uint objects_len = 0;
185  uint isect_len = 0;
187  view_layer, CTX_wm_view3d(C), &objects_len);
188  for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
189  Object *obedit = objects[ob_index];
190  BMEditMesh *em = BKE_editmesh_from_object(obedit);
191 
192  if (em->bm->totfacesel == 0) {
193  continue;
194  }
195 
196  if (exact) {
197  int nshapes = use_self ? 1 : 2;
198  has_isect = BM_mesh_boolean_knife(em->bm,
199  em->looptris,
200  em->tottri,
201  test_fn,
202  NULL,
203  nshapes,
204  use_self,
205  use_separate_all,
206  false,
207  true);
208  }
209  else {
210  has_isect = BM_mesh_intersect(em->bm,
211  em->looptris,
212  em->tottri,
213  test_fn,
214  NULL,
215  use_self,
216  use_separate_all,
217  true,
218  true,
219  true,
220  true,
221  -1,
222  eps);
223  }
224 
225  if (use_separate_cut) {
226  /* detach selected/un-selected faces */
229  }
230 
231  edbm_intersect_select(em, obedit->data, has_isect);
232 
233  if (!has_isect) {
234  isect_len++;
235  }
236  }
237  MEM_freeN(objects);
238 
239  if (isect_len == objects_len) {
240  BKE_report(op->reports, RPT_WARNING, "No intersections found");
241  }
242  return OPERATOR_FINISHED;
243 }
244 
246 {
247  uiLayout *layout = op->layout;
248  uiLayout *row;
249 
250  bool use_exact = RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT;
251 
252  uiLayoutSetPropSep(layout, true);
253  uiLayoutSetPropDecorate(layout, false);
254  row = uiLayoutRow(layout, false);
255  uiItemR(row, op->ptr, "mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
256  uiItemS(layout);
257  row = uiLayoutRow(layout, false);
258  uiItemR(row, op->ptr, "separate_mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
259  uiItemS(layout);
260 
261  row = uiLayoutRow(layout, false);
262  uiItemR(row, op->ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
263  uiItemS(layout);
264 
265  if (!use_exact) {
266  uiItemR(layout, op->ptr, "threshold", 0, NULL, ICON_NONE);
267  }
268 }
269 
271 {
272  static const EnumPropertyItem isect_mode_items[] = {
273  {ISECT_SEL, "SELECT", 0, "Self Intersect", "Self intersect selected faces"},
275  "SELECT_UNSELECT",
276  0,
277  "Selected/Unselected",
278  "Intersect selected with unselected faces"},
279  {0, NULL, 0, NULL, NULL},
280  };
281 
282  static const EnumPropertyItem isect_separate_items[] = {
283  {ISECT_SEPARATE_ALL, "ALL", 0, "All", "Separate all geometry from intersections"},
285  "CUT",
286  0,
287  "Cut",
288  "Cut into geometry keeping each side separate (Selected/Unselected only)"},
289  {ISECT_SEPARATE_NONE, "NONE", 0, "Merge", "Merge all geometry from the intersection"},
290  {0, NULL, 0, NULL, NULL},
291  };
292 
293  static const EnumPropertyItem isect_intersect_solver_items[] = {
294  {ISECT_SOLVER_FAST, "FAST", 0, "Fast", "Faster solver, some limitations"},
295  {ISECT_SOLVER_EXACT, "EXACT", 0, "Exact", "Exact solver, slower, handles more cases"},
296  {0, NULL, 0, NULL, NULL},
297  };
298 
299  /* identifiers */
300  ot->name = "Intersect (Knife)";
301  ot->description = "Cut an intersection into faces";
302  ot->idname = "MESH_OT_intersect";
303 
304  /* api callbacks */
308 
309  /* props */
310  RNA_def_enum(ot->srna, "mode", isect_mode_items, ISECT_SEL_UNSEL, "Source", "");
311  RNA_def_enum(
312  ot->srna, "separate_mode", isect_separate_items, ISECT_SEPARATE_CUT, "Separate Mode", "");
314  ot->srna, "threshold", 0.000001f, 0.0, 0.01, "Merge Threshold", "", 0.0, 0.001);
316  "solver",
317  isect_intersect_solver_items,
319  "Solver",
320  "Which Intersect solver to use");
321 
322  /* flags */
324 }
325 
328 /* -------------------------------------------------------------------- */
336 {
337  const int boolean_operation = RNA_enum_get(op->ptr, "operation");
338  bool use_swap = RNA_boolean_get(op->ptr, "use_swap");
339  bool use_self = RNA_boolean_get(op->ptr, "use_self");
340 #ifdef WITH_GMP
341  const bool use_exact = RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT;
342 #else
343  if (RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT) {
344  BKE_report(op->reports, RPT_WARNING, "Compiled without GMP, using fast solver");
345  }
346  const bool use_exact = false;
347 #endif
348  const float eps = RNA_float_get(op->ptr, "threshold");
349  int (*test_fn)(BMFace *, void *);
350  bool has_isect;
351 
352  test_fn = use_swap ? bm_face_isect_pair_swap : bm_face_isect_pair;
353  ViewLayer *view_layer = CTX_data_view_layer(C);
354  uint objects_len = 0;
355  uint isect_len = 0;
357  view_layer, CTX_wm_view3d(C), &objects_len);
358  for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
359  Object *obedit = objects[ob_index];
360  BMEditMesh *em = BKE_editmesh_from_object(obedit);
361 
362  if (em->bm->totfacesel == 0) {
363  continue;
364  }
365 
366  if (use_exact) {
367  has_isect = BM_mesh_boolean(em->bm,
368  em->looptris,
369  em->tottri,
370  test_fn,
371  NULL,
372  2,
373  use_self,
374  true,
375  false,
376  boolean_operation);
377  }
378  else {
379  has_isect = BM_mesh_intersect(em->bm,
380  em->looptris,
381  em->tottri,
382  test_fn,
383  NULL,
384  false,
385  false,
386  true,
387  true,
388  false,
389  true,
390  boolean_operation,
391  eps);
392  }
393 
394  edbm_intersect_select(em, obedit->data, has_isect);
395 
396  if (!has_isect) {
397  isect_len++;
398  }
399  }
400  MEM_freeN(objects);
401 
402  if (isect_len == objects_len) {
403  BKE_report(op->reports, RPT_WARNING, "No intersections found");
404  }
405  return OPERATOR_FINISHED;
406 }
407 
409 {
410  uiLayout *layout = op->layout;
411  uiLayout *row;
412 
413  bool use_exact = RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT;
414 
415  uiLayoutSetPropSep(layout, true);
416  uiLayoutSetPropDecorate(layout, false);
417 
418  row = uiLayoutRow(layout, false);
419  uiItemR(row, op->ptr, "operation", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
420  uiItemS(layout);
421 
422  row = uiLayoutRow(layout, false);
423  uiItemR(row, op->ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
424  uiItemS(layout);
425 
426  uiItemR(layout, op->ptr, "use_swap", 0, NULL, ICON_NONE);
427  uiItemR(layout, op->ptr, "use_self", 0, NULL, ICON_NONE);
428  if (!use_exact) {
429  uiItemR(layout, op->ptr, "threshold", 0, NULL, ICON_NONE);
430  }
431 }
432 
434 {
435  static const EnumPropertyItem isect_boolean_operation_items[] = {
436  {BMESH_ISECT_BOOLEAN_ISECT, "INTERSECT", 0, "Intersect", ""},
437  {BMESH_ISECT_BOOLEAN_UNION, "UNION", 0, "Union", ""},
438  {BMESH_ISECT_BOOLEAN_DIFFERENCE, "DIFFERENCE", 0, "Difference", ""},
439  {0, NULL, 0, NULL, NULL},
440  };
441 
442  static const EnumPropertyItem isect_boolean_solver_items[] = {
443  {ISECT_SOLVER_FAST, "FAST", 0, "Fast", "Faster solver, some limitations"},
444  {ISECT_SOLVER_EXACT, "EXACT", 0, "Exact", "Exact solver, slower, handles more cases"},
445  {0, NULL, 0, NULL, NULL},
446  };
447 
448  /* identifiers */
449  ot->name = "Intersect (Boolean)";
450  ot->description = "Cut solid geometry from selected to unselected";
451  ot->idname = "MESH_OT_intersect_boolean";
452 
453  /* api callbacks */
457 
458  /* props */
460  "operation",
461  isect_boolean_operation_items,
463  "Boolean Operation",
464  "Which boolean operation to apply");
466  "use_swap",
467  false,
468  "Swap",
469  "Use with difference intersection to swap which side is kept");
471  ot->srna, "use_self", false, "Self Intersection", "Do self-union or self-intersection");
473  ot->srna, "threshold", 0.000001f, 0.0, 0.01, "Merge Threshold", "", 0.0, 0.001);
475  "solver",
476  isect_boolean_solver_items,
478  "Solver",
479  "Which Boolean solver to use");
480 
481  /* flags */
483 }
484 
487 /* -------------------------------------------------------------------- */
492  BMFace *f,
493  const char hflag,
494  /* reusable memory buffer */
495  BLI_Buffer *edge_net_temp_buf)
496 {
497  const int f_index = BM_elem_index_get(f);
498 
499  BMLoop *l_iter;
500  BMLoop *l_first;
501  BMVert *v;
502 
503  BMFace **face_arr;
504  int face_arr_len;
505 
506  /* likely this will stay very small
507  * all verts pushed into this stack _must_ have their previous edges set! */
508  BLI_SMALLSTACK_DECLARE(vert_stack, BMVert *);
509  BLI_SMALLSTACK_DECLARE(vert_stack_next, BMVert *);
510 
511  BLI_assert(edge_net_temp_buf->count == 0);
512 
513  /* collect all edges */
514  l_iter = l_first = BM_FACE_FIRST_LOOP(f);
515  do {
516  BMIter iter;
517  BMEdge *e;
518 
519  BM_ITER_ELEM (e, &iter, l_iter->v, BM_EDGES_OF_VERT) {
520  if (BM_elem_flag_test(e, hflag) && (BM_elem_index_get(e) == f_index)) {
521  v = BM_edge_other_vert(e, l_iter->v);
522  v->e = e;
523 
524  BLI_SMALLSTACK_PUSH(vert_stack, v);
525  BLI_buffer_append(edge_net_temp_buf, BMEdge *, e);
526  }
527  }
528  } while ((l_iter = l_iter->next) != l_first);
529 
530  /* now assign all */
531  /* pop free values into the next stack */
532  while ((v = BLI_SMALLSTACK_POP_EX(vert_stack, vert_stack_next))) {
533  BMIter eiter;
534  BMEdge *e_next;
535 
536  BM_ITER_ELEM (e_next, &eiter, v, BM_EDGES_OF_VERT) {
537  if (BM_elem_flag_test(e_next, hflag) && (BM_elem_index_get(e_next) == -1)) {
538  BMVert *v_next;
539  v_next = BM_edge_other_vert(e_next, v);
540  BM_elem_index_set(e_next, f_index);
541  BLI_SMALLSTACK_PUSH(vert_stack_next, v_next);
542  BLI_buffer_append(edge_net_temp_buf, BMEdge *, e_next);
543  }
544  }
545 
546  if (BLI_SMALLSTACK_IS_EMPTY(vert_stack)) {
547  BLI_SMALLSTACK_SWAP(vert_stack, vert_stack_next);
548  }
549  }
550 
552  bm, f, edge_net_temp_buf->data, edge_net_temp_buf->count, &face_arr, &face_arr_len);
553 
554  BLI_buffer_clear(edge_net_temp_buf);
555 
556  if (face_arr_len) {
557  int i;
558  for (i = 0; i < face_arr_len; i++) {
559  BM_face_select_set(bm, face_arr[i], true);
560  BM_elem_flag_disable(face_arr[i], hflag);
561  }
562  }
563 
564  if (face_arr) {
565  MEM_freeN(face_arr);
566  }
567 }
568 
573 static bool bm_vert_in_faces_radial(BMVert *v, BMEdge *e_radial, BMFace *f_ignore)
574 {
575  BLI_assert(BM_vert_in_face(v, f_ignore) == false);
576  if (e_radial->l) {
577  BMLoop *l_iter = e_radial->l;
578  do {
579  if (l_iter->f != f_ignore) {
580  if (BM_vert_in_face(v, l_iter->f)) {
581  return true;
582  }
583  }
584  } while ((l_iter = l_iter->radial_next) != e_radial->l);
585  }
586  return false;
587 }
588 
589 #ifdef USE_NET_ISLAND_CONNECT
590 
591 struct LinkBase {
592  LinkNode *list;
593  uint list_len;
594 };
595 
597  BMFace *f_key,
598  BMEdge *e_val,
600 {
601  void **ls_base_p;
602  struct LinkBase *ls_base;
603  LinkNode *ls;
604 
605  if (!BLI_ghash_ensure_p(gh, f_key, &ls_base_p)) {
606  ls_base = *ls_base_p = BLI_memarena_alloc(mem_arena, sizeof(*ls_base));
607  ls_base->list = NULL;
608  ls_base->list_len = 0;
609  }
610  else {
611  ls_base = *ls_base_p;
612  }
613 
614  ls = BLI_memarena_alloc(mem_arena, sizeof(*ls));
615  ls->next = ls_base->list;
616  ls->link = e_val;
617  ls_base->list = ls;
618  ls_base->list_len += 1;
619 }
620 
621 static int bm_edge_sort_length_cb(const void *e_a_v, const void *e_b_v)
622 {
623  const float val_a = -BM_edge_calc_length_squared(*((BMEdge **)e_a_v));
624  const float val_b = -BM_edge_calc_length_squared(*((BMEdge **)e_b_v));
625 
626  if (val_a > val_b) {
627  return 1;
628  }
629  if (val_a < val_b) {
630  return -1;
631  }
632  return 0;
633 }
634 
636  BMesh *bm, BMFace *f, LinkNode *e_link, const int e_link_len, MemArena *mem_arena_edgenet)
637 {
638  BMEdge **edge_arr = BLI_memarena_alloc(mem_arena_edgenet, sizeof(*edge_arr) * e_link_len);
639  int edge_arr_len = 0;
640 
641  while (e_link) {
642  edge_arr[edge_arr_len++] = e_link->link;
643  e_link = e_link->next;
644  }
645 
646  {
647  uint edge_arr_holes_len;
648  BMEdge **edge_arr_holes;
650  f,
651  edge_arr,
652  e_link_len,
653  true,
654  mem_arena_edgenet,
655  &edge_arr_holes,
656  &edge_arr_holes_len)) {
657  edge_arr_len = edge_arr_holes_len;
658  edge_arr = edge_arr_holes; /* owned by the arena */
659  }
660  }
661 
662  BM_face_split_edgenet(bm, f, edge_arr, edge_arr_len, NULL, NULL);
663 
664  for (int i = e_link_len; i < edge_arr_len; i++) {
665  BM_edge_select_set(bm, edge_arr[i], true);
666  }
667 
668  if (e_link_len != edge_arr_len) {
669  /* connecting partial islands can add redundant edges
670  * sort before removal to give deterministic outcome */
671  qsort(edge_arr, edge_arr_len - e_link_len, sizeof(*edge_arr), bm_edge_sort_length_cb);
672  for (int i = e_link_len; i < edge_arr_len; i++) {
673  BMFace *f_pair[2];
674  if (BM_edge_face_pair(edge_arr[i], &f_pair[0], &f_pair[1])) {
675  if (BM_face_share_vert_count(f_pair[0], f_pair[1]) == 2) {
676  BMFace *f_new = BM_faces_join(bm, f_pair, 2, true);
677  if (f_new) {
678  BM_face_select_set(bm, f_new, true);
679  }
680  }
681  }
682  }
683  }
684 }
685 
705  BMFace *f_a,
706  BMVert *v_pivot,
707  BMFace **ftable,
708  const int ftable_len,
709  float r_v_pivot_co[3],
710  float *r_v_pivot_fac)
711 {
712  const int f_a_index = BM_elem_index_get(e_a);
713  bool found_other_self = false;
714  int found_other_face = 0;
715  BLI_SMALLSTACK_DECLARE(face_stack, BMFace *);
716 
717  /* loop over surrounding edges to check if we're part of a chain or a delimiter vertex */
718  BMEdge *e_b = v_pivot->e;
719  do {
720  if (e_b != e_a) {
721  const int f_b_index = BM_elem_index_get(e_b);
722  if (f_b_index == f_a_index) {
723  /* not an endpoint */
724  found_other_self = true;
725  }
726  else if (f_b_index != -1) {
727  BLI_assert(f_b_index < ftable_len);
728  UNUSED_VARS_NDEBUG(ftable_len);
729 
730  /* 'v_pivot' spans 2+ faces,
731  * tag to ensure we pick an edge that includes this face */
732  BMFace *f_b = ftable[f_b_index];
735  BLI_SMALLSTACK_PUSH(face_stack, f_b);
736  found_other_face++;
737  }
738  }
739  }
740  } while ((e_b = BM_DISK_EDGE_NEXT(e_b, v_pivot)) != v_pivot->e);
741 
742  BMEdge *e_split = NULL;
743 
744  /* if we have no others or the other edge is outside this face,
745  * we're an endpoint to connect to a boundary */
746  if ((found_other_self == false) || found_other_face) {
747 
748  BMLoop *l_iter, *l_first;
749  l_iter = l_first = BM_FACE_FIRST_LOOP(f_a);
750  float dist_best_sq = FLT_MAX;
751 
752  do {
753  float v_pivot_co_test[3];
754  float v_pivot_fac = line_point_factor_v3(v_pivot->co, l_iter->e->v1->co, l_iter->e->v2->co);
755  CLAMP(v_pivot_fac, 0.0f, 1.0f);
756  interp_v3_v3v3(v_pivot_co_test, l_iter->e->v1->co, l_iter->e->v2->co, v_pivot_fac);
757 
758  float dist_test_sq = len_squared_v3v3(v_pivot_co_test, v_pivot->co);
759  if ((dist_test_sq < dist_best_sq) || (e_split == NULL)) {
760  bool ok = true;
761 
762  if (UNLIKELY(BM_edge_exists(v_pivot, l_iter->e->v1) ||
763  BM_edge_exists(v_pivot, l_iter->e->v2))) {
764  /* very unlikely but will cause complications splicing the verts together,
765  * so just skip this case */
766  ok = false;
767  }
768  else if (found_other_face) {
769  /* double check that _all_ the faces used by v_pivot's edges are attached
770  * to this edge otherwise don't attempt the split since it will give
771  * non-deterministic results */
772  BMLoop *l_radial_iter = l_iter->radial_next;
773  int other_face_shared = 0;
774  if (l_radial_iter != l_iter) {
775  do {
776  if (BM_elem_flag_test(l_radial_iter->f, BM_ELEM_INTERNAL_TAG)) {
777  other_face_shared++;
778  }
779  } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter);
780  }
781  if (other_face_shared != found_other_face) {
782  ok = false;
783  }
784  }
785 
786  if (ok) {
787  e_split = l_iter->e;
788  dist_best_sq = dist_test_sq;
789  copy_v3_v3(r_v_pivot_co, v_pivot_co_test);
790  *r_v_pivot_fac = v_pivot_fac;
791  }
792  }
793  } while ((l_iter = l_iter->next) != l_first);
794  }
795 
796  {
797  /* reset the flag, for future use */
798  BMFace *f;
799  while ((f = BLI_SMALLSTACK_POP(face_stack))) {
801  }
802  }
803 
804  return e_split;
805 }
806 
807 #endif /* USE_NET_ISLAND_CONNECT */
808 
810 {
811  const char hflag = BM_ELEM_TAG;
812 
813  BMEdge *e;
814  BMIter iter;
815 
816  BLI_SMALLSTACK_DECLARE(loop_stack, BMLoop *);
817 
818  ViewLayer *view_layer = CTX_data_view_layer(C);
819  uint objects_len = 0;
821  view_layer, CTX_wm_view3d(C), &objects_len);
822  for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
823  Object *obedit = objects[ob_index];
824  BMEditMesh *em = BKE_editmesh_from_object(obedit);
825  BMesh *bm = em->bm;
826 
827  if ((bm->totedgesel == 0) || (bm->totfacesel == 0)) {
828  continue;
829  }
830 
831  {
832  BMVert *v;
833  BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
834  BM_elem_flag_disable(v, hflag);
835  }
836  }
837 
838  /* edge index is set to -1 then used to associate them with faces */
839  BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
841  BM_elem_flag_enable(e, hflag);
842 
843  BM_elem_flag_enable(e->v1, hflag);
844  BM_elem_flag_enable(e->v2, hflag);
845  }
846  else {
847  BM_elem_flag_disable(e, hflag);
848  }
849  BM_elem_index_set(e, -1); /* set_dirty */
850  }
852 
853  {
854  BMFace *f;
855  int i;
856  BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
858  BM_elem_flag_enable(f, hflag);
859  }
860  else {
861  BM_elem_flag_disable(f, hflag);
862  }
864  BM_elem_index_set(f, i); /* set_ok */
865  }
866  }
868 
869  BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
870  if (BM_elem_flag_test(e, hflag)) {
871  BMIter viter;
872  BMVert *v;
873  BM_ITER_ELEM (v, &viter, e, BM_VERTS_OF_EDGE) {
874  BMIter liter;
875  BMLoop *l;
876 
877  uint loop_stack_len;
878  BMLoop *l_best = NULL;
879 
880  BLI_assert(BLI_SMALLSTACK_IS_EMPTY(loop_stack));
881  loop_stack_len = 0;
882 
883  BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
884  if (BM_elem_flag_test(l->f, hflag)) {
885  BLI_SMALLSTACK_PUSH(loop_stack, l);
886  loop_stack_len++;
887  }
888  }
889 
890  if (loop_stack_len == 0) {
891  /* pass */
892  }
893  else if (loop_stack_len == 1) {
894  l_best = BLI_SMALLSTACK_POP(loop_stack);
895  }
896  else {
897  /* complicated case, match the edge with a face-loop */
898 
899  BMVert *v_other = BM_edge_other_vert(e, v);
900  float e_dir[3];
901 
902  /* we want closest to zero */
903  float dot_best = FLT_MAX;
904 
905  sub_v3_v3v3(e_dir, v_other->co, v->co);
906  normalize_v3(e_dir);
907 
908  while ((l = BLI_SMALLSTACK_POP(loop_stack))) {
909  float dot_test;
910 
911  /* Check dot first to save on expensive angle-comparison.
912  * ideal case is 90d difference == 0.0 dot */
913  dot_test = fabsf(dot_v3v3(e_dir, l->f->no));
914  if (dot_test < dot_best) {
915 
916  /* check we're in the correct corner
917  * (works with convex loops too) */
919  l->prev->v->co, l->v->co, v_other->co, l->f->no) <
921  l->prev->v->co, l->v->co, l->next->v->co, l->f->no)) {
922  dot_best = dot_test;
923  l_best = l;
924  }
925  }
926  }
927  }
928 
929  if (l_best) {
930  BM_elem_index_set(e, BM_elem_index_get(l_best->f)); /* set_dirty */
931  }
932  }
933  }
934  }
935 
936  {
937  BMFace *f;
938  BLI_buffer_declare_static(BMEdge **, edge_net_temp_buf, 0, 128);
939 
940  BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
941  if (BM_elem_flag_test(f, hflag)) {
942  bm_face_split_by_edges(bm, f, hflag, &edge_net_temp_buf);
943  }
944  }
945  BLI_buffer_free(&edge_net_temp_buf);
946  }
947 
948 #ifdef USE_NET_ISLAND_CONNECT
949  /* before overwriting edge index values, collect edges left untouched */
950  BLI_Stack *edges_loose = BLI_stack_new(sizeof(BMEdge *), __func__);
951  BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
953  BLI_stack_push(edges_loose, &e);
954  }
955  }
956 #endif
957 
958  EDBM_update(obedit->data,
959  &(const struct EDBMUpdate_Params){
960  .calc_looptri = true,
961  .calc_normals = true,
962  .is_destructive = true,
963  });
964 
965 #ifdef USE_NET_ISLAND_CONNECT
966  /* we may have remaining isolated regions remaining,
967  * these will need to have connecting edges created */
968  if (!BLI_stack_is_empty(edges_loose)) {
969  GHash *face_edge_map = BLI_ghash_ptr_new(__func__);
970 
972 
974 
975  {
976  BMBVHTree *bmbvh = BKE_bmbvh_new(
977  bm, em->looptris, em->tottri, BMBVH_RESPECT_SELECT, NULL, false);
978 
979  BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
980  BM_elem_index_set(e, -1); /* set_dirty */
981  }
982 
983  while (!BLI_stack_is_empty(edges_loose)) {
984  BLI_stack_pop(edges_loose, &e);
985  float e_center[3];
986  mid_v3_v3v3(e_center, e->v1->co, e->v2->co);
987 
988  BMFace *f = BKE_bmbvh_find_face_closest(bmbvh, e_center, FLT_MAX);
989  if (f) {
990  ghash_insert_face_edge_link(face_edge_map, f, e, mem_arena);
991  BM_elem_index_set(e, BM_elem_index_get(f)); /* set_dirty */
992  }
993  }
994 
995  BKE_bmbvh_free(bmbvh);
996  }
997 
999 
1001 
1002  /* detect edges chains that span faces
1003  * and splice vertices into the closest edges */
1004  {
1005  GHashIterator gh_iter;
1006 
1007  GHASH_ITER (gh_iter, face_edge_map) {
1008  BMFace *f = BLI_ghashIterator_getKey(&gh_iter);
1009  struct LinkBase *e_ls_base = BLI_ghashIterator_getValue(&gh_iter);
1010  LinkNode *e_link = e_ls_base->list;
1011 
1012  do {
1013  e = e_link->link;
1014 
1015  for (int j = 0; j < 2; j++) {
1016  BMVert *v_pivot = (&e->v1)[j];
1017  /* checking that \a v_pivot isn't in the face prevents attempting
1018  * to splice the same vertex into an edge from multiple faces */
1019  if (!BM_vert_in_face(v_pivot, f)) {
1020  float v_pivot_co[3];
1021  float v_pivot_fac;
1022  BMEdge *e_split = bm_face_split_edge_find(
1023  e, f, v_pivot, bm->ftable, bm->totface, v_pivot_co, &v_pivot_fac);
1024 
1025  if (e_split) {
1026  /* for degenerate cases this vertex may be in one
1027  * of this edges radial faces */
1028  if (!bm_vert_in_faces_radial(v_pivot, e_split, f)) {
1029  BMEdge *e_new;
1030  BMVert *v_new = BM_edge_split(bm, e_split, e_split->v1, &e_new, v_pivot_fac);
1031  if (v_new) {
1032  /* we _know_ these don't share an edge */
1033  BM_vert_splice(bm, v_pivot, v_new);
1034  BM_elem_index_set(e_new, BM_elem_index_get(e_split));
1035  }
1036  }
1037  }
1038  }
1039  }
1040 
1041  } while ((e_link = e_link->next));
1042  }
1043  }
1044 
1045  {
1046  MemArena *mem_arena_edgenet = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
1047 
1048  GHashIterator gh_iter;
1049 
1050  GHASH_ITER (gh_iter, face_edge_map) {
1051  BMFace *f = BLI_ghashIterator_getKey(&gh_iter);
1052  struct LinkBase *e_ls_base = BLI_ghashIterator_getValue(&gh_iter);
1053 
1055  bm, f, e_ls_base->list, e_ls_base->list_len, mem_arena_edgenet);
1056 
1057  BLI_memarena_clear(mem_arena_edgenet);
1058  }
1059 
1060  BLI_memarena_free(mem_arena_edgenet);
1061  }
1062 
1064 
1065  BLI_ghash_free(face_edge_map, NULL, NULL);
1066 
1067  EDBM_update(obedit->data,
1068  &(const struct EDBMUpdate_Params){
1069  .calc_looptri = true,
1070  .calc_normals = true,
1071  .is_destructive = true,
1072  });
1073  }
1074 
1075  BLI_stack_free(edges_loose);
1076 #endif /* USE_NET_ISLAND_CONNECT */
1077  }
1078  MEM_freeN(objects);
1079  return OPERATOR_FINISHED;
1080 }
1081 
1083 {
1084  /* identifiers */
1085  ot->name = "Weld Edges into Faces";
1086  ot->description = "Weld loose edges into faces (splitting them into new faces)";
1087  ot->idname = "MESH_OT_face_split_by_edges";
1088 
1089  /* api callbacks */
1092 
1093  /* flags */
1095 }
1096 
struct ViewLayer * CTX_data_view_layer(const bContext *C)
Definition: context.c:1100
struct View3D * CTX_wm_view3d(const bContext *C)
Definition: context.c:784
BMEditMesh * BKE_editmesh_from_object(struct Object *ob)
Return the BMEditMesh for a given object.
Definition: editmesh.c:58
struct BMFace * BKE_bmbvh_find_face_closest(BMBVHTree *tree, const float co[3], float dist_max)
Definition: editmesh_bvh.c:484
BMBVHTree * BKE_bmbvh_new(struct BMesh *bm, struct BMLoop *(*looptris)[3], int looptris_tot, int flag, const float(*cos_cage)[3], bool cos_cage_free)
Definition: editmesh_bvh.c:145
void BKE_bmbvh_free(BMBVHTree *tree)
Definition: editmesh_bvh.c:170
@ BMBVH_RESPECT_SELECT
#define BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, v3d, r_len)
Definition: BKE_layer.h:542
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition: report.c:83
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define BLI_buffer_append(buffer_, type_, val_)
Definition: BLI_buffer.h:50
#define BLI_buffer_declare_static(type_, name_, flag_, static_count_)
Definition: BLI_buffer.h:25
#define BLI_buffer_clear(buffer_)
Definition: BLI_buffer.h:54
#define BLI_buffer_free(name_)
Definition: BLI_buffer.h:94
BLI_INLINE void * BLI_ghashIterator_getKey(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.h:298
BLI_INLINE void * BLI_ghashIterator_getValue(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.h:302
#define GHASH_ITER(gh_iter_, ghash_)
Definition: BLI_ghash.h:321
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition: BLI_ghash.c:863
GHash * BLI_ghash_ptr_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
bool BLI_ghash_ensure_p(GHash *gh, void *key, void ***r_val) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:755
float line_point_factor_v3(const float p[3], const float l1[3], const float l2[3])
Definition: math_geom.c:3254
MINLINE float normalize_v3(float r[3])
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
float angle_signed_on_axis_v3v3v3_v3(const float v1[3], const float v2[3], const float v3[3], const float axis[3]) ATTR_WARN_UNUSED_RESULT
Definition: math_vector.c:521
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], float t)
Definition: math_vector.c:29
void mid_v3_v3v3(float r[3], const float a[3], const float b[3])
Definition: math_vector.c:237
void BLI_memarena_free(struct MemArena *ma) ATTR_NONNULL(1)
Definition: BLI_memarena.c:94
struct MemArena * BLI_memarena_new(size_t bufsize, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(2) ATTR_MALLOC
Definition: BLI_memarena.c:64
#define BLI_MEMARENA_STD_BUFSIZE
Definition: BLI_memarena.h:20
void * BLI_memarena_alloc(struct MemArena *ma, size_t size) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_ALLOC_SIZE(2)
Definition: BLI_memarena.c:116
void void BLI_memarena_clear(MemArena *ma) ATTR_NONNULL(1)
Definition: BLI_memarena.c:208
void BLI_stack_pop(BLI_Stack *stack, void *dst) ATTR_NONNULL()
Definition: stack.c:135
void BLI_stack_push(BLI_Stack *stack, const void *src) ATTR_NONNULL()
Definition: stack.c:129
bool BLI_stack_is_empty(const BLI_Stack *stack) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: stack.c:247
void BLI_stack_free(BLI_Stack *stack) ATTR_NONNULL()
Definition: stack.c:94
#define BLI_stack_new(esize, descr)
unsigned int uint
Definition: BLI_sys_types.h:67
#define UNUSED_VARS_NDEBUG(...)
#define UNUSED(x)
#define UNLIKELY(x)
Object is a sort of wrapper for general info.
#define SCE_SELECT_VERTEX
#define SCE_SELECT_EDGE
@ OPERATOR_FINISHED
void EDBM_update(struct Mesh *me, const struct EDBMUpdate_Params *params)
void EDBM_selectmode_flush(struct BMEditMesh *em)
bool ED_operator_editmesh(struct bContext *C)
Definition: screen_ops.c:433
Read Guarded memory(de)allocation.
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position CLAMP
#define C
Definition: RandGen.cpp:25
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
void uiItemS(uiLayout *layout)
uiLayout * uiLayoutRow(uiLayout *layout, bool align)
@ UI_ITEM_R_EXPAND
void uiItemR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int flag, const char *name, int icon)
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
@ OPTYPE_UNDO
Definition: WM_types.h:148
@ OPTYPE_REGISTER
Definition: WM_types.h:146
bool BM_mesh_boolean(BMesh *UNUSED(bm), struct BMLoop *(*looptris)[3], const int UNUSED(looptris_tot), int(*test_fn)(BMFace *, void *), void *UNUSED(user_data), const int UNUSED(nshapes), const bool UNUSED(use_self), const bool UNUSED(keep_hidden), const bool UNUSED(hole_tolerant), const int UNUSED(boolean_mode))
bool BM_mesh_boolean_knife(BMesh *UNUSED(bm), struct BMLoop *(*looptris)[3], const int UNUSED(looptris_tot), int(*test_fn)(BMFace *, void *), void *UNUSED(user_data), const int UNUSED(nshapes), const bool UNUSED(use_self), const bool UNUSED(use_separate_all), const bool UNUSED(hole_tolerant), const bool UNUSED(keep_hidden))
#define BM_elem_cb_check_hflag_enabled_simple(type, hflag_p)
#define BM_DISK_EDGE_NEXT(e, v)
Definition: bmesh_class.h:625
#define BM_FACE_FIRST_LOOP(p)
Definition: bmesh_class.h:622
@ BM_FACE
Definition: bmesh_class.h:386
@ BM_VERT
Definition: bmesh_class.h:383
@ BM_EDGE
Definition: bmesh_class.h:384
@ BM_ELEM_HIDDEN
Definition: bmesh_class.h:472
@ BM_ELEM_SELECT
Definition: bmesh_class.h:471
@ BM_ELEM_INTERNAL_TAG
Definition: bmesh_class.h:496
@ BM_ELEM_TAG
Definition: bmesh_class.h:484
BMFace * BM_faces_join(BMesh *bm, BMFace **faces, int totface, const bool do_del)
Join Connected Faces.
Definition: bmesh_core.c:1123
bool BM_vert_splice(BMesh *bm, BMVert *v_dst, BMVert *v_src)
Splice Vert.
Definition: bmesh_core.c:2046
#define BM_elem_index_get(ele)
Definition: bmesh_inline.h:110
#define BM_elem_flag_disable(ele, hflag)
Definition: bmesh_inline.h:15
#define BM_elem_index_set(ele, index)
Definition: bmesh_inline.h:111
#define BM_elem_flag_test(ele, hflag)
Definition: bmesh_inline.h:12
#define BM_elem_flag_enable(ele, hflag)
Definition: bmesh_inline.h:14
bool BM_mesh_intersect(BMesh *bm, struct BMLoop *(*looptris)[3], const int looptris_tot, int(*test_fn)(BMFace *f, void *user_data), void *user_data, const bool use_self, const bool use_separate, const bool use_dissolve, const bool use_island_connect, const bool use_partial_connect, const bool use_edge_tag, const int boolean_mode, const float eps)
@ BMESH_ISECT_BOOLEAN_DIFFERENCE
@ BMESH_ISECT_BOOLEAN_UNION
@ BMESH_ISECT_BOOLEAN_ISECT
#define BM_ITER_ELEM(ele, iter, data, itype)
#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_VERTS_OF_EDGE
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_VERT
@ BM_EDGES_OF_VERT
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_face_select_set(BMesh *bm, BMFace *f, const bool select)
Select Face.
void BM_edge_select_set(BMesh *bm, BMEdge *e, const bool select)
Select Edge.
void BM_mesh_elem_hflag_disable_all(BMesh *bm, const char htype, const char hflag, const bool respecthide)
void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
Definition: bmesh_mesh.cc:558
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
Definition: bmesh_mesh.cc:446
BMVert * BM_edge_split(BMesh *bm, BMEdge *e, BMVert *v, BMEdge **r_e, float fac)
Edge Split.
Definition: bmesh_mods.c:448
bool BM_face_split_edgenet_connect_islands(BMesh *bm, BMFace *f, BMEdge **edge_net_init, const uint edge_net_init_len, bool use_partial_connect, MemArena *mem_arena, BMEdge ***r_edge_net_new, uint *r_edge_net_new_len)
bool BM_face_split_edgenet(BMesh *bm, BMFace *f, BMEdge **edge_net, const int edge_net_len, BMFace ***r_face_arr, int *r_face_arr_len)
BMEdge * BM_edge_exists(BMVert *v_a, BMVert *v_b)
Definition: bmesh_query.c:1553
int BM_face_share_vert_count(BMFace *f_a, BMFace *f_b)
Definition: bmesh_query.c:999
float BM_edge_calc_length_squared(const BMEdge *e)
Definition: bmesh_query.c:533
bool BM_edge_face_pair(BMEdge *e, BMFace **r_fa, BMFace **r_fb)
Definition: bmesh_query.c:538
bool BM_vert_in_face(BMVert *v, BMFace *f)
Definition: bmesh_query.c:306
BLI_INLINE BMVert * BM_edge_other_vert(BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_edge_is_wire(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
void BM_mesh_separate_faces(BMesh *bm, BMFaceFilterFunc filter_fn, void *user_data)
void * user_data
void MESH_OT_intersect_boolean(struct wmOperatorType *ot)
static int bm_face_isect_pair_swap(BMFace *f, void *UNUSED(user_data))
static int edbm_face_split_by_edges_exec(bContext *C, wmOperator *UNUSED(op))
static bool bm_vert_in_faces_radial(BMVert *v, BMEdge *e_radial, BMFace *f_ignore)
static int edbm_intersect_boolean_exec(bContext *C, wmOperator *op)
static int bm_face_isect_self(BMFace *f, void *UNUSED(user_data))
static void bm_face_split_by_edges(BMesh *bm, BMFace *f, const char hflag, BLI_Buffer *edge_net_temp_buf)
static void edbm_intersect_select(BMEditMesh *em, struct Mesh *me, bool do_select)
@ ISECT_SEL
@ ISECT_SEL_UNSEL
static void bm_face_split_by_edges_island_connect(BMesh *bm, BMFace *f, LinkNode *e_link, const int e_link_len, MemArena *mem_arena_edgenet)
static void edbm_intersect_ui(bContext *UNUSED(C), wmOperator *op)
@ ISECT_SOLVER_EXACT
@ ISECT_SOLVER_FAST
void MESH_OT_face_split_by_edges(struct wmOperatorType *ot)
@ ISECT_SEPARATE_ALL
@ ISECT_SEPARATE_NONE
@ ISECT_SEPARATE_CUT
static void ghash_insert_face_edge_link(GHash *gh, BMFace *f_key, BMEdge *e_val, MemArena *mem_arena)
void MESH_OT_intersect(struct wmOperatorType *ot)
static int edbm_intersect_exec(bContext *C, wmOperator *op)
static void edbm_intersect_boolean_ui(bContext *UNUSED(C), wmOperator *op)
static int bm_face_isect_pair(BMFace *f, void *UNUSED(user_data))
static int bm_edge_sort_length_cb(const void *e_a_v, const void *e_b_v)
static BMEdge * bm_face_split_edge_find(BMEdge *e_a, BMFace *f_a, BMVert *v_pivot, BMFace **ftable, const int ftable_len, float r_v_pivot_co[3], float *r_v_pivot_fac)
static MemArena * mem_arena
Definition: makesdna.c:58
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
#define fabsf(x)
Definition: metal/compat.h:219
const btScalar eps
Definition: poly34.cpp:11
float RNA_float_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4957
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4863
int RNA_enum_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:5004
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, bool default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3493
PropertyRNA * RNA_def_float_distance(StructOrFunctionRNA *cont_, const char *identifier, float default_value, float hardmin, float hardmax, const char *ui_name, const char *ui_description, float softmin, float softmax)
Definition: rna_define.c:4052
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, int default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3783
void * data
Definition: BLI_buffer.h:14
size_t count
Definition: BLI_buffer.h:16
BMVert * v1
Definition: bmesh_class.h:122
BMVert * v2
Definition: bmesh_class.h:122
struct BMLoop * l
Definition: bmesh_class.h:128
struct BMLoop *(* looptris)[3]
Definition: BKE_editmesh.h:48
struct BMesh * bm
Definition: BKE_editmesh.h:40
float no[3]
Definition: bmesh_class.h:271
struct BMVert * v
Definition: bmesh_class.h:153
struct BMEdge * e
Definition: bmesh_class.h:164
struct BMLoop * radial_next
Definition: bmesh_class.h:204
struct BMLoop * prev
Definition: bmesh_class.h:233
struct BMFace * f
Definition: bmesh_class.h:171
struct BMLoop * next
Definition: bmesh_class.h:233
float co[3]
Definition: bmesh_class.h:87
struct BMEdge * e
Definition: bmesh_class.h:97
int totfacesel
Definition: bmesh_class.h:298
char elem_index_dirty
Definition: bmesh_class.h:305
short selectmode
Definition: bmesh_class.h:350
int totedgesel
Definition: bmesh_class.h:298
BMFace ** ftable
Definition: bmesh_class.h:323
int totface
Definition: bmesh_class.h:297
LinkNode * list
void * link
Definition: BLI_linklist.h:24
struct LinkNode * next
Definition: BLI_linklist.h:23
void * data
const char * name
Definition: WM_types.h:888
const char * idname
Definition: WM_types.h:890
bool(* poll)(struct bContext *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:943
struct StructRNA * srna
Definition: WM_types.h:969
const char * description
Definition: WM_types.h:893
void(* ui)(struct bContext *, struct wmOperator *)
Definition: WM_types.h:954
int(* exec)(struct bContext *, struct wmOperator *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:903
struct ReportList * reports
struct uiLayout * layout
struct PointerRNA * ptr
wmOperatorType * ot
Definition: wm_files.c:3479