Listing the "Blender Foundation" as copyright holder implied the Blender Foundation holds copyright to files which may include work from many developers. While keeping copyright on headers makes sense for isolated libraries, Blender's own code may be refactored or moved between files in a way that makes the per file copyright holders less meaningful. Copyright references to the "Blender Foundation" have been replaced with "Blender Authors", with the exception of `./extern/` since these this contains libraries which are more isolated, any changed to license headers there can be handled on a case-by-case basis. Some directories in `./intern/` have also been excluded: - `./intern/cycles/` it's own `AUTHORS` file is planned. - `./intern/opensubdiv/`. An "AUTHORS" file has been added, using the chromium projects authors file as a template. Design task: #110784 Ref !110783.
846 lines
26 KiB
C++
846 lines
26 KiB
C++
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup pybmesh
|
|
*
|
|
* This file defines the 'bmesh.utils' module.
|
|
* Utility functions for operating on 'bmesh.types'
|
|
*/
|
|
|
|
#include <Python.h>
|
|
|
|
#include "BLI_math_base.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "../mathutils/mathutils.h"
|
|
|
|
#include "bmesh.h"
|
|
#include "bmesh_py_types.h"
|
|
#include "bmesh_py_utils.h" /* own include */
|
|
|
|
#include "../generic/py_capi_utils.h"
|
|
#include "../generic/python_utildefines.h"
|
|
|
|
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_vert_collapse_edge(PyObject * /*self*/, PyObject *args)
|
|
{
|
|
BPy_BMEdge *py_edge;
|
|
BPy_BMVert *py_vert;
|
|
|
|
BMesh *bm;
|
|
BMEdge *e_new = nullptr;
|
|
|
|
if (!PyArg_ParseTuple(
|
|
args, "O!O!:vert_collapse_edge", &BPy_BMVert_Type, &py_vert, &BPy_BMEdge_Type, &py_edge))
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
BPY_BM_CHECK_OBJ(py_edge);
|
|
BPY_BM_CHECK_OBJ(py_vert);
|
|
|
|
/* this doubles for checking that the verts are in the same mesh */
|
|
if (!(py_edge->e->v1 == py_vert->v || py_edge->e->v2 == py_vert->v)) {
|
|
PyErr_SetString(PyExc_ValueError,
|
|
"vert_collapse_edge(vert, edge): the vertex is not found in the edge");
|
|
return nullptr;
|
|
}
|
|
|
|
if (BM_vert_edge_count_is_over(py_vert->v, 2)) {
|
|
PyErr_SetString(PyExc_ValueError,
|
|
"vert_collapse_edge(vert, edge): vert has more than 2 connected edges");
|
|
return nullptr;
|
|
}
|
|
|
|
bm = py_edge->bm;
|
|
|
|
e_new = BM_vert_collapse_edge(bm, py_edge->e, py_vert->v, true, true, true);
|
|
|
|
if (e_new) {
|
|
return BPy_BMEdge_CreatePyObject(bm, e_new);
|
|
}
|
|
|
|
PyErr_SetString(PyExc_ValueError,
|
|
"vert_collapse_edge(vert, edge): no new edge created, internal error");
|
|
return nullptr;
|
|
}
|
|
|
|
PyDoc_STRVAR(bpy_bm_utils_vert_collapse_faces_doc,
|
|
".. method:: vert_collapse_faces(vert, edge, fac, join_faces)\n"
|
|
"\n"
|
|
" Collapses a vertex that has only two manifold edges onto a vertex it shares an "
|
|
"edge with.\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"
|
|
" :arg fac: The factor to use when merging customdata [0 - 1].\n"
|
|
" :type fac: float\n"
|
|
" :arg join_faces: When true the faces around the vertex will be joined otherwise "
|
|
"collapse the vertex by merging the 2 edges this vertex connects to into one.\n"
|
|
" :type join_faces: bool\n"
|
|
" :return: The resulting edge from the collapse operation.\n"
|
|
" :rtype: :class:`bmesh.types.BMEdge`\n");
|
|
static PyObject *bpy_bm_utils_vert_collapse_faces(PyObject * /*self*/, PyObject *args)
|
|
{
|
|
BPy_BMEdge *py_edge;
|
|
BPy_BMVert *py_vert;
|
|
|
|
float fac;
|
|
int do_join_faces;
|
|
|
|
BMesh *bm;
|
|
BMEdge *e_new = nullptr;
|
|
|
|
if (!PyArg_ParseTuple(args,
|
|
"O!O!fi:vert_collapse_faces",
|
|
&BPy_BMVert_Type,
|
|
&py_vert,
|
|
&BPy_BMEdge_Type,
|
|
&py_edge,
|
|
&fac,
|
|
&do_join_faces))
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
BPY_BM_CHECK_OBJ(py_edge);
|
|
BPY_BM_CHECK_OBJ(py_vert);
|
|
|
|
/* this doubles for checking that the verts are in the same mesh */
|
|
if (!(py_edge->e->v1 == py_vert->v || py_edge->e->v2 == py_vert->v)) {
|
|
PyErr_SetString(PyExc_ValueError,
|
|
"vert_collapse_faces(vert, edge): the vertex is not found in the edge");
|
|
return nullptr;
|
|
}
|
|
|
|
if (BM_vert_edge_count_is_over(py_vert->v, 2)) {
|
|
PyErr_SetString(PyExc_ValueError,
|
|
"vert_collapse_faces(vert, edge): vert has more than 2 connected edges");
|
|
return nullptr;
|
|
}
|
|
|
|
bm = py_edge->bm;
|
|
|
|
e_new = BM_vert_collapse_faces(
|
|
bm, py_edge->e, py_vert->v, clamp_f(fac, 0.0f, 1.0f), true, do_join_faces, true, true);
|
|
|
|
if (e_new) {
|
|
return BPy_BMEdge_CreatePyObject(bm, e_new);
|
|
}
|
|
|
|
PyErr_SetString(PyExc_ValueError,
|
|
"vert_collapse_faces(vert, edge): no new edge created, internal error");
|
|
return nullptr;
|
|
}
|
|
|
|
PyDoc_STRVAR(bpy_bm_utils_vert_dissolve_doc,
|
|
".. method:: vert_dissolve(vert)\n"
|
|
"\n"
|
|
" Dissolve this vertex (will be removed).\n"
|
|
"\n"
|
|
" :arg vert: The vert to be dissolved.\n"
|
|
" :type vert: :class:`bmesh.types.BMVert`\n"
|
|
" :return: True when the vertex dissolve is successful.\n"
|
|
" :rtype: boolean\n");
|
|
static PyObject *bpy_bm_utils_vert_dissolve(PyObject * /*self*/, PyObject *args)
|
|
{
|
|
BPy_BMVert *py_vert;
|
|
|
|
BMesh *bm;
|
|
|
|
if (!PyArg_ParseTuple(args, "O!:vert_dissolve", &BPy_BMVert_Type, &py_vert)) {
|
|
return nullptr;
|
|
}
|
|
|
|
BPY_BM_CHECK_OBJ(py_vert);
|
|
|
|
bm = py_vert->bm;
|
|
|
|
return PyBool_FromLong(BM_vert_dissolve(bm, py_vert->v));
|
|
}
|
|
|
|
PyDoc_STRVAR(bpy_bm_utils_vert_splice_doc,
|
|
".. method:: vert_splice(vert, vert_target)\n"
|
|
"\n"
|
|
" Splice vert into vert_target.\n"
|
|
"\n"
|
|
" :arg vert: The vertex to be removed.\n"
|
|
" :type vert: :class:`bmesh.types.BMVert`\n"
|
|
" :arg vert_target: The vertex to use.\n"
|
|
" :type vert_target: :class:`bmesh.types.BMVert`\n"
|
|
"\n"
|
|
" .. note:: The verts mustn't share an edge or face.\n");
|
|
static PyObject *bpy_bm_utils_vert_splice(PyObject * /*self*/, PyObject *args)
|
|
{
|
|
BPy_BMVert *py_vert;
|
|
BPy_BMVert *py_vert_target;
|
|
|
|
BMesh *bm;
|
|
|
|
bool ok;
|
|
|
|
if (!PyArg_ParseTuple(
|
|
args, "O!O!:vert_splice", &BPy_BMVert_Type, &py_vert, &BPy_BMVert_Type, &py_vert_target))
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
BPY_BM_CHECK_OBJ(py_vert);
|
|
BPY_BM_CHECK_OBJ(py_vert_target);
|
|
|
|
bm = py_vert->bm;
|
|
BPY_BM_CHECK_SOURCE_OBJ(bm, "vert_splice", py_vert_target);
|
|
|
|
if (py_vert->v == py_vert_target->v) {
|
|
PyErr_SetString(PyExc_ValueError, "vert_splice(...): vert arguments match");
|
|
return nullptr;
|
|
}
|
|
|
|
if (BM_edge_exists(py_vert->v, py_vert_target->v)) {
|
|
PyErr_SetString(PyExc_ValueError, "vert_splice(...): verts can't share an edge");
|
|
return nullptr;
|
|
}
|
|
|
|
if (BM_vert_pair_share_face_check(py_vert->v, py_vert_target->v)) {
|
|
PyErr_SetString(PyExc_ValueError, "vert_splice(...): verts can't share a face");
|
|
return nullptr;
|
|
}
|
|
|
|
/* should always succeed */
|
|
ok = BM_vert_splice(bm, py_vert_target->v, py_vert->v);
|
|
BLI_assert(ok == true);
|
|
UNUSED_VARS_NDEBUG(ok);
|
|
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
PyDoc_STRVAR(bpy_bm_utils_vert_separate_doc,
|
|
".. method:: vert_separate(vert, edges)\n"
|
|
"\n"
|
|
" Separate this vertex at every edge.\n"
|
|
"\n"
|
|
" :arg vert: The vert to be separated.\n"
|
|
" :type vert: :class:`bmesh.types.BMVert`\n"
|
|
" :arg edges: The edges to separated.\n"
|
|
" :type edges: :class:`bmesh.types.BMEdge`\n"
|
|
" :return: The newly separated verts (including the vertex passed).\n"
|
|
" :rtype: tuple of :class:`bmesh.types.BMVert`\n");
|
|
static PyObject *bpy_bm_utils_vert_separate(PyObject * /*self*/, PyObject *args)
|
|
{
|
|
BPy_BMVert *py_vert;
|
|
PyObject *edge_seq;
|
|
|
|
BMesh *bm;
|
|
BMVert **elem;
|
|
int elem_len;
|
|
|
|
/* edges to split */
|
|
BMEdge **edge_array;
|
|
Py_ssize_t edge_array_len;
|
|
|
|
PyObject *ret;
|
|
|
|
if (!PyArg_ParseTuple(args, "O!O:vert_separate", &BPy_BMVert_Type, &py_vert, &edge_seq)) {
|
|
return nullptr;
|
|
}
|
|
|
|
BPY_BM_CHECK_OBJ(py_vert);
|
|
|
|
bm = py_vert->bm;
|
|
|
|
edge_array = static_cast<BMEdge **>(BPy_BMElem_PySeq_As_Array(&bm,
|
|
edge_seq,
|
|
0,
|
|
PY_SSIZE_T_MAX,
|
|
&edge_array_len,
|
|
BM_EDGE,
|
|
true,
|
|
true,
|
|
"vert_separate(...)"));
|
|
|
|
if (edge_array == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
BM_vert_separate(bm, py_vert->v, edge_array, edge_array_len, false, &elem, &elem_len);
|
|
/* return collected verts */
|
|
ret = BPy_BMVert_Array_As_Tuple(bm, elem, elem_len);
|
|
MEM_freeN(elem);
|
|
|
|
PyMem_FREE(edge_array);
|
|
|
|
return ret;
|
|
}
|
|
|
|
PyDoc_STRVAR(bpy_bm_utils_edge_split_doc,
|
|
".. method:: edge_split(edge, vert, fac)\n"
|
|
"\n"
|
|
" Split an edge, return the newly created data.\n"
|
|
"\n"
|
|
" :arg edge: The edge to split.\n"
|
|
" :type edge: :class:`bmesh.types.BMEdge`\n"
|
|
" :arg vert: One of the verts on the edge, defines the split direction.\n"
|
|
" :type vert: :class:`bmesh.types.BMVert`\n"
|
|
" :arg fac: The point on the edge where the new vert will be created [0 - 1].\n"
|
|
" :type fac: float\n"
|
|
" :return: The newly created (edge, vert) pair.\n"
|
|
" :rtype: tuple\n");
|
|
static PyObject *bpy_bm_utils_edge_split(PyObject * /*self*/, PyObject *args)
|
|
{
|
|
BPy_BMEdge *py_edge;
|
|
BPy_BMVert *py_vert;
|
|
float fac;
|
|
|
|
BMesh *bm;
|
|
BMVert *v_new = nullptr;
|
|
BMEdge *e_new = nullptr;
|
|
|
|
if (!PyArg_ParseTuple(
|
|
args, "O!O!f:edge_split", &BPy_BMEdge_Type, &py_edge, &BPy_BMVert_Type, &py_vert, &fac))
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
BPY_BM_CHECK_OBJ(py_edge);
|
|
BPY_BM_CHECK_OBJ(py_vert);
|
|
|
|
/* this doubles for checking that the verts are in the same mesh */
|
|
if (!(py_edge->e->v1 == py_vert->v || py_edge->e->v2 == py_vert->v)) {
|
|
PyErr_SetString(PyExc_ValueError,
|
|
"edge_split(edge, vert): the vertex is not found in the edge");
|
|
return nullptr;
|
|
}
|
|
|
|
bm = py_edge->bm;
|
|
|
|
v_new = BM_edge_split(bm, py_edge->e, py_vert->v, &e_new, clamp_f(fac, 0.0f, 1.0f));
|
|
|
|
if (v_new && e_new) {
|
|
PyObject *ret = PyTuple_New(2);
|
|
PyTuple_SET_ITEMS(
|
|
ret, BPy_BMEdge_CreatePyObject(bm, e_new), BPy_BMVert_CreatePyObject(bm, v_new));
|
|
return ret;
|
|
}
|
|
|
|
PyErr_SetString(PyExc_ValueError,
|
|
"edge_split(edge, vert): couldn't split the edge, internal error");
|
|
return nullptr;
|
|
}
|
|
|
|
PyDoc_STRVAR(bpy_bm_utils_edge_rotate_doc,
|
|
".. method:: edge_rotate(edge, ccw=False)\n"
|
|
"\n"
|
|
" Rotate the edge and return the newly created edge.\n"
|
|
" If rotating the edge fails, None will be returned.\n"
|
|
"\n"
|
|
" :arg edge: The edge to rotate.\n"
|
|
" :type edge: :class:`bmesh.types.BMEdge`\n"
|
|
" :arg ccw: When True the edge will be rotated counter clockwise.\n"
|
|
" :type ccw: boolean\n"
|
|
" :return: The newly rotated edge.\n"
|
|
" :rtype: :class:`bmesh.types.BMEdge`\n");
|
|
static PyObject *bpy_bm_utils_edge_rotate(PyObject * /*self*/, PyObject *args)
|
|
{
|
|
BPy_BMEdge *py_edge;
|
|
bool do_ccw = false;
|
|
|
|
BMesh *bm;
|
|
BMEdge *e_new = nullptr;
|
|
|
|
if (!PyArg_ParseTuple(
|
|
args, "O!|O&:edge_rotate", &BPy_BMEdge_Type, &py_edge, PyC_ParseBool, &do_ccw))
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
BPY_BM_CHECK_OBJ(py_edge);
|
|
|
|
bm = py_edge->bm;
|
|
|
|
e_new = BM_edge_rotate(bm, py_edge->e, do_ccw, 0);
|
|
|
|
if (e_new) {
|
|
return BPy_BMEdge_CreatePyObject(bm, e_new);
|
|
}
|
|
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
PyDoc_STRVAR(
|
|
bpy_bm_utils_face_split_doc,
|
|
".. method:: face_split(face, vert_a, vert_b, coords=(), use_exist=True, example=None)\n"
|
|
"\n"
|
|
" Face split with optional intermediate points.\n"
|
|
"\n"
|
|
" :arg face: The face to cut.\n"
|
|
" :type face: :class:`bmesh.types.BMFace`\n"
|
|
" :arg vert_a: First vertex to cut in the face (face must contain the vert).\n"
|
|
" :type vert_a: :class:`bmesh.types.BMVert`\n"
|
|
" :arg vert_b: Second vertex to cut in the face (face must contain the vert).\n"
|
|
" :type vert_b: :class:`bmesh.types.BMVert`\n"
|
|
" :arg coords: Optional argument to define points in between *vert_a* and *vert_b*.\n"
|
|
" :type coords: sequence of float triplets\n"
|
|
" :arg use_exist: .Use an existing edge if it exists (Only used when *coords* argument is "
|
|
"empty or omitted)\n"
|
|
" :type use_exist: boolean\n"
|
|
" :arg example: Newly created edge will copy settings from this one.\n"
|
|
" :type example: :class:`bmesh.types.BMEdge`\n"
|
|
" :return: The newly created face or None on failure.\n"
|
|
" :rtype: (:class:`bmesh.types.BMFace`, :class:`bmesh.types.BMLoop`) pair\n");
|
|
static PyObject *bpy_bm_utils_face_split(PyObject * /*self*/, PyObject *args, PyObject *kw)
|
|
{
|
|
static const char *kwlist[] = {
|
|
"face", "vert_a", "vert_b", "coords", "use_exist", "example", nullptr};
|
|
|
|
BPy_BMFace *py_face;
|
|
BPy_BMVert *py_vert_a;
|
|
BPy_BMVert *py_vert_b;
|
|
|
|
/* optional */
|
|
PyObject *py_coords = nullptr;
|
|
bool edge_exists = true;
|
|
BPy_BMEdge *py_edge_example = nullptr;
|
|
|
|
float *coords;
|
|
int ncoords = 0;
|
|
|
|
BMesh *bm;
|
|
BMFace *f_new = nullptr;
|
|
BMLoop *l_new = nullptr;
|
|
BMLoop *l_a, *l_b;
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args,
|
|
kw,
|
|
"O!O!O!|$OO&O!:face_split",
|
|
(char **)kwlist,
|
|
&BPy_BMFace_Type,
|
|
&py_face,
|
|
&BPy_BMVert_Type,
|
|
&py_vert_a,
|
|
&BPy_BMVert_Type,
|
|
&py_vert_b,
|
|
&py_coords,
|
|
PyC_ParseBool,
|
|
&edge_exists,
|
|
&BPy_BMEdge_Type,
|
|
&py_edge_example))
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
BPY_BM_CHECK_OBJ(py_face);
|
|
BPY_BM_CHECK_OBJ(py_vert_a);
|
|
BPY_BM_CHECK_OBJ(py_vert_b);
|
|
|
|
if (py_edge_example) {
|
|
BPY_BM_CHECK_OBJ(py_edge_example);
|
|
}
|
|
|
|
/* this doubles for checking that the verts are in the same mesh */
|
|
if ((l_a = BM_face_vert_share_loop(py_face->f, py_vert_a->v)) &&
|
|
(l_b = BM_face_vert_share_loop(py_face->f, py_vert_b->v)))
|
|
{
|
|
/* pass */
|
|
}
|
|
else {
|
|
PyErr_SetString(PyExc_ValueError,
|
|
"face_split(...): one of the verts passed is not found in the face");
|
|
return nullptr;
|
|
}
|
|
|
|
if (py_vert_a->v == py_vert_b->v) {
|
|
PyErr_SetString(PyExc_ValueError, "face_split(...): vert arguments must differ");
|
|
return nullptr;
|
|
}
|
|
|
|
if (py_coords) {
|
|
ncoords = mathutils_array_parse_alloc_v(&coords, 3, py_coords, "face_split(...): ");
|
|
if (ncoords == -1) {
|
|
return nullptr;
|
|
}
|
|
}
|
|
else {
|
|
if (BM_loop_is_adjacent(l_a, l_b)) {
|
|
PyErr_SetString(PyExc_ValueError, "face_split(...): verts are adjacent in the face");
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
/* --- main function body --- */
|
|
bm = py_face->bm;
|
|
|
|
if (ncoords) {
|
|
f_new = BM_face_split_n(bm,
|
|
py_face->f,
|
|
l_a,
|
|
l_b,
|
|
(float(*)[3])coords,
|
|
ncoords,
|
|
&l_new,
|
|
py_edge_example ? py_edge_example->e : nullptr);
|
|
PyMem_Free(coords);
|
|
}
|
|
else {
|
|
f_new = BM_face_split(bm,
|
|
py_face->f,
|
|
l_a,
|
|
l_b,
|
|
&l_new,
|
|
py_edge_example ? py_edge_example->e : nullptr,
|
|
edge_exists);
|
|
}
|
|
|
|
if (f_new && l_new) {
|
|
PyObject *ret = PyTuple_New(2);
|
|
PyTuple_SET_ITEMS(
|
|
ret, BPy_BMFace_CreatePyObject(bm, f_new), BPy_BMLoop_CreatePyObject(bm, l_new));
|
|
return ret;
|
|
}
|
|
|
|
PyErr_SetString(PyExc_ValueError, "face_split(...): couldn't split the face, internal error");
|
|
return nullptr;
|
|
}
|
|
|
|
PyDoc_STRVAR(bpy_bm_utils_face_split_edgenet_doc,
|
|
".. method:: face_split_edgenet(face, edgenet)\n"
|
|
"\n"
|
|
" Splits a face into any number of regions defined by an edgenet.\n"
|
|
"\n"
|
|
" :arg face: The face to split.\n"
|
|
" :type face: :class:`bmesh.types.BMFace`\n"
|
|
" :arg face: The face to split.\n"
|
|
" :type face: :class:`bmesh.types.BMFace`\n"
|
|
" :arg edgenet: Sequence of edges.\n"
|
|
" :type edgenet: :class:`bmesh.types.BMEdge`\n"
|
|
" :return: The newly created faces.\n"
|
|
" :rtype: tuple of (:class:`bmesh.types.BMFace`)\n"
|
|
"\n"
|
|
" .. note::\n"
|
|
"\n"
|
|
" Regions defined by edges need to connect to the face, otherwise they're "
|
|
"ignored as loose edges.\n");
|
|
static PyObject *bpy_bm_utils_face_split_edgenet(PyObject * /*self*/, PyObject *args, PyObject *kw)
|
|
{
|
|
static const char *kwlist[] = {"face", "edgenet", nullptr};
|
|
|
|
BPy_BMFace *py_face;
|
|
PyObject *edge_seq;
|
|
|
|
BMEdge **edge_array;
|
|
Py_ssize_t edge_array_len;
|
|
|
|
BMesh *bm;
|
|
|
|
BMFace **face_arr;
|
|
int face_arr_len;
|
|
bool ok;
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args,
|
|
kw,
|
|
"O!O:face_split_edgenet",
|
|
(char **)kwlist,
|
|
&BPy_BMFace_Type,
|
|
&py_face,
|
|
&edge_seq))
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
BPY_BM_CHECK_OBJ(py_face);
|
|
|
|
bm = py_face->bm;
|
|
|
|
edge_array = static_cast<BMEdge **>(BPy_BMElem_PySeq_As_Array(&bm,
|
|
edge_seq,
|
|
1,
|
|
PY_SSIZE_T_MAX,
|
|
&edge_array_len,
|
|
BM_EDGE,
|
|
true,
|
|
true,
|
|
"face_split_edgenet(...)"));
|
|
|
|
if (edge_array == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
/* --- main function body --- */
|
|
|
|
ok = BM_face_split_edgenet(bm, py_face->f, edge_array, edge_array_len, &face_arr, &face_arr_len);
|
|
|
|
PyMem_FREE(edge_array);
|
|
|
|
if (ok) {
|
|
PyObject *ret = BPy_BMFace_Array_As_Tuple(bm, face_arr, face_arr_len);
|
|
if (face_arr) {
|
|
MEM_freeN(face_arr);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
PyErr_SetString(PyExc_ValueError,
|
|
"face_split_edgenet(...): couldn't split the face, internal error");
|
|
return nullptr;
|
|
}
|
|
|
|
PyDoc_STRVAR(bpy_bm_utils_face_join_doc,
|
|
".. method:: face_join(faces, remove=True)\n"
|
|
"\n"
|
|
" Joins a sequence of faces.\n"
|
|
"\n"
|
|
" :arg faces: Sequence of faces.\n"
|
|
" :type faces: :class:`bmesh.types.BMFace`\n"
|
|
" :arg remove: Remove the edges and vertices between the faces.\n"
|
|
" :type remove: boolean\n"
|
|
" :return: The newly created face or None on failure.\n"
|
|
" :rtype: :class:`bmesh.types.BMFace`\n");
|
|
static PyObject *bpy_bm_utils_face_join(PyObject * /*self*/, PyObject *args)
|
|
{
|
|
BMesh *bm = nullptr;
|
|
PyObject *py_face_array;
|
|
BMFace **face_array;
|
|
Py_ssize_t face_seq_len = 0;
|
|
BMFace *f_new;
|
|
bool do_remove = true;
|
|
|
|
if (!PyArg_ParseTuple(args, "O|O&:face_join", &py_face_array, PyC_ParseBool, &do_remove)) {
|
|
return nullptr;
|
|
}
|
|
|
|
face_array = static_cast<BMFace **>(BPy_BMElem_PySeq_As_Array(&bm,
|
|
py_face_array,
|
|
2,
|
|
PY_SSIZE_T_MAX,
|
|
&face_seq_len,
|
|
BM_FACE,
|
|
true,
|
|
true,
|
|
"face_join(...)"));
|
|
|
|
if (face_array == nullptr) {
|
|
return nullptr; /* error will be set */
|
|
}
|
|
|
|
/* Go ahead and join the face!
|
|
* --------------------------- */
|
|
f_new = BM_faces_join(bm, face_array, int(face_seq_len), do_remove);
|
|
|
|
PyMem_FREE(face_array);
|
|
|
|
if (f_new) {
|
|
return BPy_BMFace_CreatePyObject(bm, f_new);
|
|
}
|
|
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
PyDoc_STRVAR(
|
|
bpy_bm_utils_face_vert_separate_doc,
|
|
".. method:: face_vert_separate(face, vert)\n"
|
|
"\n"
|
|
" Rip a vertex in a face away and add a new vertex.\n"
|
|
"\n"
|
|
" :arg face: The face to separate.\n"
|
|
" :type face: :class:`bmesh.types.BMFace`\n"
|
|
" :arg vert: A vertex in the face to separate.\n"
|
|
" :type vert: :class:`bmesh.types.BMVert`\n"
|
|
" :return vert: The newly created vertex or None on failure.\n"
|
|
" :rtype vert: :class:`bmesh.types.BMVert`\n"
|
|
"\n"
|
|
" .. note::\n"
|
|
"\n"
|
|
" This is the same as loop_separate, and has only been added for convenience.\n");
|
|
static PyObject *bpy_bm_utils_face_vert_separate(PyObject * /*self*/, PyObject *args)
|
|
{
|
|
BPy_BMFace *py_face;
|
|
BPy_BMVert *py_vert;
|
|
|
|
BMesh *bm;
|
|
BMLoop *l;
|
|
BMVert *v_old, *v_new;
|
|
|
|
if (!PyArg_ParseTuple(
|
|
args, "O!O!:face_vert_separate", &BPy_BMFace_Type, &py_face, &BPy_BMVert_Type, &py_vert))
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
bm = py_face->bm;
|
|
|
|
BPY_BM_CHECK_OBJ(py_face);
|
|
BPY_BM_CHECK_SOURCE_OBJ(bm, "face_vert_separate()", py_vert);
|
|
|
|
l = BM_face_vert_share_loop(py_face->f, py_vert->v);
|
|
|
|
if (l == nullptr) {
|
|
PyErr_SetString(PyExc_ValueError, "vertex not found in face");
|
|
return nullptr;
|
|
}
|
|
|
|
v_old = l->v;
|
|
v_new = BM_face_loop_separate(bm, l);
|
|
|
|
if (v_new != v_old) {
|
|
return BPy_BMVert_CreatePyObject(bm, v_new);
|
|
}
|
|
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
PyDoc_STRVAR(bpy_bm_utils_face_flip_doc,
|
|
".. method:: face_flip(faces)\n"
|
|
"\n"
|
|
" Flip the faces direction.\n"
|
|
"\n"
|
|
" :arg face: Face to flip.\n"
|
|
" :type face: :class:`bmesh.types.BMFace`\n");
|
|
static PyObject *bpy_bm_utils_face_flip(PyObject * /*self*/, BPy_BMFace *value)
|
|
{
|
|
if (!BPy_BMFace_Check(value)) {
|
|
PyErr_Format(PyExc_TypeError,
|
|
"face_flip(face): BMFace expected, not '%.200s'",
|
|
Py_TYPE(value)->tp_name);
|
|
return nullptr;
|
|
}
|
|
|
|
BPY_BM_CHECK_OBJ(value);
|
|
|
|
BM_face_normal_flip(value->bm, value->f);
|
|
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
PyDoc_STRVAR(bpy_bm_utils_loop_separate_doc,
|
|
".. method:: loop_separate(loop)\n"
|
|
"\n"
|
|
" Rip a vertex in a face away and add a new vertex.\n"
|
|
"\n"
|
|
" :arg loop: The loop to separate.\n"
|
|
" :type loop: :class:`bmesh.types.BMLoop`\n"
|
|
" :return vert: The newly created vertex or None on failure.\n"
|
|
" :rtype vert: :class:`bmesh.types.BMVert`\n");
|
|
static PyObject *bpy_bm_utils_loop_separate(PyObject * /*self*/, BPy_BMLoop *value)
|
|
{
|
|
BMesh *bm;
|
|
BMLoop *l;
|
|
BMVert *v_old, *v_new;
|
|
|
|
if (!BPy_BMLoop_Check(value)) {
|
|
PyErr_Format(PyExc_TypeError,
|
|
"loop_separate(loop): BMLoop expected, not '%.200s'",
|
|
Py_TYPE(value)->tp_name);
|
|
return nullptr;
|
|
}
|
|
|
|
BPY_BM_CHECK_OBJ(value);
|
|
|
|
bm = value->bm;
|
|
l = value->l;
|
|
|
|
v_old = l->v;
|
|
v_new = BM_face_loop_separate(bm, l);
|
|
|
|
if (v_new != v_old) {
|
|
return BPy_BMVert_CreatePyObject(bm, v_new);
|
|
}
|
|
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
#if (defined(__GNUC__) && !defined(__clang__))
|
|
# pragma GCC diagnostic push
|
|
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
|
#endif
|
|
|
|
static PyMethodDef BPy_BM_utils_methods[] = {
|
|
{"vert_collapse_edge",
|
|
(PyCFunction)bpy_bm_utils_vert_collapse_edge,
|
|
METH_VARARGS,
|
|
bpy_bm_utils_vert_collapse_edge_doc},
|
|
{"vert_collapse_faces",
|
|
(PyCFunction)bpy_bm_utils_vert_collapse_faces,
|
|
METH_VARARGS,
|
|
bpy_bm_utils_vert_collapse_faces_doc},
|
|
{"vert_dissolve",
|
|
(PyCFunction)bpy_bm_utils_vert_dissolve,
|
|
METH_VARARGS,
|
|
bpy_bm_utils_vert_dissolve_doc}, /* could use METH_O */
|
|
{"vert_splice",
|
|
(PyCFunction)bpy_bm_utils_vert_splice,
|
|
METH_VARARGS,
|
|
bpy_bm_utils_vert_splice_doc},
|
|
{"vert_separate",
|
|
(PyCFunction)bpy_bm_utils_vert_separate,
|
|
METH_VARARGS,
|
|
bpy_bm_utils_vert_separate_doc},
|
|
{"edge_split",
|
|
(PyCFunction)bpy_bm_utils_edge_split,
|
|
METH_VARARGS,
|
|
bpy_bm_utils_edge_split_doc},
|
|
{"edge_rotate",
|
|
(PyCFunction)bpy_bm_utils_edge_rotate,
|
|
METH_VARARGS,
|
|
bpy_bm_utils_edge_rotate_doc},
|
|
{"face_split",
|
|
(PyCFunction)bpy_bm_utils_face_split,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
bpy_bm_utils_face_split_doc},
|
|
{"face_split_edgenet",
|
|
(PyCFunction)bpy_bm_utils_face_split_edgenet,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
bpy_bm_utils_face_split_edgenet_doc},
|
|
{"face_join", (PyCFunction)bpy_bm_utils_face_join, METH_VARARGS, bpy_bm_utils_face_join_doc},
|
|
{"face_vert_separate",
|
|
(PyCFunction)bpy_bm_utils_face_vert_separate,
|
|
METH_VARARGS,
|
|
bpy_bm_utils_face_vert_separate_doc},
|
|
{"face_flip", (PyCFunction)bpy_bm_utils_face_flip, METH_O, bpy_bm_utils_face_flip_doc},
|
|
{"loop_separate",
|
|
(PyCFunction)bpy_bm_utils_loop_separate,
|
|
METH_O,
|
|
bpy_bm_utils_loop_separate_doc},
|
|
{nullptr, nullptr, 0, nullptr},
|
|
};
|
|
|
|
#if (defined(__GNUC__) && !defined(__clang__))
|
|
# pragma GCC diagnostic pop
|
|
#endif
|
|
|
|
PyDoc_STRVAR(BPy_BM_utils_doc, "This module provides access to blenders bmesh data structures.");
|
|
static PyModuleDef BPy_BM_utils_module_def = {
|
|
/*m_base*/ PyModuleDef_HEAD_INIT,
|
|
/*m_name*/ "bmesh.utils",
|
|
/*m_doc*/ BPy_BM_utils_doc,
|
|
/*m_size*/ 0,
|
|
/*m_methods*/ BPy_BM_utils_methods,
|
|
/*m_slots*/ nullptr,
|
|
/*m_traverse*/ nullptr,
|
|
/*m_clear*/ nullptr,
|
|
/*m_free*/ nullptr,
|
|
};
|
|
|
|
PyObject *BPyInit_bmesh_utils()
|
|
{
|
|
PyObject *submodule;
|
|
|
|
submodule = PyModule_Create(&BPy_BM_utils_module_def);
|
|
|
|
return submodule;
|
|
}
|