Blender V5.0
bmesh_py_utils.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2012 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11
12#include <Python.h>
13
14#include "BLI_math_base.h"
15#include "BLI_utildefines.h"
16
17#include "MEM_guardedalloc.h"
18
20
21#include "bmesh.hh"
22#include "bmesh_py_types.hh"
23#include "bmesh_py_utils.hh" /* own include */
24
28
30 /* Wrap. */
31 bpy_bm_utils_vert_collapse_edge_doc,
32 ".. method:: vert_collapse_edge(vert, edge)\n"
33 "\n"
34 " Collapse a vertex into an edge.\n"
35 "\n"
36 " :arg vert: The vert that will be collapsed.\n"
37 " :type vert: :class:`bmesh.types.BMVert`\n"
38 " :arg edge: The edge to collapse into.\n"
39 " :type edge: :class:`bmesh.types.BMEdge`\n"
40 " :return: The resulting edge from the collapse operation.\n"
41 " :rtype: :class:`bmesh.types.BMEdge`\n");
42static PyObject *bpy_bm_utils_vert_collapse_edge(PyObject * /*self*/, PyObject *args)
43{
44 BPy_BMEdge *py_edge;
45 BPy_BMVert *py_vert;
46
47 BMesh *bm;
48 BMEdge *e_new = nullptr;
49
50 if (!PyArg_ParseTuple(
51 args, "O!O!:vert_collapse_edge", &BPy_BMVert_Type, &py_vert, &BPy_BMEdge_Type, &py_edge))
52 {
53 return nullptr;
54 }
55
56 BPY_BM_CHECK_OBJ(py_edge);
57 BPY_BM_CHECK_OBJ(py_vert);
58
59 /* this doubles for checking that the verts are in the same mesh */
60 if (!(py_edge->e->v1 == py_vert->v || py_edge->e->v2 == py_vert->v)) {
61 PyErr_SetString(PyExc_ValueError,
62 "vert_collapse_edge(vert, edge): the vertex is not found in the edge");
63 return nullptr;
64 }
65
66 if (BM_vert_edge_count_is_over(py_vert->v, 2)) {
67 PyErr_SetString(PyExc_ValueError,
68 "vert_collapse_edge(vert, edge): vert has more than 2 connected edges");
69 return nullptr;
70 }
71
72 bm = py_edge->bm;
73
74 e_new = BM_vert_collapse_edge(bm, py_edge->e, py_vert->v, true, true, true);
75
76 if (e_new) {
77 return BPy_BMEdge_CreatePyObject(bm, e_new);
78 }
79
80 PyErr_SetString(PyExc_ValueError,
81 "vert_collapse_edge(vert, edge): no new edge created, internal error");
82 return nullptr;
83}
84
86 /* Wrap. */
87 bpy_bm_utils_vert_collapse_faces_doc,
88 ".. method:: vert_collapse_faces(vert, edge, fac, join_faces)\n"
89 "\n"
90 " Collapses a vertex that has only two manifold edges onto a vertex it shares an "
91 "edge with.\n"
92 "\n"
93 " :arg vert: The vert that will be collapsed.\n"
94 " :type vert: :class:`bmesh.types.BMVert`\n"
95 " :arg edge: The edge to collapse into.\n"
96 " :type edge: :class:`bmesh.types.BMEdge`\n"
97 " :arg fac: The factor to use when merging customdata [0 - 1].\n"
98 " :type fac: float\n"
99 " :arg join_faces: When true the faces around the vertex will be joined otherwise "
100 "collapse the vertex by merging the 2 edges this vertex connects to into one.\n"
101 " :type join_faces: bool\n"
102 " :return: The resulting edge from the collapse operation.\n"
103 " :rtype: :class:`bmesh.types.BMEdge`\n");
104static PyObject *bpy_bm_utils_vert_collapse_faces(PyObject * /*self*/, PyObject *args)
105{
106 BPy_BMEdge *py_edge;
107 BPy_BMVert *py_vert;
108
109 float fac;
110 int do_join_faces;
111
112 BMesh *bm;
113 BMEdge *e_new = nullptr;
114
115 if (!PyArg_ParseTuple(args,
116 "O!O!fi:vert_collapse_faces",
118 &py_vert,
120 &py_edge,
121 &fac,
122 &do_join_faces))
123 {
124 return nullptr;
125 }
126
127 BPY_BM_CHECK_OBJ(py_edge);
128 BPY_BM_CHECK_OBJ(py_vert);
129
130 /* this doubles for checking that the verts are in the same mesh */
131 if (!(py_edge->e->v1 == py_vert->v || py_edge->e->v2 == py_vert->v)) {
132 PyErr_SetString(PyExc_ValueError,
133 "vert_collapse_faces(vert, edge): the vertex is not found in the edge");
134 return nullptr;
135 }
136
137 if (BM_vert_edge_count_is_over(py_vert->v, 2)) {
138 PyErr_SetString(PyExc_ValueError,
139 "vert_collapse_faces(vert, edge): vert has more than 2 connected edges");
140 return nullptr;
141 }
142
143 bm = py_edge->bm;
144
146 bm, py_edge->e, py_vert->v, clamp_f(fac, 0.0f, 1.0f), true, do_join_faces, true, true);
147
148 if (e_new) {
149 return BPy_BMEdge_CreatePyObject(bm, e_new);
150 }
151
152 PyErr_SetString(PyExc_ValueError,
153 "vert_collapse_faces(vert, edge): no new edge created, internal error");
154 return nullptr;
155}
156
158 /* Wrap. */
159 bpy_bm_utils_vert_dissolve_doc,
160 ".. method:: vert_dissolve(vert)\n"
161 "\n"
162 " Dissolve this vertex (will be removed).\n"
163 "\n"
164 " :arg vert: The vert to be dissolved.\n"
165 " :type vert: :class:`bmesh.types.BMVert`\n"
166 " :return: True when the vertex dissolve is successful.\n"
167 " :rtype: bool\n");
168static PyObject *bpy_bm_utils_vert_dissolve(PyObject * /*self*/, PyObject *args)
169{
170 BPy_BMVert *py_vert;
171
172 BMesh *bm;
173
174 if (!PyArg_ParseTuple(args, "O!:vert_dissolve", &BPy_BMVert_Type, &py_vert)) {
175 return nullptr;
176 }
177
178 BPY_BM_CHECK_OBJ(py_vert);
179
180 bm = py_vert->bm;
181
182 return PyBool_FromLong(BM_vert_dissolve(bm, py_vert->v));
183}
184
186 /* Wrap. */
187 bpy_bm_utils_vert_splice_doc,
188 ".. method:: vert_splice(vert, vert_target)\n"
189 "\n"
190 " Splice vert into vert_target.\n"
191 "\n"
192 " :arg vert: The vertex to be removed.\n"
193 " :type vert: :class:`bmesh.types.BMVert`\n"
194 " :arg vert_target: The vertex to use.\n"
195 " :type vert_target: :class:`bmesh.types.BMVert`\n"
196 "\n"
197 " .. note:: The verts mustn't share an edge or face.\n");
198static PyObject *bpy_bm_utils_vert_splice(PyObject * /*self*/, PyObject *args)
199{
200 const char *error_prefix = "vert_splice(...)";
201 BPy_BMVert *py_vert;
202 BPy_BMVert *py_vert_target;
203
204 BMesh *bm;
205
206 bool ok;
207
208 if (!PyArg_ParseTuple(
209 args, "O!O!:vert_splice", &BPy_BMVert_Type, &py_vert, &BPy_BMVert_Type, &py_vert_target))
210 {
211 return nullptr;
212 }
213
214 BPY_BM_CHECK_OBJ(py_vert);
215 BPY_BM_CHECK_OBJ(py_vert_target);
216
217 bm = py_vert->bm;
218 BPY_BM_CHECK_SOURCE_OBJ(bm, error_prefix, py_vert_target);
219
220 if (py_vert->v == py_vert_target->v) {
221 PyErr_Format(PyExc_ValueError, "%s: vert arguments match", error_prefix);
222 return nullptr;
223 }
224
225 if (BM_edge_exists(py_vert->v, py_vert_target->v)) {
226 PyErr_Format(PyExc_ValueError, "%s: verts cannot share an edge", error_prefix);
227 return nullptr;
228 }
229
230 if (BM_vert_pair_share_face_check(py_vert->v, py_vert_target->v)) {
231 PyErr_Format(PyExc_ValueError, "%s: verts cannot share a face", error_prefix);
232 return nullptr;
233 }
234
235 /* should always succeed */
236 ok = BM_vert_splice(bm, py_vert_target->v, py_vert->v);
237 BLI_assert(ok == true);
239
240 Py_RETURN_NONE;
241}
242
244 /* Wrap. */
245 bpy_bm_utils_vert_separate_doc,
246 ".. method:: vert_separate(vert, edges)\n"
247 "\n"
248 " Separate this vertex at every edge.\n"
249 "\n"
250 " :arg vert: The vert to be separated.\n"
251 " :type vert: :class:`bmesh.types.BMVert`\n"
252 " :arg edges: The edges to separated.\n"
253 " :type edges: :class:`bmesh.types.BMEdge`\n"
254 " :return: The newly separated verts (including the vertex passed).\n"
255 " :rtype: tuple[:class:`bmesh.types.BMVert`, ...]\n");
256static PyObject *bpy_bm_utils_vert_separate(PyObject * /*self*/, PyObject *args)
257{
258 const char *error_prefix = "vert_separate(...)";
259 BPy_BMVert *py_vert;
260 PyObject *edge_seq;
261
262 BMesh *bm;
263 BMVert **elem;
264 int elem_len;
265
266 PyObject *ret;
267
268 if (!PyArg_ParseTuple(args, "O!O:vert_separate", &BPy_BMVert_Type, &py_vert, &edge_seq)) {
269 return nullptr;
270 }
271
272 BPY_BM_CHECK_OBJ(py_vert);
273
274 bm = py_vert->bm;
275
276 /* Edges to split. */
277 Py_ssize_t edge_array_num;
278 BMEdge **edge_array = BPy_BMEdge_PySeq_As_Array(
279 &bm, edge_seq, 0, PY_SSIZE_T_MAX, &edge_array_num, true, true, error_prefix);
280
281 if (edge_array == nullptr) {
282 return nullptr;
283 }
284
285 BM_vert_separate(bm, py_vert->v, edge_array, edge_array_num, false, &elem, &elem_len);
286 /* return collected verts */
287 ret = BPy_BMVert_Array_As_Tuple(bm, elem, elem_len);
288 MEM_freeN(elem);
289
290 PyMem_FREE(edge_array);
291
292 return ret;
293}
294
296 /* Wrap. */
297 bpy_bm_utils_edge_split_doc,
298 ".. method:: edge_split(edge, vert, fac)\n"
299 "\n"
300 " Split an edge, return the newly created data.\n"
301 "\n"
302 " :arg edge: The edge to split.\n"
303 " :type edge: :class:`bmesh.types.BMEdge`\n"
304 " :arg vert: One of the verts on the edge, defines the split direction.\n"
305 " :type vert: :class:`bmesh.types.BMVert`\n"
306 " :arg fac: The point on the edge where the new vert will be created [0 - 1].\n"
307 " :type fac: float\n"
308 " :return: The newly created (edge, vert) pair.\n"
309 " :rtype: tuple[:class:`bmesh.types.BMEdge`, :class:`bmesh.types.BMVert`]\n");
310static PyObject *bpy_bm_utils_edge_split(PyObject * /*self*/, PyObject *args)
311{
312 BPy_BMEdge *py_edge;
313 BPy_BMVert *py_vert;
314 float fac;
315
316 BMesh *bm;
317 BMVert *v_new = nullptr;
318 BMEdge *e_new = nullptr;
319
320 if (!PyArg_ParseTuple(
321 args, "O!O!f:edge_split", &BPy_BMEdge_Type, &py_edge, &BPy_BMVert_Type, &py_vert, &fac))
322 {
323 return nullptr;
324 }
325
326 BPY_BM_CHECK_OBJ(py_edge);
327 BPY_BM_CHECK_OBJ(py_vert);
328
329 /* this doubles for checking that the verts are in the same mesh */
330 if (!(py_edge->e->v1 == py_vert->v || py_edge->e->v2 == py_vert->v)) {
331 PyErr_SetString(PyExc_ValueError,
332 "edge_split(edge, vert): the vertex is not found in the edge");
333 return nullptr;
334 }
335
336 bm = py_edge->bm;
337
338 v_new = BM_edge_split(bm, py_edge->e, py_vert->v, &e_new, clamp_f(fac, 0.0f, 1.0f));
339
340 if (v_new && e_new) {
341 PyObject *ret = PyTuple_New(2);
344 return ret;
345 }
346
347 PyErr_SetString(PyExc_ValueError,
348 "edge_split(edge, vert): couldn't split the edge, internal error");
349 return nullptr;
350}
351
353 /* Wrap. */
354 bpy_bm_utils_edge_rotate_doc,
355 ".. method:: edge_rotate(edge, ccw=False)\n"
356 "\n"
357 " Rotate the edge and return the newly created edge.\n"
358 " If rotating the edge fails, None will be returned.\n"
359 "\n"
360 " :arg edge: The edge to rotate.\n"
361 " :type edge: :class:`bmesh.types.BMEdge`\n"
362 " :arg ccw: When True the edge will be rotated counter clockwise.\n"
363 " :type ccw: bool\n"
364 " :return: The newly rotated edge.\n"
365 " :rtype: :class:`bmesh.types.BMEdge`\n");
366static PyObject *bpy_bm_utils_edge_rotate(PyObject * /*self*/, PyObject *args)
367{
368 BPy_BMEdge *py_edge;
369 bool do_ccw = false;
370
371 BMesh *bm;
372 BMEdge *e_new = nullptr;
373
374 if (!PyArg_ParseTuple(
375 args, "O!|O&:edge_rotate", &BPy_BMEdge_Type, &py_edge, PyC_ParseBool, &do_ccw))
376 {
377 return nullptr;
378 }
379
380 BPY_BM_CHECK_OBJ(py_edge);
381
382 bm = py_edge->bm;
383
384 e_new = BM_edge_rotate(bm, py_edge->e, do_ccw, 0);
385
386 if (e_new) {
387 return BPy_BMEdge_CreatePyObject(bm, e_new);
388 }
389
390 Py_RETURN_NONE;
391}
392
394 /* Wrap. */
395 bpy_bm_utils_face_split_doc,
396 ".. method:: face_split(face, vert_a, vert_b, *, coords=(), use_exist=True, example=None)\n"
397 "\n"
398 " Face split with optional intermediate points.\n"
399 "\n"
400 " :arg face: The face to cut.\n"
401 " :type face: :class:`bmesh.types.BMFace`\n"
402 " :arg vert_a: First vertex to cut in the face (face must contain the vert).\n"
403 " :type vert_a: :class:`bmesh.types.BMVert`\n"
404 " :arg vert_b: Second vertex to cut in the face (face must contain the vert).\n"
405 " :type vert_b: :class:`bmesh.types.BMVert`\n"
406 " :arg coords: Optional sequence of 3D points in between *vert_a* and *vert_b*.\n"
407 " :type coords: Sequence[Sequence[float]]\n"
408 " :arg use_exist: .Use an existing edge if it exists (Only used when *coords* argument is "
409 "empty or omitted)\n"
410 " :type use_exist: bool\n"
411 " :arg example: Newly created edge will copy settings from this one.\n"
412 " :type example: :class:`bmesh.types.BMEdge`\n"
413 " :return: The newly created face or None on failure.\n"
414 " :rtype: tuple[:class:`bmesh.types.BMFace`, :class:`bmesh.types.BMLoop`]\n");
415static PyObject *bpy_bm_utils_face_split(PyObject * /*self*/, PyObject *args, PyObject *kw)
416{
417 static const char *kwlist[] = {
418 "face", "vert_a", "vert_b", "coords", "use_exist", "example", nullptr};
419
420 BPy_BMFace *py_face;
421 BPy_BMVert *py_vert_a;
422 BPy_BMVert *py_vert_b;
423
424 /* optional */
425 PyObject *py_coords = nullptr;
426 bool edge_exists = true;
427 BPy_BMEdge *py_edge_example = nullptr;
428
429 float *coords;
430 int ncoords = 0;
431
432 BMesh *bm;
433 BMFace *f_new = nullptr;
434 BMLoop *l_new = nullptr;
435 BMLoop *l_a, *l_b;
436
437 if (!PyArg_ParseTupleAndKeywords(args,
438 kw,
439 "O!O!O!|$OO&O!:face_split",
440 (char **)kwlist,
442 &py_face,
444 &py_vert_a,
446 &py_vert_b,
447 &py_coords,
449 &edge_exists,
451 &py_edge_example))
452 {
453 return nullptr;
454 }
455
456 BPY_BM_CHECK_OBJ(py_face);
457 BPY_BM_CHECK_OBJ(py_vert_a);
458 BPY_BM_CHECK_OBJ(py_vert_b);
459
460 if (py_edge_example) {
461 BPY_BM_CHECK_OBJ(py_edge_example);
462 }
463
464 /* this doubles for checking that the verts are in the same mesh */
465 if ((l_a = BM_face_vert_share_loop(py_face->f, py_vert_a->v)) &&
466 (l_b = BM_face_vert_share_loop(py_face->f, py_vert_b->v)))
467 {
468 /* pass */
469 }
470 else {
471 PyErr_SetString(PyExc_ValueError,
472 "face_split(...): one of the verts passed is not found in the face");
473 return nullptr;
474 }
475
476 if (py_vert_a->v == py_vert_b->v) {
477 PyErr_SetString(PyExc_ValueError, "face_split(...): vert arguments must differ");
478 return nullptr;
479 }
480
481 if (py_coords) {
482 ncoords = mathutils_array_parse_alloc_v(&coords, 3, py_coords, "face_split(...): ");
483 if (ncoords == -1) {
484 return nullptr;
485 }
486 }
487 else {
488 if (BM_loop_is_adjacent(l_a, l_b)) {
489 PyErr_SetString(PyExc_ValueError, "face_split(...): verts are adjacent in the face");
490 return nullptr;
491 }
492 }
493
494 /* --- main function body --- */
495 bm = py_face->bm;
496
497 if (ncoords) {
498 f_new = BM_face_split_n(bm,
499 py_face->f,
500 l_a,
501 l_b,
502 (float (*)[3])coords,
503 ncoords,
504 &l_new,
505 py_edge_example ? py_edge_example->e : nullptr);
506 PyMem_Free(coords);
507 }
508 else {
509 f_new = BM_face_split(bm,
510 py_face->f,
511 l_a,
512 l_b,
513 &l_new,
514 py_edge_example ? py_edge_example->e : nullptr,
515 edge_exists);
516 }
517
518 if (f_new && l_new) {
519 PyObject *ret = PyTuple_New(2);
522 return ret;
523 }
524
525 PyErr_SetString(PyExc_ValueError, "face_split(...): couldn't split the face, internal error");
526 return nullptr;
527}
528
530 /* Wrap. */
531 bpy_bm_utils_face_split_edgenet_doc,
532 ".. method:: face_split_edgenet(face, edgenet)\n"
533 "\n"
534 " Splits a face into any number of regions defined by an edgenet.\n"
535 "\n"
536 " :arg face: The face to split.\n"
537 " :type face: :class:`bmesh.types.BMFace`\n"
538 " :arg face: The face to split.\n"
539 " :type face: :class:`bmesh.types.BMFace`\n"
540 " :arg edgenet: Sequence of edges.\n"
541 " :type edgenet: Sequence[:class:`bmesh.types.BMEdge`]\n"
542 " :return: The newly created faces.\n"
543 " :rtype: tuple[:class:`bmesh.types.BMFace`, ...]\n"
544 "\n"
545 " .. note::\n"
546 "\n"
547 " Regions defined by edges need to connect to the face, otherwise they're "
548 "ignored as loose edges.\n");
549static PyObject *bpy_bm_utils_face_split_edgenet(PyObject * /*self*/, PyObject *args, PyObject *kw)
550{
551 const char *error_prefix = "face_split_edgenet(...)";
552 static const char *kwlist[] = {"face", "edgenet", nullptr};
553
554 BPy_BMFace *py_face;
555 PyObject *edge_seq;
556
557 BMesh *bm;
558
559 bool ok;
560
561 if (!PyArg_ParseTupleAndKeywords(args,
562 kw,
563 "O!O:face_split_edgenet",
564 (char **)kwlist,
566 &py_face,
567 &edge_seq))
568 {
569 return nullptr;
570 }
571
572 BPY_BM_CHECK_OBJ(py_face);
573
574 bm = py_face->bm;
575
576 Py_ssize_t edge_array_num;
577 BMEdge **edge_array = BPy_BMEdge_PySeq_As_Array(
578 &bm, edge_seq, 1, PY_SSIZE_T_MAX, &edge_array_num, true, true, error_prefix);
579
580 if (edge_array == nullptr) {
581 return nullptr;
582 }
583
584 /* --- main function body --- */
586 ok = BM_face_split_edgenet(bm, py_face->f, edge_array, edge_array_num, &face_arr);
587
588 PyMem_FREE(edge_array);
589
590 if (ok) {
591 PyObject *ret = BPy_BMFace_Array_As_Tuple(bm, face_arr.data(), face_arr.size());
592 return ret;
593 }
594
595 PyErr_SetString(PyExc_ValueError,
596 "face_split_edgenet(...): couldn't split the face, internal error");
597 return nullptr;
598}
599
601 /* Wrap. */
602 bpy_bm_utils_face_join_doc,
603 ".. method:: face_join(faces, remove=True)\n"
604 "\n"
605 " Joins a sequence of faces.\n"
606 "\n"
607 " :arg faces: Sequence of faces.\n"
608 " :type faces: :class:`bmesh.types.BMFace`\n"
609 " :arg remove: Remove the edges and vertices between the faces.\n"
610 " :type remove: bool\n"
611 " :return: The newly created face or None on failure.\n"
612 " :rtype: :class:`bmesh.types.BMFace`\n");
613static PyObject *bpy_bm_utils_face_join(PyObject * /*self*/, PyObject *args)
614{
615 const char *error_prefix = "face_join(...)";
616 BMesh *bm = nullptr;
617 PyObject *py_face_array;
618 BMFace *f_new;
619 bool do_remove = true;
620
621 if (!PyArg_ParseTuple(args, "O|O&:face_join", &py_face_array, PyC_ParseBool, &do_remove)) {
622 return nullptr;
623 }
624
625 Py_ssize_t face_seq_len = 0;
626 BMFace **face_array = BPy_BMFace_PySeq_As_Array(
627 &bm, py_face_array, 2, PY_SSIZE_T_MAX, &face_seq_len, true, true, error_prefix);
628 if (face_array == nullptr) {
629 return nullptr; /* error will be set */
630 }
631
632 /* Go ahead and join the face!
633 * --------------------------- */
634 BMFace *f_double;
635 f_new = BM_faces_join(bm, face_array, int(face_seq_len), do_remove, &f_double);
636 /* See #BM_faces_join note on callers asserting when `r_double` is non-null. */
637 BLI_assert_msg(f_double == nullptr,
638 "Doubled face detected at " AT ". Resulting mesh may be corrupt.");
639
640 PyMem_FREE(face_array);
641
642 if (f_new) {
643 return BPy_BMFace_CreatePyObject(bm, f_new);
644 }
645
646 Py_RETURN_NONE;
647}
648
650 /* Wrap. */
651 bpy_bm_utils_face_vert_separate_doc,
652 ".. method:: face_vert_separate(face, vert)\n"
653 "\n"
654 " Rip a vertex in a face away and add a new vertex.\n"
655 "\n"
656 " :arg face: The face to separate.\n"
657 " :type face: :class:`bmesh.types.BMFace`\n"
658 " :arg vert: A vertex in the face to separate.\n"
659 " :type vert: :class:`bmesh.types.BMVert`\n"
660 " :return vert: The newly created vertex or None on failure.\n"
661 " :rtype vert: :class:`bmesh.types.BMVert`\n"
662 "\n"
663 " .. note::\n"
664 "\n"
665 " This is the same as loop_separate, and has only been added for convenience.\n");
666static PyObject *bpy_bm_utils_face_vert_separate(PyObject * /*self*/, PyObject *args)
667{
668 const char *error_prefix = "face_vert_separate()";
669 BPy_BMFace *py_face;
670 BPy_BMVert *py_vert;
671
672 BMesh *bm;
673 BMLoop *l;
674 BMVert *v_old, *v_new;
675
676 if (!PyArg_ParseTuple(
677 args, "O!O!:face_vert_separate", &BPy_BMFace_Type, &py_face, &BPy_BMVert_Type, &py_vert))
678 {
679 return nullptr;
680 }
681
682 bm = py_face->bm;
683
684 BPY_BM_CHECK_OBJ(py_face);
685 BPY_BM_CHECK_SOURCE_OBJ(bm, error_prefix, py_vert);
686
687 l = BM_face_vert_share_loop(py_face->f, py_vert->v);
688
689 if (l == nullptr) {
690 PyErr_Format(PyExc_ValueError, "%s: vertex not found in face", error_prefix);
691 return nullptr;
692 }
693
694 v_old = l->v;
695 v_new = BM_face_loop_separate(bm, l);
696
697 if (v_new != v_old) {
698 return BPy_BMVert_CreatePyObject(bm, v_new);
699 }
700
701 Py_RETURN_NONE;
702}
703
705 /* Wrap. */
706 bpy_bm_utils_face_flip_doc,
707 ".. method:: face_flip(faces)\n"
708 "\n"
709 " Flip the faces direction.\n"
710 "\n"
711 " :arg face: Face to flip.\n"
712 " :type face: :class:`bmesh.types.BMFace`\n");
713static PyObject *bpy_bm_utils_face_flip(PyObject * /*self*/, BPy_BMFace *value)
714{
715 if (!BPy_BMFace_Check(value)) {
716 PyErr_Format(PyExc_TypeError,
717 "face_flip(face): BMFace expected, not '%.200s'",
718 Py_TYPE(value)->tp_name);
719 return nullptr;
720 }
721
722 BPY_BM_CHECK_OBJ(value);
723
724 BM_face_normal_flip(value->bm, value->f);
725
726 Py_RETURN_NONE;
727}
728
730 /* Wrap. */
731 bpy_bm_utils_loop_separate_doc,
732 ".. method:: loop_separate(loop)\n"
733 "\n"
734 " Rip a vertex in a face away and add a new vertex.\n"
735 "\n"
736 " :arg loop: The loop to separate.\n"
737 " :type loop: :class:`bmesh.types.BMLoop`\n"
738 " :return vert: The newly created vertex or None on failure.\n"
739 " :rtype vert: :class:`bmesh.types.BMVert`\n");
740static PyObject *bpy_bm_utils_loop_separate(PyObject * /*self*/, BPy_BMLoop *value)
741{
742 BMesh *bm;
743 BMLoop *l;
744 BMVert *v_old, *v_new;
745
746 if (!BPy_BMLoop_Check(value)) {
747 PyErr_Format(PyExc_TypeError,
748 "loop_separate(loop): BMLoop expected, not '%.200s'",
749 Py_TYPE(value)->tp_name);
750 return nullptr;
751 }
752
753 BPY_BM_CHECK_OBJ(value);
754
755 bm = value->bm;
756 l = value->l;
757
758 v_old = l->v;
759 v_new = BM_face_loop_separate(bm, l);
760
761 if (v_new != v_old) {
762 return BPy_BMVert_CreatePyObject(bm, v_new);
763 }
764
765 Py_RETURN_NONE;
766}
767
769 /* Wrap. */
770 bpy_bm_utils_uv_select_check_doc,
771 ".. method:: uv_select_check(bm, /, *, sync=True, flush=False, contiguous=False)\n"
772 "\n"
773 " Split an edge, return the newly created data.\n"
774 "\n"
775 " :arg sync: Check the data is properly synchronized between UV's and the underlying mesh. "
776 "Failure to synchronize with the mesh selection may cause tools not to behave properly.\n"
777 " :type sync: bool\n"
778 " :arg flush: Check the selection has been properly flushed between elements "
779 "(based on the current :class:`BMesh.select_mode`).\n"
780 " :type flush: bool\n"
781 " :arg contiguous: Check connected UV's and edges have a matching selection state.\n"
782 " :type contiguous: bool\n"
783 " :return: An error dictionary or None when there are no errors found.\n"
784 " :rtype: dict[str, int] | None\n");
785static PyObject *bpy_bm_utils_uv_select_check(PyObject * /*self*/, PyObject *args, PyObject *kwds)
786{
787 const char *error_prefix = "uv_select_check(...)";
788 BPy_BMesh *py_bm;
789 bool check_sync = true;
790 bool check_contiguous = false;
791 bool check_flush = false;
792
793 static const char *_keywords[] = {
794 "",
795 "sync",
796 "flush",
797 "contiguous",
798 nullptr,
799 };
800 static _PyArg_Parser _parser = {
802 "O!" /* `bm` */
803 "|$" /* Optional keyword only arguments. */
804 "O&" /* `sync` */
805 "O&" /* `flush` */
806 "O&" /* `contiguous` */
807 ":uv_select_check",
808 _keywords,
809 nullptr,
810 };
811 if (!_PyArg_ParseTupleAndKeywordsFast(args,
812 kwds,
813 &_parser,
815 &py_bm,
817 &check_sync,
819 &check_flush,
821 &check_contiguous))
822 {
823 return nullptr;
824 }
825
826 BPY_BM_CHECK_OBJ(py_bm);
827
828 BMesh *bm = py_bm->bm;
829 if (check_sync) {
830 if (bpy_bm_check_uv_select_sync_valid(bm, error_prefix) == -1) {
831 return nullptr;
832 }
833 }
834
835 const int cd_loop_uv_offset = check_contiguous ?
837 -1;
838 if (check_contiguous) {
839 if (cd_loop_uv_offset == -1) {
840 PyErr_SetString(PyExc_ValueError, "contiguous=True for a mesh without UV coordinates");
841 return nullptr;
842 }
843 }
844
845 UVSelectValidateInfo info = {};
846 const bool is_valid = BM_mesh_uvselect_is_valid(
847 bm, cd_loop_uv_offset, check_sync, check_flush, check_contiguous, &info);
848 if (is_valid) {
849 Py_RETURN_NONE;
850 }
851
852 PyObject *result = PyDict_New();
853
854#define DICT_ADD_INT_MEMBER(info_struct, member) \
855 PyDict_SetItemString(result, STRINGIFY(member), PyLong_FromLong(info_struct.member))
856
857 {
858 UVSelectValidateInfo_Sync &info_sub = info.sync;
859 DICT_ADD_INT_MEMBER(info_sub, count_uv_vert_any_selected_with_vert_unselected);
860 DICT_ADD_INT_MEMBER(info_sub, count_uv_vert_none_selected_with_vert_selected);
861
862 DICT_ADD_INT_MEMBER(info_sub, count_uv_edge_any_selected_with_edge_unselected);
863 DICT_ADD_INT_MEMBER(info_sub, count_uv_edge_none_selected_with_edge_selected);
864 }
865
866 if (check_flush) {
867 UVSelectValidateInfo_Flush &info_sub = info.flush;
868 DICT_ADD_INT_MEMBER(info_sub, count_uv_edge_selected_with_any_verts_unselected);
869 DICT_ADD_INT_MEMBER(info_sub, count_uv_edge_unselected_with_all_verts_selected);
870
871 DICT_ADD_INT_MEMBER(info_sub, count_uv_face_selected_with_any_verts_unselected);
872 DICT_ADD_INT_MEMBER(info_sub, count_uv_face_unselected_with_all_verts_selected);
873
874 DICT_ADD_INT_MEMBER(info_sub, count_uv_face_selected_with_any_edges_unselected);
875 DICT_ADD_INT_MEMBER(info_sub, count_uv_face_unselected_with_all_edges_selected);
876 }
877
878 if (check_contiguous) {
880 DICT_ADD_INT_MEMBER(info_sub, count_uv_vert_non_contiguous_selected);
881 DICT_ADD_INT_MEMBER(info_sub, count_uv_edge_non_contiguous_selected);
882 }
883
884 if (check_flush && check_contiguous) {
886 DICT_ADD_INT_MEMBER(info_sub, count_uv_vert_isolated_in_edge_or_face_mode);
887 DICT_ADD_INT_MEMBER(info_sub, count_uv_vert_isolated_in_face_mode);
888 DICT_ADD_INT_MEMBER(info_sub, count_uv_edge_isolated_in_face_mode);
889 }
890
891#undef DICT_ADD_INT_MEMBER
892
893 return result;
894}
895
896#ifdef __GNUC__
897# ifdef __clang__
898# pragma clang diagnostic push
899# pragma clang diagnostic ignored "-Wcast-function-type"
900# else
901# pragma GCC diagnostic push
902# pragma GCC diagnostic ignored "-Wcast-function-type"
903# endif
904#endif
905
906static PyMethodDef BPy_BM_utils_methods[] = {
907 {"vert_collapse_edge",
909 METH_VARARGS,
910 bpy_bm_utils_vert_collapse_edge_doc},
911 {"vert_collapse_faces",
913 METH_VARARGS,
914 bpy_bm_utils_vert_collapse_faces_doc},
915 {"vert_dissolve",
916 (PyCFunction)bpy_bm_utils_vert_dissolve,
917 METH_VARARGS,
918 bpy_bm_utils_vert_dissolve_doc}, /* could use METH_O */
919 {"vert_splice",
920 (PyCFunction)bpy_bm_utils_vert_splice,
921 METH_VARARGS,
922 bpy_bm_utils_vert_splice_doc},
923 {"vert_separate",
924 (PyCFunction)bpy_bm_utils_vert_separate,
925 METH_VARARGS,
926 bpy_bm_utils_vert_separate_doc},
927 {"edge_split",
928 (PyCFunction)bpy_bm_utils_edge_split,
929 METH_VARARGS,
930 bpy_bm_utils_edge_split_doc},
931 {"edge_rotate",
932 (PyCFunction)bpy_bm_utils_edge_rotate,
933 METH_VARARGS,
934 bpy_bm_utils_edge_rotate_doc},
935 {"face_split",
936 (PyCFunction)bpy_bm_utils_face_split,
937 METH_VARARGS | METH_KEYWORDS,
938 bpy_bm_utils_face_split_doc},
939 {"face_split_edgenet",
941 METH_VARARGS | METH_KEYWORDS,
942 bpy_bm_utils_face_split_edgenet_doc},
943 {"face_join", (PyCFunction)bpy_bm_utils_face_join, METH_VARARGS, bpy_bm_utils_face_join_doc},
944 {"face_vert_separate",
946 METH_VARARGS,
947 bpy_bm_utils_face_vert_separate_doc},
948 {"face_flip", (PyCFunction)bpy_bm_utils_face_flip, METH_O, bpy_bm_utils_face_flip_doc},
949 {"loop_separate",
950 (PyCFunction)bpy_bm_utils_loop_separate,
951 METH_O,
952 bpy_bm_utils_loop_separate_doc},
953 {"uv_select_check",
954 (PyCFunction)bpy_bm_utils_uv_select_check,
955 METH_VARARGS | METH_KEYWORDS,
956 bpy_bm_utils_uv_select_check_doc},
957 {nullptr, nullptr, 0, nullptr},
958};
959
960#ifdef __GNUC__
961# ifdef __clang__
962# pragma clang diagnostic pop
963# else
964# pragma GCC diagnostic pop
965# endif
966#endif
967
969 /* Wrap. */
970 BPy_BM_utils_doc,
971 "This module provides access to blenders bmesh data structures.");
972static PyModuleDef BPy_BM_utils_module_def = {
973 /*m_base*/ PyModuleDef_HEAD_INIT,
974 /*m_name*/ "bmesh.utils",
975 /*m_doc*/ BPy_BM_utils_doc,
976 /*m_size*/ 0,
977 /*m_methods*/ BPy_BM_utils_methods,
978 /*m_slots*/ nullptr,
979 /*m_traverse*/ nullptr,
980 /*m_clear*/ nullptr,
981 /*m_free*/ nullptr,
982};
983
985{
986 PyObject *submodule;
987
988 submodule = PyModule_Create(&BPy_BM_utils_module_def);
989
990 return submodule;
991}
int CustomData_get_offset(const CustomData *data, eCustomDataType type)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
MINLINE float clamp_f(float value, float min, float max)
#define UNUSED_VARS_NDEBUG(...)
#define AT
@ CD_PROP_FLOAT2
Read Guarded memory(de)allocation.
void BM_vert_separate(BMesh *bm, BMVert *v, BMEdge **e_in, int e_in_len, const bool copy_select, BMVert ***r_vout, int *r_vout_len)
BMFace * BM_faces_join(BMesh *bm, BMFace **faces, int totface, const bool do_del, BMFace **r_double)
Join Connected Faces.
bool BM_vert_splice(BMesh *bm, BMVert *v_dst, BMVert *v_src)
Splice Vert.
BMesh * bm
BMFace * BM_face_split_n(BMesh *bm, BMFace *f, BMLoop *l_a, BMLoop *l_b, float cos[][3], int n, BMLoop **r_l, BMEdge *example)
Face Split with intermediate points.
BMVert * BM_face_loop_separate(BMesh *bm, BMLoop *l_sep)
Rip a single face from a vertex fan.
BMEdge * BM_vert_collapse_faces(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, float fac, const bool do_del, const bool join_faces, const bool kill_degenerate_faces, const bool kill_duplicate_faces)
Vert Collapse Faces.
bool BM_vert_dissolve(BMesh *bm, BMVert *v)
Dissolve Vert.
Definition bmesh_mods.cc:22
BMVert * BM_edge_split(BMesh *bm, BMEdge *e, BMVert *v, BMEdge **r_e, float fac)
Edge Split.
BMEdge * BM_vert_collapse_edge(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, const bool do_del, const bool kill_degenerate_faces, const bool kill_duplicate_faces)
Vert Collapse Faces.
BMEdge * BM_edge_rotate(BMesh *bm, BMEdge *e, const bool ccw, const short check_flag)
Rotate Edge.
BMFace * BM_face_split(BMesh *bm, BMFace *f, BMLoop *l_a, BMLoop *l_b, BMLoop **r_l, BMEdge *example, const bool no_double)
Face Split.
void BM_face_normal_flip(BMesh *bm, BMFace *f)
bool BM_face_split_edgenet(BMesh *bm, BMFace *f, BMEdge **edge_net, const int edge_net_len, blender::Vector< BMFace * > *r_face_arr)
PyObject * BPy_BMFace_CreatePyObject(BMesh *bm, BMFace *f)
PyObject * BPy_BMVert_Array_As_Tuple(BMesh *bm, BMVert **elem, Py_ssize_t elem_num)
PyTypeObject BPy_BMesh_Type
PyObject * BPy_BMVert_CreatePyObject(BMesh *bm, BMVert *v)
PyObject * BPy_BMLoop_CreatePyObject(BMesh *bm, BMLoop *l)
PyObject * BPy_BMFace_Array_As_Tuple(BMesh *bm, BMFace **elem, Py_ssize_t elem_num)
PyTypeObject BPy_BMEdge_Type
BMEdge ** BPy_BMEdge_PySeq_As_Array(BMesh **r_bm, PyObject *seq, Py_ssize_t min, Py_ssize_t max, Py_ssize_t *r_seq_num, bool do_unique_check, bool do_bm_check, const char *error_prefix)
PyTypeObject BPy_BMVert_Type
int bpy_bm_check_uv_select_sync_valid(BMesh *bm, const char *error_prefix)
PyTypeObject BPy_BMFace_Type
PyObject * BPy_BMEdge_CreatePyObject(BMesh *bm, BMEdge *e)
BMFace ** BPy_BMFace_PySeq_As_Array(BMesh **r_bm, PyObject *seq, Py_ssize_t min, Py_ssize_t max, Py_ssize_t *r_seq_num, bool do_unique_check, bool do_bm_check, const char *error_prefix)
#define BPy_BMFace_Check(v)
#define BPY_BM_CHECK_OBJ(obj)
#define BPy_BMLoop_Check(v)
#define BPY_BM_CHECK_SOURCE_OBJ(bm, errmsg,...)
PyObject * BPyInit_bmesh_utils()
static PyObject * bpy_bm_utils_vert_splice(PyObject *, PyObject *args)
static PyModuleDef BPy_BM_utils_module_def
static PyObject * bpy_bm_utils_vert_collapse_faces(PyObject *, PyObject *args)
PyDoc_STRVAR(bpy_bm_utils_vert_collapse_edge_doc, ".. method:: vert_collapse_edge(vert, edge)\n" "\n" " Collapse a vertex into an edge.\n" "\n" " :arg vert: The vert that will be collapsed.\n" " :type vert: :class:`bmesh.types.BMVert`\n" " :arg edge: The edge to collapse into.\n" " :type edge: :class:`bmesh.types.BMEdge`\n" " :return: The resulting edge from the collapse operation.\n" " :rtype: :class:`bmesh.types.BMEdge`\n")
static PyObject * bpy_bm_utils_face_split(PyObject *, PyObject *args, PyObject *kw)
static PyObject * bpy_bm_utils_face_join(PyObject *, PyObject *args)
static PyObject * bpy_bm_utils_vert_collapse_edge(PyObject *, PyObject *args)
static PyObject * bpy_bm_utils_vert_dissolve(PyObject *, PyObject *args)
static PyObject * bpy_bm_utils_vert_separate(PyObject *, PyObject *args)
static PyObject * bpy_bm_utils_loop_separate(PyObject *, BPy_BMLoop *value)
static PyObject * bpy_bm_utils_uv_select_check(PyObject *, PyObject *args, PyObject *kwds)
static PyObject * bpy_bm_utils_face_flip(PyObject *, BPy_BMFace *value)
static PyObject * bpy_bm_utils_edge_split(PyObject *, PyObject *args)
static PyObject * bpy_bm_utils_face_split_edgenet(PyObject *, PyObject *args, PyObject *kw)
static PyObject * bpy_bm_utils_edge_rotate(PyObject *, PyObject *args)
static PyMethodDef BPy_BM_utils_methods[]
static PyObject * bpy_bm_utils_face_vert_separate(PyObject *, PyObject *args)
#define DICT_ADD_INT_MEMBER(info_struct, member)
bool BM_vert_pair_share_face_check(BMVert *v_a, BMVert *v_b)
BMEdge * BM_edge_exists(BMVert *v_a, BMVert *v_b)
BMLoop * BM_face_vert_share_loop(BMFace *f, BMVert *v)
Return the Loop Shared by Face and Vertex.
BLI_INLINE bool BM_loop_is_adjacent(const BMLoop *l_a, const BMLoop *l_b) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
#define BM_vert_edge_count_is_over(v, n)
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMLoop * l_b
bool BM_mesh_uvselect_is_valid(BMesh *bm, const int cd_loop_uv_offset, const bool check_sync, const bool check_flush, const bool check_contiguous, UVSelectValidateInfo *info_p)
int64_t size() const
nullptr float
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
int mathutils_array_parse_alloc_v(float **array, int array_dim, PyObject *value, const char *error_prefix)
Definition mathutils.cc:259
int PyC_ParseBool(PyObject *o, void *p)
header-only compatibility defines.
#define PY_ARG_PARSER_HEAD_COMPAT()
header-only utilities
#define PyTuple_SET_ITEMS(op_arg,...)
return ret
BMVert * v1
BMVert * v2
PyObject_VAR_HEAD BMesh * bm
PyObject_VAR_HEAD BMesh * bm
PyObject_VAR_HEAD BMesh * bm
PyObject_VAR_HEAD BMesh * bm
PyObject_VAR_HEAD BMesh * bm
UVSelectValidateInfo_Flush flush
UVSelectValidateInfo_FlushAndContiguous flush_contiguous
UVSelectValidateInfo_Sync sync
UVSelectValidateInfo_Contiguous contiguous