Blender  V3.3
bmo_create.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
9 #include "MEM_guardedalloc.h"
10 
11 #include "BLI_listbase.h"
12 
13 #include "bmesh.h"
14 
15 #include "intern/bmesh_operators_private.h" /* own include */
16 
17 #define ELE_NEW 1
18 #define ELE_OUT 2
19 
21 {
22  /* NOTE(@campbellbarton): doing the best thing here isn't always easy create vs dissolve,
23  * its nice to support but it _really_ gives issues we might have to not call dissolve. */
24 
25  BMOIter oiter;
26  BMHeader *h;
27  int totv = 0, tote = 0, totf = 0;
28  const short mat_nr = BMO_slot_int_get(op->slots_in, "mat_nr");
29  const bool use_smooth = BMO_slot_bool_get(op->slots_in, "use_smooth");
30 
31  /* count number of each element type we were passe */
32  BMO_ITER (h, &oiter, op->slots_in, "geom", BM_VERT | BM_EDGE | BM_FACE) {
33  switch (h->htype) {
34  case BM_VERT:
36  totv++;
37  break;
38  case BM_EDGE:
40  tote++;
41  break;
42  case BM_FACE:
44  totf++;
45  break;
46  }
47  }
48 
49  /* --- Support Edge Creation ---
50  * simple case when we only have 2 verts selected.
51  */
52  if (totv == 2 && tote == 0 && totf == 0) {
53  BMVert *verts[2];
54  BMEdge *e;
55 
56  if (BMO_iter_as_array(op->slots_in, "geom", BM_VERT, (void **)verts, 2) == 2) {
57  /* create edge */
60  tote += 1;
62  }
63  return;
64  }
65 
66  /* --- Support for Special Case ---
67  * where there is a contiguous edge ring with one isolated vertex.
68  *
69  * This example shows 2 edges created from 3 verts
70  * with 1 free standing vertex. Dotted lines denote the 2 edges that are created.
71  *
72  * note that this works for any sided shape.
73  *
74  * +--------+
75  * | .
76  * | .
77  * | .
78  * | .
79  * +........+ <-- starts out free standing.
80  *
81  */
82 
83  /* Here we check for consistency and create 2 edges */
84  if (totf == 0 && totv >= 4 && totv == tote + 2) {
85  /* find a free standing vertex and 2 endpoint verts */
86  BMVert *v, *v_free = NULL, *v_a = NULL, *v_b = NULL;
87  bool ok = true;
88 
89  BMO_ITER (v, &oiter, op->slots_in, "geom", BM_VERT) {
90  /* count how many flagged edges this vertex uses */
91  const int tot_edges = BMO_iter_elem_count_flag(bm, BM_EDGES_OF_VERT, v, ELE_NEW, true);
92  if (tot_edges == 0) {
93  /* only accept 1 free vert */
94  if (v_free == NULL) {
95  v_free = v;
96  }
97  else {
98  ok = false;
99  } /* only ever want one of these */
100  }
101  else if (tot_edges == 1) {
102  if (v_a == NULL) {
103  v_a = v;
104  }
105  else if (v_b == NULL) {
106  v_b = v;
107  }
108  else {
109  ok = false;
110  } /* only ever want 2 of these */
111  }
112  else if (tot_edges == 2) {
113  /* do nothing, regular case */
114  }
115  else {
116  ok = false; /* if a vertex has 3+ edge users then cancel - this is only simple cases */
117  }
118 
119  if (ok == false) {
120  break;
121  }
122  }
123 
124  if (ok == true && v_free && v_a && v_b) {
125  BMEdge *e;
126 
127  e = BM_edge_create(bm, v_free, v_a, NULL, BM_CREATE_NO_DOUBLE);
129 
130  e = BM_edge_create(bm, v_free, v_b, NULL, BM_CREATE_NO_DOUBLE);
132  tote += 2;
133  }
134  }
135  /* --- end special case support, continue as normal --- */
136 
137  /* -------------------------------------------------------------------- */
138  /* EdgeNet Create */
139  if (tote != 0) {
140  /* call edgenet prepare op so additional face creation cases work */
141 
143  BMO_op_initf(bm, &op_sub, op->flag, "edgenet_prepare edges=%fe", ELE_NEW);
144  BMO_op_exec(bm, &op_sub);
145  BMO_slot_buffer_flag_enable(bm, op_sub.slots_out, "edges.out", BM_EDGE, ELE_NEW);
147 
149  &op_sub,
150  op->flag,
151  "edgenet_fill edges=%fe mat_nr=%i use_smooth=%b sides=%i",
152  ELE_NEW,
153  mat_nr,
154  use_smooth,
155  10000);
156 
157  BMO_op_exec(bm, &op_sub);
158 
159  /* return if edge net create did something */
160  if (BMO_slot_buffer_len(op_sub.slots_out, "faces.out")) {
161  BMO_slot_copy(&op_sub, slots_out, "faces.out", op, slots_out, "faces.out");
163  return;
164  }
165 
167  }
168 
169  /* -------------------------------------------------------------------- */
170  /* Dissolve Face */
171  if (totf != 0) { /* should be (totf > 1)... see below */
172  /* NOTE: allow this to run on single faces so running on a single face
173  * won't go on to create a face, treating them as random */
175  BMO_op_initf(bm, &op_sub, op->flag, "dissolve_faces faces=%ff", ELE_NEW);
176  BMO_op_exec(bm, &op_sub);
177 
178  /* if we dissolved anything, then return */
179  if (BMO_slot_buffer_len(op_sub.slots_out, "region.out")) {
180  BMO_slot_copy(&op_sub, slots_out, "region.out", op, slots_out, "faces.out");
182  return;
183  }
184 
186  }
187 
188  /* -------------------------------------------------------------------- */
189  /* Fill EdgeLoop's - fills isolated loops, different from edgenet */
190  if (tote > 2) {
192  /* NOTE: in most cases 'edgenet_fill' will handle this case since in common cases
193  * users fill in empty spaces, however its possible to have an edge selection around
194  * existing geometry that makes 'edgenet_fill' fail. */
195  BMO_op_initf(bm, &op_sub, op->flag, "edgeloop_fill edges=%fe", ELE_NEW);
196  BMO_op_exec(bm, &op_sub);
197 
198  /* return if edge loop fill did something */
199  if (BMO_slot_buffer_len(op_sub.slots_out, "faces.out")) {
200  BMO_slot_copy(&op_sub, slots_out, "faces.out", op, slots_out, "faces.out");
202  return;
203  }
204 
206  }
207 
208  /* -------------------------------------------------------------------- */
209  /* Continue with ad-hoc fill methods since operators fail,
210  * edge, vcloud... may add more */
211 
212  if (0) { /* nice feature but perhaps it should be a different tool? */
213 
214  /* tricky feature for making a line/edge from selection history...
215  *
216  * Rather than do nothing, when 5+ verts are selected, check if they are in our history,
217  * when this is so, we can make edges from them, but _not_ a face,
218  * if it is the intention to make a face the user can just hit F again
219  * since there will be edges next time around.
220  *
221  * if all history verts have ELE_NEW flagged and the total number of history verts == totv,
222  * then we know the history contains all verts here and we can continue...
223  */
224 
225  BMEditSelection *ese;
226  int tot_ese_v = 0;
227 
228  for (ese = bm->selected.first; ese; ese = ese->next) {
229  if (ese->htype == BM_VERT) {
230  if (BMO_vert_flag_test(bm, (BMVert *)ese->ele, ELE_NEW)) {
231  tot_ese_v++;
232  }
233  else {
234  /* unflagged vert means we are not in sync */
235  tot_ese_v = -1;
236  break;
237  }
238  }
239  }
240 
241  if (tot_ese_v == totv) {
242  BMVert *v_prev = NULL;
243  /* yes, all select-history verts are accounted for, now make edges */
244 
245  for (ese = bm->selected.first; ese; ese = ese->next) {
246  if (ese->htype == BM_VERT) {
247  BMVert *v = (BMVert *)ese->ele;
248  if (v_prev) {
251  }
252  v_prev = v;
253  }
254  }
255  }
257  /* done creating edges */
258 
259  return;
260  }
261 
262  /* -------------------------------------------------------------------- */
263  /* Fill Vertex Cloud
264  *
265  * last resort when all else fails.
266  */
267  if (totv > 2) {
268  /* TODO: some of these vertices may be connected by edges,
269  * this connectivity could be used rather than treating
270  * them as a bunch of isolated verts. */
271 
272  BMVert **vert_arr = MEM_mallocN(sizeof(BMVert *) * totv, __func__);
273  BMFace *f;
274 
275  totv = BMO_iter_as_array(op->slots_in, "geom", BM_VERT, (void **)vert_arr, totv);
276 
277  BM_verts_sort_radial_plane(vert_arr, totv);
278 
279  /* create edges and find the winding (if faces are attached to any existing edges) */
280  f = BM_face_create_ngon_verts(bm, vert_arr, totv, NULL, BM_CREATE_NO_DOUBLE, true, true);
281 
282  if (f) {
284  f->mat_nr = mat_nr;
285  if (use_smooth) {
287  }
290  }
291 
292  MEM_freeN(vert_arr);
293  }
294 }
Read Guarded memory(de)allocation.
@ BM_FACE
Definition: bmesh_class.h:386
@ BM_VERT
Definition: bmesh_class.h:383
@ BM_EDGE
Definition: bmesh_class.h:384
@ BM_ELEM_SMOOTH
Definition: bmesh_class.h:477
BMFace * BM_face_create_ngon_verts(BMesh *bm, BMVert **vert_arr, const int len, const BMFace *f_example, const eBMCreateFlag create_flag, const bool calc_winding, const bool create_edges)
void BM_verts_sort_radial_plane(BMVert **vert_arr, int len)
void BM_face_copy_shared(BMesh *bm, BMFace *f, BMLoopFilterFunc filter_fn, void *user_data)
copies face loop data from shared adjacent faces.
BMEdge * BM_edge_create(BMesh *bm, BMVert *v1, BMVert *v2, const BMEdge *e_example, const eBMCreateFlag create_flag)
Main function for creating a new edge.
Definition: bmesh_core.c:123
@ BM_CREATE_NO_DOUBLE
Definition: bmesh_core.h:14
#define BM_elem_flag_enable(ele, hflag)
Definition: bmesh_inline.h:14
int BMO_iter_elem_count_flag(BMesh *bm, const char itype, void *data, const short oflag, const bool value)
Elem Iter Tool Flag Count.
int BMO_iter_as_array(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, const char restrictmask, void **array, const int len)
Operator Iterator as Array.
@ BM_EDGES_OF_VERT
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BMO_slot_buffer_flag_enable(BMesh *bm, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, char htype, short oflag)
BMO_FLAG_BUFFER.
#define BMO_edge_flag_enable(bm, e, oflag)
#define BMO_vert_flag_enable(bm, e, oflag)
void BMO_op_exec(BMesh *bm, BMOperator *op)
BMESH OPSTACK EXEC OP.
void BMO_slot_buffer_from_enabled_flag(BMesh *bm, BMOperator *op, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, char htype, short oflag)
#define BMO_face_flag_enable(bm, e, oflag)
#define BMO_slot_copy(op_src, slots_src, slot_name_src, op_dst, slots_dst, slot_name_dst)
#define BMO_ITER(ele, iter, slot_args, slot_name, restrict_flag)
#define BMO_vert_flag_test(bm, e, oflag)
bool BMO_op_initf(BMesh *bm, BMOperator *op, int flag, const char *fmt,...)
int BMO_slot_int_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
void BMO_op_finish(BMesh *bm, BMOperator *op)
BMESH OPSTACK FINISH OP.
int BMO_slot_buffer_len(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
bool BMO_slot_bool_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
#define ELE_NEW
Definition: bmo_create.c:17
#define ELE_OUT
Definition: bmo_create.c:18
void bmo_contextual_create_exec(BMesh *bm, BMOperator *op)
Definition: bmo_create.c:20
static double op_sub(double a, double b)
static float verts[][3]
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:33
struct BMEditSelection * next
Definition: bmesh_marking.h:10
short mat_nr
Definition: bmesh_class.h:281
char htype
Definition: bmesh_class.h:64
struct BMOpSlot slots_out[BMO_OP_MAX_SLOTS]
struct BMOpSlot slots_in[BMO_OP_MAX_SLOTS]
ListBase selected
Definition: bmesh_class.h:356
void * first
Definition: DNA_listBase.h:31