Blender  V2.93
editmesh_intersect.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
21 #include "MEM_guardedalloc.h"
22 
23 #include "DNA_object_types.h"
24 
25 #include "BLI_buffer.h"
26 #include "BLI_linklist_stack.h"
27 #include "BLI_math.h"
28 #include "BLI_memarena.h"
29 #include "BLI_stack.h"
30 
31 #include "BKE_context.h"
32 #include "BKE_editmesh.h"
33 #include "BKE_editmesh_bvh.h"
34 #include "BKE_layer.h"
35 #include "BKE_report.h"
36 
37 #include "RNA_access.h"
38 #include "RNA_define.h"
39 
40 #include "WM_types.h"
41 
42 #include "UI_interface.h"
43 #include "UI_resources.h"
44 
45 #include "ED_mesh.h"
46 #include "ED_screen.h"
47 
48 #include "intern/bmesh_private.h"
49 
50 #include "mesh_intern.h" /* own include */
51 
52 #include "tools/bmesh_boolean.h"
53 #include "tools/bmesh_intersect.h"
54 #include "tools/bmesh_separate.h"
55 
56 /* detect isolated holes and fill them */
57 #define USE_NET_ISLAND_CONNECT
58 
62 static int bm_face_isect_self(BMFace *f, void *UNUSED(user_data))
63 {
65  return 0;
66  }
67  return -1;
68 }
69 
73 static int bm_face_isect_pair(BMFace *f, void *UNUSED(user_data))
74 {
76  return -1;
77  }
79  return 1;
80  }
81  return 0;
82 }
83 
89 {
91  return -1;
92  }
94  return 0;
95  }
96  return 1;
97 }
98 
102 static void edbm_intersect_select(BMEditMesh *em, struct Mesh *me, bool do_select)
103 {
104  if (do_select) {
106 
108  BMIter iter;
109  BMEdge *e;
110 
111  BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
113  BM_edge_select_set(em->bm, e, true);
114  }
115  }
116  }
117  }
118 
120  EDBM_update_generic(me, true, true);
121 }
122 
123 /* -------------------------------------------------------------------- */
129 enum {
132 };
133 
134 enum {
138 };
139 
140 enum {
143 };
144 
146 {
147  const int mode = RNA_enum_get(op->ptr, "mode");
148  int (*test_fn)(BMFace *, void *);
149  bool use_separate_all = false;
150  bool use_separate_cut = false;
151  const int separate_mode = RNA_enum_get(op->ptr, "separate_mode");
152  const float eps = RNA_float_get(op->ptr, "threshold");
153 #ifdef WITH_GMP
154  const bool exact = RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT;
155 #else
156  if (RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT) {
157  BKE_report(op->reports, RPT_WARNING, "Compiled without GMP, using fast solver");
158  }
159  const bool exact = false;
160 #endif
161  bool use_self;
162  bool has_isect;
163 
164  switch (mode) {
165  case ISECT_SEL:
166  test_fn = bm_face_isect_self;
167  use_self = true;
168  break;
169  default: /* ISECT_SEL_UNSEL */
170  test_fn = bm_face_isect_pair;
171  use_self = false;
172  break;
173  }
174 
175  switch (separate_mode) {
176  case ISECT_SEPARATE_ALL:
177  use_separate_all = true;
178  break;
179  case ISECT_SEPARATE_CUT:
180  if (use_self == false) {
181  use_separate_cut = true;
182  }
183  else {
184  /* we could support this but would require more advanced logic inside 'BM_mesh_intersect'
185  * for now just separate all */
186  use_separate_all = true;
187  }
188  break;
189  default: /* ISECT_SEPARATE_NONE */
190  break;
191  }
192  ViewLayer *view_layer = CTX_data_view_layer(C);
193  uint objects_len = 0;
194  uint isect_len = 0;
196  view_layer, CTX_wm_view3d(C), &objects_len);
197  for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
198  Object *obedit = objects[ob_index];
199  BMEditMesh *em = BKE_editmesh_from_object(obedit);
200 
201  if (em->bm->totfacesel == 0) {
202  continue;
203  }
204 
205  if (exact) {
206  int nshapes = use_self ? 1 : 2;
207  has_isect = BM_mesh_boolean_knife(em->bm,
208  em->looptris,
209  em->tottri,
210  test_fn,
211  NULL,
212  nshapes,
213  use_self,
214  use_separate_all,
215  false,
216  true);
217  }
218  else {
219  has_isect = BM_mesh_intersect(em->bm,
220  em->looptris,
221  em->tottri,
222  test_fn,
223  NULL,
224  use_self,
225  use_separate_all,
226  true,
227  true,
228  true,
229  true,
230  -1,
231  eps);
232  }
233 
234  if (use_separate_cut) {
235  /* detach selected/un-selected faces */
238  }
239 
240  edbm_intersect_select(em, obedit->data, has_isect);
241 
242  if (!has_isect) {
243  isect_len++;
244  }
245  }
246  MEM_freeN(objects);
247 
248  if (isect_len == objects_len) {
249  BKE_report(op->reports, RPT_WARNING, "No intersections found");
250  }
251  return OPERATOR_FINISHED;
252 }
253 
255 {
256  uiLayout *layout = op->layout;
258  uiLayout *row;
259  PointerRNA ptr;
260 
261  RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
262 
263  bool use_exact = RNA_enum_get(&ptr, "solver") == ISECT_SOLVER_EXACT;
264 
265  uiLayoutSetPropSep(layout, true);
266  uiLayoutSetPropDecorate(layout, false);
267  row = uiLayoutRow(layout, false);
268  uiItemR(row, &ptr, "mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
269  uiItemS(layout);
270  row = uiLayoutRow(layout, false);
271  uiItemR(row, &ptr, "separate_mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
272  uiItemS(layout);
273 
274  row = uiLayoutRow(layout, false);
275  uiItemR(row, &ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
276  uiItemS(layout);
277 
278  if (!use_exact) {
279  uiItemR(layout, &ptr, "threshold", 0, NULL, ICON_NONE);
280  }
281 }
282 
284 {
285  static const EnumPropertyItem isect_mode_items[] = {
286  {ISECT_SEL, "SELECT", 0, "Self Intersect", "Self intersect selected faces"},
288  "SELECT_UNSELECT",
289  0,
290  "Selected/Unselected",
291  "Intersect selected with unselected faces"},
292  {0, NULL, 0, NULL, NULL},
293  };
294 
295  static const EnumPropertyItem isect_separate_items[] = {
296  {ISECT_SEPARATE_ALL, "ALL", 0, "All", "Separate all geometry from intersections"},
298  "CUT",
299  0,
300  "Cut",
301  "Cut into geometry keeping each side separate (Selected/Unselected only)"},
302  {ISECT_SEPARATE_NONE, "NONE", 0, "Merge", "Merge all geometry from the intersection"},
303  {0, NULL, 0, NULL, NULL},
304  };
305 
306  static const EnumPropertyItem isect_intersect_solver_items[] = {
307  {ISECT_SOLVER_FAST, "FAST", 0, "Fast", "Faster solver, some limitations"},
308  {ISECT_SOLVER_EXACT, "EXACT", 0, "Exact", "Exact solver, slower, handles more cases"},
309  {0, NULL, 0, NULL, NULL},
310  };
311 
312  /* identifiers */
313  ot->name = "Intersect (Knife)";
314  ot->description = "Cut an intersection into faces";
315  ot->idname = "MESH_OT_intersect";
316 
317  /* api callbacks */
321 
322  /* props */
323  RNA_def_enum(ot->srna, "mode", isect_mode_items, ISECT_SEL_UNSEL, "Source", "");
324  RNA_def_enum(
325  ot->srna, "separate_mode", isect_separate_items, ISECT_SEPARATE_CUT, "Separate Mode", "");
327  ot->srna, "threshold", 0.000001f, 0.0, 0.01, "Merge Threshold", "", 0.0, 0.001);
329  "solver",
330  isect_intersect_solver_items,
332  "Solver",
333  "Which Intersect solver to use");
334 
335  /* flags */
337 }
338 
341 /* -------------------------------------------------------------------- */
349 {
350  const int boolean_operation = RNA_enum_get(op->ptr, "operation");
351  bool use_swap = RNA_boolean_get(op->ptr, "use_swap");
352  bool use_self = RNA_boolean_get(op->ptr, "use_self");
353 #ifdef WITH_GMP
354  const bool use_exact = RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT;
355 #else
356  if (RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT) {
357  BKE_report(op->reports, RPT_WARNING, "Compiled without GMP, using fast solver");
358  }
359  const bool use_exact = false;
360 #endif
361  const float eps = RNA_float_get(op->ptr, "threshold");
362  int (*test_fn)(BMFace *, void *);
363  bool has_isect;
364 
365  test_fn = use_swap ? bm_face_isect_pair_swap : bm_face_isect_pair;
366  ViewLayer *view_layer = CTX_data_view_layer(C);
367  uint objects_len = 0;
368  uint isect_len = 0;
370  view_layer, CTX_wm_view3d(C), &objects_len);
371  for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
372  Object *obedit = objects[ob_index];
373  BMEditMesh *em = BKE_editmesh_from_object(obedit);
374 
375  if (em->bm->totfacesel == 0) {
376  continue;
377  }
378 
379  if (use_exact) {
380  has_isect = BM_mesh_boolean(em->bm,
381  em->looptris,
382  em->tottri,
383  test_fn,
384  NULL,
385  2,
386  use_self,
387  true,
388  false,
389  boolean_operation);
390  }
391  else {
392  has_isect = BM_mesh_intersect(em->bm,
393  em->looptris,
394  em->tottri,
395  test_fn,
396  NULL,
397  false,
398  false,
399  true,
400  true,
401  false,
402  true,
403  boolean_operation,
404  eps);
405  }
406 
407  edbm_intersect_select(em, obedit->data, has_isect);
408 
409  if (!has_isect) {
410  isect_len++;
411  }
412  }
413  MEM_freeN(objects);
414 
415  if (isect_len == objects_len) {
416  BKE_report(op->reports, RPT_WARNING, "No intersections found");
417  }
418  return OPERATOR_FINISHED;
419 }
420 
422 {
423  uiLayout *layout = op->layout;
424  uiLayout *row;
426  PointerRNA ptr;
427 
428  RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
429 
430  bool use_exact = RNA_enum_get(&ptr, "solver") == ISECT_SOLVER_EXACT;
431 
432  uiLayoutSetPropSep(layout, true);
433  uiLayoutSetPropDecorate(layout, false);
434 
435  row = uiLayoutRow(layout, false);
436  uiItemR(row, &ptr, "operation", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
437  uiItemS(layout);
438 
439  row = uiLayoutRow(layout, false);
440  uiItemR(row, &ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
441  uiItemS(layout);
442 
443  uiItemR(layout, &ptr, "use_swap", 0, NULL, ICON_NONE);
444  uiItemR(layout, &ptr, "use_self", 0, NULL, ICON_NONE);
445  if (!use_exact) {
446  uiItemR(layout, &ptr, "threshold", 0, NULL, ICON_NONE);
447  }
448 }
449 
451 {
452  static const EnumPropertyItem isect_boolean_operation_items[] = {
453  {BMESH_ISECT_BOOLEAN_ISECT, "INTERSECT", 0, "Intersect", ""},
454  {BMESH_ISECT_BOOLEAN_UNION, "UNION", 0, "Union", ""},
455  {BMESH_ISECT_BOOLEAN_DIFFERENCE, "DIFFERENCE", 0, "Difference", ""},
456  {0, NULL, 0, NULL, NULL},
457  };
458 
459  static const EnumPropertyItem isect_boolean_solver_items[] = {
460  {ISECT_SOLVER_FAST, "FAST", 0, "Fast", "Faster solver, some limitations"},
461  {ISECT_SOLVER_EXACT, "EXACT", 0, "Exact", "Exact solver, slower, handles more cases"},
462  {0, NULL, 0, NULL, NULL},
463  };
464 
465  /* identifiers */
466  ot->name = "Intersect (Boolean)";
467  ot->description = "Cut solid geometry from selected to unselected";
468  ot->idname = "MESH_OT_intersect_boolean";
469 
470  /* api callbacks */
474 
475  /* props */
477  "operation",
478  isect_boolean_operation_items,
480  "Boolean Operation",
481  "Which boolean operation to apply");
483  "use_swap",
484  false,
485  "Swap",
486  "Use with difference intersection to swap which side is kept");
487  RNA_def_boolean(ot->srna, "use_self", false, "Self", "Do self-union or self-intersection");
489  ot->srna, "threshold", 0.000001f, 0.0, 0.01, "Merge Threshold", "", 0.0, 0.001);
491  "solver",
492  isect_boolean_solver_items,
494  "Solver",
495  "Which Boolean solver to use");
496 
497  /* flags */
499 }
500 
503 /* -------------------------------------------------------------------- */
508  BMFace *f,
509  const char hflag,
510  /* reusable memory buffer */
511  BLI_Buffer *edge_net_temp_buf)
512 {
513  const int f_index = BM_elem_index_get(f);
514 
515  BMLoop *l_iter;
516  BMLoop *l_first;
517  BMVert *v;
518 
519  BMFace **face_arr;
520  int face_arr_len;
521 
522  /* likely this will stay very small
523  * all verts pushed into this stack _must_ have their previous edges set! */
524  BLI_SMALLSTACK_DECLARE(vert_stack, BMVert *);
525  BLI_SMALLSTACK_DECLARE(vert_stack_next, BMVert *);
526 
527  BLI_assert(edge_net_temp_buf->count == 0);
528 
529  /* collect all edges */
530  l_iter = l_first = BM_FACE_FIRST_LOOP(f);
531  do {
532  BMIter iter;
533  BMEdge *e;
534 
535  BM_ITER_ELEM (e, &iter, l_iter->v, BM_EDGES_OF_VERT) {
536  if (BM_elem_flag_test(e, hflag) && (BM_elem_index_get(e) == f_index)) {
537  v = BM_edge_other_vert(e, l_iter->v);
538  v->e = e;
539 
540  BLI_SMALLSTACK_PUSH(vert_stack, v);
541  BLI_buffer_append(edge_net_temp_buf, BMEdge *, e);
542  }
543  }
544  } while ((l_iter = l_iter->next) != l_first);
545 
546  /* now assign all */
547  /* pop free values into the next stack */
548  while ((v = BLI_SMALLSTACK_POP_EX(vert_stack, vert_stack_next))) {
549  BMIter eiter;
550  BMEdge *e_next;
551 
552  BM_ITER_ELEM (e_next, &eiter, v, BM_EDGES_OF_VERT) {
553  if (BM_elem_flag_test(e_next, hflag) && (BM_elem_index_get(e_next) == -1)) {
554  BMVert *v_next;
555  v_next = BM_edge_other_vert(e_next, v);
556  BM_elem_index_set(e_next, f_index);
557  BLI_SMALLSTACK_PUSH(vert_stack_next, v_next);
558  BLI_buffer_append(edge_net_temp_buf, BMEdge *, e_next);
559  }
560  }
561 
562  if (BLI_SMALLSTACK_IS_EMPTY(vert_stack)) {
563  BLI_SMALLSTACK_SWAP(vert_stack, vert_stack_next);
564  }
565  }
566 
568  bm, f, edge_net_temp_buf->data, edge_net_temp_buf->count, &face_arr, &face_arr_len);
569 
570  BLI_buffer_clear(edge_net_temp_buf);
571 
572  if (face_arr_len) {
573  int i;
574  for (i = 0; i < face_arr_len; i++) {
575  BM_face_select_set(bm, face_arr[i], true);
576  BM_elem_flag_disable(face_arr[i], hflag);
577  }
578  }
579 
580  if (face_arr) {
581  MEM_freeN(face_arr);
582  }
583 }
584 
589 static bool bm_vert_in_faces_radial(BMVert *v, BMEdge *e_radial, BMFace *f_ignore)
590 {
591  BLI_assert(BM_vert_in_face(v, f_ignore) == false);
592  if (e_radial->l) {
593  BMLoop *l_iter = e_radial->l;
594  do {
595  if (l_iter->f != f_ignore) {
596  if (BM_vert_in_face(v, l_iter->f)) {
597  return true;
598  }
599  }
600  } while ((l_iter = l_iter->radial_next) != e_radial->l);
601  }
602  return false;
603 }
604 
605 #ifdef USE_NET_ISLAND_CONNECT
606 
607 struct LinkBase {
608  LinkNode *list;
609  uint list_len;
610 };
611 
613  BMFace *f_key,
614  BMEdge *e_val,
616 {
617  void **ls_base_p;
618  struct LinkBase *ls_base;
619  LinkNode *ls;
620 
621  if (!BLI_ghash_ensure_p(gh, f_key, &ls_base_p)) {
622  ls_base = *ls_base_p = BLI_memarena_alloc(mem_arena, sizeof(*ls_base));
623  ls_base->list = NULL;
624  ls_base->list_len = 0;
625  }
626  else {
627  ls_base = *ls_base_p;
628  }
629 
630  ls = BLI_memarena_alloc(mem_arena, sizeof(*ls));
631  ls->next = ls_base->list;
632  ls->link = e_val;
633  ls_base->list = ls;
634  ls_base->list_len += 1;
635 }
636 
637 static int bm_edge_sort_length_cb(const void *e_a_v, const void *e_b_v)
638 {
639  const float val_a = -BM_edge_calc_length_squared(*((BMEdge **)e_a_v));
640  const float val_b = -BM_edge_calc_length_squared(*((BMEdge **)e_b_v));
641 
642  if (val_a > val_b) {
643  return 1;
644  }
645  if (val_a < val_b) {
646  return -1;
647  }
648  return 0;
649 }
650 
652  BMesh *bm, BMFace *f, LinkNode *e_link, const int e_link_len, MemArena *mem_arena_edgenet)
653 {
654  BMEdge **edge_arr = BLI_memarena_alloc(mem_arena_edgenet, sizeof(*edge_arr) * e_link_len);
655  int edge_arr_len = 0;
656 
657  while (e_link) {
658  edge_arr[edge_arr_len++] = e_link->link;
659  e_link = e_link->next;
660  }
661 
662  {
663  uint edge_arr_holes_len;
664  BMEdge **edge_arr_holes;
666  f,
667  edge_arr,
668  e_link_len,
669  true,
670  mem_arena_edgenet,
671  &edge_arr_holes,
672  &edge_arr_holes_len)) {
673  edge_arr_len = edge_arr_holes_len;
674  edge_arr = edge_arr_holes; /* owned by the arena */
675  }
676  }
677 
678  BM_face_split_edgenet(bm, f, edge_arr, edge_arr_len, NULL, NULL);
679 
680  for (int i = e_link_len; i < edge_arr_len; i++) {
681  BM_edge_select_set(bm, edge_arr[i], true);
682  }
683 
684  if (e_link_len != edge_arr_len) {
685  /* connecting partial islands can add redundant edges
686  * sort before removal to give deterministic outcome */
687  qsort(edge_arr, edge_arr_len - e_link_len, sizeof(*edge_arr), bm_edge_sort_length_cb);
688  for (int i = e_link_len; i < edge_arr_len; i++) {
689  BMFace *f_pair[2];
690  if (BM_edge_face_pair(edge_arr[i], &f_pair[0], &f_pair[1])) {
691  if (BM_face_share_vert_count(f_pair[0], f_pair[1]) == 2) {
692  BMFace *f_new = BM_faces_join(bm, f_pair, 2, true);
693  if (f_new) {
694  BM_face_select_set(bm, f_new, true);
695  }
696  }
697  }
698  }
699  }
700 }
701 
721  BMFace *f_a,
722  BMVert *v_pivot,
723  BMFace **ftable,
724  const int ftable_len,
725  float r_v_pivot_co[3],
726  float *r_v_pivot_fac)
727 {
728  const int f_a_index = BM_elem_index_get(e_a);
729  bool found_other_self = false;
730  int found_other_face = 0;
731  BLI_SMALLSTACK_DECLARE(face_stack, BMFace *);
732 
733  /* loop over surrounding edges to check if we're part of a chain or a delimiter vertex */
734  BMEdge *e_b = v_pivot->e;
735  do {
736  if (e_b != e_a) {
737  const int f_b_index = BM_elem_index_get(e_b);
738  if (f_b_index == f_a_index) {
739  /* not an endpoint */
740  found_other_self = true;
741  }
742  else if (f_b_index != -1) {
743  BLI_assert(f_b_index < ftable_len);
744  UNUSED_VARS_NDEBUG(ftable_len);
745 
746  /* 'v_pivot' spans 2+ faces,
747  * tag to ensure we pick an edge that includes this face */
748  BMFace *f_b = ftable[f_b_index];
751  BLI_SMALLSTACK_PUSH(face_stack, f_b);
752  found_other_face++;
753  }
754  }
755  }
756  } while ((e_b = BM_DISK_EDGE_NEXT(e_b, v_pivot)) != v_pivot->e);
757 
758  BMEdge *e_split = NULL;
759 
760  /* if we have no others or the other edge is outside this face,
761  * we're an endpoint to connect to a boundary */
762  if ((found_other_self == false) || found_other_face) {
763 
764  BMLoop *l_iter, *l_first;
765  l_iter = l_first = BM_FACE_FIRST_LOOP(f_a);
766  float dist_best_sq = FLT_MAX;
767 
768  do {
769  float v_pivot_co_test[3];
770  float v_pivot_fac = line_point_factor_v3(v_pivot->co, l_iter->e->v1->co, l_iter->e->v2->co);
771  CLAMP(v_pivot_fac, 0.0f, 1.0f);
772  interp_v3_v3v3(v_pivot_co_test, l_iter->e->v1->co, l_iter->e->v2->co, v_pivot_fac);
773 
774  float dist_test_sq = len_squared_v3v3(v_pivot_co_test, v_pivot->co);
775  if ((dist_test_sq < dist_best_sq) || (e_split == NULL)) {
776  bool ok = true;
777 
778  if (UNLIKELY(BM_edge_exists(v_pivot, l_iter->e->v1) ||
779  BM_edge_exists(v_pivot, l_iter->e->v2))) {
780  /* very unlikely but will cause complications splicing the verts together,
781  * so just skip this case */
782  ok = false;
783  }
784  else if (found_other_face) {
785  /* double check that _all_ the faces used by v_pivot's edges are attached
786  * to this edge otherwise don't attempt the split since it will give
787  * non-deterministic results */
788  BMLoop *l_radial_iter = l_iter->radial_next;
789  int other_face_shared = 0;
790  if (l_radial_iter != l_iter) {
791  do {
792  if (BM_elem_flag_test(l_radial_iter->f, BM_ELEM_INTERNAL_TAG)) {
793  other_face_shared++;
794  }
795  } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter);
796  }
797  if (other_face_shared != found_other_face) {
798  ok = false;
799  }
800  }
801 
802  if (ok) {
803  e_split = l_iter->e;
804  dist_best_sq = dist_test_sq;
805  copy_v3_v3(r_v_pivot_co, v_pivot_co_test);
806  *r_v_pivot_fac = v_pivot_fac;
807  }
808  }
809  } while ((l_iter = l_iter->next) != l_first);
810  }
811 
812  {
813  /* reset the flag, for future use */
814  BMFace *f;
815  while ((f = BLI_SMALLSTACK_POP(face_stack))) {
817  }
818  }
819 
820  return e_split;
821 }
822 
823 #endif /* USE_NET_ISLAND_CONNECT */
824 
826 {
827  const char hflag = BM_ELEM_TAG;
828 
829  BMEdge *e;
830  BMIter iter;
831 
832  BLI_SMALLSTACK_DECLARE(loop_stack, BMLoop *);
833 
834  ViewLayer *view_layer = CTX_data_view_layer(C);
835  uint objects_len = 0;
837  view_layer, CTX_wm_view3d(C), &objects_len);
838  for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
839  Object *obedit = objects[ob_index];
840  BMEditMesh *em = BKE_editmesh_from_object(obedit);
841  BMesh *bm = em->bm;
842 
843  if ((bm->totedgesel == 0) || (bm->totfacesel == 0)) {
844  continue;
845  }
846 
847  {
848  BMVert *v;
849  BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
850  BM_elem_flag_disable(v, hflag);
851  }
852  }
853 
854  /* edge index is set to -1 then used to associate them with faces */
855  BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
857  BM_elem_flag_enable(e, hflag);
858 
859  BM_elem_flag_enable(e->v1, hflag);
860  BM_elem_flag_enable(e->v2, hflag);
861  }
862  else {
863  BM_elem_flag_disable(e, hflag);
864  }
865  BM_elem_index_set(e, -1); /* set_dirty */
866  }
868 
869  {
870  BMFace *f;
871  int i;
872  BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
874  BM_elem_flag_enable(f, hflag);
875  }
876  else {
877  BM_elem_flag_disable(f, hflag);
878  }
880  BM_elem_index_set(f, i); /* set_ok */
881  }
882  }
884 
885  BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
886  if (BM_elem_flag_test(e, hflag)) {
887  BMIter viter;
888  BMVert *v;
889  BM_ITER_ELEM (v, &viter, e, BM_VERTS_OF_EDGE) {
890  BMIter liter;
891  BMLoop *l;
892 
893  uint loop_stack_len;
894  BMLoop *l_best = NULL;
895 
896  BLI_assert(BLI_SMALLSTACK_IS_EMPTY(loop_stack));
897  loop_stack_len = 0;
898 
899  BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
900  if (BM_elem_flag_test(l->f, hflag)) {
901  BLI_SMALLSTACK_PUSH(loop_stack, l);
902  loop_stack_len++;
903  }
904  }
905 
906  if (loop_stack_len == 0) {
907  /* pass */
908  }
909  else if (loop_stack_len == 1) {
910  l_best = BLI_SMALLSTACK_POP(loop_stack);
911  }
912  else {
913  /* complicated case, match the edge with a face-loop */
914 
915  BMVert *v_other = BM_edge_other_vert(e, v);
916  float e_dir[3];
917 
918  /* we want closest to zero */
919  float dot_best = FLT_MAX;
920 
921  sub_v3_v3v3(e_dir, v_other->co, v->co);
922  normalize_v3(e_dir);
923 
924  while ((l = BLI_SMALLSTACK_POP(loop_stack))) {
925  float dot_test;
926 
927  /* Check dot first to save on expensive angle-comparison.
928  * ideal case is 90d difference == 0.0 dot */
929  dot_test = fabsf(dot_v3v3(e_dir, l->f->no));
930  if (dot_test < dot_best) {
931 
932  /* check we're in the correct corner
933  * (works with convex loops too) */
935  l->prev->v->co, l->v->co, v_other->co, l->f->no) <
937  l->prev->v->co, l->v->co, l->next->v->co, l->f->no)) {
938  dot_best = dot_test;
939  l_best = l;
940  }
941  }
942  }
943  }
944 
945  if (l_best) {
946  BM_elem_index_set(e, BM_elem_index_get(l_best->f)); /* set_dirty */
947  }
948  }
949  }
950  }
951 
952  {
953  BMFace *f;
954  BLI_buffer_declare_static(BMEdge **, edge_net_temp_buf, 0, 128);
955 
956  BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
957  if (BM_elem_flag_test(f, hflag)) {
958  bm_face_split_by_edges(bm, f, hflag, &edge_net_temp_buf);
959  }
960  }
961  BLI_buffer_free(&edge_net_temp_buf);
962  }
963 
964 #ifdef USE_NET_ISLAND_CONNECT
965  /* before overwriting edge index values, collect edges left untouched */
966  BLI_Stack *edges_loose = BLI_stack_new(sizeof(BMEdge *), __func__);
967  BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
969  BLI_stack_push(edges_loose, &e);
970  }
971  }
972 #endif
973 
975  EDBM_update_generic(obedit->data, true, true);
976 
977 #ifdef USE_NET_ISLAND_CONNECT
978  /* we may have remaining isolated regions remaining,
979  * these will need to have connecting edges created */
980  if (!BLI_stack_is_empty(edges_loose)) {
981  GHash *face_edge_map = BLI_ghash_ptr_new(__func__);
982 
984 
986 
987  {
988  BMBVHTree *bmbvh = BKE_bmbvh_new(
989  bm, em->looptris, em->tottri, BMBVH_RESPECT_SELECT, NULL, false);
990 
991  BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
992  BM_elem_index_set(e, -1); /* set_dirty */
993  }
994 
995  while (!BLI_stack_is_empty(edges_loose)) {
996  BLI_stack_pop(edges_loose, &e);
997  float e_center[3];
998  mid_v3_v3v3(e_center, e->v1->co, e->v2->co);
999 
1000  BMFace *f = BKE_bmbvh_find_face_closest(bmbvh, e_center, FLT_MAX);
1001  if (f) {
1002  ghash_insert_face_edge_link(face_edge_map, f, e, mem_arena);
1003  BM_elem_index_set(e, BM_elem_index_get(f)); /* set_dirty */
1004  }
1005  }
1006 
1007  BKE_bmbvh_free(bmbvh);
1008  }
1009 
1011 
1013 
1014  /* detect edges chains that span faces
1015  * and splice vertices into the closest edges */
1016  {
1017  GHashIterator gh_iter;
1018 
1019  GHASH_ITER (gh_iter, face_edge_map) {
1020  BMFace *f = BLI_ghashIterator_getKey(&gh_iter);
1021  struct LinkBase *e_ls_base = BLI_ghashIterator_getValue(&gh_iter);
1022  LinkNode *e_link = e_ls_base->list;
1023 
1024  do {
1025  e = e_link->link;
1026 
1027  for (int j = 0; j < 2; j++) {
1028  BMVert *v_pivot = (&e->v1)[j];
1029  /* checking that \a v_pivot isn't in the face prevents attempting
1030  * to splice the same vertex into an edge from multiple faces */
1031  if (!BM_vert_in_face(v_pivot, f)) {
1032  float v_pivot_co[3];
1033  float v_pivot_fac;
1034  BMEdge *e_split = bm_face_split_edge_find(
1035  e, f, v_pivot, bm->ftable, bm->totface, v_pivot_co, &v_pivot_fac);
1036 
1037  if (e_split) {
1038  /* for degenerate cases this vertex may be in one
1039  * of this edges radial faces */
1040  if (!bm_vert_in_faces_radial(v_pivot, e_split, f)) {
1041  BMEdge *e_new;
1042  BMVert *v_new = BM_edge_split(bm, e_split, e_split->v1, &e_new, v_pivot_fac);
1043  if (v_new) {
1044  /* we _know_ these don't share an edge */
1045  BM_vert_splice(bm, v_pivot, v_new);
1046  BM_elem_index_set(e_new, BM_elem_index_get(e_split));
1047  }
1048  }
1049  }
1050  }
1051  }
1052 
1053  } while ((e_link = e_link->next));
1054  }
1055  }
1056 
1057  {
1058  MemArena *mem_arena_edgenet = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
1059 
1060  GHashIterator gh_iter;
1061 
1062  GHASH_ITER (gh_iter, face_edge_map) {
1063  BMFace *f = BLI_ghashIterator_getKey(&gh_iter);
1064  struct LinkBase *e_ls_base = BLI_ghashIterator_getValue(&gh_iter);
1065 
1067  bm, f, e_ls_base->list, e_ls_base->list_len, mem_arena_edgenet);
1068 
1069  BLI_memarena_clear(mem_arena_edgenet);
1070  }
1071 
1072  BLI_memarena_free(mem_arena_edgenet);
1073  }
1074 
1076 
1077  BLI_ghash_free(face_edge_map, NULL, NULL);
1078 
1080  EDBM_update_generic(obedit->data, true, true);
1081  }
1082 
1083  BLI_stack_free(edges_loose);
1084 #endif /* USE_NET_ISLAND_CONNECT */
1085  }
1086  MEM_freeN(objects);
1087  return OPERATOR_FINISHED;
1088 }
1089 
1091 {
1092  /* identifiers */
1093  ot->name = "Weld Edges into Faces";
1094  ot->description = "Weld loose edges into faces (splitting them into new faces)";
1095  ot->idname = "MESH_OT_face_split_by_edges";
1096 
1097  /* api callbacks */
1100 
1101  /* flags */
1103 }
1104 
struct wmWindowManager * CTX_wm_manager(const bContext *C)
Definition: context.c:689
struct ViewLayer * CTX_data_view_layer(const bContext *C)
Definition: context.c:1044
struct View3D * CTX_wm_view3d(const bContext *C)
Definition: context.c:760
BMEditMesh * BKE_editmesh_from_object(struct Object *ob)
Return the BMEditMesh for a given object.
Definition: editmesh.c:85
BMBVHTree * BKE_bmbvh_new(struct BMesh *bm, struct BMLoop *(*looptris)[3], int looptris_tot, int flag, const float(*cos_cage)[3], const bool cos_cage_free)
Definition: editmesh_bvh.c:161
@ BMBVH_RESPECT_SELECT
struct BMFace * BKE_bmbvh_find_face_closest(BMBVHTree *tree, const float co[3], const float dist_max)
Definition: editmesh_bvh.c:500
void BKE_bmbvh_free(BMBVHTree *tree)
Definition: editmesh_bvh.c:186
#define BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, v3d, r_len)
Definition: BKE_layer.h:426
void BKE_report(ReportList *reports, ReportType type, const char *message)
Definition: report.c:104
#define BLI_assert(a)
Definition: BLI_assert.h:58
#define BLI_buffer_append(buffer_, type_, val_)
Definition: BLI_buffer.h:64
#define BLI_buffer_declare_static(type_, name_, flag_, static_count_)
Definition: BLI_buffer.h:39
#define BLI_buffer_clear(buffer_)
Definition: BLI_buffer.h:68
#define BLI_buffer_free(name_)
Definition: BLI_buffer.h:92
BLI_INLINE void * BLI_ghashIterator_getKey(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.h:146
BLI_INLINE void * BLI_ghashIterator_getValue(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.h:150
#define GHASH_ITER(gh_iter_, ghash_)
Definition: BLI_ghash.h:169
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition: BLI_ghash.c:1008
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:851
float line_point_factor_v3(const float p[3], const float l1[3], const float l2[3])
Definition: math_geom.c:3449
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], const float t)
Definition: math_vector.c:49
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:587
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 mid_v3_v3v3(float r[3], const float a[3], const float b[3])
Definition: math_vector.c:270
void BLI_memarena_free(struct MemArena *ma) ATTR_NONNULL(1)
Definition: BLI_memarena.c:109
#define BLI_MEMARENA_STD_BUFSIZE
Definition: BLI_memarena.h:36
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:131
void BLI_memarena_clear(MemArena *ma) ATTR_NONNULL(1)
Definition: BLI_memarena.c:185
struct MemArena * BLI_memarena_new(const size_t bufsize, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(2) ATTR_MALLOC
Definition: BLI_memarena.c:79
void BLI_stack_pop(BLI_Stack *stack, void *dst) ATTR_NONNULL()
Definition: stack.c:175
void BLI_stack_push(BLI_Stack *stack, const void *src) ATTR_NONNULL()
Definition: stack.c:163
bool BLI_stack_is_empty(const BLI_Stack *stack) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: stack.c:310
void BLI_stack_free(BLI_Stack *stack) ATTR_NONNULL()
Definition: stack.c:114
#define BLI_stack_new(esize, descr)
unsigned int uint
Definition: BLI_sys_types.h:83
#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_generic(struct Mesh *me, const bool do_tessellation, const bool is_destructive)
void EDBM_mesh_normals_update(struct BMEditMesh *em)
bool ED_operator_editmesh(struct bContext *C)
Definition: screen_ops.c:404
Read Guarded memory(de)allocation.
Group RGB to Bright Vector Camera CLAMP
#define C
Definition: RandGen.cpp:39
@ UI_ITEM_R_EXPAND
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
void uiItemS(uiLayout *layout)
uiLayout * uiLayoutRow(uiLayout *layout, bool align)
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:155
@ OPTYPE_REGISTER
Definition: WM_types.h:153
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:556
@ 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
#define BM_FACE_FIRST_LOOP(p)
Definition: bmesh_class.h:553
BMFace * BM_faces_join(BMesh *bm, BMFace **faces, int totface, const bool do_del)
Join Connected Faces.
Definition: bmesh_core.c:1209
bool BM_vert_splice(BMesh *bm, BMVert *v_dst, BMVert *v_src)
Splice Vert.
Definition: bmesh_core.c:2284
#define BM_elem_index_get(ele)
Definition: bmesh_inline.h:124
#define BM_elem_flag_disable(ele, hflag)
Definition: bmesh_inline.h:29
#define BM_elem_index_set(ele, index)
Definition: bmesh_inline.h:125
#define BM_elem_flag_test(ele, hflag)
Definition: bmesh_inline.h:26
#define BM_elem_flag_enable(ele, hflag)
Definition: bmesh_inline.h:28
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.c:2276
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
Definition: bmesh_mesh.c:2152
BMVert * BM_edge_split(BMesh *bm, BMEdge *e, BMVert *v, BMEdge **r_e, float fac)
Edge Split.
Definition: bmesh_mods.c:591
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:1995
int BM_face_share_vert_count(BMFace *f_a, BMFace *f_b)
Definition: bmesh_query.c:1268
float BM_edge_calc_length_squared(const BMEdge *e)
Definition: bmesh_query.c:721
bool BM_edge_face_pair(BMEdge *e, BMFace **r_fa, BMFace **r_fb)
Definition: bmesh_query.c:732
bool BM_vert_in_face(BMVert *v, BMFace *f)
Definition: bmesh_query.c:431
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)
@ ISECT_SOLVER_EXACT
@ ISECT_SOLVER_FAST
static void edbm_intersect_ui(bContext *C, wmOperator *op)
void MESH_OT_face_split_by_edges(struct wmOperatorType *ot)
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)
@ ISECT_SEPARATE_ALL
@ ISECT_SEPARATE_NONE
@ ISECT_SEPARATE_CUT
static void edbm_intersect_boolean_ui(bContext *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)
#define fabsf(x)
static MemArena * mem_arena
Definition: makesdna.c:155
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
const btScalar eps
Definition: poly34.cpp:11
void RNA_pointer_create(ID *id, StructRNA *type, void *data, PointerRNA *r_ptr)
Definition: rna_access.c:146
float RNA_float_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:6355
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:6261
int RNA_enum_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:6402
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, bool default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3481
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:4041
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:3771
void * data
Definition: BLI_buffer.h:28
size_t count
Definition: BLI_buffer.h:30
BMVert * v1
Definition: bmesh_class.h:134
BMVert * v2
Definition: bmesh_class.h:134
struct BMLoop * l
Definition: bmesh_class.h:140
struct BMLoop *(* looptris)[3]
Definition: BKE_editmesh.h:60
struct BMesh * bm
Definition: BKE_editmesh.h:52
float no[3]
Definition: bmesh_class.h:280
struct BMVert * v
Definition: bmesh_class.h:165
struct BMEdge * e
Definition: bmesh_class.h:176
struct BMLoop * radial_next
Definition: bmesh_class.h:216
struct BMLoop * prev
Definition: bmesh_class.h:245
struct BMFace * f
Definition: bmesh_class.h:183
struct BMLoop * next
Definition: bmesh_class.h:245
float co[3]
Definition: bmesh_class.h:99
struct BMEdge * e
Definition: bmesh_class.h:109
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:40
struct LinkNode * next
Definition: BLI_linklist.h:39
void * data
const char * name
Definition: WM_types.h:721
const char * idname
Definition: WM_types.h:723
bool(* poll)(struct bContext *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:776
struct StructRNA * srna
Definition: WM_types.h:802
const char * description
Definition: WM_types.h:726
void(* ui)(struct bContext *, struct wmOperator *)
Definition: WM_types.h:787
int(* exec)(struct bContext *, struct wmOperator *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:736
struct ReportList * reports
IDProperty * properties
struct uiLayout * layout
struct wmOperatorType * type
struct PointerRNA * ptr
PointerRNA * ptr
Definition: wm_files.c:3157
wmOperatorType * ot
Definition: wm_files.c:3156